diff options
| author | srdusr <[email protected]> | 2024-05-14 00:42:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2024-05-14 00:42:00 +0200 |
| commit | dd31bf5ac2a692617d478478d36c733586b93cf8 (patch) | |
| tree | 2adb42ad8b24172cbbea3bf362a0d284acd54439 /crates/wayland/src/input.rs | |
| parent | a9dd8a6d4947cb537f7919c5a66ba4624af61800 (diff) | |
| download | srdwm-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/input.rs')
| -rw-r--r-- | crates/wayland/src/input.rs | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/crates/wayland/src/input.rs b/crates/wayland/src/input.rs index 76607ee..6959c8f 100644 --- a/crates/wayland/src/input.rs +++ b/crates/wayland/src/input.rs @@ -20,6 +20,11 @@ use smithay::wayland::shell::wlr_layer::{KeyboardInteractivity, Layer}; use srdwm_core::{Event as CoreEvent, Modifiers, TitlebarHit, WindowId}; +/// Modifier that turns a drag anywhere in a window into move/resize. +/// Matches the `SUPER` the shipped and ported configs use for +/// `bindm ... movewindow` / `resizewindow`. +const DRAG_MODIFIER: Modifiers = Modifiers::SUPER; + use crate::state::CompState; pub(crate) fn last_pointer_pos(state: &CompState) -> Point<f64, Logical> { @@ -134,6 +139,7 @@ pub(crate) fn focus_window(state: &mut CompState, id: WindowId) { pub(crate) fn handle_pointer_button(state: &mut CompState, pos: Point<f64, Logical>, button: u32, pressed: bool, time: u32) { const BTN_LEFT: u32 = 0x110; + const BTN_RIGHT: u32 = 0x111; let serial = SERIAL_COUNTER.next_serial(); // Locked: forward the click to the lock surface (it may have a button or @@ -146,6 +152,33 @@ pub(crate) fn handle_pointer_button(state: &mut CompState, pos: Point<f64, Logic return; } + // Modifier+drag: with the modifier held, dragging *anywhere* in a window + // moves it (left button) or resizes it from the nearest corner (right + // button) - the `bindm SUPER, mouse:272/273` gesture. Without this a + // window can only be moved by its titlebar, which is useless for + // windows that have none (fullscreen, CSD apps, layer surfaces). + // + // Checked before the titlebar hit-test so the modifier wins over the + // decoration: holding the modifier and grabbing the titlebar should + // still move, not press a titlebar button. + if pressed && (button == BTN_LEFT || button == BTN_RIGHT) { + let mods = state.seat.get_keyboard().map(|k| core_modifiers_from_xkb(&k.modifier_state())); + if mods.is_some_and(|m| m.contains(DRAG_MODIFIER)) { + let target = state.wm.borrow().window_at(pos.x as i32, pos.y as i32); + if let Some(id) = target { + focus_window(state, id); + let mut wm = state.wm.borrow_mut(); + if button == BTN_LEFT { + wm.start_drag(id, pos.x as i32, pos.y as i32); + } else { + let edge = wm.nearest_corner(id, pos.x as i32, pos.y as i32); + wm.start_resize(id, edge, pos.x as i32, pos.y as i32); + } + return; + } + } + } + if pressed && button == BTN_LEFT { let layer_hit = layer_surface_under(state, pos); if let Some((surface, _)) = &layer_hit { @@ -275,3 +308,37 @@ pub(crate) fn core_modifiers_from_xkb(mods: &smithay::input::keyboard::Modifiers } m } + +/// Modifier+scroll cycles workspaces, consuming the event. +/// +/// Returns `true` if it handled the scroll, in which case the caller must +/// *not* also forward it to the client. Generic over the input backend for +/// the same reason the keyboard handler is: both backends deliver scroll +/// through smithay's `PointerAxisEvent` trait. +pub(crate) fn handle_workspace_scroll<B, E>(state: &mut CompState, event: &E) -> bool +where + B: smithay::backend::input::InputBackend, + E: smithay::backend::input::PointerAxisEvent<B>, +{ + use smithay::backend::input::Axis; + + if state.lock.locked { + return false; + } + let mods = state.seat.get_keyboard().map(|k| core_modifiers_from_xkb(&k.modifier_state())); + if !mods.is_some_and(|m| m.contains(DRAG_MODIFIER)) { + return false; + } + let Some(v) = event.amount(Axis::Vertical).filter(|v| *v != 0.0) else { return false }; + + let mut wm = state.wm.borrow_mut(); + let ids: Vec<_> = wm.workspaces().iter().map(|w| w.id).collect(); + if ids.is_empty() { + return false; + } + let current = ids.iter().position(|&id| id == wm.current_workspace()).unwrap_or(0); + // Scrolling down (positive) advances, matching `workspace, e+1`. + let next = if v > 0.0 { (current + 1) % ids.len() } else { (current + ids.len() - 1) % ids.len() }; + wm.switch_workspace(ids[next]); + true +} |