| Age | Commit message (Collapse) | Author | Files | Lines |
|
|
|
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.
|
|
Reported live, in stages: general input sluggishness, then specifically
dock/bar buttons not responding on the secondary monitor. Root-caused
jointly with a peer session (dotfiles-16), who independently instrumented
AGS itself (both bar and dock report correct visible/realized/revealed
state - the client is asking for the right thing) and srdwm's own
layer_hit_test log (the dock received zero hits across ~40 minutes while
the same output's wallpaper and bar took hundreds).
Confirmed against smithay 0.7.0's own source (desktop/wayland/layer.rs::
arrange): LayerMap::arrange() divides the output's physical mode by its
own scale before arranging layers, so LayerMap::layer_geometry() is
logical, not physical. Two call sites used it as physical, this
compositor's convention everywhere else:
- input/layers.rs::layer_surface_under_layers compared the physical
pointer position directly against logical layer geometry. On a
sub-1.0 scale output, logical space is larger than physical, so a
bottom-anchored dock's rect sat entirely past the pointer's reachable
range - permanently unclickable. A top-anchored bar only lost its own
right-hand end, which is what made this look like "the dock is
broken" rather than a scale bug affecting every layer surface there.
- elements.rs::output_layer_elements pushed the same logical position
straight into the physical framebuffer - for the dock, past the
bottom edge entirely, painting nothing.
Both fixed the same way udev/platform.rs::monitors() and udev/outputs.rs
already fix the identical unit mismatch for usable-area computation
(existing precedent, not a new technique): multiply by output.
current_scale().fractional_scale(), rounding to the nearest physical
pixel, before use.
Also removed a temporary per-pointer-motion-event diagnostic log in
layer_hit_test, still live from an earlier debugging session and
explicitly marked for removal but never removed - a real, measurable
cost on the hot input path, likely the direct cause of the separately
reported general slowness.
Full workspace test suite and clippy clean.
|
|
Pre-existing issues in the uncommitted rust-rewrite work (unused
imports, over-arity glyph-drawing functions, a couple of complex inline
types, or_insert_with(T::default) instead of or_default(), and one
unsimplified test-only arithmetic expression), plus two imports left
unused by switching to or_default(). None of these are behavior
changes. Full workspace build + clippy -D warnings + test suite (378
tests) all green after this.
|
|
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.
|
|
popup_targets (used for both drawing and hit-testing xdg_popup surfaces)
never subtracted the xdg_surface::set_window_geometry content offset the
rest of the codebase already accounts for - a CSD window's popups were
placed relative to its raw, unshifted buffer origin instead of its real
visible content, self-consistently wrong in both rendering and click
routing.
|
|
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.
|
|
~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).
|
|
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".
|
|
opacity
The user asked for per-window opacity (MISSING.md's `windowrule = opacity`
gap) and pushed back on treating smithay's convenience wrappers as a hard
ceiling: "don't rely on smithay, it won't have everything we need." Looked
again at why opacity was ruled out earlier - `render_output`/
`space_render_elements` take one `alpha` for the whole frame's `self.space`
content, no per-element control - and found a path that doesn't need
nesting smithay's internal `SpaceRenderElements` type (the approach that
hit an unresolvable generic-bounds wall investigating fullscreen-hiding
earlier): call `render_elements_from_surface_tree` directly,
once per window and once per layer-shell surface, each with its own alpha,
wrapping the result in the *existing* `OverlayElement::Surface` variant.
That primitive was already proven safe in this codebase (cursor.rs's
client-image path, this file's own popup rendering) - reusing it here for
a window's main content is the same call, not a new one.
Both `udev.rs` and `winit.rs`'s render loops now build window content and
layer-shell surfaces themselves (`elements.rs`: `surface_content_elements`,
`output_layer_elements`, `window_wl_surface` for the Wayland/XWayland
split), in the correct front-to-back order, then call
`OutputDamageTracker::render_output` directly instead of the
`space::render_output`/`space_render_elements` convenience wrappers. Content
itself needs no occlusion clipping against `occluders` (unlike border/
titlebar bitmaps) - pushed in the same front-to-back order as everything
else, ordinary painter's-algorithm draw order already occludes it correctly,
the same property it had via `self.space`'s own order before. `self.space`
stays mapped and `resync_stacking_order`-maintained exactly as before; only
the render step stopped reading from it.
A comment in winit.rs's render loop warned that a near-identical earlier
attempt was reverted for a real ordering bug (whichever window was created
first always painted in front, regardless of focus). That bug's actual root
cause, identified and fixed since, was `Space::map_element` silently
re-stacking on every geometry sync, independent of which render path was
used - see `resync_stacking_order`. This rewrite never reads `Space`'s
internal order for rendering at all (`ids` comes from `WindowManager.order`
directly, the same source `hit_test` already trusts), so that specific bug
class can't recur here regardless of whether `resync_stacking_order` ever
drifts again.
Bonus from the same infrastructure: the bar/dock now genuinely don't render
at all (not just get covered) for a fullscreen window - `output_layer_elements`
skips `Layer::Top`/`Overlay` entirely when any visible window is fullscreen,
the hardening this work backed away from earlier for being too risky to
build via the nested-SpaceRenderElements approach. `capture_offscreen`
(winit.rs's screencopy path) picked up opacity-aware content and layer-shell
inclusion too, though not full parity with the on-screen loop (still no
border/shadow strips there - a pre-existing, separately-flagged gap).
Opacity itself: `Window.opacity` (core), `WindowRuleActions.opacity` /
`srd.rule(..., { opacity = 0.9 })`, `srd.window.set_opacity()`. Caught live,
before commit: opacity was wired into `add_window`'s own rule match but not
`reapply_rules_if_pending` - the *only* path a class-based rule actually
takes effect through for a native Wayland client, since `add_window`'s own
attempt always runs against a still-empty `app_id` (see the regression test
next to the existing one covering the identical historical bug for
`decorated`). Found by setting an isolated `SRDWM_CONFIG_PATH` test config
with `srd.rule({ class = "Alacritty" }, { opacity = 0.4 })` against a nested
instance and pixel-sampling a real screenshot: predicted blend (242,230,53)
at 0.4 over (10,10,15) is (103,98,30); measured (105,100,33).
Verified live in a nested session, screenshotting the *host* compositor
(shows the real on-screen render, unlike grim against the nested socket,
which - separately discovered this work - routes through
`capture_offscreen`): stacking order correct with two overlapping windows
(topmost fully occludes the one behind it, the exact scenario the reverted
attempt got wrong), opacity blend matches prediction. Also confirmed, by
testing the previous commit against the same scene, that upside-down
content on this backend is a pre-existing bug unrelated to this change --
noted, not fixed here.
cargo build --workspace (all 9 crates), cargo clippy --workspace (0 new
warnings), cargo test --workspace (197 tests, 0 failed, includes 2 new
regression tests).
|
|
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).
|
|
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.
|