| Age | Commit message (Collapse) | Author | Files | Lines |
|
Extends the SRDWM_GPU=1 opt-in path (Phase 1, fa0c7f1) from a
capability probe into an actual, working GPU render pipeline for
exactly one head, per the plan this was built from
the plan file.
gpu.rs: probe now goes all the way through EGLContext, GlesRenderer,
DrmDevice (real DRM device, separate duped fd from the existing legacy
Card), and DrmOutputManager construction, returning both a GpuContext
and its DrmDeviceNotifier on success. GpuContext::initialize_output
drives one crtc/mode/connector through DrmOutputManager, storing the
resulting GpuOutput for the render loop to find.
Confirmed while reading smithay's own source directly (not assumed):
DrmDevice::new's disable_connectors parameter is not an atomic-vs-
legacy switch - DrmDevice::create_internal tries atomic capability
first and falls back to a Legacy internal variant automatically,
exposed via DrmDevice::is_atomic(), logged here rather than assumed.
platform.rs: probes at startup, calls initialize_output for the first
head only (Phase 2's deliberate scope - see the plan), registers the
DrmDeviceNotifier as its own calloop event source alongside (not
replacing) the existing legacy DRM-fd registration.
render.rs: render_udev_frame's per-head loop checks, before any of the
existing Pixman-specific element-building logic runs, whether this
head's crtc matches the GPU context's initialized output; if so,
renders a plain clear color through render_frame/queue_frame and
continues to the next head, completely bypassing the Pixman path for
that head. Every other head, and this same head whenever the GPU
context or its output is absent, is entirely unaffected.
session.rs: register_gpu_drm_notifier handles DrmEvent::VBlank by
calling frame_submitted() on the matching GpuOutput (required per
queue_frame's own doc comment, or the swapchain runs out of buffers)
and logs DrmEvent::Error without treating it as fatal.
Deliberately out of scope for this phase (documented in the plan):
window content/decorations/cursor on the GPU head (clear color only),
multi-monitor GPU rendering (one head only), and VT-switch pause/
resume for the GPU head specifically (DrmOutputManager's own pause()/
activate() calls are a different API surface from the
existing manual set_crtc+DPMS reassertion, and porting that pairing
correctly needs its own isolated verification pass).
SRDWM_GPU unset (the default) is unaffected: every step above only
runs when it's set to "1", and every failure at any step falls back
to the existing, untouched Pixman path with a logged reason, same
fallback contract Phase 1 already established.
|
|
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.
|
|
Remaining files from the backlog: Lua config engine
additions (general/register/support/window), workspace Cargo.toml/
Cargo.lock churn from the new dependencies added elsewhere in this
sweep, srdwm/main.rs wiring, and docs (DEFAULTS.md plus a new
SESSION_HANDOFF.md written mid-session for continuity across a restart
- see that file's own header for what it is and isn't).
|
|
built-in bitmap
The compositor's own pointer - over decorations and the desktop, and as
the fallback for any named shape with no dedicated art - was always the
hand-rasterized 24px ARROW bitmap, regardless of what theme the rest of the
session was using. Confirmed live (by the AGS peer session) that this
machine's actual GTK cursor theme is Sweet-cursors at size 24 (both
gtk-3.0 and gtk-4.0 settings.ini, and gsettings, agree), installed and
present, but nothing read it - XCURSOR_THEME/XCURSOR_SIZE aren't set on
this work either, so even naive env-based theme loading would have
found nothing.
load_theme_arrow (cursor.rs) now resolves a theme name and size --
XCURSOR_THEME/XCURSOR_SIZE first, falling back to GTK's own
gtk-cursor-theme-name/-size out of settings.ini, since that's what's
actually authoritative in practice here - and loads left_ptr via the
`xcursor` crate (the same one anvil and other smithay compositors use),
picking whichever nominal size the theme ships is closest to the target.
XCursor pixel data comes back as straight RGBA off disk; only the channel
order needs converting to the BGRA every other buffer in this file uses for
Fourcc::Argb8888 (see arrow_bitmap's own byte order) - the data is already
premultiplied alpha per the file format spec, same as everything else built
here, so no premultiplication step. Falls back to the existing built-in
bitmap arrow on any failure (theme/icon missing, corrupt file, a pixel
count that doesn't match the declared dimensions) - same "always present
beats prettier but sometimes absent" reasoning the built-in arrow's own doc
comment already gave for not doing this at all, kept intact as the
fallback rather than replaced.
The built-in arrow's hotspot was implicitly (0, 0) - its tip, baked into
where render_elements positioned it. A real theme's hotspot is data
(CursorBuffers::arrow_hotspot), not necessarily the bitmap's corner, so
both `_ =>` arrow arms in render_elements now subtract it like every other
named shape already does.
Verified live on this machine: resolves theme="Sweet-cursors" size=24 from
settings.ini, loads a real 30x30 left_ptr image with hotspot (4,4) --
checked with a temporary probe test, removed before committing. Two
lightweight unit tests (rgba_to_bgra_*) cover the channel-reorder byte math
without needing a theme installed, so they run everywhere. cargo build
--workspace, cargo clippy -p srdwm-wayland (0 new warnings), cargo test -p
srdwm-wayland cursor (13/13), cargo test -p srdwm-core (111/111) all pass.
Known remaining gap, not addressed here: the loaded size still doesn't
track output scale (a HiDPI output gets whatever size settings.ini says
regardless of scale factor) - MemoryRenderBuffer's own scale param is
hardcoded to 1 throughout this file already, a pre-existing limitation
this change doesn't touch.
|
|
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).
|
|
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.
|
|
- 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.
|
|
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.
|