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 <psychon@znc.in>
This commit is contained in:
Uli Schlachter 2021-01-04 09:18:35 +01:00 committed by Victor Berger
parent a6c40a002b
commit 734b541ecd
3 changed files with 49 additions and 9 deletions

View File

@ -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"

View File

@ -768,6 +768,9 @@ fn surface_commit(
buffer_utils: &BufferUtils,
window_map: &RefCell<MyWindowMap>,
) {
#[cfg(feature = "xwayland")]
super::xwayland::commit_hook(surface);
let mut geometry = None;
let mut min_size = (0, 0);
let mut max_size = (0, 0);

View File

@ -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<RustConnection>,
atoms: Atoms,
log: slog::Logger,
unpaired_surfaces: HashMap<u32, Window>,
}
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::<WlSurface>(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::<Rc<RefCell<X11State>>>() {
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());
}
}
}
}