srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src/udev/outputs.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/udev/outputs.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/udev/outputs.rs')
-rw-r--r--crates/wayland/src/udev/outputs.rs54
1 files changed, 53 insertions, 1 deletions
diff --git a/crates/wayland/src/udev/outputs.rs b/crates/wayland/src/udev/outputs.rs
index c3291d8..a0169a4 100644
--- a/crates/wayland/src/udev/outputs.rs
+++ b/crates/wayland/src/udev/outputs.rs
@@ -371,7 +371,7 @@ impl CompState {
head.output.change_current_state(None, None, None, Some((x_logical, 0).into()));
placed.push((head.output.clone(), head.location));
x_physical += head.size.0;
- x_logical += (head.size.0 as f64 / scale).round() as i32;
+ x_logical = next_logical_x(x_logical, head.size.0, scale);
}
for (output, location) in placed {
if let Some(entry) = self.outputs.iter_mut().find(|e| e.output == output) {
@@ -385,3 +385,55 @@ impl CompState {
}
}
+/// The actual arithmetic behind [`CompState::relayout_outputs`]'s logical-x
+/// accumulation - pulled out so it's testable without a real `Output`/DRM
+/// head, the same reasoning `udev/mod.rs::bounds_of` already applies to
+/// `UdevState::bounds`. Takes the previous head's own resulting logical x,
+/// this head's physical width, and this head's fractional scale; returns
+/// the *next* head's logical x.
+fn next_logical_x(prev_logical_x: i32, physical_width: i32, scale: f64) -> i32 {
+ prev_logical_x + (physical_width as f64 / scale).round() as i32
+}
+
+#[cfg(test)]
+mod relayout_tests {
+ use super::next_logical_x;
+
+ /// The exact scenario this function exists to fix, using the exact
+ /// figures a peer session measured live from inside GTK
+ /// (`Gdk.Display.get_monitors()`) before the fix: `HDMI-A-1` at 1920
+ /// physical / ~0.843 scale (2276 logical), `eDP-1` at 1920 physical /
+ /// 1.0 scale placed after it. The bug this guards against: passing the
+ /// raw physical accumulator straight into `change_current_state`
+ /// advertised `eDP-1` at logical x=1920 - inside `HDMI-A-1`'s own
+ /// logical extent (0..2276), a real, measured ~356px overlap.
+ #[test]
+ fn a_sub_one_scale_head_is_not_overrun_by_the_next_heads_logical_x() {
+ let hdmi_logical_end = next_logical_x(0, 1920, 1920.0 / 2276.0);
+ assert_eq!(hdmi_logical_end, 2276);
+ let edp_logical_x = next_logical_x(hdmi_logical_end, 1920, 1.0);
+ assert!(edp_logical_x >= hdmi_logical_end, "eDP-1 logical x ({edp_logical_x}) must not land inside HDMI-A-1's own logical extent (0..{hdmi_logical_end})");
+ assert_eq!(edp_logical_x, 2276 + 1920);
+ }
+
+ /// Every output at `scale == 1.0` (this machine's actual current,
+ /// user-chosen configuration - see docs/TODO.md's "HDMI-A-1 forced to
+ /// scale 1.0" entry) must reduce to plain physical accumulation, byte
+ /// for byte - this is the case that was already correct before the
+ /// fix and must stay that way.
+ #[test]
+ fn every_output_at_unit_scale_reduces_to_plain_physical_accumulation() {
+ assert_eq!(next_logical_x(0, 1920, 1.0), 1920);
+ assert_eq!(next_logical_x(1920, 1920, 1.0), 3840);
+ }
+
+ /// A scale above 1.0 (a HiDPI output) narrows logical space relative to
+ /// physical - the next head's logical x must land *before* its own
+ /// physical offset would suggest, not after.
+ #[test]
+ fn a_scale_above_one_narrows_the_next_heads_logical_x() {
+ let logical_end = next_logical_x(0, 3840, 2.0);
+ assert_eq!(logical_end, 1920);
+ }
+}
+