feat(lockscreen): add background image + optional blur#476
Conversation
Add bg_image, bg_image_overlay, and bg_image_blur config options to the lockscreen module. When bg_image is set the lockscreen paints the image full-screen under a semi-transparent overlay; blur is optional and off by default. Implementation (per @NilOmniscient's bgimage suggestion in trip-zip#401): Outer background .bgimage = wallpaper surface └─ Inner background .bg = semi-transparent overlay └─ UI content A single-layer variant (bg=overlay + bgimage=image) does not work because wibox.container.background paints `bg` BEFORE `bgimage`; the overlay would end up beneath the image and be invisible. Children are drawn after the parent's bg+bgimage pass, so the overlay must live on a child background. Cover screens (non-primary in multi-monitor) follow the same pattern. Their inner background has no child widget, so its :fit() would return 0x0 and paint nothing; forced_width/forced_height on the inner force it to fill the screen geometry. Optional blur (disabled by default, opt-in only): lockscreen_bg_image_blur = false -- off (default) lockscreen_bg_image_blur = true -- defaults (radius=15, 3 passes) lockscreen_bg_image_blur = 25 -- custom radius lockscreen_bg_image_blur = { radius = 30, passes = 5 } lockscreen_bg_image_blur = function(surface) ... end -- fully custom Built-in blur is a multi-pass bilinear downscale/upscale via cairo, visually close to a Gaussian for a one-shot render. Radius/passes are clamped (1-100 / 1-10). lgi/cairo is loaded lazily via pcall so plain-color and plain-image modes still work without lgi. Intermediate surfaces are :finish()'d after each pass to release backing memory without waiting for GC. Custom blur return values are duck-type validated; on failure the original surface is used. Blurred surfaces are cached per (path, spec signature) so the pipeline runs once, not per monitor; custom function specs are not cached. lockscreen.invalidate_bg_cache() exposed for explicit refresh. Config merge handles `false` as a meaningful override for bg_image, bg_image_blur and lock_screen so a theme's `true` default cannot override an explicit user `false`. Fix Lua pairs() bug: defaults had bg_image=nil and lock_screen=nil which pairs() silently skips. Changed to false so the config resolver loop visits these keys and picks up beautiful.lockscreen_* values. Without bg_image set, behavior is unchanged.
Port feat/lockscreen-bg-image-upstream (PR trip-zip#476) onto main. Replaces the imagebox-based stack implementation with the cleaner bgimage + two-layer approach suggested by @NilOmniscient in trip-zip#401: Outer background .bgimage = wallpaper surface └─ Inner background .bg = semi-transparent overlay └─ UI content Single-layer (bg=overlay + bgimage=image) does not work because wibox.container.background paints `bg` BEFORE `bgimage` — the overlay would sit beneath the image and be invisible. Children draw after the parent's bg+bgimage pass, so the overlay has to live on a child. Cover screens (multi-monitor) follow the same two-layer pattern. The cover's inner background has no child widget so its :fit() would return 0x0 and paint nothing; forced_width/forced_height pin it to the screen geometry. Optional blur (off by default, fully opt-in): lockscreen_bg_image_blur = false | true | number | { radius, passes } | function(surface) -> surface Built-in blur is a multi-pass bilinear downscale/upscale via cairo. Radius/passes clamped (1-100 / 1-10). lgi/cairo is loaded lazily so plain-color and plain-image modes work without lgi. Intermediate cairo surfaces are :finish()'d per pass; blurred surfaces are cached per (path, spec signature); custom function specs bypass the cache. `lockscreen.invalidate_bg_cache()` exposed for explicit refresh. Config merge uses explicit nil checks for bg_image / bg_image_blur / lock_screen so `opts.bg_image_blur = false` can override a theme's `lockscreen_bg_image_blur = true`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2318038 to
0b5edfa
Compare
|
Sorry @raven2cz I have been in such a weird state with the layout branch (and my day job) that I have had a hard time getting these reviewed. I'm taking a little trip for the next 4 days but when I get back I'm going to try to get these all merged. Thanks for all your efforts and patience with me. |
|
No worries. Better to be careful and do it gradually rather than all at once. After all, it is already quite a large project, and it will be better to always test things step by step. This is not related to this issue, but it seems to me that something new is not working in GIMP when it is opened through XWayland. The menus somehow close by themselves. This will need some investigation, whether some fix is causing it, or whether it is yet another new Nvidia quirk... Otherwise, I finally finished my whole project, so after 4 months I am fully on Wayland with SomeWM! It is already really nicely usable. I think that once you properly finish the full 2.0 version, we will have a really great compositor here, one that can basically be used for anything and, most importantly, customized however people need. |
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.
Description
Closes #401. Adds three lockscreen config options:
lockscreen_bg_image: path to a wallpaper shown full-screen under the UI.lockscreen_bg_image_overlay: semi-transparent dim overlay (default#000000aa).lockscreen_bg_image_blur: optional, off by default. See below.Without
bg_imageset, behavior is identical to the current solid-color path.Implementation
Reply to @NilOmniscient in #401: took the
wibox.container.background+bgimagepath as suggested. Needed one small adjustment: the dim overlay has to sit on an inner background, not the outer one:{ { ui_content, bg = overlay_color, -- dim painted here widget = wibox.container.background, }, bgimage = wallpaper_surface, -- image painted here widget = wibox.container.background, }wibox.container.backgroundpaintsbgbeforebgimage, so a single-layer variant (bg = overlay,bgimage = imageon the same node) would place the overlay beneath the image and make it invisible. Children draw after the parent's bg+bgimage pass, so the overlay has to live on a child background to sit on top.Multi-monitor cover screens follow the same two-layer pattern. The cover's inner background has no child widget (it only paints the overlay), so
:fit()would return 0x0 and the dim would render at zero size; forcedforced_width/forced_heighton the inner pin it to the screen geometry.Optional blur
Off by default; opt-in only:
Built-in blur is a multi-pass bilinear downscale/upscale via cairo, visually close to a Gaussian, single allocation per pass. Radius/passes are clamped (1-100, 1-10) so a stray large value can't hang on allocation.
lgi/cairois loaded lazily (pcall) only when blur is requested. Intermediate cairo surfaces are:finish()'d after each pass to release backing memory synchronously rather than waiting for GC. Custom blur return values are duck-type validated; a throwing or malformed callback transparently falls back to the original surface. Blurred surfaces are cached per(path, spec signature)so the blur pipeline runs once, not once per monitor; custom function specs are intentionally not cached (may close over mutable state).lockscreen.invalidate_bg_cache()is exposed for explicit refresh.Config merge uses explicit nil checks for
bg_image/bg_image_blur/lock_screenso thatopts.bg_image_blur = falsecorrectly overrides a theme'slockscreen_bg_image_blur = true(the usualorchain would silently ignore the user'sfalse).Bug fix included
defaults.bg_imageanddefaults.lock_screenwerenil, whichpairs()silently skips, so the config resolver never visited those keys andbeautiful.lockscreen_*values were never picked up. Changed tofalse.Configuration example
Test plan
bg_image): unchangedbg_imageonly: wallpaper + default dim overlaybg_image+ numeric blur: tested live withbg_image_blur = 15lua5.1 -e 'assert(loadfile("lua/lockscreen.lua"))'Config additions are all opt-in, existing themes keep behaving as before.