diff options
| author | srdusr <[email protected]> | 2024-04-04 14:18:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2024-04-04 14:18:00 +0200 |
| commit | 2083ca7c88bc3e4b52ec68af384f334be1da2141 (patch) | |
| tree | 891e42b16ad98da20d4349004cac64b6d75d7471 /crates/wayland/src/lib.rs | |
| parent | 43175c78a5b450eb108738187b72e80d36a7bf5d (diff) | |
| download | srdwm-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.rs | 46 |
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(); |