The lock started decoding its wallpaper only once locked, with the cache off, and first at the view's unsized native resolution. A machine suspending right after locking froze that decode partway, so waking showed the password field on a bare background and the wallpaper popped in after it. On this machine the wallpaper took ~208ms to become ready, and the suspend followed the lock by 66ms. The lock service now keeps each screen's lock wallpaper decoded in the image cache, as the lock view requests it: same URL, the screen's logical size, PreserveAspectCrop. The view waits for its size and reads from the cache, so the wallpaper is ready within ~3ms of the lock starting. The version in the cached URL follows the file's mtime and size, so a wallpaper overwritten in place still reloads. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
47 lines
1.8 KiB
QML
47 lines
1.8 KiB
QML
import QtQuick
|
|
import qs.Commons
|
|
|
|
// The shell draws stills only. OWE owns video backgrounds on the desktop, and
|
|
// it feeds the lock screen through its own socket.
|
|
Item {
|
|
id: root
|
|
|
|
property string path: ""
|
|
property int version: 0
|
|
// Unversioned images cache by path. A caller whose version changes with every
|
|
// new file, as the lock's does, may cache too: the version is in the URL.
|
|
property bool cached: version === 0
|
|
// A caller that knows the size its screen needs, in physical pixels, sets
|
|
// constrainDecode and decodeSize. The image then waits for that size rather
|
|
// than decoding at the file's native size first.
|
|
property bool constrainDecode: false
|
|
property size decodeSize: Qt.size(0, 0)
|
|
readonly property var current: imageLoader.item
|
|
readonly property bool ready: current ? current.ready : false
|
|
readonly property bool video: Util.isVideoPath(path)
|
|
// Cache-bust images selected in a running lock session, so a theme switch
|
|
// that replaces the file behind an unchanged path shows the new pixels.
|
|
readonly property url imageUrl: path && !video ? Util.fileUrl(path) + (version ? "?v=" + version : "") : ""
|
|
|
|
Loader {
|
|
id: imageLoader
|
|
anchors.fill: parent
|
|
active: root.path !== "" && !root.video
|
|
sourceComponent: imageComponent
|
|
}
|
|
|
|
Component {
|
|
id: imageComponent
|
|
|
|
Image {
|
|
readonly property bool ready: status === Image.Ready
|
|
source: !root.constrainDecode || root.decodeSize.width > 0 ? root.imageUrl : ""
|
|
fillMode: Image.PreserveAspectCrop
|
|
asynchronous: true
|
|
cache: root.cached
|
|
sourceSize.width: root.constrainDecode ? root.decodeSize.width : (root.version > 0 ? width : 0)
|
|
sourceSize.height: root.constrainDecode ? root.decodeSize.height : (root.version > 0 ? height : 0)
|
|
}
|
|
}
|
|
}
|