srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src/desktop_icons.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/desktop_icons.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/desktop_icons.rs')
-rw-r--r--crates/wayland/src/desktop_icons.rs54
1 files changed, 41 insertions, 13 deletions
diff --git a/crates/wayland/src/desktop_icons.rs b/crates/wayland/src/desktop_icons.rs
index 4ba54f4..7cfcbd1 100644
--- a/crates/wayland/src/desktop_icons.rs
+++ b/crates/wayland/src/desktop_icons.rs
@@ -58,19 +58,35 @@ impl DesktopIcon {
}
pub(crate) struct DesktopIcons {
- /// Top-left of the grid's own `(0, 0)` cell, in global space - the
- /// primary monitor's usable-area origin plus `GRID_MARGIN`.
- pub(crate) origin: (i32, i32),
+ /// Top-left of the grid's own `(0, 0)` cell, in global space, one per
+ /// participating monitor - each monitor's own usable-area origin plus
+ /// `GRID_MARGIN`. The same `icons` list is mirrored at every origin
+ /// (`general.desktop_icons_all_monitors`, see `ensure_desktop_icons`):
+ /// one shared set of icons/cells, rendered and hit-tested again at each
+ /// monitor's own corner, rather than a separate icon set per monitor --
+ /// dragging a mirrored copy on any monitor moves the one underlying
+ /// icon, which then shows in its new cell everywhere it's mirrored.
+ /// Exactly one entry (the primary monitor's) when that config flag is
+ /// off, matching the original single-monitor behaviour.
+ pub(crate) origins: Vec<(i32, i32)>,
pub(crate) icons: Vec<DesktopIcon>,
}
impl DesktopIcons {
- /// Which icon (if any) global-space point `(x, y)` falls on - same
- /// shape as `ContextMenu::row_at`. Returns an index into `self.icons`,
- /// not the icon itself, so a caller holding `&mut self` can still
- /// mutate the match without a borrow conflict.
- pub(crate) fn icon_at(&self, x: i32, y: i32) -> Option<usize> {
- self.icons.iter().position(|icon| icon.contains(self.origin, x, y))
+ /// Which icon (if any) global-space point `(x, y)` falls on, checked
+ /// against every mirrored origin - same shape as `ContextMenu::
+ /// row_at`. Returns an index into `self.icons` plus the origin it
+ /// matched (needed by drag-start to grab the copy actually clicked,
+ /// not always the primary monitor's), not the icon itself, so a caller
+ /// holding `&mut self` can still mutate the match without a borrow
+ /// conflict.
+ pub(crate) fn icon_at(&self, x: i32, y: i32) -> Option<(usize, (i32, i32))> {
+ for &origin in &self.origins {
+ if let Some(i) = self.icons.iter().position(|icon| icon.contains(origin, x, y)) {
+ return Some((i, origin));
+ }
+ }
+ None
}
}
@@ -276,13 +292,25 @@ mod tests {
#[test]
fn icon_at_matches_only_its_own_cell() {
let icons = DesktopIcons {
- origin: (100, 100),
+ origins: vec![(100, 100)],
icons: vec![DesktopIcon { id: "a".into(), label: "a".into(), kind: IconKind::File, target: PathBuf::new(), cell: (1, 0), selected: false }],
};
- let (left, top) = icons.icons[0].top_left(icons.origin);
- assert_eq!(icons.icon_at(left, top), Some(0), "top-left corner of the cell");
- assert_eq!(icons.icon_at(left + CELL_WIDTH - 1, top + CELL_HEIGHT - 1), Some(0), "bottom-right pixel of the cell");
+ let origin = icons.origins[0];
+ let (left, top) = icons.icons[0].top_left(origin);
+ assert_eq!(icons.icon_at(left, top), Some((0, origin)), "top-left corner of the cell");
+ assert_eq!(icons.icon_at(left + CELL_WIDTH - 1, top + CELL_HEIGHT - 1), Some((0, origin)), "bottom-right pixel of the cell");
assert_eq!(icons.icon_at(left - 1, top), None, "just left of the cell");
assert_eq!(icons.icon_at(left + CELL_WIDTH, top), None, "just right of the cell");
}
+
+ #[test]
+ fn icon_at_checks_every_mirrored_origin() {
+ let icons = DesktopIcons {
+ origins: vec![(0, 0), (2000, 0)],
+ icons: vec![DesktopIcon { id: "a".into(), label: "a".into(), kind: IconKind::File, target: PathBuf::new(), cell: (0, 0), selected: false }],
+ };
+ assert_eq!(icons.icon_at(10, 10), Some((0, (0, 0))), "matches the first monitor's mirror");
+ assert_eq!(icons.icon_at(2010, 10), Some((0, (2000, 0))), "matches the second monitor's mirror, with its own origin");
+ assert_eq!(icons.icon_at(1000, 10), None, "the gap between the two monitors matches neither");
+ }
}