srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src/lib.rs
AgeCommit message (Collapse)AuthorFilesLines
2025-09-10Wayland backend: desktop icons v2, menu rebuild, layer-shell/scale fixes,srdusr1-0/+3
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.
2025-08-17Fix desktop icons v1 regressions: bar overlap, wrong order; add proper menussrdusr1-0/+1
Live testing found v1 genuinely broken, not just rough: 1. Icons weren't rendering reliably at all - ensure_desktop_icons only ever computed the grid's origin once, on whichever render pass happened to be first. AGS's own top bar registers its exclusive zone after that first pass, so origin got permanently baked in at the pre-bar geometry. Confirmed live via a temporary diagnostic log. Fixed by re-deriving origin from the primary monitor's current geometry on every call instead of just the first. 2. Fixed icons (Home/Computer/Trash) always sorted before real files -- confirmed wrong via direct question. The whole list now sorts alphabetically by label, case-insensitive, fixed icons included. 3. "Set as Wallpaper" was the wrong feature: removed entirely (DesktopMenuAction::SetWallpaper, general.wallpaper_command, is_image_path). The user wants that handled by their real file manager once opened, not reimplemented here. Also adds real menu functionality per "where are all the options": Rename (inline text edit, new CompState::renaming_icon field and keyboard redirect mirroring NativeLock::password's existing precedent), Delete (moves to ~/.local/share/Trash per the freedesktop.org spec, new trash.rs module, same-filesystem case, no confirmation - this is the reversible move-to-trash, not a permanent delete), Empty Trash on the Trash icon, and Open Terminal Here / Open in File Manager on the bare-desktop menu (new general.terminal config key). 133 wayland-crate tests (up from 106), full workspace build and clippy clean.
2025-08-15Add real desktop icons plus right-click desktop/icon context menussrdusr1-0/+3
Closes "right-click on bare desktop" - previously a true no-op, nothing rendered above the wallpaper at all. Requested directly: a real desktop "just like windows does" - Home/Computer/Trash plus one icon per real ~/Desktop entry, individually draggable with persisted grid positions, double-click to open, right-click menus (per-icon "Open" plus "Set as Wallpaper" for image files when general.wallpaper_command is set; bare desktop "New Folder"/"Refresh"). Architecture mirrors the existing context_menu.rs/snap_flyout.rs "compositor-owned floating UI" pattern: desktop_icons.rs (data model, grid layout, filesystem scan, hit-test), desktop_icons_state.rs (JSON persistence, same shape as monitor_layout.rs), desktop_menu.rs (the new right-click menu, reusing decoration::render_context_menu's existing rasterizer), state/desktop_icons.rs (CompState glue: rescan/select/drag/ open/persist), and a new decoration::render_desktop_icon rasterizer -- hand-drawn glyphs, since no PNG/SVG decoding capability exists anywhere in this workspace. Wired into both render loops (udev and winit) above the wallpaper and below every window, and into input/pointer.rs's button/ motion handlers for selection, drag, double-click, and both menus. Four new config keys: general.desktop_icons (default true - a directly requested, purely visual feature, unlike the opt-in-while-experimental general.gpu), general.file_manager, general.desktop_icon_single_click, general.wallpaper_command (all default off/empty). Deliberately out of scope for this pass, stated up front: move-to-trash and "Empty Trash" (destructive, no confirmation-dialog primitive to gate them on yet), filesystem watching, multi-select, per-mimetype icon art, icons on any monitor but the primary one. 124 wayland-crate tests (up from 106), full workspace build and clippy clean.
2025-02-15Checkpoint: preserve all uncommitted rust-rewrite worktree worksrdusr1-2/+8
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-24Accumulate wayland-crate additions: capture rendering, focus/Space sync, VT ↵srdusr1-0/+4
resume, plumbing Bundles the remaining wayland-crate changes built up here, touching both backends (udev and winit) and the shared input/rendering code: - udev/capture.rs: off-screen Pixman render of an arbitrary (not necessarily on-screen) workspace's window content to a PPM file -- what crates/core's capture-request queue drives, for a workspace switcher's thumbnail previews. wlr-screencopy structurally can't do this (it can only see what an output is presenting), which is why this exists as a separate render path rather than reusing it. - input::focus_window now also raises the window in smithay's own Space, not just core's stacking order - Space is what actually renders on top and what pointer hit-testing reads, so any focus path that skipped this (an IPC "focus" dispatch, concretely) left a window genuinely focused while still rendering, and receiving clicks, underneath whatever was already topmost. Both backends' poll loops now re-sync this after any IPC mutation. - udev/session.rs's VT-switch resume fix (drains a stale pending page flip before reasserting CRTCs) already has its own earlier, cleanly isolated commit - not duplicated here. - Assorted decoration/cursor/rounded-corners/output-management/ screencopy/XWayland changes and their cross-backend wiring. Coarser than the repo's usual one-purpose-per-commit convention, deliberately - see the core-crate sweep commit's own message for why.
2024-08-03Fix doc-comment file references left stale by today's six module splitssrdusr1-1/+1
~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-09Rounded corners on udev/Pixman backend, opt-in and off by defaultsrdusr1-0/+1
CPU-side rounded corners for the software-only udev/Pixman renderer, which has no shader stage to hook the existing GLES version into. Reads a window's own committed wl_shm buffer, punches premultiplied- alpha holes into the four corner regions, and hands the masked copy to MemoryRenderBuffer - the same path already used for titlebar/ border/shadow bitmaps, so it composites through the ordinary unmasked path and the corners genuinely disappear rather than being painted over. Cached per window, invalidated by a per-commit content_epoch counter rather than rebuilt every frame, so an idle window costs nothing once masked. general.rounded_corners now defaults per backend instead of one global true: on for GLES/winit (a real GPU shader, no measurable cost), off for udev/Pixman (an untested-on-real-hardware CPU cost for constantly-repainting clients) - WindowManager.rounded_corners_enabled is Option<bool> so the backend can tell "unset" from "explicitly off".
2024-06-24Real rounded corners on client content (GLES/winit backend), via a custom shadersrdusr1-0/+1
decoration.rs's round_top_corners only ever clipped the compositor's own titlebar/border bitmap - its own doc comment already said why nothing more had been done: clipping arbitrary client content needs a real per-pixel mask, "a much bigger change than this cosmetic pass". That's this change, for the one backend that can do it cheaply: the udev backend's PixmanRenderer is software-only with no shader stage at all, but GlesRenderer (winit) has a real custom-shader path (`compile_custom_texture_shader`, `TextureShaderElement`) that a first look at smithay's higher-level convenience APIs missed entirely. crates/wayland/src/rounded_corners.rs: a GLSL fragment shader masking a window's texture against a rounded-rect signed-distance field while sampling it (the same technique cosmic-comp/niri use for GPU-side rounded corners) - built by hand from the surface's own committed texture/view/ damage state (`RendererSurfaceState`'s public accessors), since no smithay convenience wrapper builds a masked element at all (`CropRenderElement` only crops to a rectangle). A decorated window rounds only its bottom two corners - the top two are already rounded, on the titlebar's own CPU bitmap, by decoration.rs, at the exact same `CORNER_RADIUS` (now `pub(crate)`, shared between the two so the curve reads as one continuous radius, not two different ones meeting at a seam) - an undecorated/CSD window rounds all four, since its content is the window's whole visible extent. Falls back to plain unrounded content on any failure (shader didn't compile, no committed buffer yet, a single-pixel-buffer surface), same "always show something over a prettier maybe-nothing" reasoning cursor.rs's built-in-arrow fallback already uses. Deliberately scoped to a window's *main* surface only, not subsurfaces - documented as a real, if narrow, follow-up rather than attempted here. `TextureShaderElement` only implements `RenderElement<GlesRenderer>`, not the generic `RenderElement<R>` every `OverlayElement<R>` variant needs, so it can't be added to that shared enum without breaking `OverlayElement< PixmanRenderer>` (used identically by udev.rs) the moment a GLES-only variant showed up in it. `WinitElement<=GlesRenderer>` (new, winit.rs-only) wraps the existing enum as one variant instead of touching it - this is the same lesson as the fullscreen-hiding investigation earlier this session, just resolved cleanly this time: nesting a *foreign* generic type inside your own hits real bound-resolution walls; wrapping your own already-working type inside a new concrete-renderer enum doesn't, because smithay's own `render_elements!` macro documents exactly this `<=ConcreteRenderer>` form. Config: `general.rounded_corners` (default `true`), `srd.window` unaffected - this is a `general.*` compositor-behavior knob, not a per-window rule action like `opacity`. Verified live: shader compiles without error on this machine's real Mesa/ llvmpipe GL driver, and a decorated wezterm window's bottom-left and bottom-right corners both show a real, smoothly anti-aliased curve on the actual client-rendered pixels (not a compositor bitmap) in a host-session screenshot - qualitatively sharper than `round_top_corners`' deliberate hard cutoff, since a GPU shader can afford a ~2px smoothstep a CPU bitmap pass isn't worth adding for. cargo build --workspace (all 9 crates), cargo clippy --workspace (0 new warnings), cargo test --workspace (197 tests, 0 failed).
2024-05-30Fix decoration drift during animated transitions; checkpoint ↵srdusr1-0/+9
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-3/+4
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-05-14Draw a cursor, handle the lid, and everything a real config needssrdusr1-0/+1
Groundwork for actually daily-driving this: porting the user's Hyprland config exposed what srdwm couldn't yet express, and testing on a bare TTY exposed something worse. A visible mouse cursor. Nothing drew a pointer at all - on a bare TTY the mouse was simply invisible. It hid because the nested backend runs inside another compositor, which draws a cursor over srdwm's window; only the DRM backend, i.e. the actual session path, was affected. A built-in arrow is now composited above everything on the output the pointer is on. It's a reviewable ASCII bitmap rather than an XCursor theme: a cursor that is always present beats a prettier one that sometimes isn't, the same reasoning as decoration.rs's font fallback. Client-set cursor surfaces and named shapes are still not rendered, so an app asking for an I-beam gets the arrow. Lid switch. libinput switch events are handled and surfaced to config as srd.on("lid_closed"/"lid_open", fn), so closing the lid can lock and suspend instead of doing nothing. Config-driven additions, each needed by a binding in the ported config and none of which existed: fullscreen (Window.fullscreen was a dead field -- declared, never read or written), directional window move that swaps with the neighbour and reorders the stack so tiling follows, focus cycling, modifier+drag to move/resize anywhere in a window rather than only by the titlebar, modifier+scroll to change workspace, and 8 XF86 media/power keysyms taken from the system's own XF86keysym.h. The keysym tables are hand-maintained in both directions and a key missing from either fails silently, so a round-trip test now covers every one the configs bind. Verified in the QEMU VM: a bare-TTY screendump shows a recognisable arrow at the pointer position (113 white fill + 58 black outline pixels at screen centre, where the pointer starts).
2024-04-08Wayland: clipboard, session lock, screencopy, multi-monitor; modularizesrdusr1-690/+12
Implements the protocols that were blocking srdwm-wayland from being a real session, plus multi-monitor, and splits the backend into modules. Everything here was verified by running it against real clients, not just compiled. Protocols - wlr-layer-shell + xdg-output: bars/launchers/notifications. xdg-output is not optional in practice - without it wofi segfaults rather than degrading, since it calls get_xdg_output without null-checking. - Clipboard: wl_data_device_manager, primary selection, and wlr-data-control. Data-control is what `wl-paste --watch cliphist store` needs, as it reads the selection without holding focus. Selection focus now follows keyboard focus, without which a focused window can neither copy nor paste. - ext-session-lock: `locked` gates rendering and input. No key is treated as a WM binding while locked - the config binds Mod4+Return to spawn a terminal, so honouring bindings at a locked screen would defeat the lock. The lock is confirmed only after a client-content-free frame has actually been presented, never at request time. - wlr-screencopy (hand-written; smithay ships no helper) for grim/slurp. Multi-monitor Every connected connector becomes a head with its own buffers, damage tracker and page-flip state, matched by CRTC so differing refresh rates don't gate each other. Outputs are reached through primary_output/output_at/ output_for_wl rather than a single field, which kept the change to ~11 call sites. Session lock creates one lock surface per output and waits for all of them, so a second monitor can't still show the desktop when the locker is told the session is safe. Modes are picked by the PREFERRED flag, not list order, and CRTCs are never double-assigned. Bugs found by testing, not review - Nothing gave a newly-created window Wayland focus: a freshly-opened app received no keystrokes and could not paste until clicked. - Opening a window at a locked screen stole keyboard focus. Caught by counting wl_keyboard.enter delivered to a client launched while locked: 1 before the guard, 0 after. A killed locker correctly leaves it locked. - Reading back the winit EGL window surface destroyed the GL context on the first screencopy capture, taking the compositor down. Root-caused by A/B-ing the same build with only the readback removed; capture now renders an offscreen pass. - Output mode was resent every frame at 60Hz, flooding any client bound to wl_output with duplicate mode/done events. - general.default_layout was defaulted and validated but never read, so setting it did nothing. srdwm is dynamic-first (Windows/macOS style, with drag-to-edge snapping); tiling is one opt-in layout, and that stays true. - .gitignore's unanchored `srdwm` matched any path component of that name, silently excluding the whole crates/srdwm source crate - the binary crate the workspace lists as a member, so a fresh clone could not build. Modularization lib.rs went from ~1260 lines to 78: state, protocols, input, lock, screencopy, winit and udev now each own one responsibility. lock is grouped by feature rather than kind on purpose, since its security invariant spans state, protocol handling and rendering at once. Multi-monitor was verified in the QEMU VM with a two-output virtio-gpu: both heads screendumped at their own resolution showing srdwm's clear colour, and a window forced to global x=1500 landed on head 1 at head-local x=220 while head 0 stayed empty. Known limitation, now documented: the nested winit backend stalls while its window is occluded, because the host stops scheduling frames and eglSwapBuffers blocks.
2024-04-05Track the daily-driver blockers for srdwm-wayland as TODOssrdusr1-0/+9
layer-shell, clipboard (wl_data_device_manager), session-lock, and udev-backend multi-monitor support are the gaps identified when deciding srdwm isn't ready to add to a session picker yet - recorded in docs/IMPLEMENTATION_STATUS.md's "Not implemented anywhere yet" section and as inline TODOs at the relevant code (CompState's delegate_*! list, udev.rs's find_connected_output).
2024-04-04Fix XWayland: it now actually renders and receives keyboard inputsrdusr1-5/+41
Three real bugs found and fixed via WAYLAND_DEBUG=1 protocol tracing in the QEMU VM, plus a fourth found along the way: - XWayland tried glamor (GBM rendering) first, which fails against this deliberately software-only compositor; its post-failure fallback path never used the xwayland_shell_v1 protocol at all, so X11Surface:: wl_surface() never resolved. Fixed by shadowing `Xwayland` on PATH with a wrapper script that always re-execs it with -shm (smithay's XWayland::spawn hardcodes its own argv and can't be bypassed either, since XWaylandClientData's fields are private). - Even with -shm, set_mapped(true) was only called after wl_surface() already resolved, deadlocking XWayland (it never advances a window past surface creation until the map is granted). Fixed by calling set_mapped(true) unconditionally in map_window_request. - The window then rendered as a ~1px sliver: initial geometry was seeded from X11Surface::geometry(), which can still be a tiny default at MapRequest time. Fixed by using the same 800x600 default the xdg-shell path already uses. - Typing didn't reach the window until a broader, XWayland-independent bug was fixed: nothing in the Wayland backend ever called KeyboardHandle::set_focus, so no window (native or X11) could ever receive keyboard input. Fixed in handle_pointer_button, along with TitlebarHit::Close being X11-surface-blind. Verified live: xterm launched via XWayland renders correctly sized and decorated, and a synthetic keypress sequence (ls + Enter) executed in its shell, screendump-confirmed.
2024-04-02Add window rules, real config validation, and a Wayland DRM/udev backendsrdusr1-54/+171
- 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/+594
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.