| Age | Commit message (Collapse) | Author | Files | Lines |
|
across a monitor seam
Nemo's right-click context menu was the last open punch-list item, parked
twice as untestable. It works: verified end to end in a throwaway nested
compositor, menu and submenu both, at the correct position and stacking.
The popup path itself needed no fix, so the POPUP-GEOM-DIAG/POPUP-GRAB-DIAG
diagnostics are removed.
Two real bugs turned up in the way of testing it.
zwlr_virtual_pointer was a silent no-op on the winit backend. Every
Motion/MotionAbsolute handler read UdevState::bounds() behind an early
return when state.udev was None, and that field is Some only for the DRM
backend. The protocol advertised its global, accepted create_virtual_pointer
and accepted every request, then discarded all motion with no error and no
log. That is the backend a nested instance runs on, so the only safe way to
drive a throwaway compositor - a Wayland client of that compositor, which
cannot reach any other session, unlike ydotool's /dev/uinput writes - did
not work at all. Bounds now come from WindowManager::monitors() when udev is
absent; both backends fill that list from Platform::monitors().
The winit backend's screencopy pass rendered no popups and no shadows. It
re-renders the scene offscreen, and that second scene was missing tiers, so
grim on a nested instance reported the opposite of the truth: a menu drawing
perfectly on screen photographed as absent. The DRM backend never had this,
since it serves screencopy from the on-screen frame it just drew. Border
strips are still missing from that pass, called out in the code rather than
left silent.
Also fixed, from the "windows show a bit in the other monitor" report:
shadow_rect expanded by SHADOW_SIZE on every side with no monitor-boundary
awareness, so a window flush against a seam put its 24px shadow strip on the
neighbouring screen. shadow_rect_clipped clips to the bounding box of the
monitors the window's geometry actually touches - not just its assigned
one, since a window straddling a seam really does occupy both and clipping
there would cut its shadow off mid-body. The bitmap's own extent stays
unclipped, because the src rectangle indexes into it; only the fragment list
is clipped. Six tests on the incident's own numbers. Not confirmed on
screen: the nested backend cannot produce a second monitor.
New tool: tools/virtual-pointer-click, a scriptable virtual-pointer driver
that acknowledges each command after its round-trip, so a test script can
put a screenshot between a move and the click that follows it.
489 tests pass, clippy clean.
|
|
toggle_floating
The tiled-shadow-tint fix earlier today gated the shadow on Window::floating
alone. arrange_workspace only reads floating under the "tiling" layout, so
every window on this project's own default "dynamic" layout starts, and
stays, floating: false - the gate misread that as "tiled, no shadow"
regardless of which layout was actually running, so shadows silently
vanished under dynamic mode entirely, recoverable only by pressing Super+S
(toggle_floating), which then looked like that key toggles a tint rather
than floating. Fixed by checking the workspace's own layout name first:
a window is only "currently tiled" when its workspace runs "tiling" AND
it hasn't opted out via floating. DecorationSignature's floating field is
now currently_tiled, since a layout switch changes this for every window
on a workspace without touching any of their own floating fields.
Also disabled general.shadows in the user's own config per direct
request - never asked for, on by default, and a real problem for
color-accuracy work regardless of how correctly it renders otherwise.
Also fixed both context menus (titlebar and desktop) silently truncating
labels past a fixed 170px width with no indication - widened dynamically
to each menu's own real widest label via a new measure_text_width helper.
|
|
customization
Reported live: "looks very ugly currently and some of it doesn't make
sense." Both were real. Every row, including a bare divider, took one
full TITLEBAR_HEIGHT slot, so a separator was a 1px hairline in the
middle of 32px of empty space; "Move to Workspace" faked a section
caption by embedding box-drawing characters directly in an ordinary
item's label, which rendered - and behaved, until the click-dispatch
site's own special case - exactly like a clickable row that did
nothing. Separately, "Floating" was always offered even though
Window::floating only affects the "tiling" layout: toggling it under
this project's own default "dynamic" layout visibly changes nothing,
reading as a broken control rather than an inapplicable one.
ContextMenu (crates/core/src/context_menu.rs) gained real Separator
(9px) and Header (22px, non-interactive, dimmed) row kinds with their
own small heights, replacing the label-hack outright. Both backends'
rendering now sum each row's own real height instead of assuming one
uniform value, so hit-testing and pixels can't disagree about where a
row is. Floating is omitted entirely outside the tiling layout.
New, in direct response to "allow customizing from there as well": a
Customize section with live Button Style / Button Side toggles. Each
flips the matching ThemeConfig field and immediately redraws every open
window's titlebar - not routed through srd set's own path, which is
scoped to windows created after the call for lack of a redraw hook it
can reach; a menu action that didn't visibly change the titlebar you
clicked would be its own "doesn't make sense" bug.
Full workspace build/test/clippy clean (242 core tests, +8; 152
wayland, net-even after rewriting the old label-hack tests).
|
|
wrong-password shake
The native lock UI was a flat bordered rectangle with three left-aligned
text lines and no shadow, clock, or identity marker - reported directly
as looking unfinished. Splits the redesign across a new transparent-canvas
header (time, date, circular avatar, username) above a redesigned,
centered password box with a real drop shadow and a dimmed placeholder
prompt, plus a genuine on-screen QWERTY-shaped keyboard with working
Shift/Backspace/Return/Space and real click hit-testing shared with the
render path via one `lock_stack_layout` function, and a damped-sine shake
on a failed attempt.
LockConfig gains show_clock/show_keyboard/avatar_bg, each independently
srd.set-able and documented in a new theme.lock.* section in DEFAULTS.md.
native_lock_render_elements now takes one NativeLockFrame struct instead
of positional buffer arguments now that it composites five optional
layers instead of two.
Full workspace build/test/clippy clean (152 wayland tests, +6 new).
|
|
Reported live: "looks weird and unpolished... need a lot more items".
Compared directly against the exact AGS reference this project's own
menu rebuild already targets rather than guessing:
- Highlighted rows used a flat, fully-saturated fill instead of the
reference's subtle 22%-accent-into-background wash. New decoration::
color::mix_rgb (channel-wise linear blend, generalizing brighten/
darken's fixed-target blends to an arbitrary second colour/ratio) lets
render_context_menu reproduce that same ratio.
- Every separator row was a label string of Unicode box-drawing
characters rendered as text glyphs, which render inconsistently at
small sizes - a label that's entirely U+2500 now draws a real 1px
hairline instead; a label that mixes it with real text ("--- Move to
Workspace ---", a deliberate section-header convention) still renders
as text, unchanged.
- "Select All" added to the bare-desktop menu, the one action every
mainstream desktop's own menu offers that this one lacked.
New tests needed real care: the panel's own rounded-corner distance
field softens alpha within its radius of any canvas edge, not just the
visible corners, so a naive full-row pixel scan against bg picked that
up as a false positive on the first attempt - fixed by scanning only
rows/columns confirmed (via a throwaway debug dump) to sit inside the
panel's genuinely flat interior.
Full workspace build/test/clippy clean, built and installed. Real
submenus and per-row icons remain real, separate scope - this
project's floating-menu UI has no nested-panel concept yet.
|
|
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.
|
|
Requested directly: default icons "look very rudimentary... make slightly
blue as well, polished look".
Two changes to decoration::render_desktop_icon and its five draw_*_glyph
helpers:
- New fill_rounded_rect primitive: softly rounded corners (the same
clamp-then-distance smoothstep construction rounded_corners_pixman::
apply_corner_mask already established for content masking, not a new
technique) plus a vertical top-to-bottom gradient instead of one flat
fill - the same light-source cue buttons.rs's own glossy_shade uses
for the titlebar dots, as a plain linear gradient here. Applied to each
glyph's main body shape; small details (folder tab edge, computer
stand, trash ridges, home roof) stay flat/sharp.
- A dedicated ICON_COLOR constant (a clean mid-blue) instead of reading
theme.titlebar_fg_focused - that field is whatever the user's own
titlebar accent happens to be configured to, which could be any
colour; these glyphs want a consistent, recognisable blue palette of
their own, independent of theme.
Build/test/clippy already verified clean as part of the layer-shell scale
fix commit just before this one (same source tree, installed together).
|
|
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.
|
|
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.
|
|
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.
|
|
MISSING.md listed the border frame's bottom/left/right strips as
staying square while the top one (and the titlebar above it) rounds --
"unrelated to client content rounding... not attempted." The left/
right strips genuinely can't participate (border_strips' geometry has
them span only the height between the top and bottom strips, no
corner to round), but the bottom strip is exactly the same shape as
the top one and had no reason left to stay square.
decoration::render_border_bottom mirrors render_border_top exactly
(round_bottom_corners mirrors round_top_corners), cached the same way
in a new border_bottom_decorations map, and drawn in both render loops
via the same all-or-nothing occlusion check the top strip already
uses - pulled out of the left/right strips' per-fragment occlusion
splitting into its own dedicated bitmap path, matching top's existing
trade-off (cropping a rounded bitmap's source rect per fragment is
real extra work for a strip this thin) rather than inventing a new one.
One real bug caught before it shipped: round_bottom_corners' corner-
centre math (height - r - 1) panics on unsigned underflow whenever the
radius clamp lands on the strip's own full height (a real, common case
- a 2px-thick test strip hits it immediately). Fixed by computing the
centre as a signed offset instead, mirroring how the existing dx/dy
distance math already avoids the same class of issue.
|
|
- 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.
|
|
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).
|
|
clicks
Two independent daily-driving gaps closed in one pass, both from MISSING.md
and live user feedback:
Drop shadows (general.shadows, default true). Reuses the exact "bitmap
drawn outside geometry, cached like the border" technique border_strips/
render_border_top already established - decoration::shadow_bitmap rasterizes
a linear alpha falloff (Chebyshev/square-ring distance, not a true blur --
no blur primitive exists without a GPU shader, and the udev backend's
PixmanRenderer is software-only) from SHADOW_MAX_ALPHA (90/255, deliberately
subtle) at the window's own edge down to fully transparent SHADOW_SIZE (12px)
out. Cached in CompState::shadow_buffers, rebuilt at the same trigger points
as border_top_decorations (redraw_decoration_buffer), for the identical
damage-tracking reason: a fresh Id every frame means OutputDamageTracker
never finds a previous-frame match. No shadow for a maximized or fullscreen
window, matching the Hyprland/GNOME convention MISSING.md measures against.
Resize grab margin: 10px -> 6px (general.resize_margin, now configurable,
same call-site-count-preserving change as threading a new parameter through
one indirection point: ResizeEdge::hit_test's only production caller is
WindowManager::hit_test, so this didn't need touching every backend despite
hit_test being shared verbatim across X11/Wayland/Windows/macOS). Reported
live: ordinary clicks near any window edge - a link near a browser's edge,
a button near a panel's edge - regularly registered as a resize-edge grab
instead of reaching the client, not just an occasional near-miss, because
the 10px band was measured inward from the client's own content rect. 6px
stays comfortably grabbable while giving content back most of its edge.
Verified: cargo build --workspace (all 9 crates including the windows/macos
stub backends), cargo clippy --workspace (0 new warnings), cargo test across
core/wayland/config/x11 (188 tests, 0 failed). Shadow rendering verified at
the render-element level live in a nested session (correct geometry, alpha,
buffer contents) - grim/screencopy itself turned out to route through
winit.rs's separate capture_offscreen path, which only ever drew
`decorations` (titlebars), never borders or shadows, so screenshots taken
this way have never shown either; a real gap, not fixed in this pass.
|
|
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).
|
|
- 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.
|