diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index 2362f04..cb94ebd 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -38,12 +38,25 @@ use crate::{ window_map::{Kind as SurfaceKind, WindowMap}, }; +#[cfg(feature = "xwayland")] +use crate::xwayland::X11SurfaceRole; + +// The xwayland feature only adds a X11Surface role, but the macro does not support #[cfg] +#[cfg(not(feature = "xwayland"))] define_roles!(Roles => [ XdgSurface, XdgSurfaceRole ] [ ShellSurface, ShellSurfaceRole] [ DnDIcon, DnDIconRole ] [ CursorImage, CursorImageRole ] ); +#[cfg(feature = "xwayland")] +define_roles!(Roles => + [ XdgSurface, XdgSurfaceRole ] + [ ShellSurface, ShellSurfaceRole] + [ X11Surface, X11SurfaceRole ] + [ DnDIcon, DnDIconRole ] + [ CursorImage, CursorImageRole ] +); pub type MyWindowMap = WindowMap; @@ -219,6 +232,10 @@ impl PointerGrab for ResizeSurfaceGrab { (self.last_window_size.0 as u32, self.last_window_size.1 as u32), self.edges.into(), ), + #[cfg(feature = "xwayland")] + SurfaceKind::X11(_) => { + // TODO: What to do here? Send the update via X11? + } } } diff --git a/anvil/src/state.rs b/anvil/src/state.rs index 1bffee3..e703e4e 100644 --- a/anvil/src/state.rs +++ b/anvil/src/state.rs @@ -161,7 +161,12 @@ impl AnvilState { #[cfg(feature = "xwayland")] let _xwayland = { - let xwm = XWm::new(handle.clone(), log.clone()); + let xwm = XWm::new( + handle.clone(), + shell_handles.token, + shell_handles.window_map.clone(), + log.clone(), + ); XWayland::init(xwm, handle.clone(), display.clone(), &mut (), log.clone()).unwrap() }; diff --git a/anvil/src/window_map.rs b/anvil/src/window_map.rs index 54eb33f..2eec66f 100644 --- a/anvil/src/window_map.rs +++ b/anvil/src/window_map.rs @@ -13,10 +13,14 @@ use smithay::{ }; use crate::shell::SurfaceData; +#[cfg(feature = "xwayland")] +use crate::xwayland::X11Surface; pub enum Kind { Xdg(ToplevelSurface), Wl(ShellSurface), + #[cfg(feature = "xwayland")] + X11(X11Surface), } // We implement Clone manually because #[derive(..)] would require R: Clone. @@ -25,6 +29,8 @@ impl Clone for Kind { match self { Kind::Xdg(xdg) => Kind::Xdg(xdg.clone()), Kind::Wl(wl) => Kind::Wl(wl.clone()), + #[cfg(feature = "xwayland")] + Kind::X11(x11) => Kind::X11(x11.clone()), } } } @@ -37,12 +43,16 @@ where match *self { Kind::Xdg(ref t) => t.alive(), Kind::Wl(ref t) => t.alive(), + #[cfg(feature = "xwayland")] + Kind::X11(ref t) => t.alive(), } } pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> { match *self { Kind::Xdg(ref t) => t.get_surface(), Kind::Wl(ref t) => t.get_surface(), + #[cfg(feature = "xwayland")] + Kind::X11(ref t) => t.get_surface(), } } @@ -51,6 +61,8 @@ where match (self, other) { (Kind::Xdg(a), Kind::Xdg(b)) => a.equals(b), (Kind::Wl(a), Kind::Wl(b)) => a.equals(b), + #[cfg(feature = "xwayland")] + (Kind::X11(a), Kind::X11(b)) => a.equals(b), _ => false, } } diff --git a/anvil/src/xwayland/mod.rs b/anvil/src/xwayland/mod.rs index efb2e33..a082e4f 100644 --- a/anvil/src/xwayland/mod.rs +++ b/anvil/src/xwayland/mod.rs @@ -5,6 +5,7 @@ use smithay::{ calloop::LoopHandle, wayland_server::{protocol::wl_surface::WlSurface, Client}, }, + wayland::compositor::CompositorToken, xwayland::XWindowManager, }; @@ -22,7 +23,11 @@ use x11rb::{ rust_connection::{DefaultStream, RustConnection}, }; -use crate::AnvilState; +use crate::{ + shell::{MyWindowMap, Roles}, + window_map::Kind, + AnvilState, +}; use x11rb_event_source::X11Source; @@ -32,18 +37,31 @@ mod x11rb_event_source; /// After XWayland was started, the actual state is kept in `X11State`. pub struct XWm { handle: LoopHandle, + token: CompositorToken, + window_map: Rc>, log: slog::Logger, } impl XWm { - pub fn new(handle: LoopHandle, log: slog::Logger) -> Self { - Self { handle, log } + pub fn new( + handle: LoopHandle, + token: CompositorToken, + window_map: Rc>, + log: slog::Logger, + ) -> Self { + Self { + handle, + token, + window_map, + log, + } } } impl XWindowManager for XWm { fn xwayland_ready(&mut self, connection: UnixStream, client: Client) { - let (wm, source) = X11State::start_wm(connection, self.log.clone()).unwrap(); + let (wm, source) = + X11State::start_wm(connection, self.token, self.window_map.clone(), self.log.clone()).unwrap(); let wm = Rc::new(RefCell::new(wm)); client.data_map().insert_if_missing(|| Rc::clone(&wm)); self.handle @@ -73,10 +91,17 @@ struct X11State { atoms: Atoms, log: slog::Logger, unpaired_surfaces: HashMap, + token: CompositorToken, + window_map: Rc>, } impl X11State { - fn start_wm(connection: UnixStream, log: slog::Logger) -> Result<(Self, X11Source), Box> { + fn start_wm( + connection: UnixStream, + token: CompositorToken, + window_map: Rc>, + log: slog::Logger, + ) -> Result<(Self, X11Source), Box> { // Create an X11 connection. XWayland only uses screen 0. let screen = 0; let stream = DefaultStream::from_unix_stream(connection)?; @@ -119,6 +144,8 @@ impl X11State { conn: Rc::clone(&conn), atoms, unpaired_surfaces: Default::default(), + token, + window_map, log, }; @@ -187,6 +214,17 @@ impl X11State { fn new_window(&mut self, window: Window, surface: WlSurface) { debug!(self.log, "Matched X11 surface {:x?} to {:x?}", window, surface); + + if self.token.give_role_with(&surface, X11SurfaceRole).is_err() { + // It makes no sense to post a protocol error here since that would only kill Xwayland + error!(self.log, "Surface {:x?} already has a role?!", surface); + return; + } + + let x11surface = X11Surface { surface }; + self.window_map + .borrow_mut() + .insert(Kind::X11(x11surface), (0, 0)); } } @@ -204,3 +242,28 @@ pub fn commit_hook(surface: &WlSurface) { } } } + +pub struct X11SurfaceRole; + +#[derive(Clone)] +pub struct X11Surface { + surface: WlSurface, +} + +impl X11Surface { + pub fn alive(&self) -> bool { + self.surface.as_ref().is_alive() + } + + pub fn equals(&self, other: &Self) -> bool { + self.alive() && other.alive() && self.surface.as_ref().equals(&other.surface.as_ref()) + } + + pub fn get_surface(&self) -> Option<&WlSurface> { + if self.alive() { + Some(&self.surface) + } else { + None + } + } +}