Repository navigation
Keep the old wallpaper on screen until the new one has drawn - #35
Merged
Merged
Conversation
Switching wallpapers faded the current backend out at once and created the next one underneath. A cold scene start takes seconds (shader compiles), so the desktop showed the bare background colour, with the icons over it, for all of that; scene->scene and video->video swaps did not even fade, since they reassigned `source` on the live item. Each backend now exposes `frameShown`, set on its first frame (InfoShow, a static pane, starts true). backendLoader.load() keeps the backend that is on screen as `_outgoing`, stacked above and still playing, until the new one sets frameShown; then it fades out over 250 ms (an instant cut with "Reduce animations") and is destroyed. A backend that never reached the screen during rapid switching is dropped at once, so what the user sees stays up rather than a blank gap. A 25 s timeout guarantees the old layer never lingers; the backends' own load watchdogs still swap in InfoShow on failure. A different wallpaper now always gets a fresh backend, even of the same type, so it goes through this handoff. A path change on the same workshop id (another page of the same web wallpaper) still swaps in place, as before; folder videos carry no id, so for them any path change is a new wallpaper. The outgoing backend must also stop following background.*, which describes the next wallpaper from the moment the switch starts: WallpaperWorkShopId changes first and read_wallpaper_config resolves synchronously, so the old wallpaper immediately took the new one's display mode, speed and user properties and stretched or cropped until the swap finished. Backends gain freezeOptions(), which pins those to their current values; load() calls it on the outgoing backend, and the new wallpaper's options are read after that (readCurrentOptions, before `source` is set, so USER_PROPS still precede LOAD_SCENE). The workshopid handler defers its read with Qt.callLater for the same reason. tst_main_handoff.qml drives this through the real main.qml: the old scene stays on top until the new one draws and then fades, the outgoing scene keeps its own display mode, a rapid A->B->C switch keeps A on screen, and a same-id path change does not rebuild.
…nt colour During a wallpaper handoff the old backend stays on screen while background.* already describes the next wallpaper. freezeOptions() only pinned fill mode, speed and user properties, so the old layer still followed the next wallpaper in three ways: - Scene.qml: pin postprocessingOverride. A change reloaded the outgoing scene while it was the visible layer. - Scene.qml, Mpv.qml, QtMultimedia.qml, QtWebView.qml: pin the mute flag, and (Scene, Mpv, QtMultimedia) the volume. A wallpaper muted per wallpaper used to unmute and play over the new one. Volume lives in a closure inside Common.createVolumeFade, so the fade handle gets a freeze() that drops the binding on its timer. - main.qml: wallpaper.accentColorChanged() fired from the new backend's first-frame handler while the old layer was still opaque on top, so Plasma sampled the old wallpaper. It now waits for the outgoing fade to finish (emitAccentColor, called from outgoingFade.onFinished) and still fires at once when nothing is on top. Tests: tst_main_handoff.qml covers postprocessing, mute, volume and both accent-colour cases through the real main.qml; the Mpv, QtMultimedia and QtWebView backend tests each check that freezeOptions() keeps mute.
CaptSilver
approved these changes
Oct 13, 2026
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.
Problem
On a switch the current backend faded out at once. A cold scene start takes seconds (shader compiles), so the bare background colour showed, with the desktop icons over it. scene->scene and video->video swaps even hard-cut, because they reassigned
sourceon the live item.The outgoing backend also followed
background.*, which describes the next wallpaper from the moment the switch starts.WallpaperWorkShopIdchanges first andread_wallpaper_configresolves synchronously, so the old wallpaper stretched or cropped to the new one's display mode until the swap finished. This is visible when only one of the two wallpapers has a per-wallpaperdisplay_mode.Change
frameShown. Each backend exposes it, set on its first frame.InfoShowstarts attrue.backendLoader.load()keeps the backend on screen as_outgoing, stacked above and still playing, until the new one setsframeShown. Then it fades out over 250 ms (an instant cut with Reduce animations) and is destroyed.test_web_to_web_switch_mounts_fresh_backend). Folder videos have no id, so for them any path change counts as a new wallpaper.freezeOptions(). Backends gain this; it pins display mode, speed and user props, andload()calls it on the outgoing backend. The new wallpaper's options are read after that, and still beforesource, so USER_PROPS precede LOAD_SCENE.onWorkshopidChangeddefers its read withQt.callLater.Tests
tst_main_handoff.qmldrives the realmain.qmlthroughScreenRigand checks that:tools/scripts/preflight.shpasses (run inside afedora-toolbox:latestcontainer built fromDEPS_FEDORA,WEK_IN_CI=1). One note: on this laptop the fuzz step times out on the checked-in seedtests/fuzz_corpus/WPShaderCompile/seed/58392d50…(~33 s per compile under ASAN+UBSAN, identical on cleanmain), so I ran preflight with--no-fuzzand then all fuzz targets with the same settings except-timeout=60: no findings.Setup: Plasma 6 (Wayland), KWin, Intel Iris Xe (RPL-P), 2520x1680 @ 120 Hz laptop panel, Manjaro.