Support xdg decorations
This commit is contained in:
parent
b2613aec05
commit
0559ace758
5 changed files with 328 additions and 4 deletions
|
|
@ -19,6 +19,8 @@ use wayland_protocols::wp::relative_pointer::zv1::client::{
|
||||||
zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1,
|
zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1,
|
||||||
zwp_relative_pointer_v1::ZwpRelativePointerV1,
|
zwp_relative_pointer_v1::ZwpRelativePointerV1,
|
||||||
};
|
};
|
||||||
|
use wayland_protocols::xdg::decoration::zv1::client::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1;
|
||||||
|
use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1;
|
||||||
use wayland_protocols::{
|
use wayland_protocols::{
|
||||||
wp::{
|
wp::{
|
||||||
linux_dmabuf::zv1::client::{
|
linux_dmabuf::zv1::client::{
|
||||||
|
|
@ -140,6 +142,8 @@ delegate_noop!(Globals: ZxdgOutputManagerV1);
|
||||||
delegate_noop!(Globals: ZwpPointerConstraintsV1);
|
delegate_noop!(Globals: ZwpPointerConstraintsV1);
|
||||||
delegate_noop!(Globals: ZwpTabletManagerV2);
|
delegate_noop!(Globals: ZwpTabletManagerV2);
|
||||||
delegate_noop!(Globals: XdgActivationV1);
|
delegate_noop!(Globals: XdgActivationV1);
|
||||||
|
delegate_noop!(Globals: ZxdgDecorationManagerV1);
|
||||||
|
delegate_noop!(Globals: ignore ZxdgToplevelDecorationV1);
|
||||||
|
|
||||||
impl Dispatch<WlRegistry, GlobalListContents> for Globals {
|
impl Dispatch<WlRegistry, GlobalListContents> for Globals {
|
||||||
fn event(
|
fn event(
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ mod tests;
|
||||||
|
|
||||||
use self::event::*;
|
use self::event::*;
|
||||||
use crate::clientside::*;
|
use crate::clientside::*;
|
||||||
use crate::xstate::{WindowDims, WmHints, WmName, WmNormalHints};
|
use crate::xstate::{Decorations, WindowDims, WmHints, WmName, WmNormalHints};
|
||||||
use crate::{X11Selection, XConnection};
|
use crate::{X11Selection, XConnection};
|
||||||
use log::{debug, warn};
|
use log::{debug, warn};
|
||||||
use rustix::event::{poll, PollFd, PollFlags};
|
use rustix::event::{poll, PollFd, PollFlags};
|
||||||
|
|
@ -22,6 +22,10 @@ use std::os::fd::{AsFd, BorrowedFd};
|
||||||
use std::os::unix::net::UnixStream;
|
use std::os::unix::net::UnixStream;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
use wayland_client::{globals::Global, protocol as client, Proxy};
|
use wayland_client::{globals::Global, protocol as client, Proxy};
|
||||||
|
use wayland_protocols::xdg::decoration::zv1::client::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1;
|
||||||
|
use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::{
|
||||||
|
self, ZxdgToplevelDecorationV1,
|
||||||
|
};
|
||||||
use wayland_protocols::{
|
use wayland_protocols::{
|
||||||
wp::{
|
wp::{
|
||||||
linux_dmabuf::zv1::{client as c_dmabuf, server as s_dmabuf},
|
linux_dmabuf::zv1::{client as c_dmabuf, server as s_dmabuf},
|
||||||
|
|
@ -87,6 +91,7 @@ pub struct WindowAttributes {
|
||||||
pub title: Option<WmName>,
|
pub title: Option<WmName>,
|
||||||
pub class: Option<String>,
|
pub class: Option<String>,
|
||||||
pub group: Option<x::Window>,
|
pub group: Option<x::Window>,
|
||||||
|
pub decorations: Option<Decorations>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
|
||||||
|
|
@ -244,6 +249,7 @@ struct ToplevelData {
|
||||||
toplevel: XdgToplevel,
|
toplevel: XdgToplevel,
|
||||||
xdg: XdgSurfaceData,
|
xdg: XdgSurfaceData,
|
||||||
fullscreen: bool,
|
fullscreen: bool,
|
||||||
|
decoration: Option<ZxdgToplevelDecorationV1>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -509,6 +515,7 @@ pub struct ServerState<C: XConnection> {
|
||||||
activation_state: Option<ActivationState>,
|
activation_state: Option<ActivationState>,
|
||||||
global_output_offset: GlobalOutputOffset,
|
global_output_offset: GlobalOutputOffset,
|
||||||
global_offset_updated: bool,
|
global_offset_updated: bool,
|
||||||
|
decoration_manager: Option<ZxdgDecorationManagerV1>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: XConnection> ServerState<C> {
|
impl<C: XConnection> ServerState<C> {
|
||||||
|
|
@ -542,6 +549,14 @@ impl<C: XConnection> ServerState<C> {
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
|
let decoration_manager = clientside
|
||||||
|
.global_list
|
||||||
|
.bind::<ZxdgDecorationManagerV1, _, _>(&qh, 1..=1, ())
|
||||||
|
.inspect_err(|e| {
|
||||||
|
warn!("Could not bind xdg decoration ({e:?}). Windows might not have decorations.")
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
dh.create_global::<Self, XwaylandShellV1, _>(1, ());
|
dh.create_global::<Self, XwaylandShellV1, _>(1, ());
|
||||||
clientside
|
clientside
|
||||||
.global_list
|
.global_list
|
||||||
|
|
@ -578,6 +593,7 @@ impl<C: XConnection> ServerState<C> {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
global_offset_updated: false,
|
global_offset_updated: false,
|
||||||
|
decoration_manager,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -717,6 +733,35 @@ impl<C: XConnection> ServerState<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_win_decorations(&mut self, window: x::Window, decorations: Decorations) {
|
||||||
|
if self.decoration_manager.is_none() {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(win) = self.windows.get_mut(&window) else {
|
||||||
|
debug!("not setting decorations for unknown window {window:?}");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if win.attrs.decorations != Some(decorations) {
|
||||||
|
debug!("setting {window:?} decorations {decorations:?}");
|
||||||
|
if let Some(key) = win.surface_key {
|
||||||
|
if let Some(object) = self.objects.get(key) {
|
||||||
|
let surface: &SurfaceData = object.as_ref();
|
||||||
|
if let Some(SurfaceRole::Toplevel(Some(data))) = &surface.role {
|
||||||
|
data.decoration
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.set_mode(decorations.into());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn!("could not set decorations on {window:?}: stale surface")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
win.attrs.decorations = Some(decorations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_window_serial(&mut self, window: x::Window, serial: [u32; 2]) {
|
pub fn set_window_serial(&mut self, window: x::Window, serial: [u32; 2]) {
|
||||||
let Some(win) = self.windows.get_mut(&window) else {
|
let Some(win) = self.windows.get_mut(&window) else {
|
||||||
warn!("Tried to set serial for unknown window {window:?}");
|
warn!("Tried to set serial for unknown window {window:?}");
|
||||||
|
|
@ -1224,6 +1269,17 @@ impl<C: XConnection> ServerState<C> {
|
||||||
toplevel.set_fullscreen(None);
|
toplevel.set_fullscreen(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let decoration = self.decoration_manager.as_ref().map(|decoration_manager| {
|
||||||
|
let decoration = decoration_manager.get_toplevel_decoration(&toplevel, &self.qh, ());
|
||||||
|
decoration.set_mode(
|
||||||
|
window
|
||||||
|
.attrs
|
||||||
|
.decorations
|
||||||
|
.map_or(zxdg_toplevel_decoration_v1::Mode::ServerSide, From::from),
|
||||||
|
);
|
||||||
|
decoration
|
||||||
|
});
|
||||||
|
|
||||||
let surface: &SurfaceData = self.objects[surface_key].as_ref();
|
let surface: &SurfaceData = self.objects[surface_key].as_ref();
|
||||||
if let (Some(activation_state), Some(token)) = (
|
if let (Some(activation_state), Some(token)) = (
|
||||||
self.activation_state.as_ref(),
|
self.activation_state.as_ref(),
|
||||||
|
|
@ -1240,6 +1296,7 @@ impl<C: XConnection> ServerState<C> {
|
||||||
},
|
},
|
||||||
toplevel,
|
toplevel,
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
|
decoration,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
mod selection;
|
mod selection;
|
||||||
use selection::{Selection, SelectionData};
|
use selection::{Selection, SelectionData};
|
||||||
|
use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1;
|
||||||
|
|
||||||
use crate::{server::WindowAttributes, XConnection};
|
use crate::{server::WindowAttributes, XConnection};
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
|
@ -246,7 +247,11 @@ impl XState {
|
||||||
|
|
||||||
self.set_root_property(self.atoms.wm_check, x::ATOM_WINDOW, &[self.wm_window]);
|
self.set_root_property(self.atoms.wm_check, x::ATOM_WINDOW, &[self.wm_window]);
|
||||||
self.set_root_property(self.atoms.active_win, x::ATOM_WINDOW, &[x::Window::none()]);
|
self.set_root_property(self.atoms.active_win, x::ATOM_WINDOW, &[x::Window::none()]);
|
||||||
self.set_root_property(self.atoms.supported, x::ATOM_ATOM, &[self.atoms.active_win]);
|
self.set_root_property(
|
||||||
|
self.atoms.supported,
|
||||||
|
x::ATOM_ATOM,
|
||||||
|
&[self.atoms.active_win, self.atoms.motif_wm_hints],
|
||||||
|
);
|
||||||
|
|
||||||
self.connection
|
self.connection
|
||||||
.send_and_check_request(&x::ChangeProperty {
|
.send_and_check_request(&x::ChangeProperty {
|
||||||
|
|
@ -491,6 +496,7 @@ impl XState {
|
||||||
let class = self.get_wm_class(window);
|
let class = self.get_wm_class(window);
|
||||||
let wm_hints = self.get_wm_hints(window);
|
let wm_hints = self.get_wm_hints(window);
|
||||||
let size_hints = self.get_wm_size_hints(window);
|
let size_hints = self.get_wm_size_hints(window);
|
||||||
|
let motif_wm_hints = self.get_motif_wm_hints(window);
|
||||||
|
|
||||||
let geometry = self.connection.wait_for_reply(geometry)?;
|
let geometry = self.connection.wait_for_reply(geometry)?;
|
||||||
debug!("{window:?} geometry: {geometry:?}");
|
debug!("{window:?} geometry: {geometry:?}");
|
||||||
|
|
@ -503,6 +509,7 @@ impl XState {
|
||||||
let class = class.resolve()?;
|
let class = class.resolve()?;
|
||||||
let wm_hints = wm_hints.resolve()?;
|
let wm_hints = wm_hints.resolve()?;
|
||||||
let size_hints = size_hints.resolve()?;
|
let size_hints = size_hints.resolve()?;
|
||||||
|
let motif_wm_hints = motif_wm_hints.resolve()?;
|
||||||
|
|
||||||
Ok(WindowAttributes {
|
Ok(WindowAttributes {
|
||||||
override_redirect: attrs.override_redirect(),
|
override_redirect: attrs.override_redirect(),
|
||||||
|
|
@ -517,6 +524,7 @@ impl XState {
|
||||||
class,
|
class,
|
||||||
group: wm_hints.and_then(|h| h.window_group),
|
group: wm_hints.and_then(|h| h.window_group),
|
||||||
size_hints,
|
size_hints,
|
||||||
|
decorations: motif_wm_hints.and_then(|h| h.decorations),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -535,6 +543,9 @@ impl XState {
|
||||||
if let Some(hints) = attrs.size_hints {
|
if let Some(hints) = attrs.size_hints {
|
||||||
server_state.set_size_hints(window, hints);
|
server_state.set_size_hints(window, hints);
|
||||||
}
|
}
|
||||||
|
if let Some(decorations) = attrs.decorations {
|
||||||
|
server_state.set_win_decorations(window, decorations);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property_cookie(
|
fn get_property_cookie(
|
||||||
|
|
@ -660,6 +671,28 @@ impl XState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_motif_wm_hints(
|
||||||
|
&self,
|
||||||
|
window: x::Window,
|
||||||
|
) -> PropertyCookieWrapper<impl PropertyResolver<Output = MotifWmHints>> {
|
||||||
|
let cookie = self.get_property_cookie(
|
||||||
|
window,
|
||||||
|
self.atoms.motif_wm_hints,
|
||||||
|
self.atoms.motif_wm_hints,
|
||||||
|
5,
|
||||||
|
);
|
||||||
|
let resolver = |reply: x::GetPropertyReply| {
|
||||||
|
let data: &[u32] = reply.value();
|
||||||
|
MotifWmHints::from(data)
|
||||||
|
};
|
||||||
|
|
||||||
|
PropertyCookieWrapper {
|
||||||
|
connection: &self.connection,
|
||||||
|
cookie,
|
||||||
|
resolver,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_pid(&self, window: x::Window) -> Option<u32> {
|
fn get_pid(&self, window: x::Window) -> Option<u32> {
|
||||||
let Some(pid) = self
|
let Some(pid) = self
|
||||||
.connection
|
.connection
|
||||||
|
|
@ -718,6 +751,13 @@ impl XState {
|
||||||
unwrap_or_skip_bad_window!(self.get_wm_class(window).resolve()).unwrap();
|
unwrap_or_skip_bad_window!(self.get_wm_class(window).resolve()).unwrap();
|
||||||
server_state.set_win_class(window, class);
|
server_state.set_win_class(window, class);
|
||||||
}
|
}
|
||||||
|
x if x == self.atoms.motif_wm_hints => {
|
||||||
|
let motif_hints =
|
||||||
|
unwrap_or_skip_bad_window!(self.get_motif_wm_hints(window).resolve()).unwrap();
|
||||||
|
if let Some(decorations) = motif_hints.decorations {
|
||||||
|
server_state.set_win_decorations(window, decorations);
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if !self.handle_selection_property_change(&event)
|
if !self.handle_selection_property_change(&event)
|
||||||
&& log::log_enabled!(log::Level::Debug)
|
&& log::log_enabled!(log::Level::Debug)
|
||||||
|
|
@ -750,6 +790,7 @@ xcb::atoms_struct! {
|
||||||
active_win => b"_NET_ACTIVE_WINDOW" only_if_exists = false,
|
active_win => b"_NET_ACTIVE_WINDOW" only_if_exists = false,
|
||||||
client_list => b"_NET_CLIENT_LIST" only_if_exists = false,
|
client_list => b"_NET_CLIENT_LIST" only_if_exists = false,
|
||||||
supported => b"_NET_SUPPORTED" only_if_exists = false,
|
supported => b"_NET_SUPPORTED" only_if_exists = false,
|
||||||
|
motif_wm_hints => b"_MOTIF_WM_HINTS" only_if_exists = false,
|
||||||
utf8_string => b"UTF8_STRING" only_if_exists = false,
|
utf8_string => b"UTF8_STRING" only_if_exists = false,
|
||||||
clipboard => b"CLIPBOARD" only_if_exists = false,
|
clipboard => b"CLIPBOARD" only_if_exists = false,
|
||||||
targets => b"TARGETS" only_if_exists = false,
|
targets => b"TARGETS" only_if_exists = false,
|
||||||
|
|
@ -795,6 +836,12 @@ bitflags! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct MotifWmHintsFlags: u32 {
|
||||||
|
const Decorations = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct WinSize {
|
pub struct WinSize {
|
||||||
pub width: i32,
|
pub width: i32,
|
||||||
|
|
@ -849,6 +896,52 @@ impl From<&[u32]> for WmHints {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum Decorations {
|
||||||
|
Client = 0,
|
||||||
|
Server = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u32> for Decorations {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: u32) -> Result<Self, ()> {
|
||||||
|
match value {
|
||||||
|
0 => Ok(Self::Client),
|
||||||
|
1 => Ok(Self::Server),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Decorations> for zxdg_toplevel_decoration_v1::Mode {
|
||||||
|
fn from(value: Decorations) -> Self {
|
||||||
|
match value {
|
||||||
|
Decorations::Client => zxdg_toplevel_decoration_v1::Mode::ClientSide,
|
||||||
|
Decorations::Server => zxdg_toplevel_decoration_v1::Mode::ServerSide,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, PartialEq, Eq)]
|
||||||
|
pub struct MotifWmHints {
|
||||||
|
pub decorations: Option<Decorations>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&[u32]> for MotifWmHints {
|
||||||
|
fn from(value: &[u32]) -> Self {
|
||||||
|
let mut ret = Self::default();
|
||||||
|
|
||||||
|
let flags = MotifWmHintsFlags::from_bits_truncate(value[0]);
|
||||||
|
|
||||||
|
if flags.contains(MotifWmHintsFlags::Decorations) {
|
||||||
|
ret.decorations = value[2].try_into().ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum SetState {
|
pub enum SetState {
|
||||||
Remove,
|
Remove,
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,9 @@ use std::sync::{
|
||||||
};
|
};
|
||||||
use std::thread::JoinHandle;
|
use std::thread::JoinHandle;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use wayland_protocols::xdg::shell::server::xdg_toplevel;
|
use wayland_protocols::xdg::{
|
||||||
|
decoration::zv1::server::zxdg_toplevel_decoration_v1, shell::server::xdg_toplevel,
|
||||||
|
};
|
||||||
use wayland_server::Resource;
|
use wayland_server::Resource;
|
||||||
use xcb::{x, Xid};
|
use xcb::{x, Xid};
|
||||||
use xwayland_satellite as xwls;
|
use xwayland_satellite as xwls;
|
||||||
|
|
@ -317,6 +319,7 @@ xcb::atoms_struct! {
|
||||||
multiple => b"MULTIPLE",
|
multiple => b"MULTIPLE",
|
||||||
wm_state => b"WM_STATE",
|
wm_state => b"WM_STATE",
|
||||||
wm_check => b"_NET_SUPPORTING_WM_CHECK",
|
wm_check => b"_NET_SUPPORTING_WM_CHECK",
|
||||||
|
motif_wm_hints => b"_MOTIF_WM_HINTS" only_if_exists = false,
|
||||||
mime1 => b"text/plain" only_if_exists = false,
|
mime1 => b"text/plain" only_if_exists = false,
|
||||||
mime2 => b"blah/blah" only_if_exists = false,
|
mime2 => b"blah/blah" only_if_exists = false,
|
||||||
incr => b"INCR",
|
incr => b"INCR",
|
||||||
|
|
@ -1519,3 +1522,57 @@ fn negative_output_coordinates() {
|
||||||
assert_eq!(ptr_reply.win_x(), 30);
|
assert_eq!(ptr_reply.win_x(), 30);
|
||||||
assert_eq!(ptr_reply.win_y(), 40);
|
assert_eq!(ptr_reply.win_y(), 40);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn xdg_decorations() {
|
||||||
|
let mut f = Fixture::new();
|
||||||
|
let mut connection = Connection::new(&f.display);
|
||||||
|
|
||||||
|
let window = connection.new_window(connection.root, 0, 0, 20, 20, false);
|
||||||
|
let surface = f.map_as_toplevel(&mut connection, window);
|
||||||
|
let data = f.testwl.get_surface_data(surface).unwrap();
|
||||||
|
// The default decoration mode in x11 is SDD
|
||||||
|
assert_eq!(
|
||||||
|
data.toplevel()
|
||||||
|
.decoration
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|(_, decoration)| *decoration),
|
||||||
|
Some(zxdg_toplevel_decoration_v1::Mode::ServerSide)
|
||||||
|
);
|
||||||
|
|
||||||
|
// CSD
|
||||||
|
connection.set_property(
|
||||||
|
window,
|
||||||
|
connection.atoms.motif_wm_hints,
|
||||||
|
connection.atoms.motif_wm_hints,
|
||||||
|
&[2u32, 0, 0, 0, 0],
|
||||||
|
);
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(1));
|
||||||
|
f.testwl.dispatch();
|
||||||
|
let data = f.testwl.get_surface_data(surface).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
data.toplevel()
|
||||||
|
.decoration
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|(_, decoration)| *decoration),
|
||||||
|
Some(zxdg_toplevel_decoration_v1::Mode::ClientSide)
|
||||||
|
);
|
||||||
|
|
||||||
|
// SSD
|
||||||
|
connection.set_property(
|
||||||
|
window,
|
||||||
|
connection.atoms.motif_wm_hints,
|
||||||
|
connection.atoms.motif_wm_hints,
|
||||||
|
&[2u32, 0, 1, 0, 0],
|
||||||
|
);
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(1));
|
||||||
|
f.testwl.dispatch();
|
||||||
|
let data = f.testwl.get_surface_data(surface).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
data.toplevel()
|
||||||
|
.decoration
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|(_, decoration)| *decoration),
|
||||||
|
Some(zxdg_toplevel_decoration_v1::Mode::ServerSide)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,10 @@ use wayland_protocols::{
|
||||||
xdg_activation_token_v1::{self, XdgActivationTokenV1},
|
xdg_activation_token_v1::{self, XdgActivationTokenV1},
|
||||||
xdg_activation_v1::{self, XdgActivationV1},
|
xdg_activation_v1::{self, XdgActivationV1},
|
||||||
},
|
},
|
||||||
|
decoration::zv1::server::{
|
||||||
|
zxdg_decoration_manager_v1::{self, ZxdgDecorationManagerV1},
|
||||||
|
zxdg_toplevel_decoration_v1::{self, ZxdgToplevelDecorationV1},
|
||||||
|
},
|
||||||
shell::server::{
|
shell::server::{
|
||||||
xdg_popup::{self, XdgPopup},
|
xdg_popup::{self, XdgPopup},
|
||||||
xdg_positioner::{self, XdgPositioner},
|
xdg_positioner::{self, XdgPositioner},
|
||||||
|
|
@ -62,7 +66,7 @@ use wayland_server::{
|
||||||
wl_shm_pool::WlShmPool,
|
wl_shm_pool::WlShmPool,
|
||||||
wl_surface::WlSurface,
|
wl_surface::WlSurface,
|
||||||
},
|
},
|
||||||
Client, Dispatch, Display, DisplayHandle, GlobalDispatch, Resource,
|
Client, Dispatch, Display, DisplayHandle, GlobalDispatch, Resource, WEnum,
|
||||||
};
|
};
|
||||||
use wl_drm::server::wl_drm::WlDrm;
|
use wl_drm::server::wl_drm::WlDrm;
|
||||||
|
|
||||||
|
|
@ -123,6 +127,10 @@ pub struct Toplevel {
|
||||||
pub closed: bool,
|
pub closed: bool,
|
||||||
pub title: Option<String>,
|
pub title: Option<String>,
|
||||||
pub app_id: Option<String>,
|
pub app_id: Option<String>,
|
||||||
|
pub decoration: Option<(
|
||||||
|
ZxdgToplevelDecorationV1,
|
||||||
|
Option<zxdg_toplevel_decoration_v1::Mode>,
|
||||||
|
)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
|
@ -383,6 +391,7 @@ impl Server {
|
||||||
dh.create_global::<State, WlDataDeviceManager, _>(3, ());
|
dh.create_global::<State, WlDataDeviceManager, _>(3, ());
|
||||||
dh.create_global::<State, ZwpTabletManagerV2, _>(1, ());
|
dh.create_global::<State, ZwpTabletManagerV2, _>(1, ());
|
||||||
dh.create_global::<State, XdgActivationV1, _>(1, ());
|
dh.create_global::<State, XdgActivationV1, _>(1, ());
|
||||||
|
dh.create_global::<State, ZxdgDecorationManagerV1, _>(1, ());
|
||||||
global_noop!(ZwpLinuxDmabufV1);
|
global_noop!(ZwpLinuxDmabufV1);
|
||||||
global_noop!(ZwpRelativePointerManagerV1);
|
global_noop!(ZwpRelativePointerManagerV1);
|
||||||
global_noop!(WpViewporter);
|
global_noop!(WpViewporter);
|
||||||
|
|
@ -733,6 +742,7 @@ simple_global_dispatch!(WlCompositor);
|
||||||
simple_global_dispatch!(XdgWmBase);
|
simple_global_dispatch!(XdgWmBase);
|
||||||
simple_global_dispatch!(ZxdgOutputManagerV1);
|
simple_global_dispatch!(ZxdgOutputManagerV1);
|
||||||
simple_global_dispatch!(ZwpTabletManagerV2);
|
simple_global_dispatch!(ZwpTabletManagerV2);
|
||||||
|
simple_global_dispatch!(ZxdgDecorationManagerV1);
|
||||||
|
|
||||||
impl Dispatch<ZwpTabletManagerV2, ()> for State {
|
impl Dispatch<ZwpTabletManagerV2, ()> for State {
|
||||||
fn request(
|
fn request(
|
||||||
|
|
@ -1219,6 +1229,7 @@ impl Dispatch<XdgSurface, SurfaceId> for State {
|
||||||
closed: false,
|
closed: false,
|
||||||
title: None,
|
title: None,
|
||||||
app_id: None,
|
app_id: None,
|
||||||
|
decoration: None,
|
||||||
};
|
};
|
||||||
let data = state.surfaces.get_mut(surface_id).unwrap();
|
let data = state.surfaces.get_mut(surface_id).unwrap();
|
||||||
data.role = Some(SurfaceRole::Toplevel(t));
|
data.role = Some(SurfaceRole::Toplevel(t));
|
||||||
|
|
@ -1676,3 +1687,105 @@ impl Dispatch<XdgActivationTokenV1, Mutex<ActivationTokenData>> for State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Dispatch<ZxdgDecorationManagerV1, ()> for State {
|
||||||
|
fn request(
|
||||||
|
state: &mut Self,
|
||||||
|
_: &Client,
|
||||||
|
resource: &ZxdgDecorationManagerV1,
|
||||||
|
request: <ZxdgDecorationManagerV1 as Resource>::Request,
|
||||||
|
_: &(),
|
||||||
|
_: &DisplayHandle,
|
||||||
|
data_init: &mut wayland_server::DataInit<'_, Self>,
|
||||||
|
) {
|
||||||
|
match request {
|
||||||
|
zxdg_decoration_manager_v1::Request::GetToplevelDecoration { id, toplevel } => {
|
||||||
|
let surface_id = *toplevel.data::<SurfaceId>().unwrap();
|
||||||
|
let data = state.surfaces.get_mut(&surface_id).unwrap();
|
||||||
|
let Some(SurfaceRole::Toplevel(toplevel)) = &mut data.role else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
if toplevel.decoration.is_some() {
|
||||||
|
resource.post_error(
|
||||||
|
zxdg_toplevel_decoration_v1::Error::AlreadyConstructed,
|
||||||
|
"Toplevel already has an decoration object",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
toplevel.decoration = Some((data_init.init(id, surface_id), None));
|
||||||
|
}
|
||||||
|
zxdg_decoration_manager_v1::Request::Destroy => {}
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<ZxdgToplevelDecorationV1, SurfaceId> for State {
|
||||||
|
fn request(
|
||||||
|
state: &mut Self,
|
||||||
|
_: &Client,
|
||||||
|
resource: &ZxdgToplevelDecorationV1,
|
||||||
|
request: <ZxdgToplevelDecorationV1 as Resource>::Request,
|
||||||
|
surface_id: &SurfaceId,
|
||||||
|
_: &DisplayHandle,
|
||||||
|
_: &mut wayland_server::DataInit<'_, Self>,
|
||||||
|
) {
|
||||||
|
match request {
|
||||||
|
zxdg_toplevel_decoration_v1::Request::SetMode { mode } => {
|
||||||
|
let WEnum::Value(mode) = mode else {
|
||||||
|
resource.post_error(
|
||||||
|
zxdg_toplevel_decoration_v1::Error::InvalidMode,
|
||||||
|
"Invalid decoration mode",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Some(data) = state.surfaces.get_mut(surface_id) {
|
||||||
|
let Some(SurfaceRole::Toplevel(toplevel)) = &mut data.role else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
*toplevel
|
||||||
|
.decoration
|
||||||
|
.as_mut()
|
||||||
|
.map(|(_, decoration)| decoration)
|
||||||
|
.unwrap() = Some(mode);
|
||||||
|
} else {
|
||||||
|
resource.post_error(
|
||||||
|
zxdg_toplevel_decoration_v1::Error::Orphaned,
|
||||||
|
"Toplevel was destroyed",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zxdg_toplevel_decoration_v1::Request::UnsetMode => {
|
||||||
|
if let Some(data) = state.surfaces.get_mut(surface_id) {
|
||||||
|
let Some(SurfaceRole::Toplevel(toplevel)) = &mut data.role else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
*toplevel
|
||||||
|
.decoration
|
||||||
|
.as_mut()
|
||||||
|
.map(|(_, decoration)| decoration)
|
||||||
|
.unwrap() = None;
|
||||||
|
} else {
|
||||||
|
resource.post_error(
|
||||||
|
zxdg_toplevel_decoration_v1::Error::Orphaned,
|
||||||
|
"Toplevel was destroyed",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zxdg_toplevel_decoration_v1::Request::Destroy => {
|
||||||
|
if let Some(data) = state.surfaces.get_mut(surface_id) {
|
||||||
|
let Some(SurfaceRole::Toplevel(toplevel)) = &mut data.role else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
toplevel.decoration = None;
|
||||||
|
} else {
|
||||||
|
resource.post_error(
|
||||||
|
zxdg_toplevel_decoration_v1::Error::Orphaned,
|
||||||
|
"Toplevel was destroyed",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue