fix(wallpaper): paint correctly when output layout has a negative origin#484
Open
raven2cz wants to merge 5 commits into
Open
fix(wallpaper): paint correctly when output layout has a negative origin#484raven2cz wants to merge 5 commits into
raven2cz wants to merge 5 commits into
Conversation
awful.wallpaper's paint() was inherited from AwesomeWM where the X11
root window is guaranteed to start at (0, 0), so root.size() alone was
enough and per-screen geometry.x/y were always non-negative. On Wayland
wlr_output_layout can place outputs at negative coordinates — e.g. a
portrait monitor left of the primary at x=-2160 — and the
AwesomeWM-shaped code quietly clipped those screens off the cairo
surface, leaving their wallpaper blank after the buffer reached the
compositor.
Fix the whole paint pipeline to honor the layout origin:
* root.c: add root.geometry() Lua getter returning {x, y, width, height}
from wlr_output_layout_get_box (complements root.size()).
* lua/awful/wallpaper.lua: fetch root.geometry(), keep allocating the
target surface at the bbox size but cr:translate(-root_x, -root_y) so
that absolute layout coordinates used by every s.geometry.x / geo.x
draw below still land on-surface. Compensate the source-to-target
copy's set_source_surface offset by (root_x, root_y) since the source
is already in surface-pixel space.
* root.c: update the cache and legacy-fallback paths that extract the
pattern into per-screen / full-layout buffers.
create_wallpaper_cache_entry now receives the layout origin and uses
it when computing the cairo translate that carves each screen region
out of the pattern. The fallback path copies 1:1 (both surfaces are
bbox-sized with matching pixel origin), so it drops the old -x,-y
translate.
Verified live on a three-monitor layout (HP U28 portrait at transform
"90", x=-2160; Dell G3223Q primary at 0,0): awful.wallpaper now renders
the portrait wallpaper on HP and the landscape wallpaper on Dell in a
single paint pass.
raven2cz
added a commit
to raven2cz/somewm
that referenced
this pull request
May 14, 2026
Phase 5 — the fork-only Lua features. Upstream did not touch any of these four files in the sync window, so each is taken wholesale. - lua/awful/mouse/snap.lua: aerosnap dwell-time gate (PR trip-zip#522) — defers the snap placeholder by a dwell timer to kill cross-monitor flicker. - lua/awful/wallpaper.lua: paint() negative-origin fix (PR trip-zip#484) — translates by -root_x/-root_y so wallpaper paints correctly when the output layout has a negative origin (depends on root.c luaA_root_geometry, Phase 3b). - lua/lockscreen.lua: background-image feature (PR trip-zip#476) — bg_image, bg_image_overlay, bg_image_blur, multipass blur, surface cache. - lua/somewm/init.lua: register the tag_slide submodule (the fork-only lua/somewm/tag_slide.lua came in Phase 1). KEEP-UPSTREAM (verified empty diff, no edit): lua/awful/input.lua, ipc.lua, screenshot.lua (fork is behind upstream's 48e19a0 / ipc / HiDPI work); lua/awful/permissions/init.lua (PR trip-zip#516 nil-screen Lua fix dropped — upstream's C-layer 7c932c7 supersedes it); the icon-resolution unit lua/awful/client.lua + widget/clienticon.lua + widget/tasklist.lua (decision D1 — adopt upstream's resolve_icon, drop the fork's get_icon_path API). Sandbox-verified: headless startup clean, all four fork Lua modules require() successfully, no Lua errors.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
awful.wallpaperis inherited from AwesomeWM where X11 guarantees the root window is anchored at(0, 0), soroot.size()alone was sufficient and everyscreen.geometry.x/ywas non-negative. On Wayland,wlr_output_layouthappily places outputs at negative coordinates: a portrait monitor mounted to the left of the primary, or a vertical stack with a monitor abovey=0. In that case the AwesomeWM-shaped Lua paint path silently clips the affected screens off the cairo surface, so the wallpaper ends up blank on those outputs after the buffer reaches the compositor.Reproduction
Any multi-monitor configuration where at least one output has a negative
positionin the layout. Simplest case: portrait monitor to the left of a landscape primary:Set wallpapers via
awful.wallpaper { screen = s, widget = ... }on both screens. Before: wallpaper renders on the primary only, the negative-x output shows blank. After: wallpaper renders on both outputs in a single paint pass.Root cause
The cairo surface in
awful.wallpaper'spaint()is allocated atroot.size()(the layout bounding box) with an implicit pixel origin at(0, 0). Drawing code then usess.geometry.x / s.geometry.y(absolute layout coordinates) directly as cairo user-space coordinates. For an output at negative layoutx, the draws fall outside the surface pixel range and are discarded. The same mismatch propagates into the C-side wallpaper cache path (create_wallpaper_cache_entry) and the legacy fallback path inroot_set_wallpaper_cached, which translate the cairo pattern assuming pixel(0, 0)corresponds to layout(0, 0).Fix
Teach the pipeline the pattern invariant
pattern_pixel(u, v) == layout(u + layout_x, v + layout_y)and apply it consistently:root.c: newroot.geometry()Lua getter returning{x, y, width, height}fromwlr_output_layout_get_box. Complements the existingroot.size(). On X11 it always returnsx=0, y=0, width=..., height=...(no behavior change), on Wayland it exposes the real origin.lua/awful/wallpaper.lua: fetchroot.geometry()instead of justroot.size(), keep the target surface at the bounding-box dimensions, andcr:translate(-root_x, -root_y)right after creating the cairo context so every subsequents.geometry.x / geo.x(still absolute layout coordinates) lands on a valid surface pixel. The source-to-target copy compensates withcr:set_source_surface(source, root_x, root_y)becausesourceis already in surface-pixel space and needs the outer translate cancelled out.root.c:create_wallpaper_cache_entrynow takes the layout origin so its pattern-extract translate carves out the right region for each screen (cairo_translate(cr, layout_x - x, layout_y - y)instead ofcairo_translate(cr, -x, -y)). The legacy fallback path allocates a bbox-sized surface whose pixel origin already matches the pattern, so it drops the old-x, -ytranslate and copies 1:1; the scene buffer node is positioned atlayout_box.x, layout_box.yso negative origins still land correctly in the scene graph.No new API surface besides
root.geometry(); existingroot.size()is unchanged. External Lua code (themes, widgets, third-partyawful.wallpaperwrappers) keeps working unchanged. The fix is purely coordinate bookkeeping; no new allocations, no extra paint passes.Test plan
(0, 0): no regression, wallpaper renders as before (the newroot.geometry()returns{x=0, y=0, ...}so the cairo translate is a no-op).position = {x=-2160, y=0},transform = "90", logical 2160x3840): portrait wallpaper renders on the rotated output, landscape wallpaper on the primary, in a singleawful.wallpaperpaint pass.main; no new warnings.