srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/core/src/window.rs
AgeCommit message (Collapse)AuthorFilesLines
2025-02-15Checkpoint: preserve all uncommitted rust-rewrite worktree worksrdusr1-84/+559
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.
2024-10-31Add global-menu support (dbusmenu/appmenu) for Wayland and X11 clientssrdusr1-5/+106
Exposes each window's application menu (Firefox/GTK's dbusmenu export, X11's _GTK_APPLICATION_OBJECT_PATH-style menus via global_menu.rs) so an external panel can render it as a system menu bar rather than each window drawing its own, the same convention appmenu.rs/gtk_shell.rs and appmenu_registrar.rs wire up across both backends.
2024-08-22Add per-window resize-margin override (Hyprland's extend_border_grab_area)srdusr1-0/+6
MISSING.md had listed this as needing to touch srdwm_core::window:: hit_test's signature at every call site, including the honest-stub Windows/macOS backends - overstated on closer inspection: that shared function already takes a plain resize_margin: i32 parameter, agnostic to where the value comes from. The only real change needed was reading Window.resize_margin.unwrap_or(wm-wide default) instead of always the WM-wide value, at the single call site inside WindowManager::hit_test in core - no backend touched at all. Window.resize_margin: Option<i32>, WindowRuleActions.resize_margin to match, applied in add_window/reapply_rules_if_pending the same way opacity already is. Settable via a rule action or srd.window.set_resize_margin(n) on the focused window.
2024-08-20Give corner resize real priority over a plain edgesrdusr1-11/+118
Reported live, repeatedly: corners felt like they had no priority over sides. They didn't, structurally - a corner only ever registered in the exact pixel square where both edges' own resize_margin zones happened to overlap (6x6px at the default 6px margin), nothing wider. A click a little further along either axis, still clearly aiming for the corner, fell back to a single straight-edge resize instead. resize_edge_at now checks a separate, wider corner zone (CORNER_MARGIN * resize_margin) first, so a corner claims a real, deliberately larger target the way GNOME/KDE already do - diagonal resize is a harder grab than a straight edge and deserves more room, not the same or less. Also fixed a related, previously-unreachable case: a *decorated* window's whole titlebar band returned Drag/Close/Maximize/Minimize unconditionally, so top-left/top-right diagonal resize never had a code path at all, even at the titlebar's own corner pixels. Added top-left resize as a small, genuine corner square (both axes, not just one) checked before drag/buttons. Deliberately did NOT add the matching top-right case: that corner is the close button on every mainstream desktop, and trading a well-known, expected target for a rarely-wanted one at exactly the spot a miss costs the most isn't a trade worth making. Four new regression tests cover: reaching a corner past the old tight margin, falling back to a plain edge just past the new wider one, the newly-reachable decorated top-left corner, and confirming top-right still closes rather than competing with resize.
2024-06-22Bypass smithay's Space rendering for window/layer content: real per-window ↵srdusr1-0/+8
opacity The user asked for per-window opacity (MISSING.md's `windowrule = opacity` gap) and pushed back on treating smithay's convenience wrappers as a hard ceiling: "don't rely on smithay, it won't have everything we need." Looked again at why opacity was ruled out earlier - `render_output`/ `space_render_elements` take one `alpha` for the whole frame's `self.space` content, no per-element control - and found a path that doesn't need nesting smithay's internal `SpaceRenderElements` type (the approach that hit an unresolvable generic-bounds wall investigating fullscreen-hiding earlier): call `render_elements_from_surface_tree` directly, once per window and once per layer-shell surface, each with its own alpha, wrapping the result in the *existing* `OverlayElement::Surface` variant. That primitive was already proven safe in this codebase (cursor.rs's client-image path, this file's own popup rendering) - reusing it here for a window's main content is the same call, not a new one. Both `udev.rs` and `winit.rs`'s render loops now build window content and layer-shell surfaces themselves (`elements.rs`: `surface_content_elements`, `output_layer_elements`, `window_wl_surface` for the Wayland/XWayland split), in the correct front-to-back order, then call `OutputDamageTracker::render_output` directly instead of the `space::render_output`/`space_render_elements` convenience wrappers. Content itself needs no occlusion clipping against `occluders` (unlike border/ titlebar bitmaps) - pushed in the same front-to-back order as everything else, ordinary painter's-algorithm draw order already occludes it correctly, the same property it had via `self.space`'s own order before. `self.space` stays mapped and `resync_stacking_order`-maintained exactly as before; only the render step stopped reading from it. A comment in winit.rs's render loop warned that a near-identical earlier attempt was reverted for a real ordering bug (whichever window was created first always painted in front, regardless of focus). That bug's actual root cause, identified and fixed since, was `Space::map_element` silently re-stacking on every geometry sync, independent of which render path was used - see `resync_stacking_order`. This rewrite never reads `Space`'s internal order for rendering at all (`ids` comes from `WindowManager.order` directly, the same source `hit_test` already trusts), so that specific bug class can't recur here regardless of whether `resync_stacking_order` ever drifts again. Bonus from the same infrastructure: the bar/dock now genuinely don't render at all (not just get covered) for a fullscreen window - `output_layer_elements` skips `Layer::Top`/`Overlay` entirely when any visible window is fullscreen, the hardening this work backed away from earlier for being too risky to build via the nested-SpaceRenderElements approach. `capture_offscreen` (winit.rs's screencopy path) picked up opacity-aware content and layer-shell inclusion too, though not full parity with the on-screen loop (still no border/shadow strips there - a pre-existing, separately-flagged gap). Opacity itself: `Window.opacity` (core), `WindowRuleActions.opacity` / `srd.rule(..., { opacity = 0.9 })`, `srd.window.set_opacity()`. Caught live, before commit: opacity was wired into `add_window`'s own rule match but not `reapply_rules_if_pending` - the *only* path a class-based rule actually takes effect through for a native Wayland client, since `add_window`'s own attempt always runs against a still-empty `app_id` (see the regression test next to the existing one covering the identical historical bug for `decorated`). Found by setting an isolated `SRDWM_CONFIG_PATH` test config with `srd.rule({ class = "Alacritty" }, { opacity = 0.4 })` against a nested instance and pixel-sampling a real screenshot: predicted blend (242,230,53) at 0.4 over (10,10,15) is (103,98,30); measured (105,100,33). Verified live in a nested session, screenshotting the *host* compositor (shows the real on-screen render, unlike grim against the nested socket, which - separately discovered this work - routes through `capture_offscreen`): stacking order correct with two overlapping windows (topmost fully occludes the one behind it, the exact scenario the reverted attempt got wrong), opacity blend matches prediction. Also confirmed, by testing the previous commit against the same scene, that upside-down content on this backend is a pre-existing bug unrelated to this change -- noted, not fixed here. cargo build --workspace (all 9 crates), cargo clippy --workspace (0 new warnings), cargo test --workspace (197 tests, 0 failed, includes 2 new regression tests).
2024-06-20Add drop shadows and shrink the resize grab margin that was eating content ↵srdusr1-24/+34
clicks Two independent daily-driving gaps closed in one pass, both from MISSING.md and live user feedback: Drop shadows (general.shadows, default true). Reuses the exact "bitmap drawn outside geometry, cached like the border" technique border_strips/ render_border_top already established - decoration::shadow_bitmap rasterizes a linear alpha falloff (Chebyshev/square-ring distance, not a true blur -- no blur primitive exists without a GPU shader, and the udev backend's PixmanRenderer is software-only) from SHADOW_MAX_ALPHA (90/255, deliberately subtle) at the window's own edge down to fully transparent SHADOW_SIZE (12px) out. Cached in CompState::shadow_buffers, rebuilt at the same trigger points as border_top_decorations (redraw_decoration_buffer), for the identical damage-tracking reason: a fresh Id every frame means OutputDamageTracker never finds a previous-frame match. No shadow for a maximized or fullscreen window, matching the Hyprland/GNOME convention MISSING.md measures against. Resize grab margin: 10px -> 6px (general.resize_margin, now configurable, same call-site-count-preserving change as threading a new parameter through one indirection point: ResizeEdge::hit_test's only production caller is WindowManager::hit_test, so this didn't need touching every backend despite hit_test being shared verbatim across X11/Wayland/Windows/macOS). Reported live: ordinary clicks near any window edge - a link near a browser's edge, a button near a panel's edge - regularly registered as a resize-edge grab instead of reaching the client, not just an occasional near-miss, because the 10px band was measured inward from the client's own content rect. 6px stays comfortably grabbable while giving content back most of its edge. Verified: cargo build --workspace (all 9 crates including the windows/macos stub backends), cargo clippy --workspace (0 new warnings), cargo test across core/wayland/config/x11 (188 tests, 0 failed). Shadow rendering verified at the render-element level live in a nested session (correct geometry, alpha, buffer contents) - grim/screencopy itself turned out to route through winit.rs's separate capture_offscreen path, which only ever drew `decorations` (titlebars), never borders or shadows, so screenshots taken this way have never shown either; a real gap, not fixed in this pass.
2024-05-30Fix decoration drift during animated transitions; checkpoint ↵srdusr1-11/+225
IPC/global-menu/output-management work Border/titlebar decoration was built from Window.geometry (the animation's final target) in both wayland backends' render loops, while sync_geometry already draws a window's actual content at window_anims' interpolated rect during any maximize/fullscreen/open-slide tween. Border and content read two different rectangles for the whole transition, so the border visibly detached from the window it was outlining - reported as "borders aren't flush." Both udev.rs and winit.rs now read the same animated rect for titlebar placement, border-strip placement, and the occlusion test against later windows in stacking order. Verified: cargo build --workspace, cargo clippy (0 new warnings), cargo test -p srdwm-core (111/111). Also checkpoints substantial protocol/IPC work from prior sessions that had accumulated uncommitted: gtk-shell1 support (gtk_shell.rs, the vendored gtk-shell.xml, xwayland.rs's X11-side mirror) backing the global app menu; zwlr_foreign_toplevel_manager_v1 (foreign_toplevel.rs) broadcasting distinct maximized/minimized/fullscreen/activated state per window; output_management (ext-output-management + layer-shell exclusive-zone reservation tracking); workspace.rs and context_menu.rs; a Unix-socket IPC crate (platform/src/ ipc.rs) and a `srd` control-CLI crate (crates/ctl); xkb_config.rs; and a theme module (core/src/theme.rs). A peer session working the AGS shell concurrently verified several of these live against a running srdwm: the global menu rendering a real app's File/Edit menu over gtk-shell1, and foreign-toplevel correctly reporting maximized and fullscreen as independent, non-simultaneous states with the geometry each implies (maximize stops at a reserved top bar and past a dock; fullscreen reaches the true monitor edge).
2024-05-29Config at ~/.config/srd; cursor shapes, key repeat, pin, mouse defaultssrdusr1-2/+9
Config path drops a level: ~/.config/srd, not ~/.config/srdwm/srd, which said the same thing twice. No other user-facing path had the same problem -- srdwm reads the config dir and writes nothing else. Cursor shapes. A client's own cursor surface is now rendered with the hotspot it declared, so an I-beam over text or a hand over a link shows the app's image instead of srdwm's arrow. The built-in arrow stays as the fallback when no client has set one, over decorations and the desktop. Named shapes still fall back to the arrow; most toolkits set a surface. Decorations and cursors now share one OverlayElement type, since render_output takes a single custom-element slice. Key repeat (srd.bind_repeat, Hyprland's binde). Held volume, brightness and switcher keys repeat at the seat's own rate rather than firing once. Driven from the poll loop, not a timer source: the winit backend has no calloop loop of its own, and poll_events already runs continuously in both backends. Repeat stops when *that* key is released, not when any key is. Always-on-top / pin, for the picture-in-picture and HUD rules that used it. Window::always_on_top was another declared-but-never-read field. Enforced in WindowManager's stacking order rather than at render time, so every consumer of stacking_order gets it and none can forget to honour it. Mouse-only window management, checked end to end: drag the titlebar to move, drag any edge or corner to resize, titlebar buttons to close/maximise/ minimise, click to focus, drag to a screen edge to snap, and now double-click the titlebar to maximise. The resize grab band went from 6px to 10px - a hairline is genuinely hard to hit with a mouse, which is why Hyprland ships extend_border_grab_area. Also removed the emoji status markers from docs/IMPLEMENTATION_STATUS.md.
2024-04-02Rewrite srdwm in Rust: working X11 and Wayland backends, Lua configsrdusr1-0/+221
The C++ prototype (moved to legacy-cpp/) was mostly a design skeleton: X11 and Windows backends were partially real, Wayland created the wlroots object graph but never wired a single event listener, macOS was stub except monitor enumeration, and the Lua engine's srd.bind() stored a key-combo string but never the actual closure. See docs/PRIOR_ART.md for the full audit. This replaces it with a Cargo workspace: - srdwm-core: platform-independent window/workspace/monitor state, a real master-stack tiling layout, and SmartPlacement grid/cascade/ snap-to-edge placement - fixing several bugs in the C++ version (hardcoded 2-column grid, cascade that never cascaded, snap-to-edge that always returned a fixed rect). 35 unit tests. - srdwm-config: the srd Lua API via mlua, implementing the surface docs/DEFAULTS.md always documented but the C++ engine never actually built (srd.window.close()/focus(direction), srd.workspace.next(), real keybinding closures, require("srd") support). 10 unit tests. - srdwm-x11: a real reparenting WM with a drawn title bar (buttons, drag, resize), verified live under Xephyr - frame placement and client offset match srdwm-core's computed geometry exactly, and the decoration renders correctly on screen. - srdwm-wayland: a from-scratch smithay compositor (the C++ version had nothing working to port from) - runs via the winit backend, tracks xdg-shell toplevels through the same WindowManager and hit-testing code X11 uses, verified to start/render/run without crashing. Decorations are solid-color (no text yet); see docs/IMPLEMENTATION_STATUS.md for exact scope. - srdwm-windows / srdwm-macos: structured, cfg-gated designs informed by komorebi/glazewm and yabai/AeroSpace respectively (see docs/PRIOR_ART.md), honestly marked as unbuilt/unverified since this sandbox has no Windows or macOS target.