From 734b541ecdd1a35d46c17e8b4013fe16d4130a6c Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Mon, 4 Jan 2021 09:18:35 +0100 Subject: [PATCH] Map between X11 Windows and WlSurfaces Xwayland gives us a mapping between X11 window and WlSurface IDs via special WL_SURFACE_ID messages. This commit uses these messages to find the corresponding WlSurface. For this, the new client.get_resource API from wayland-server is needed. Signed-off-by: Uli Schlachter --- Cargo.toml | 2 +- anvil/src/shell.rs | 3 +++ anvil/src/xwayland/mod.rs | 53 +++++++++++++++++++++++++++++++++------ 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 223d6b0..6fe012b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ udev = { version = "0.5", optional = true } wayland-commons = { version = "0.28", optional = true } wayland-egl = { version = "0.28", optional = true } wayland-protocols = { version = "0.28", features = ["unstable_protocols", "server"], optional = true } -wayland-server = { version = "0.28", optional = true } +wayland-server = { version = "0.28.3", optional = true } wayland-sys = { version = "0.28", optional = true } winit = { version = "0.23.0", optional = true } xkbcommon = "0.4.0" diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs index f3e9d8f..2362f04 100644 --- a/anvil/src/shell.rs +++ b/anvil/src/shell.rs @@ -768,6 +768,9 @@ fn surface_commit( buffer_utils: &BufferUtils, window_map: &RefCell, ) { + #[cfg(feature = "xwayland")] + super::xwayland::commit_hook(surface); + let mut geometry = None; let mut min_size = (0, 0); let mut max_size = (0, 0); diff --git a/anvil/src/xwayland/mod.rs b/anvil/src/xwayland/mod.rs index 3cc9b2b..efb2e33 100644 --- a/anvil/src/xwayland/mod.rs +++ b/anvil/src/xwayland/mod.rs @@ -1,9 +1,9 @@ -use std::{convert::TryFrom, os::unix::net::UnixStream, rc::Rc}; +use std::{cell::RefCell, collections::HashMap, convert::TryFrom, os::unix::net::UnixStream, rc::Rc}; use smithay::{ reexports::{ calloop::LoopHandle, - wayland_server::Client, + wayland_server::{protocol::wl_surface::WlSurface, Client}, }, xwayland::XWindowManager, }; @@ -15,7 +15,7 @@ use x11rb::{ composite::{ConnectionExt as _, Redirect}, xproto::{ ChangeWindowAttributesAux, ConfigWindow, ConfigureWindowAux, ConnectionExt as _, EventMask, - WindowClass, + Window, WindowClass, }, Event, }, @@ -42,12 +42,15 @@ impl XWm { } impl XWindowManager for XWm { - fn xwayland_ready(&mut self, connection: UnixStream, _client: Client) { - let (mut wm, source) = X11State::start_wm(connection, self.log.clone()).unwrap(); + fn xwayland_ready(&mut self, connection: UnixStream, client: Client) { + let (wm, source) = X11State::start_wm(connection, self.log.clone()).unwrap(); + let wm = Rc::new(RefCell::new(wm)); + client.data_map().insert_if_missing(|| Rc::clone(&wm)); self.handle .insert_source(source, move |events, _, _| { + let mut wm = wm.borrow_mut(); for event in events.into_iter() { - wm.handle_event(event)?; + wm.handle_event(event, &client)?; } Ok(()) }) @@ -69,6 +72,7 @@ struct X11State { conn: Rc, atoms: Atoms, log: slog::Logger, + unpaired_surfaces: HashMap, } impl X11State { @@ -114,13 +118,14 @@ impl X11State { let wm = Self { conn: Rc::clone(&conn), atoms, + unpaired_surfaces: Default::default(), log, }; Ok((wm, X11Source::new(conn))) } - fn handle_event(&mut self, event: Event) -> Result<(), ReplyOrIdError> { + fn handle_event(&mut self, event: Event, client: &Client) -> Result<(), ReplyOrIdError> { debug!(self.log, "X11: Got event {:?}", event); match event { Event::ConfigureRequest(r) => { @@ -155,15 +160,47 @@ impl X11State { } Event::ClientMessage(msg) => { if msg.type_ == self.atoms.WL_SURFACE_ID { + // We get a WL_SURFACE_ID message when Xwayland creates a WlSurface for a + // window. Both the creation of the surface and this client message happen at + // roughly the same time and are sent over different sockets (X11 socket and + // wayland socket). Thus, we could receive these two in any order. Hence, it + // can happen that we get None below when X11 was faster than Wayland. + let id = msg.data.as_data32()[0]; + let surface = client.get_resource::(id); info!( self.log, - "X11 surface {:x?} corresponds to WlSurface {:x}", msg.window, id, + "X11 surface {:x?} corresponds to WlSurface {:x} = {:?}", msg.window, id, surface, ); + match surface { + None => { + self.unpaired_surfaces.insert(id, msg.window); + } + Some(surface) => self.new_window(msg.window, surface), + } } } _ => {} } Ok(()) } + + fn new_window(&mut self, window: Window, surface: WlSurface) { + debug!(self.log, "Matched X11 surface {:x?} to {:x?}", window, surface); + } +} + +// Called when a WlSurface commits. +pub fn commit_hook(surface: &WlSurface) { + // Is this the Xwayland client? + if let Some(client) = surface.as_ref().client() { + if let Some(x11) = client.data_map().get::>>() { + let mut inner = x11.borrow_mut(); + // Is the surface among the unpaired surfaces (see comment next to WL_SURFACE_ID + // handling above) + if let Some(window) = inner.unpaired_surfaces.remove(&surface.as_ref().id()) { + inner.new_window(window, surface.clone()); + } + } + } }