srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src/lib.rs
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2024-04-04 14:18:00 +0200
committersrdusr <[email protected]>2024-04-04 14:18:00 +0200
commit2083ca7c88bc3e4b52ec68af384f334be1da2141 (patch)
tree891e42b16ad98da20d4349004cac64b6d75d7471 /crates/wayland/src/lib.rs
parent43175c78a5b450eb108738187b72e80d36a7bf5d (diff)
downloadsrdwm-2083ca7c88bc3e4b52ec68af384f334be1da2141.tar.gz
srdwm-2083ca7c88bc3e4b52ec68af384f334be1da2141.zip
Fix XWayland: it now actually renders and receives keyboard input
Three real bugs found and fixed via WAYLAND_DEBUG=1 protocol tracing in the QEMU VM, plus a fourth found along the way: - XWayland tried glamor (GBM rendering) first, which fails against this deliberately software-only compositor; its post-failure fallback path never used the xwayland_shell_v1 protocol at all, so X11Surface:: wl_surface() never resolved. Fixed by shadowing `Xwayland` on PATH with a wrapper script that always re-execs it with -shm (smithay's XWayland::spawn hardcodes its own argv and can't be bypassed either, since XWaylandClientData's fields are private). - Even with -shm, set_mapped(true) was only called after wl_surface() already resolved, deadlocking XWayland (it never advances a window past surface creation until the map is granted). Fixed by calling set_mapped(true) unconditionally in map_window_request. - The window then rendered as a ~1px sliver: initial geometry was seeded from X11Surface::geometry(), which can still be a tiny default at MapRequest time. Fixed by using the same 800x600 default the xdg-shell path already uses. - Typing didn't reach the window until a broader, XWayland-independent bug was fixed: nothing in the Wayland backend ever called KeyboardHandle::set_focus, so no window (native or X11) could ever receive keyboard input. Fixed in handle_pointer_button, along with TitlebarHit::Close being X11-surface-blind. Verified live: xterm launched via XWayland renders correctly sized and decorated, and a synthetic keypress sequence (ls + Enter) executed in its shell, screendump-confirmed.
Diffstat (limited to 'crates/wayland/src/lib.rs')
-rw-r--r--crates/wayland/src/lib.rs46
1 files changed, 41 insertions, 5 deletions
diff --git a/crates/wayland/src/lib.rs b/crates/wayland/src/lib.rs
index b2fa9dc..507102e 100644
--- a/crates/wayland/src/lib.rs
+++ b/crates/wayland/src/lib.rs
@@ -562,6 +562,39 @@ fn handle_pointer_position(state: &mut CompState, pos: Point<f64, Logical>, time
}
}
+/// The underlying `wl_surface` for a mapped window, regardless of whether
+/// it's a native `xdg-shell` toplevel or an XWayland `X11Surface` --
+/// `desktop::Window` exposes these as two separate accessors with no
+/// shared one.
+fn dwindow_wl_surface(w: &DWindow) -> Option<WlSurface> {
+ if let Some(top) = w.toplevel() {
+ return Some(top.wl_surface().clone());
+ }
+ w.x11_surface().and_then(|x| x.wl_surface())
+}
+
+/// Requests a client close its window, whichever kind it is.
+fn close_dwindow(w: &DWindow) {
+ if let Some(top) = w.toplevel() {
+ top.send_close();
+ } else if let Some(x11) = w.x11_surface() {
+ let _ = x11.close();
+ }
+}
+
+/// Focuses `id` in our own `WindowManager` *and* gives its surface real
+/// Wayland/X11 keyboard focus - without this, a window can be raised and
+/// tiled correctly yet never receive a single keystroke.
+fn focus_window(state: &mut CompState, id: WindowId) {
+ state.wm.borrow_mut().focus_window(id);
+ state.pending.borrow_mut().push(CoreEvent::WindowFocused(id));
+ let surface = state.id_to_window.get(&id).and_then(dwindow_wl_surface);
+ if let Some(keyboard) = state.seat.get_keyboard() {
+ let serial = SERIAL_COUNTER.next_serial();
+ keyboard.set_focus(state, surface, serial);
+ }
+}
+
fn handle_pointer_button(state: &mut CompState, pos: Point<f64, Logical>, button: u32, pressed: bool, time: u32) {
const BTN_LEFT: u32 = 0x110;
let serial = SERIAL_COUNTER.next_serial();
@@ -569,13 +602,12 @@ fn handle_pointer_button(state: &mut CompState, pos: Point<f64, Logical>, button
if pressed && button == BTN_LEFT {
let hit = state.wm.borrow().hit_test(pos.x as i32, pos.y as i32);
if let Some((id, hit)) = hit {
- state.wm.borrow_mut().focus_window(id);
- state.pending.borrow_mut().push(CoreEvent::WindowFocused(id));
+ focus_window(state, id);
match hit {
TitlebarHit::Drag => state.wm.borrow_mut().start_drag(id, pos.x as i32, pos.y as i32),
TitlebarHit::Close => {
- if let Some(w) = state.id_to_window.get(&id).and_then(|w| w.toplevel()) {
- w.send_close();
+ if let Some(w) = state.id_to_window.get(&id) {
+ close_dwindow(w);
}
}
TitlebarHit::Maximize => {
@@ -586,7 +618,11 @@ fn handle_pointer_button(state: &mut CompState, pos: Point<f64, Logical>, button
TitlebarHit::Resize(edge) => state.wm.borrow_mut().start_resize(id, edge, pos.x as i32, pos.y as i32),
}
} else if let Some((window, _loc)) = state.space.element_under(pos) {
- state.space.raise_element(&window.clone(), true);
+ let window = window.clone();
+ state.space.raise_element(&window, true);
+ if let Some(&id) = dwindow_wl_surface(&window).and_then(|s| state.surface_to_id.get(&s)) {
+ focus_window(state, id);
+ }
}
} else if !pressed {
let mut wm = state.wm.borrow_mut();