srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/platform
AgeCommit message (Collapse)AuthorFilesLines
2026-08-25Move a window in steps, and let the keyboard resize one at allsrdusr1-0/+8
Two of the owner's reported gaps, both about moving a window without a mouse. Super+Shift+HJKL slammed the window to the far edge of the monitor in one press: "it should move in increments - one side, middle, other side - not just extreme left/up/right/down". It now steps an eighth of the monitor per press, which crosses the screen in eight, passes through the middle at the fourth, and stops flush against the edge rather than short of it. A fraction rather than a pixel count, so the same key feels the same on a laptop panel and a 4K display. Only outside a tiling layout. There a window does not own its geometry -- stepping it would be undone by the next arrange - so the neighbour swap stays, and three tests that assumed swapping on a dynamic workspace now say which layout they mean. Keyboard resize did not exist. Super+right-drag has resized with the mouse for a while, which is why this went unnoticed, but nothing resized a window without one. `srd.window.resize("right", "grow"|"shrink")` grows or shrinks from the far edge in that direction, leaving the top-left where it is, and is bounded by the same minimums a drag is and by the monitor's usable area - a window can be resized neither to nothing nor off the screen, and a test holds each key down forty times to prove it. Wired into the owner's config: Super+Ctrl+arrows resize (the arrow says which way the far edge moves, so Right always widens), and Super+Ctrl+L locks the screen. Hyprland only ever bound the lock to XF86ScreenSaver, which most keyboards do not have; Super+L is the convention everywhere else but is already focus-right here. Ctrl+arrows rather than Ctrl+HJKL because Super+Ctrl+K is already kill-process. Verified through the real path in a nested compositor running that config: 89 bindings, all 89 described, the five new ones registered.
2026-08-05Show a keybinding the way a person writes it, and let bind_repeat be describedsrdusr1-1/+11
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 grabbedsrdusr2-2/+10
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-13Expose key bindings over IPC so a launcher can list themsrdusr2-0/+25
Asked whether srdwm's bindings show in the AGS launcher. They could not: srd.bind lives entirely in the Lua engine, and nothing published a binding anywhere a client could read it. There was no IPC command, no field in any response, and bound_keys() was used only by main.rs to register grabs. srd.bind now takes an optional third argument, a description, and the loaded set is copied into the WindowManager after the initial load and after every reload. Core neither owns nor interprets them - it has no Lua state and never dispatches a key - it holds them so the IPC layer, which is handed a WindowManager and nothing else, can serve them. New `srd keybindings` returns combo and description pairs, sorted so a UI listing them does not reshuffle on every refresh. Every binding in the shipped config now carries a description, so the feature is useful without the user writing any. Verified live in a nested instance: 46 bindings published, 0 without a description, and editing the config file updated the list without a restart (which also exercised reload-on-write again). Also verified, for the separate report that windows cannot be moved to another workspace from the AGS workspace pills: the compositor side works. `srd dispatch move workspace <id> 2` moved a window from workspace 1 to 2 and correctly hid it, since workspace 1 was active. Nothing to fix here; the missing piece is on the shell side. 515 tests pass, clippy clean.
2026-04-28Stop a config reload from undoing a change the user just made by handsrdusr4-1/+79
Reloading rebuilds the theme and general settings from the config file. That is right for a file edit, but it also wiped every live `srd set` - and the titlebar right-click menu's "Customize" rows are built entirely out of live `srd set`s. Changing a button style from that menu and then saving init.lua for any unrelated reason silently reverted it. Survivable while reloads only happened on Mod4+Ctrl+r. The reload-on-write support added in the previous commit makes a reload happen on every save, which turns a rare surprise into a reliable one. A control that silently reverts is worse than no control, so this is a defect rather than a documented quirk. The AGS peer session reached the same conclusion from the other side while deciding whether to build Settings controls against these values, and would have had to label them session-only. Every setting changed live is recorded on the WindowManager as key -> raw JSON text, and replayed after each reload through the very same handle_set that applied it, so a replayed setting cannot behave differently from a real one. Recorded only on success, so a rejected value is never replayed, and only for real client calls, so the replay cannot rewrite what it is reading. Last write wins per key. Raw JSON text because core has no serde dependency and no reason to gain one for this; the platform crate parses it back. Verified live in a nested compositor: set button_side left and button_mode fixed, saved an unrelated config edit, both survived, and the log reported re-applying two live settings. Three tests cover the round trip, the rejected-value case and the one-entry-per-key case. A live value is still a session override rather than a persisted setting. That distinction is now written down in DEFAULTS.md instead of being a trap. 515 tests pass, clippy clean.
2026-04-27Build the eight asks recovered from the previous session's transcriptsrdusr2-0/+17
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.
2026-01-31Fix desktop-icon deselection and workspace-teleport-on-close; document a ↵srdusr2-0/+13
shadow limit Desktop icons stayed highlighted after clicking a window: select_desktop_icon(None) was only ever called from start_desktop_marquee, never from the one place every focus path (click, Alt-Tab, dock IPC, scratchpad show, snap flyout) already funnels through. Added the deselect there instead of per-caller. Closing a focused window could silently switch the user's active workspace: remove_window's fallback picked self.order.last(), but that list is global, not per-workspace, so it could land on a background window elsewhere - and focus_window already switches workspace to match whatever it's given (a real, separate feature for a deliberate srd dispatch focus). Fixed by preferring a same-workspace window first. New general.close_focus_follows_workspace (default false, live-settable) controls what happens only when nothing is left on the current workspace at all: off leaves focus at nothing, matching Windows/GNOME/macOS; on restores the old always-follow-the-global-fallback behaviour. Three new tests. Also documented, not fixed: shadows can still bleed onto a neighbouring *monitor* near a multi-output seam (shadow_rect has no monitor-boundary awareness), found via a live cross-monitor screenshot. Moot for this session since general.shadows is already off in the live config, but a real, open gap for anyone who re-enables shadows on a multi-monitor setup.
2025-11-16Live-expose workspace.per_monitor, titlebar buttons, and desktop iconssrdusr3-1/+219
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 everywheresrdusr3-0/+179
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-10-29Fix set_monitor_split never actually reaching srd monitorssrdusr2-26/+38
Live-tested right after shipping it and caught immediately: srd dispatch set output split returned ok, but srd monitors kept reporting the whole, unsplit output. WindowManager::monitors is a passive cache, only refreshed when a backend re-queries and calls set_monitors again - the IPC handler mutated the split map directly but never triggered that requery, unlike set_output_position's own drain site, which already pushes a "just go recompute" event after applying. Makes it a proper queued cross-boundary request instead, the same shape as every other backend-owned effect on this socket: WindowManager:: request_monitor_split/drain_monitor_split_requests, dispatch queues instead of mutating, the udev backend's poll drains it, applies via set_monitor_split, and pushes the same recompute event. srd.monitor. split's Lua config-time path is untouched - it runs before the very first startup query, so it never had this problem.
2025-10-28Fix a fake monitor's layer-shell surfaces misrouting onto the real primarysrdusr2-0/+44
Live incident, root-caused jointly with the AGS peer session: creating a fake monitor visibly shrank the real primary output's usable area (full_y stayed 0 throughout - its true position never moved) each time, tracking almost exactly one bar height per fake monitor created. create_virtual_head registered its new Output in udev.virtual_heads but never in CompState::outputs, the list output_for_wl searches to resolve a client-named wl_output back to anything. new_layer_surface's own fallback for an output it can't resolve is landing on the primary output - so AGS's own per-monitor bar, aimed at the fake monitor it reasonably believed was a new real one, silently landed on the real primary output instead, stacking its own exclusive-zone reservation on top of the real bar already there. Two fake monitors, two misrouted bars, two zone increments, matching the observed climb exactly. Fixed by registering (and, on removal, deregistering) a virtual head's Output in CompState::outputs the same way bring_up_head already does for a real one. Also adds Monitor::is_virtual / MonitorInfo's "virtual" JSON field, requested directly by the AGS peer session as the real discriminator their own temporary FAKE- name-pattern match was standing in for. The X-position half of this same incident was AGS's own remembered- layout restore treating a fake monitor's wl_output as a real hotplug -- already fixed on their side (readArrangeable() now filters split/virtual outputs).
2025-10-26Live-expose monitor split, clean up leftover debug diagnosticssrdusr2-4/+99
srd.monitor.split only ever ran at Lua config load despite being a plain WindowManager mutation that every backend's monitors() already reads fresh on each call. Adds srd dispatch set output split <name|id> <parts> [rows|columns] (IPC set_monitor_split), same id-resolves-to-name pattern set_output_enabled already uses. Also removes eight log::warn!("XXX-DIAG ...") lines left behind from live debugging in the multi-session shift that landed in 3c41fc4 - the same "temporary, never removed" pattern already fixed twice earlier this session. Several fired on genuinely constant interaction (every title change, every workspace switch, every layer-shell surface hide), not just a one-off leftover. Left xdg_shell.rs's own POPUP-GEOM-DIAG/ POPUP-GRAB-DIAG alone - that one is a still-open, self-documented investigation, not litter. Also documents (docs/TODO.md, not a code change) a live incident where creating a second fake monitor visibly corrupted the real monitor's position and kept drifting with no further input - not root-caused srdwm-side, flagged to the AGS peer session since a fake monitor's real wl_output global is indistinguishable from a real hotplug to GDK/GTK. And documents a deliberate decision not to blind-port window decoration rendering onto the experimental, never-live-tested GPU render path.
2025-10-03Make the secondary-cursor sprite opt-in and expire stale entriessrdusr2-0/+11
Live report: a second cursor appeared uninvited and unusably (frozen, uncontrollable) on screen. Multi-cursor Phase 1 rendered one sprite per physical libinput pointer device that had ever reported a position, with no way to turn it off and no expiry - so a phantom device (a real mouse's side-button/scroll cluster enumerating as its own HID path is a common case) that reports once and never moves again left a frozen ghost sprite with nothing to control or dismiss it. Adds general.multi_cursor (default false, live-settable via `srd set multi_cursor <bool>`) and keys secondary_cursors to (Point, Instant) so both the recording side (udev/session.rs) and the render side (udev/render.rs) drop any entry older than SECONDARY_CURSOR_TIMEOUT (1.5s). The "agent controls a window without interrupting the user" use case this report also raised was never gated on this flag - that's Multi-cursor Phase 2's pinned virtual-pointer delivery, which never shows a visible cursor at all.
2025-10-03Split ipc.rs into ipc/ by concern, and fix a stale READMEsrdusr5-1894/+1920
Codebase modularization, requested directly. Surveyed the whole workspace first: at ~38k lines it's already organized by topic (crates/core/src/manager/, crates/wayland/src/{state,udev,decoration}/ already split into small per-concern files) - crates/platform/src/ ipc.rs was the one real outlier, 1894 lines holding the socket lifecycle, every payload type, both dispatch match statements, and its own tests all in one file. Split into ipc/{mod,types,dispatch,tests}.rs by concern, matching the established pattern exactly - mod.rs keeps IpcServer itself, types.rs the response/event structs and snapshot functions, dispatch.rs handle_request/handle_set, tests.rs the existing suite moved verbatim. Extracted via exact line-range copies against git's own HEAD content (not retyped), specifically to rule out a transcription bug in a file this central. Pure reorganization: build/test/clippy clean before and after, exact same test count (29 in crates/platform) both times. README.md separately corrected: it still linked to legacy-cpp/ (deleted this shift) and described the Wayland backend as the smaller, less-done one - backwards from current reality, where Wayland is the daily-driver target and by far the more complete backend.
2025-09-26Fake (headless) monitors, and fix new windows all opening in one spotsrdusr1-0/+21
Two independent pieces landed together this pass - both real, both scoped, see docs/TODO.md for the full narrative on each: Fake monitors: a genuinely independent, additional wl_output with no DRM connector/CRTC behind it at all - distinct from srd.monitor.split (divides one real output's own placement rectangle). Researched niri's own Headless backend first (cloned at ~/reference-wms/niri): its render() never actually composites anything, a no-render stub for that project's test suite only. This one is real: it renders whatever is placed on it, on demand, whenever a zwlr_screencopy_manager_v1 client asks for a frame. New crates/wayland/src/udev/virtual_heads.rs (create/remove a real Output + global, render-on-demand for screencopy, integrated into platform.rs's monitors() as a genuine srdwm_core::Monitor so core placement/workspace code needs zero special-casing). New IPC/CLI: srd dispatch create fake-monitor <name> <w>x<h> / remove fake-monitor <name>. Core-side request queue in crates/core/src/manager/fake_monitor.rs. Placement bug, root-caused and fixed: every new window opened alone landed in the exact same spot, "not at all like Windows" (reported live). SmartPlacement::place tried a grid cell first, and grid's own cell count is existing.len() + 1 - with nothing else open (opening one app at a time, the ordinary case), that's always 1, so a 1x1 grid returns the same single cell forever regardless of session history. Cascade had the same bug in a second form (its own step was existing.len() % max_steps, also always 0 with nothing open). Fixed: WindowManager::next_cascade_step (a Cell - add_window's own target_monitor stays borrowed across the call) advances on every real placement and is never reset by a window closing; place() now skips grid entirely when nothing else is open, going straight to cascade, since grid's real job (dividing space among concurrent windows) has nothing to divide when there's no concurrency. Full workspace build/test/clippy clean (223 core / 141 wayland / 29 platform / 24 ctl / 28 config / 10 x11 tests, 0 failed, 0 clippy warnings), built and installed.
2025-08-31Wire aspect_ratio, phone_mode and pin_input through config/IPC/CLIsrdusr1-0/+43
The Lua and IPC/CLI surface for the three new core primitives (crates/core: aspect_ratio rule action, general.phone_mode, virtual- pointer window pinning): - srd.rule(..., { aspect_ratio = "9:16" }): parses a "W:H" string into a validated (u32, u32), rejecting a malformed value as a real Lua error at config-load time rather than silently ignoring it. - general.phone_mode config default, plus srd set phone_mode <bool> for the live equivalent (same shape as animations/shadows/rounded_corners). - pin_input IPC dispatch ({"cmd":"pin_input","pid":<pid>,"id":<window id>}, id omitted to unpin) and its CLI surface, srd dispatch pin input <pid> <window-id> / unpin input <pid>. Keyed by the owning client's process id, not an opaque per-object id nothing outside the Wayland backend could ever learn - a controlling tool already knows its own pid for free. See docs/TODO.md for the full design reasoning behind each of these.
2025-02-15Checkpoint: preserve all uncommitted rust-rewrite worktree worksrdusr1-46/+628
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-12-05Accumulate platform/ctl-crate additions: capture workspace, IPC responses, ↵srdusr3-34/+802
CLI verbs Bundles related IPC-surface and CLI additions built up over this session: - capture_workspace IPC command + srd capture workspace CLI verb (see the wayland-crate commit for the off-screen render this drives). - Expanded IPC response payloads (monitors, client events, global menu info) and their matching CLI plumbing. Same reasoning as the core-crate sweep commit for why this is coarser than the repo's usual convention: too entangled to split safely without a dedicated review pass.
2024-10-31Add global-menu support (dbusmenu/appmenu) for Wayland and X11 clientssrdusr1-0/+142
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-10-30Add srdwm's own native session-lock screen (blur, PAM auth)srdusr1-0/+58
A real ext-session-lock-v1 implementation drawn by the compositor itself, not a hand-off to an external locker process: a live-blurred background, PAM authentication on a background thread, and its own config surface (lock_config.rs) rather than hardcoded appearance.
2024-08-21Add toggle_maximize/toggle_fullscreen IPC dispatch actionssrdusr1-0/+14
Same shape as the existing toggle_visibility/focus/close dispatch actions - lets an external script (or a live diagnostic check) drive either without a keybinding already existing to trigger it. Immediately useful for verifying the maximize/fullscreen contract on a live session without synthesizing any input at all.
2024-08-19Document that ClientInfo's geometry is the frame rect, not contentsrdusr1-0/+10
A peer session pointed out srd clients reports a decorated window's rect 30px taller than X11 reports the client's own content window -- correct (Window.geometry is the frame rect, TITLEBAR_HEIGHT included on top, the same convention hit-testing/rendering already use internally), but genuinely undocumented from an external IPC consumer's point of view. Made the frame-vs-content distinction explicit on the field itself rather than leaving it to be reverse- engineered from a pixel diff.
2024-08-03Fix doc-comment file references left stale by today's six module splitssrdusr1-2/+2
~17 comments across the codebase still pointed at udev.rs/winit.rs/ state.rs by their old flat-file names after those became udev/, winit/, state/ directories - found while auditing what this work rushed, since the split verification (function/struct-name diffing, full test suite) checked structural correctness but never comment accuracy. Updated each to either the specific new file (e.g. "see state.rs's REPEAT_DELAY" -> "see state/mod.rs's REPEAT_DELAY", "udev.rs's monitors()" -> "udev/platform.rs's monitors()") or the bare module name where the reference was already generic ("the udev/winit backends", not a specific location).
2024-07-10Fix three pre-existing clippy warningssrdusr1-1/+1
- ipc.rs: drop a u64 -> u64 no-op cast (WindowId is a plain u64 alias). - decoration.rs: draw_line took 8 args; bundle the two endpoints into (i32, i32) tuples instead of four loose coordinates. - output_management.rs: collapse a WEnum::Value if-let directly into the outer SetTransform match arm's pattern.
2024-05-30Fix decoration drift during animated transitions; checkpoint ↵srdusr3-0/+512
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-7/+54
- 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 configsrdusr2-0/+110
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.