srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src/protocols
AgeCommit message (Collapse)AuthorFilesLines
2026-08-30Indent doc list continuationssrdusr2-10/+10
2026-07-27Reach GTK4's window buttons too, and speak the decoration protocol GTK knowssrdusr1-0/+74
Two findings from reading what other projects do, and then measuring this machine rather than trusting the reading. GTK has never implemented xdg-decoration, so a compositor that advertises only that protocol is invisible to every GTK client on the decoration question. What GTK does implement is KDE's older org_kde_kwin_server_decoration - confirmed by reading libgtk-4's own symbol strings, where the manager, the mode enum and the default-mode handler are all present, and absent from libgtk-3. That is the channel a KDE session uses. srdwm now advertises it alongside xdg-decoration, with both answering from the same policy (theme.default_decorated, theme.force_server_side) so a client is told the same thing whichever it asks through. Measured what that actually buys, with WAYLAND_DEBUG on a real GTK4 client: it binds the manager and receives default_mode(2) = Server, and then never creates a decoration object for its window. So it changes nothing for a GTK application's own header bar, and it is kept because it is the correct thing to advertise and because clients that do honour it - Qt and KDE's own -- now get server-side decoration from srdwm instead of nothing. The second finding is the one that fixes what was reported. GTK3 and GTK4 put their window buttons under different CSS selectors, and the generated stylesheet named only GTK3's. A diagnostic rule proved it both ways: a flat colour reached Nemo (GTK3) through `headerbar button.titlebutton` and gnome-calculator (GTK4) through `windowcontrols button`, and neither selector reached the other toolkit. Every style is now written for both, so a GTK4 application is styled rather than silently skipped. `headerbar` itself works in both, so the titlebar block needed no split. Verified on screen: gnome-calculator, a GTK4/libadwaita application, now draws its header bar in srdwm's own titlebar colour with srdwm's text colour, where before it kept its theme's. Also worth writing down, because it bounds what any of this can achieve: an application's header bar is its own widget. No protocol removes it. GTK_CSD=0 does not, the KDE protocol does not, and neither does forcing server-side decoration - that only adds a second titlebar above the first. What a compositor can do is make the two look like one, which is what this does.
2026-07-27Hide a window only when srdwm knows it has not drawn, not when a lookup says sosrdusr1-3/+3
Regression I introduced two commits ago, reported live: "I can click close where the button would normally be and it does close, but it is still invisible." The gate that stops an empty frame being drawn before a client paints asked the renderer, from inside the render loop, whether a window's surface had a buffer attached right now - and treated "no" as "do not draw". That question is only meaningful for a native xdg-shell toplevel. An XWayland window's surface state does not describe it the same way, so the answer came back no on every frame and the window was never drawn again, while srdwm's own hit-testing carried on working perfectly: an invisible window that still takes clicks, which is a worse failure than the empty frame it was meant to prevent. Inverted to the fail-safe direction. `new_managed_window` - the one path that creates a native toplevel - puts the window into `awaiting_first_buffer`, and `commit` takes it out on the first commit that carries a buffer. The render and capture paths test that set and nothing else. A window is now hidden only when srdwm itself put it there, so no window whose plumbing works differently can be hidden by a lookup that did not apply to it: the XWayland map path never touches the set, and neither can anything else. The buffer question still gets asked, but only in `commit`, about a surface it was just handed, where it is the right question. Verified both halves: an ordinary spawn still shows no frame before content (26 captured frames with content, 0 without), and the only way into the set is one line in one function.
2026-07-23Do not draw a window before its client has painted anythingsrdusr1-0/+18
Reported as "before a window spawns, the border corners look funny". A toplevel is placed, sized and decorated the moment its role is created, which is well before the client draws. srdwm was rendering it from that moment, so what appeared first was an empty frame: border, titlebar and shadow standing around bare desktop, at the guessed 800x600 placeholder size, with nothing inside. When the real buffer arrived the frame snapped to the real size. Measured in a nested session, capturing a cold terminal's spawn with grim: four consecutive captured frames spanning 540ms showed a complete red border with zero client content inside it, at 642px outer height, which then settled at 610 - a jump of exactly one TITLEBAR_HEIGHT. After this change the same capture has no such frame at all: every frame that shows a border shows content in it, and the height does not change afterward. Two parts: - Nothing is drawn for a window that has never committed a buffer. All five paths that draw a frame agree on this - both udev render loops (Pixman and GPU), the winit render loop, and both screencopy paths, so a screenshot cannot show a frame the screen does not. - The open-slide starts at the first commit that carries a buffer rather than at role creation. A cold terminal took ~800ms to paint, long enough for the whole tween to finish against the empty frame, so the window simply appeared, already at rest, with no animation at all. It now animates where it can actually be seen. The answer latches once true (windows_shown_once), so a window that has legitimately shown something is never hidden again by this however its buffer state changes. A window that cannot be resolved to a surface counts as drawable, deliberately: this hides a window only on positive evidence that it has never drawn, so nothing whose surface plumbing works differently - an XWayland window - can be hidden by a lookup that did not apply to it. Same shape, and the same reason, as sync_layer_visibility's own has_buffer branch, which layer surfaces have had all along. 533 tests pass, clippy clean.
2026-06-01Let negotiating clients be forced to server-side decoration, and document ↵srdusr1-0/+5
the GTK half Asked to research how KDE and GNOME make decorations consistent, after being told too quickly that srdwm could only control its own titlebar. Measured against a nested srdwm, one client at a time: Qt/KDE creates a decoration object and asks for server-side; winit creates one and asks for CLIENT-side; GTK never creates one at all. The xdg-decoration spec says the compositor "can decide not to use the client's mode and enforce a different mode instead" and that the client "must obey" - so the first two are srdwm's to decide, and it had simply been deferring. The same spec closes the door on the third: a client that does not negotiate continues to self-decorate, and GTK is not having the conversation. New theme.decorations.force_server_side, default off, overrides the client's requested mode. Verified: with it off Alacritty draws its own content to the window's top edge, with it on the same window gets srdwm's titlebar -- (0,0,0) versus (46,52,64) sampled at three rows. Off by default because it cannot move a GTK button and can stack srdwm's titlebar on a client that draws its own regardless, which is the Firefox case already recorded here. The GTK half needs no compositor code, only the desktop setting GTK actually reads - xdg-desktop-portal's org.gnome.desktop.wm.preferences button-layout for GTK4, gtk-decoration-layout for GTK3. This machine was serving "close,minimize,maximize:" (left) while srdwm's own button_side was right, which is the entire mismatch. Documented in DEFAULTS.md with the mapping from button_side to layout string, and noted that this is exactly what kde-gtk-config does for KWin. 525 tests pass, clippy clean.
2026-04-20Confirm Nemo's popup works; fix the two bugs that hid it, and shadow bleed ↵srdusr1-25/+3
across a monitor seam Nemo's right-click context menu was the last open punch-list item, parked twice as untestable. It works: verified end to end in a throwaway nested compositor, menu and submenu both, at the correct position and stacking. The popup path itself needed no fix, so the POPUP-GEOM-DIAG/POPUP-GRAB-DIAG diagnostics are removed. Two real bugs turned up in the way of testing it. zwlr_virtual_pointer was a silent no-op on the winit backend. Every Motion/MotionAbsolute handler read UdevState::bounds() behind an early return when state.udev was None, and that field is Some only for the DRM backend. The protocol advertised its global, accepted create_virtual_pointer and accepted every request, then discarded all motion with no error and no log. That is the backend a nested instance runs on, so the only safe way to drive a throwaway compositor - a Wayland client of that compositor, which cannot reach any other session, unlike ydotool's /dev/uinput writes - did not work at all. Bounds now come from WindowManager::monitors() when udev is absent; both backends fill that list from Platform::monitors(). The winit backend's screencopy pass rendered no popups and no shadows. It re-renders the scene offscreen, and that second scene was missing tiers, so grim on a nested instance reported the opposite of the truth: a menu drawing perfectly on screen photographed as absent. The DRM backend never had this, since it serves screencopy from the on-screen frame it just drew. Border strips are still missing from that pass, called out in the code rather than left silent. Also fixed, from the "windows show a bit in the other monitor" report: shadow_rect expanded by SHADOW_SIZE on every side with no monitor-boundary awareness, so a window flush against a seam put its 24px shadow strip on the neighbouring screen. shadow_rect_clipped clips to the bounding box of the monitors the window's geometry actually touches - not just its assigned one, since a window straddling a seam really does occupy both and clipping there would cut its shadow off mid-body. The bitmap's own extent stays unclipped, because the src rectangle indexes into it; only the fragment list is clipped. Six tests on the incident's own numbers. Not confirmed on screen: the nested backend cannot produce a second monitor. New tool: tools/virtual-pointer-click, a scriptable virtual-pointer driver that acknowledges each command after its round-trip, so a test script can put a screenshot between a move and the click that follows it. 489 tests pass, clippy clean.
2025-12-01Let a new window pick its own size instead of forcing a guessed placeholdersrdusr1-0/+7
Root-caused "windows spawn small and square, not remembering placement or size": new_managed_window hardcoded a fresh toplevel's geometry to 800x632 before the client had said anything about its own preferred size, and sync_geometry forced that guess onto the client's very first xdg_toplevel::configure unconditionally. Per xdg-shell, size: None on that first configure is how every mainstream compositor lets a client pick its own natural size instead; this one never did, so every app converged on the same placeholder rectangle regardless of what it would have chosen. Window::size_is_provisional marks a size that really was just the guess (not a remembered geometry, a rule's explicit geometry action, or a maximize/phone-mode fill, none of which are guesses). sync_geometry sends size: None for such a window's first configure; a new adopt_provisional_size, called from the commit handler, adopts the client's own real first size into Window::geometry the moment it commits one, clamping only position so a bigger-than-guessed window can't hang off its monitor's edge. Live-verified in a nested compositor: a zenity dialog now renders at its own compact natural size instead of being stretched to the old guess.
2025-02-15Checkpoint: preserve all uncommitted rust-rewrite worktree worksrdusr11-0/+958
Safety commit before reconciling this worktree with main, which has diverged with its own separate fixes today. Nothing here is reviewed or curated yet - this exists purely so none of this work can be lost to a git operation, disk issue, or worktree cleanup while that reconciliation happens.