srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src/input/pointer.rs
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2025-09-10 11:24:00 +0200
committersrdusr <[email protected]>2025-09-10 11:24:00 +0200
commit5ee627928d0cb017e59d796c71793c35d5b65d33 (patch)
tree3285a7df82a04b178fb41fae220d60fe1dd38bd7 /crates/wayland/src/input/pointer.rs
parent4a55182ce17fd4474f8be1e8819a41b7ccd11045 (diff)
downloadsrdwm-5ee627928d0cb017e59d796c71793c35d5b65d33.tar.gz
srdwm-5ee627928d0cb017e59d796c71793c35d5b65d33.zip
Wayland backend: desktop icons v2, menu rebuild, layer-shell/scale fixes,
XWayland stability, GPU rendering, and multi-cursor Phase 2 The bulk of a multi-session shift's real work landed in crates/wayland. Full root-cause/verification narrative for every item below lives in docs/TODO.md (each has its own dated entry); this is the summary: Desktop shell: - Real desktop icons v2 (state/desktop_icons.rs, desktop_icons.rs): fixed origin-baked-before-the-bar-connects, fixed-icon sort order, and a proper Rename/Delete-to-Trash menu (window_memory.rs backs the rename-persistence side). Rubber-band marquee multi-select. - icon_theme.rs: real freedesktop icon-theme lookup (inherits chain, hicolor fallback) rendering actual theme SVGs via resvg/tiny-skia, replacing the hand-drawn placeholder glyphs. - Context/desktop menus (decoration.rs, desktop_menu.rs, state/menu.rs) rebuilt to match the project's own AGS panel styling: rounded floating panel, tinted-fill row highlight, real separators, a much fuller titlebar window-menu action set. Layer-shell / multi-monitor: - Layer-shell hit-testing and render positioning (input/pointer.rs, udev/render.rs's element placement) now correctly convert LayerMap's logical geometry into physical pixels on a fractionally-scaled output - root cause of a bottom-anchored dock being unclickable and unpainted while a top-anchored bar on the same output worked. udev/outputs.rs's relayout_outputs gained the same physical/logical split for cross-output positioning, now backed by a real unit test (next_logical_x) built from the original measured incident numbers. - state/geometry.rs: a window's border/decoration no longer briefly clips when moved between differently-scaled monitors mid-drag. XWayland / stability: - xwayland.rs, udev/session.rs, udev/platform.rs: fixed a 100%- reproducible cold-start XKEYBOARD crash-loop (XWayland's own stdin inherited a real, already-owned VT; env passthrough and idle-callback spawn timing were both real, independent gaps) that had silently taken down all X11-app support and the global-menu registrar every session. - state/toplevel.rs, state/lifecycle.rs: XWayland dialog detection via WM_TRANSIENT_FOR, not just a native xdg_toplevel parent. Rendering: - udev/render.rs, decoration.rs: real GPU (GBM+EGL+DrmCompositor) window-content and cursor rendering on the udev backend, falling back to the untouched Pixman path automatically on any init failure. - decoration/tests.rs, state/mod.rs: rounded-corner/border fixes for interactive resize lag and cross-monitor moves. Multi-cursor Phase 2 (virtual_pointer.rs, new; state/mod.rs, udev/ platform.rs, winit/nested_platform.rs): pins a zwlr_virtual_pointer_ unstable_v1 object to a specific window, bypassing the shared seat/ focus/pointer_pos path entirely via hand-rolled wl_pointer.enter/motion/ button/frame/leave against every WlPointer the target client has bound (PointerHandle::client_pointers). Lets an agent operate one window while a human uses another, genuinely simultaneously, with zero client cooperation and no second wl_seat (confirmed a dead end: real clients only ever bind the first seat advertised). Full workspace build/test/clippy clean.
Diffstat (limited to 'crates/wayland/src/input/pointer.rs')
-rw-r--r--crates/wayland/src/input/pointer.rs45
1 files changed, 40 insertions, 5 deletions
diff --git a/crates/wayland/src/input/pointer.rs b/crates/wayland/src/input/pointer.rs
index f0e034e..602f613 100644
--- a/crates/wayland/src/input/pointer.rs
+++ b/crates/wayland/src/input/pointer.rs
@@ -231,6 +231,8 @@ pub(crate) fn handle_pointer_position(state: &mut CompState, pos: Point<f64, Log
// A no-op whenever no desktop icon is currently being dragged - see
// `CompState::update_desktop_icon_drag`'s own doc comment.
state.update_desktop_icon_drag((pos.x as i32, pos.y as i32));
+ // Likewise a no-op whenever no marquee selection is in progress.
+ state.update_desktop_marquee((pos.x as i32, pos.y as i32));
// Tells core which monitor the pointer is physically over right now --
// core has no pointer of its own to know this (see `pointer_monitor`'s
@@ -505,6 +507,17 @@ pub(crate) fn handle_pointer_button(state: &mut CompState, pos: Point<f64, Logic
if let Some(menu) = state.context_menu.take() {
if let Some(row) = menu.row_at(pos.x as i32, pos.y as i32) {
let (_, action) = menu.items[row];
+ // A separator row occupies real space (`row_at` resolves a
+ // click on it same as any other) but isn't a real action --
+ // same "click does nothing, menu stays open" convention any
+ // native menu's own divider follows, rather than either
+ // running a no-op action or dismissing the whole menu on
+ // what was very possibly a slightly-off click at a real
+ // item just above/below it.
+ if matches!(action, crate::context_menu::MenuAction::Separator) {
+ state.context_menu = Some(menu);
+ return;
+ }
state.close_context_menu();
state.run_context_menu_action(menu.window, action);
} else {
@@ -528,6 +541,10 @@ pub(crate) fn handle_pointer_button(state: &mut CompState, pos: Point<f64, Logic
if let Some(menu) = state.desktop_menu.take() {
if let Some(row) = menu.row_at(pos.x as i32, pos.y as i32) {
let (_, action) = menu.items[row].clone();
+ if matches!(action, crate::desktop_menu::DesktopMenuAction::Separator) {
+ state.desktop_menu = Some(menu);
+ return;
+ }
state.close_desktop_menu();
state.run_desktop_menu_action(action);
} else {
@@ -635,19 +652,28 @@ pub(crate) fn handle_pointer_button(state: &mut CompState, pos: Point<f64, Logic
// comment): a desktop icon here, single- or double-click
// per `general.desktop_icon_single_click`, otherwise clear
// whatever was selected.
- let icon_hit = state.desktop_icons.as_ref().and_then(|icons| icons.icon_at(pos.x as i32, pos.y as i32).map(|i| icons.icons[i].id.clone()));
+ let icon_hit = state
+ .desktop_icons
+ .as_ref()
+ .and_then(|icons| icons.icon_at(pos.x as i32, pos.y as i32).map(|(i, origin)| (icons.icons[i].id.clone(), origin)));
match icon_hit {
- Some(id) => {
+ Some((id, origin)) => {
let single_click_opens = state.wm.borrow().desktop_icon_single_click;
if single_click_opens || state.is_double_click_icon(&id, time) {
state.select_desktop_icon(Some(&id));
state.open_desktop_icon(&id);
} else {
state.select_desktop_icon(Some(&id));
- state.start_desktop_icon_drag(&id, (pos.x as i32, pos.y as i32));
+ state.start_desktop_icon_drag(&id, origin, (pos.x as i32, pos.y as i32));
}
}
- None => state.select_desktop_icon(None),
+ // Genuinely bare desktop, not just "no icon under the
+ // pointer" - starts a rubber-band selection instead of
+ // only clearing whatever was selected before. The one
+ // "click and drag" desktop interaction this compositor
+ // never had (reported live next to "missing click and
+ // drag stuff like from windows").
+ None => state.start_desktop_marquee((pos.x as i32, pos.y as i32)),
}
}
}
@@ -681,7 +707,7 @@ pub(crate) fn handle_pointer_button(state: &mut CompState, pos: Point<f64, Logic
(BTN_RIGHT, None)
if layer_surface_under(state, pos).is_none() && !state.space.element_under(pos).is_some_and(|(w, _)| dwindow_is_visible(state, w)) =>
{
- let icon_hit = state.desktop_icons.as_ref().and_then(|icons| icons.icon_at(pos.x as i32, pos.y as i32).map(|i| icons.icons[i].id.clone()));
+ let icon_hit = state.desktop_icons.as_ref().and_then(|icons| icons.icon_at(pos.x as i32, pos.y as i32).map(|(i, _origin)| icons.icons[i].id.clone()));
match icon_hit {
Some(id) => {
state.select_desktop_icon(Some(&id));
@@ -697,6 +723,7 @@ pub(crate) fn handle_pointer_button(state: &mut CompState, pos: Point<f64, Logic
// always checked on release, same as `was_dragging`/`was_resizing`
// below, just for a desktop icon instead of a window.
state.end_desktop_icon_drag();
+ state.end_desktop_marquee();
let mut wm = state.wm.borrow_mut();
let was_dragging = wm.is_dragging();
let was_resizing = wm.is_resizing();
@@ -712,6 +739,14 @@ pub(crate) fn handle_pointer_button(state: &mut CompState, pos: Point<f64, Logic
} else if was_resizing {
wm.end_resize();
}
+ // Persists whatever `end_drag`/`end_resize` just updated in
+ // `remembered_geometry` - a real user action (button released
+ // after a drag/resize), not a per-frame event, so writing the
+ // whole small table each time is cheap and needs no separate
+ // dirty-tracking. See `window_memory.rs`'s own doc comment.
+ if was_dragging || was_resizing {
+ crate::window_memory::save_all(wm.all_remembered_geometry());
+ }
drop(wm);
// `end_drag` can snap the geometry one more time (edge/top-of-
// screen snapping, `SmartPlacement::snap_zone`) *after* the last