From 5ee627928d0cb017e59d796c71793c35d5b65d33 Mon Sep 17 00:00:00 2001 From: srdusr <99972264+srdusr@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:24:00 +0200 Subject: 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. --- crates/wayland/src/decoration/tests.rs | 37 +++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) (limited to 'crates/wayland/src/decoration') diff --git a/crates/wayland/src/decoration/tests.rs b/crates/wayland/src/decoration/tests.rs index c8eb9b2..5cbc12f 100644 --- a/crates/wayland/src/decoration/tests.rs +++ b/crates/wayland/src/decoration/tests.rs @@ -813,14 +813,26 @@ fn context_menu_highlighted_row_has_a_different_background_than_the_rest() { } #[test] -fn context_menu_border_is_opaque_at_every_edge() { +fn context_menu_panel_is_opaque_in_the_middle_but_rounded_at_the_corners() { + // Reported live: the old hard-square panel with a single 1px border + // read as "squished... not at all polished" next to this project's own + // AGS reference (`GlobalMenu/style.scss`'s `popover box.menu-list`, + // flat rows with no border, only the panel itself rounded). A real + // rounded corner means the exact corner pixel is now transparent, not + // opaque - the opposite of what this test used to assert - while an + // edge's midpoint (away from any corner's curve) and the panel's own + // interior stay fully opaque either way. let items = [("Close", false)]; let buf = render_context_menu(100, 28, &items, (0, 0, 0), (0xff, 0xff, 0xff), (0, 0, 0), (0x99, 0x99, 0x99)); let alpha_at = |x: usize, y: usize| buf[(y * 100 + x) * 4 + 3]; - assert_eq!(alpha_at(0, 0), 255); - assert_eq!(alpha_at(99, 0), 255); - assert_eq!(alpha_at(0, 27), 255); - assert_eq!(alpha_at(99, 27), 255); + assert_eq!(alpha_at(0, 0), 0, "the exact corner pixel is now outside the rounded curve, not a hard square"); + // 2px in from the flat top/bottom edges, at the midpoint (far enough + // from either corner's own curve, and past this helper's own ~1px + // antialiasing band around every edge - see `fill_rounded_rect`'s + // shared smoothstep construction, not specific to this test). + assert_eq!(alpha_at(50, 2), 255, "just inside the flat top edge, away from either corner, is opaque"); + assert_eq!(alpha_at(50, 25), 255, "just inside the flat bottom edge is opaque"); + assert_eq!(alpha_at(50, 14), 255, "the panel's interior stays opaque"); } #[test] @@ -844,6 +856,21 @@ fn snap_flyout_border_is_opaque_at_every_outer_edge() { assert_eq!(alpha_at(width as usize - 1, height as usize - 1), 255); } +#[test] +fn render_desktop_icon_actually_draws_something_visible_for_every_kind() { + // Reported live: desktop icons construct and push correctly (confirmed + // via a live diagnostic: non-empty element list, correct position, + // no import error) but nothing visible shows up on screen - this + // isolates whether the rasterizer itself is the problem, offline, + // without needing a live compositor round-trip to check. + use crate::desktop_icons::IconKind; + for kind in [IconKind::Home, IconKind::Computer, IconKind::Trash, IconKind::Folder, IconKind::File] { + let buf = render_desktop_icon(88, 88, kind, "Test", false, (74, 144, 226), (240, 240, 240), (100, 100, 100), None); + let opaque_pixels = buf.chunks_exact(4).filter(|px| px[3] > 0).count(); + assert!(opaque_pixels > 100, "{kind:?} drew only {opaque_pixels} non-transparent pixels out of {}", buf.len() / 4); + } +} + #[test] fn snap_flyout_has_an_internal_grid_line_between_columns() { let labels = ["A", "B", "C", "D", "E", "F"]; -- cgit v1.2.3