srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src/udev.rs
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2024-05-14 00:42:00 +0200
committersrdusr <[email protected]>2024-05-14 00:42:00 +0200
commitdd31bf5ac2a692617d478478d36c733586b93cf8 (patch)
tree2adb42ad8b24172cbbea3bf362a0d284acd54439 /crates/wayland/src/udev.rs
parenta9dd8a6d4947cb537f7919c5a66ba4624af61800 (diff)
downloadsrdwm-dd31bf5ac2a692617d478478d36c733586b93cf8.tar.gz
srdwm-dd31bf5ac2a692617d478478d36c733586b93cf8.zip
Draw a cursor, handle the lid, and everything a real config needs
Groundwork for actually daily-driving this: porting the user's Hyprland config exposed what srdwm couldn't yet express, and testing on a bare TTY exposed something worse. A visible mouse cursor. Nothing drew a pointer at all - on a bare TTY the mouse was simply invisible. It hid because the nested backend runs inside another compositor, which draws a cursor over srdwm's window; only the DRM backend, i.e. the actual session path, was affected. A built-in arrow is now composited above everything on the output the pointer is on. It's a reviewable ASCII bitmap rather than an XCursor theme: a cursor that is always present beats a prettier one that sometimes isn't, the same reasoning as decoration.rs's font fallback. Client-set cursor surfaces and named shapes are still not rendered, so an app asking for an I-beam gets the arrow. Lid switch. libinput switch events are handled and surfaced to config as srd.on("lid_closed"/"lid_open", fn), so closing the lid can lock and suspend instead of doing nothing. Config-driven additions, each needed by a binding in the ported config and none of which existed: fullscreen (Window.fullscreen was a dead field -- declared, never read or written), directional window move that swaps with the neighbour and reorders the stack so tiling follows, focus cycling, modifier+drag to move/resize anywhere in a window rather than only by the titlebar, modifier+scroll to change workspace, and 8 XF86 media/power keysyms taken from the system's own XF86keysym.h. The keysym tables are hand-maintained in both directions and a key missing from either fails silently, so a round-trip test now covers every one the configs bind. Verified in the QEMU VM: a bare-TTY screendump shows a recognisable arrow at the pointer position (113 white fill + 58 black outline pixels at screen centre, where the pointer starts).
Diffstat (limited to 'crates/wayland/src/udev.rs')
-rw-r--r--crates/wayland/src/udev.rs44
1 files changed, 41 insertions, 3 deletions
diff --git a/crates/wayland/src/udev.rs b/crates/wayland/src/udev.rs
index aca4865..30517f8 100644
--- a/crates/wayland/src/udev.rs
+++ b/crates/wayland/src/udev.rs
@@ -159,6 +159,9 @@ impl CompState {
// Drained before the `&mut self.udev` borrow below, so screencopy can
// be serviced with the renderer that borrow owns.
let mut captures = std::mem::take(&mut self.screencopy_pending);
+ // Same reason: the cursor needs the renderer that borrow owns.
+ let cursor_status = self.cursor_status.clone();
+ let cursor_buffer = self.cursor_buffer.clone();
// Which heads are eligible, and what each needs, gathered before the
// mutable borrow of `self.udev`.
@@ -196,6 +199,19 @@ impl CompState {
let mut custom_elements: Vec<MemoryRenderBufferRenderElement<PixmanRenderer>> = Vec::new();
if !locked {
+ // Cursor first: `render_output` draws custom elements
+ // front-to-back, so the earliest element is topmost. On a
+ // bare TTY nothing else draws a pointer - see `cursor.rs`.
+ let pointer_pos = udev.pointer_pos;
+ let hsize = udev.heads[index].size;
+ custom_elements.extend(crate::cursor::render_elements(
+ &cursor_status,
+ &cursor_buffer,
+ &mut udev.renderer,
+ pointer_pos,
+ origin,
+ hsize,
+ ));
for (geom, deco) in &decorations {
let pos = ((geom.x - origin.x) as f64, (geom.y - origin.y) as f64);
match MemoryRenderBufferRenderElement::from_buffer(&mut udev.renderer, pos, deco, None, None, None, Kind::Unspecified) {
@@ -538,6 +554,8 @@ impl UdevPlatform {
_screencopy_state: crate::screencopy::ScreencopyState::new::<CompState>(&display_handle),
screencopy_pending: Vec::new(),
lock: Default::default(),
+ cursor_status: smithay::input::pointer::CursorImageStatus::default_named(),
+ cursor_buffer: crate::cursor::make_buffer(),
wm: wm.clone(),
surface_to_id: HashMap::new(),
id_to_window: HashMap::new(),
@@ -832,10 +850,30 @@ fn handle_libinput_event(state: &mut CompState, event: InputEvent<LibinputInputB
let pressed = event.state() == BackendButtonState::Pressed;
handle_pointer_button(state, pos, button, pressed, event.time_msec());
}
+ // Laptop lid. libinput reports this as a switch toggle; without
+ // handling it, closing the lid does nothing at all - no lock, no
+ // suspend - which is a genuine problem on a laptop rather than a
+ // missing nicety.
+ InputEvent::SwitchToggle { event } => {
+ // Fully qualified: libinput's own `Switch` is also in scope here.
+ use smithay::backend::input::{SwitchState, SwitchToggleEvent};
+ if matches!(event.switch(), Some(smithay::reexports::input::event::switch::Switch::Lid)) {
+ let closed = event.state() == SwitchState::On;
+ log::info!("lid {}", if closed { "closed" } else { "opened" });
+ state.pending.borrow_mut().push(CoreEvent::LidSwitch { closed });
+ }
+ }
InputEvent::PointerAxis { event } => {
- // Scroll: forwarded to the focused client via the pointer axis
- // frame, no WM-level handling (matches the winit backend, which
- // doesn't handle scroll either).
+ // Modifier+scroll switches workspace instead of reaching the
+ // client - the `bind = SUPER, mouse_down/up, workspace, e+1/e-1`
+ // gesture. Checked first so the client never sees these events;
+ // forwarding them too would scroll the window under the cursor
+ // as a side effect of changing workspace.
+ if crate::input::handle_workspace_scroll(state, &event) {
+ return;
+ }
+ // Otherwise: forwarded to the focused client via the pointer axis
+ // frame, no WM-level handling.
let Some(pointer) = state.seat.get_pointer() else { return };
let source = event.source();
let mut frame = AxisFrame::new(event.time_msec()).source(source);