<NME> mod.rs
<BEF> use std::{convert::TryFrom, os::unix::net::UnixStream, rc::Rc};

use smithay::{
    reexports::{
        calloop::LoopHandle,
        wayland_server::Client,
    },
    xwayland::XWindowManager,
};
use x11rb::{
    connection::Connection as _,
    errors::ReplyOrIdError,
    protocol::{
        composite::{ConnectionExt as _, Redirect},
        composite::{ConnectionExt as _, Redirect},
        xproto::{
            ChangeWindowAttributesAux, ConfigWindow, ConfigureWindowAux, ConnectionExt as _, EventMask,
            WindowClass,
        },
        Event,
    },
};

impl<BackendData: 'static> AnvilState<BackendData> {
    pub fn start_xwayland(&mut self) {
        if let Err(e) = self.xwayland.start(self.handle.clone()) {
            error!(self.log, "Failed to start XWayland: {}", e);
        }
    }

    pub fn xwayland_ready(&mut self, connection: UnixStream, client: Client) {
        let (wm, source) = X11State::start_wm(connection, client, self.log.clone()).unwrap();
        self.x11_state = Some(wm);
        let log = self.log.clone();
        self.handle
            .insert_source(source, move |event, _, data| {
                if let Some(x11) = data.state.x11_state.as_mut() {
                    match x11.handle_event(event, &data.display.handle(), &mut data.state.space) {
                        Ok(()) => {}
                        Err(err) => error!(log, "Error while handling X11 event: {}", err),
                    }
}

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();
        self.handle
            .insert_source(source, move |events, _, _| {
                for event in events.into_iter() {
                    wm.handle_event(event)?;
                }
                Ok(())
            })
        _ANVIL_CLOSE_CONNECTION,
    }
}

/// The actual runtime state of the XWayland integration.
#[derive(Debug)]
pub struct X11State {
    conn: Arc<RustConnection>,
    atoms: Atoms,
    client: Client,
    log: slog::Logger,
    unpaired_surfaces: HashMap<u32, (X11Window, Point<i32, Logical>)>,
}

impl X11State {
    conn: Rc<RustConnection>,
    atoms: Atoms,
    log: slog::Logger,
}

impl X11State {
        let stream = DefaultStream::from_unix_stream(connection)?;
        let conn = RustConnection::connect_to_stream(stream, screen)?;
        let atoms = Atoms::new(&conn)?.reply()?;

        let screen = &conn.setup().roots[0];

        // Actually become the WM by redirecting some operations
        conn.change_window_attributes(
            screen.root,
            &ChangeWindowAttributesAux::default().event_mask(EventMask::SUBSTRUCTURE_REDIRECT),
        )?;

        // Tell XWayland that we are the WM by acquiring the WM_S0 selection. No X11 clients are accepted before this.
        let win = conn.generate_id()?;
        conn.create_window(
            screen.root_depth,
            win,
            screen.root,
            // x, y, width, height, border width
            0,
            0,
            1,
            1,
            0,
            WindowClass::INPUT_OUTPUT,
            x11rb::COPY_FROM_PARENT,
            &Default::default(),
        )?;
        conn.set_selection_owner(win, atoms.WM_S0, x11rb::CURRENT_TIME)?;

        // XWayland wants us to do this to function properly...?
        conn.composite_redirect_subwindows(screen.root, Redirect::MANUAL)?;

        conn.flush()?;

        let conn = Arc::new(conn);
        let wm = Self {
            conn: Arc::clone(&conn),
            atoms,
        let wm = Self {
            conn: Rc::clone(&conn),
            atoms,
            log,
        };

        Ok((wm, X11Source::new(conn)))
    }

    fn handle_event(&mut self, event: Event) -> Result<(), ReplyOrIdError> {
        debug!(self.log, "X11: Got event {:?}", event);
        match event {
            Event::ConfigureRequest(r) => {
        debug!(self.log, "X11: Got event {:?}", event);
        match event {
            Event::ConfigureRequest(r) => {
                // Just grant the wish
                let mut aux = ConfigureWindowAux::default();
                if r.value_mask & u16::from(ConfigWindow::STACK_MODE) != 0 {
                    aux = aux.stack_mode(r.stack_mode);
                }
                if r.value_mask & u16::from(ConfigWindow::SIBLING) != 0 {
                    aux = aux.sibling(r.sibling);
                }
                if r.value_mask & u16::from(ConfigWindow::X) != 0 {
                    aux = aux.x(i32::try_from(r.x).unwrap());
                }
                if r.value_mask & u16::from(ConfigWindow::Y) != 0 {
                    aux = aux.y(i32::try_from(r.y).unwrap());
                }
                if r.value_mask & u16::from(ConfigWindow::WIDTH) != 0 {
                    aux = aux.width(u32::try_from(r.width).unwrap());
                }
                if r.value_mask & u16::from(ConfigWindow::HEIGHT) != 0 {
                    aux = aux.height(u32::try_from(r.height).unwrap());
                }
                if r.value_mask & u16::from(ConfigWindow::BORDER_WIDTH) != 0 {
                    aux = aux.border_width(u32::try_from(r.border_width).unwrap());
                }
                self.conn.configure_window(r.window, &aux)?;
            }
            }
            Event::ClientMessage(msg) => {
                if msg.type_ == self.atoms.WL_SURFACE_ID {
                    let id = msg.data.as_data32()[0];
                    info!(
                        self.log,
                        "X11 surface {:x?} corresponds to WlSurface {:x}", msg.window, id,
                    );
                }
            }
            _ => {}
        }
        Ok(())
    }
}
            _ => {}
        }
        self.conn.flush()?;
        Ok(())
    }

    fn new_window(
        &mut self,
        window: X11Window,
        surface: WlSurface,
        location: Point<i32, Logical>,
        space: &mut Space<Window>,
    ) {
        debug!(self.log, "Matched X11 surface {:x?} to {:x?}", window, surface);

        if give_role(&surface, "x11_surface").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 };
        space.map_element(Window::new(Kind::X11(x11surface)), location, true);
    }
}

// Called when a WlSurface commits.
pub fn commit_hook(surface: &WlSurface, dh: &DisplayHandle, state: &mut X11State, space: &mut Space<Window>) {
    if let Ok(client) = dh.get_client(surface.id()) {
        // Is this the Xwayland client?
        if client == state.client {
            // Is the surface among the unpaired surfaces (see comment next to WL_SURFACE_ID
            // handling above)
            if let Some((window, location)) = state.unpaired_surfaces.remove(&surface.id().protocol_id()) {
                state.new_window(window, surface.clone(), location, space);
            }
        }
    }
}

<MSG> 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 <346c0ad49ac8427b61b3b55abbe37dc0ebd524f2@znc.in>

<DFF> @@ -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());
+            }
+        }
+    }
 }

Map between X11 Windows and WlSurfaces