srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/core/src/lib.rs
AgeCommit message (Collapse)AuthorFilesLines
2026-08-05Show a keybinding the way a person writes it, and let bind_repeat be describedsrdusr1-1/+1
Measured with dotfiles-1a, who own the launcher that displays these: `srd keybindings` returned 84 bindings and 84 empty descriptions - 100% - so every entry their launcher showed was a bare key combo with nothing to say what it does. Two causes, one on each side of the boundary. `srd.bind_repeat` never accepted a description. `srd.bind` has taken an optional third argument all along, but its repeating sibling took only two, and mlua drops a surplus argument silently rather than raising - so a config that documented its repeat bindings got no error and no description. It now takes one exactly like `bind`. The combos themselves were reported in their internal dispatch form: `Shift+Mod4+h`. `Mod4` is the X11 modifier's name, not a key's; nothing on a keyboard is labelled Mod4, and the canonical Ctrl/Shift/Alt/Mod4 ordering renders the owner's own `Super+Shift+h` binding back to them inside out. `srd keybindings` now reports a display form - Super, and the order people write - while everything internal keeps the canonical form it dispatches on. The display form parses back to the same binding, so it can be pasted into a config, and a test pins that round trip rather than trusting it. The owner's own config now describes all 84 bindings; verified through the real path, in a nested compositor running that config: 84 of 84 described, zero occurrences of "Mod4".
2026-05-13Report whether a listed key binding is actually grabbedsrdusr1-0/+1
Requested by the AGS session while wiring its launcher to srd keybindings: without this the launcher would list a shortcut that does nothing and give no way to tell, which is the same silent-lie class of bug as the rest of the work today. The backend is handed one combo list, once, before connecting - X11 turns it into XGrabKey calls, Wayland into its intercept set. A reload re-registers the actions but cannot re-register the grabs, so a combination added to the config since startup is bound as far as the config engine is concerned and still goes straight to the focused client when pressed. That snapshot is now recorded on the WindowManager at the exact point it is handed to the backend, taken once rather than per-arm so the reported set and the grabbed set cannot drift apart, and each entry in srd keybindings carries a `grabbed` flag. Verified live in a nested instance, both states observed: 46 bindings and 46 grabbed at startup; then appending a new combination to the config gave 47 bindings, 46 grabbed, with the new one reported as not grabbed and carrying its description. Two unit tests cover the set being replaced rather than accumulated, and a combo outside it reading as not grabbed. Also recorded, from the AGS session's own checks: moving a window to a workspace was their bug, not a missing compositor feature - the Overview's previews had a drop target and the bar's workspace dots had none, so the gesture worked on one surface and silently did nothing on the other. And the static half of "are all keybindings working" is clean for the running session: keybindings.lua was last modified 06:02:55 and the compositor started 18:11:13, so every combination in it was grabbed at boot. 517 tests pass, clippy clean.
2026-05-11Fix spawn placement under the top bar, add per-window minimum sizes, and ↵srdusr1-1/+1
clean up maximize Four reports after restarting into today's build, with a screenshot. The screenshot was measured, not eyeballed: top bar y=0..29, a 4px accent border at y=30..33, titlebar from y=34, bottom border at y=1027..1030, and 49px of bare desktop below it. Windows spawning too close to the top bar. A remembered position was validated only by asking whether it landed on some monitor's full_geometry, which includes the strip a top bar reserves, so an app whose remembered y was small reopened with its titlebar under the bar. That is why it was "sometimes": it depended on the stored value, and the live store holds wezterm at y=44 and firefox at y=69 against a 30px bar. Remembered positions are now clamped into the monitor's usable area. Placement not surviving a logout. Window memory does persist, but five of the eleven entries in the live store were saved with a second monitor attached, at x >= 2000. Those points match no current monitor and were discarded outright, falling back to a fresh cascade, so those apps appeared to remember nothing. Such a position is now clamped onto a monitor that exists instead. Per-window minimum sizes. One global floor is wrong in both directions. Three sources now, in increasing precedence: the global floor, the client's own declared minimum (xdg_toplevel.set_min_size or ICCCM hints), and a min_width/min_height window rule overriding both. A rule wins permanently -- the backend refreshes the client's declared minimum on every decoration redraw and must not undo a deliberate override. Maximize, three faults in one report. A maximized window now draws no border: its edges are the screen's edges, and the only place maximize stops short is the bar strip, which is exactly where the measured line was. maximize_geometry_for no longer subtracts a bottom-anchored dock's exclusive zone, so maximize runs to the bottom of the screen and the dock floats over it; top, left and right are still honoured. general.maximize_covers_dock = false restores the old behaviour. With the border gone the window sits flush under the bar instead of with an accent line crowding it. Verified: seven new tests on the real numbers from the live store, and maximize geometry measured live in a nested instance (a window maximized on a split half reports exactly that half's rect). NOT confirmed on screen: the border removal and the dock behaviour - the nested backend has no bar or dock to reserve a zone, and an attempt to check the border produced a failing control, since srd set border_width only affects windows created after it. 515 tests pass, clippy clean.
2026-04-27Build the eight asks recovered from the previous session's transcriptsrdusr1-1/+1
The punch list was not the whole record. These are the owner's own typed requests, read back out of the previous session's transcript rather than guessed at, then each checked against the code before being treated as open. Three they suspected were already done really were: dialogs already had a Close-only titlebar, inactive dimming already existed, and the corner resize hitbox had already been tuned. Snap layouts on drag, asked for twice. Edge snapping worked but committed silently on release with nothing shown first, so there was no way to know it would happen or where. A translucent drop-target preview now follows the drag, and throwing the pointer at a monitor's top edge drops down the existing six-cell grid to aim at. The preview calls the same snap_zone that end_drag does, so the two cannot disagree. Two defects found by screenshot before landing: moving down onto the flyout closed it, and its labels overflowed at a fixed cell width - the same "text goes out of view" fault already fixed once for the context menu. New File now offers real types, chosen by extension, with the de-duplication counter placed before the extension so the file stays what it says it is. Refresh re-reads init.lua and fires a new srd.on("refresh") handler instead of only re-scanning the icon grid. What refresh means beyond srdwm's own config stays the config's decision. general.config_reload_on_write (default on) applies an edited config on save, via an mtime sweep rather than an inotify watch: no new dependency, same behaviour on every target, and unaffected by editors that write through a temp file. A real bug behind "what happens when our config fails": you lost every keybinding. do_reload cleared the binding, handler and repeat tables before re-executing and never restored them, so a syntax error left neither the old config nor the new one, and the only key still working was the reload combo nobody thinks to press. The tables are now restored on any failure and config errors reach notify-send, not just the log. srd.lock() and a default Mod4+Ctrl+l binding: the built-in lock screen could not be reached from Lua at all. Native rather than shelling out, because a lock key that shells out fails silently when the binary is not on PATH. Default bindings added for srd.window.move and a dynamic/tiling toggle, both of which existed with no way to reach them, plus srd.layout.get() so the toggle reads the live workspace rather than the configured default. Dialogs open centred, and are excluded from remembered geometry in both directions - that table is keyed by app_id, which a dialog shares with the window that spawned it, so dialogs inherited an unrelated position and size and then overwrote it with their own. theme.decorations.title_bar.button_mode (dynamic by default, or fixed) drops the Maximize button on a window whose client pinned min == max size, where pressing it can do nothing. Maximize is removed from the slot list rather than skipped in place on both the render and hit-test sides, so the remaining buttons close the gap identically; three tests pin that agreement, which is what fails silently when it drifts. Also: the nested backend's screencopy pass now draws both menus, the flyout and the drag preview. Four investigations in one day started from a screenshot missing a tier, so that pass carries an explicit list of what it still omits and the on-screen loop points at it. 512 tests pass, clippy clean.
2025-11-16Live-expose workspace.per_monitor, titlebar buttons, and desktop iconssrdusr1-1/+1
Closes the remaining "config-file only" gaps from the AGS capability survey. workspace.per_monitor gets srd set per_monitor <bool> - safe to flip live since a monitor with no per-monitor override already falls back to current_workspace regardless of mode, so nothing visually jumps on toggle. theme.decorations.title_bar.button_style/button_side/button_order and general.desktop_icons/desktop_icons_all_monitors all get the same live-set + SettingsResponse readback treatment as everything else this session. New srdwm_core::format_button_order is parse_button_order's exact inverse, so button_order's readback matches the same string shape srd set itself accepts.
2025-11-07Make tiling's master/stack ratio live, add settings readback everywheresrdusr1-1/+1
Investigated the "tiling needs a lot of work" report directly. The MasterStackLayout algorithm itself was already correct; the real gap was that dragging or resizing a tiled window did nothing durable (raw geometry that the next arrange_workspace silently discarded), and master_ratio/master_count had no live path at all (config-file only). A resize-drag on the shared master/stack boundary now live-adjusts TilingConfig::master_ratio and re-arranges the group immediately; srd set master_ratio/master_count do the same for a keybind or script. Found and fixed a real bug while building this: start_resize's own focus_window call re-stacks its target in self.order before the ratio-drag decision used to be made, silently misclassifying real master-column grabs. Fixed by deciding ratio-drag status (and freezing the membership snapshot it depends on) before that raise happens, applying MasterStackLayout directly against the frozen snapshot rather than re-deriving membership from the by-then-reordered live order. Live- verified in a nested compositor, not just unit-tested. Also closes the readback gaps flagged directly by the AGS peer session: border_width/border_color/corner_radius/decoration_mode/gap_inner/ gap_outer/master_ratio/master_count were all live-settable via srd set with no way to read the current value back, and pin_input had no readback at all. SettingsResponse now reports all of them; a new pinned_inputs query (srd pinned inputs) lists every currently pinned pid/window.
2025-08-25X11 backend: right-click titlebar window menu, matching Wayland's ownsrdusr1-0/+2
Closes the one real gap an X11/Wayland feature-parity audit found this session (desktop icons, window-position memory, and static exclusive-zone reservation were already shared or Wayland-only by nature - see docs/TODO.md's own audit entry for the full breakdown). MenuAction/ContextMenu (row set, labels, row_at hit-testing) move from crates/wayland/src/context_menu.rs into crates/core/src/context_menu.rs -- pure state and geometry with nothing Wayland-specific in it, so X11 needing the same rows is shared data, not duplicated logic. The Wayland crate's own context_menu.rs is now a one-line re-export so every existing crate::context_menu::... call site keeps working unchanged. X11 has no compositor-level input dispatch to intercept every click the way Wayland's input/pointer.rs does, so the X11 side (crates/x11/src/platform/context_menu.rs, new) draws the menu into its own small override-redirect popup window and grabs the pointer for the duration so a click anywhere dismisses it, matching the Wayland backend's own convention. events.rs's ButtonPress handler now reads the real button number instead of hardcoding every press as a left click - a real latent bug (right-clicking a titlebar button would have silently performed its left-click action). Live-verified end to end in an isolated Xvfb + srdwm --x11 instance: full row set including the workspace picker, Minimize runs and closes the menu, a second window's menu dismisses cleanly on outside click, normal focus/click behaviour continues working afterward. See docs/TODO.md for the full investigation and verification narrative.
2025-02-15Checkpoint: preserve all uncommitted rust-rewrite worktree worksrdusr1-2/+5
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-11-30Accumulate core-crate additions: capture requests, focus/workspace fixes, ↵srdusr1-3/+5
test coverage Bundles several related changes to crates/core built up over this session rather than committed incrementally: - WindowManager::request_capture_workspace/drain_capture_requests (new manager/capture.rs) - backend-agnostic queuing for an off-screen workspace render, see the wayland-side commit for why this exists. - focus_window now switches workspace as a side effect when the target isn't on the current one, matching Hyprland's focuswindow convention (manager/focus.rs). - Assorted window/rules/theme field additions and their test coverage. Left less granular than the repo's usual one-purpose-per-commit convention deliberately: these accumulated across a long session without being committed as they landed, and are too entangled line-by-line to safely split apart now without risking mis-attributing changes to the wrong commit message.
2024-05-30Fix decoration drift during animated transitions; checkpoint ↵srdusr1-2/+5
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-04-02Add window rules, real config validation, and a Wayland DRM/udev backendsrdusr1-1/+4
- srd.rule(): match windows by title/class, apply floating/maximized/ workspace/geometry/decoration actions on creation (crates/core/src/rules.rs) - srd.validate_config()/srd.debug.*: real range/format checks and status/profiling helpers, replacing the always-true stub - Wayland titlebar text rendering via fontdue, unit-tested without a display (crates/wayland/src/decoration.rs) - Wayland precise keybinding matching, replacing the "any Super-held key" heuristic, sharing the keysym table with X11 (moved to crates/core/src/keysyms.rs) - Wayland DRM/udev backend (crates/wayland/src/udev.rs): runs as the real compositor on a bare TTY via libseat/libinput/KMS, software rendering via Pixman + dumb buffers (no GBM/EGL required) - srdwm_platform::detect() fix, found via VM testing: a bare TTY with no DISPLAY/WAYLAND_DISPLAY now correctly resolves to Wayland instead of an X11 backend that can never work there - XWayland integration groundwork (crates/wayland/src/xwayland.rs): spawn, X11Wm, and full XwmHandler event routing into the same WindowManager/ Space pipeline as native clients. Windows don't render yet - a real glamor-vs-software-renderer conflict in XWayland's own fallback path, root-caused via WAYLAND_DEBUG tracing and documented in docs/IMPLEMENTATION_STATUS.md rather than worked around blind. All verified live in an isolated QEMU VM: X11 backend shows two decorated, correctly-tiled xterms with real title text; the DRM/udev Wayland backend opens the GPU, initializes input, and scans out a rendered frame via KMS page-flip.
2024-04-02Rewrite srdwm in Rust: working X11 and Wayland backends, Lua configsrdusr1-0/+17
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.