* feat: parallax-scroll wallpaper
Add a `Scrolling` wallpaper fill mode that translates the wallpaper crop
with the active workspace — like Android home-screen parallax, but along
niri's vertical workspace axis.
The image is scaled to cover the screen on its non-scroll axis, and the
active workspace index drives a fractional offset into the cropped
overflow along the scroll axis. Scroll position is spring-animated
CPU-side and handed to a minimal single-texture shader as a UV offset.
Per-monitor scroll position is published into SessionData so the lock
screen renders the same crop as the active workspace, keeping visual
continuity across lock/unlock.
Two implementation details worth calling out for review:
- QSG_USE_SIMPLE_ANIMATION_DRIVER=1 is exported to the spawned quickshell
process. The default animation driver advances in fixed ~16ms steps,
capping the scroll at 60Hz and desyncing it from compositor motion on
high-refresh displays; the simple driver advances by real elapsed time,
restoring native-refresh pacing. Removing it visibly regresses to 60Hz.
- The wallpaper survives wl_output rebind cycles (e.g. OLED image-cleaning
on DPMS soft-off), which otherwise leave a stuck or void background.
Recovery re-anchors the scroll target on output-lifecycle signals,
rebuilds the ShaderEffect against the current render context, and
re-attaches the wallpaper-layer surface on unlock for parallax-active
monitors — guarded against lock state so the shader gets reliable frame
hints.
* simplify bindings and gate lock screen shader in a loader
---------
Co-authored-by: bbedward <bbedward@gmail.com>
* feat(wallpaper): support configurable background color
* chore(translations): update settings search index
* revert(wallpaper): keep Pad naming instead of Center
- Keep wallpaper surfaces persistent and remove `updatesEnabled` throttling that could leave wallpapers grey or frozen after DPMS, suspend, fullscreen, or output changes
Fixes#2612Fixes#2299Fixes#2272Fixes#2028
After unlocking the screen (startup lock or wake from sleep), the desktop
showed Hyprland's background color instead of the wallpaper.
WallpaperBackground disables QML updates via updatesEnabled after a 1-second
settle timer. While WlSessionLock is active, Hyprland does not composite the
background layer, so when the lock is released it needs a fresh Wayland buffer
— but none is committed because the render loop is already paused.
The previous attempt used SessionService.sessionUnlocked, which is unreliable
for the startup lock case: DMSService is not yet connected when lock() is
called at startup, so notifyLoginctl is a no-op and the loginctl state never
transitions, meaning sessionUnlocked never fires.
Fix by tracking the shell lock state directly from Lock.qml's shouldLock via
a new IdleService.isShellLocked property. WallpaperBackground watches this and
re-enables rendering for 1 second on unlock, ensuring a fresh buffer is
committed to Wayland before the compositor resumes displaying the layer.