The background plugin now watches for the OWE daemon socket. While OWE is running, the desktop yields video playback to it and the shell keeps stills. The lock screen keeps its own playback. This lets Omarchy cooperate with OWE without OWE editing shell.json, so the engine can ship as a package. Add a package-list note that owe-wallpaper-engine must be added once it is packaged.
92 lines
3.0 KiB
QML
92 lines
3.0 KiB
QML
import QtQuick
|
|
import qs.Commons
|
|
|
|
Item {
|
|
id: root
|
|
|
|
property string path: ""
|
|
property int version: 0
|
|
property bool playbackEnabled: true
|
|
property bool audioEnabled: false
|
|
// The desktop yields video playback to OWE while it runs, so two engines
|
|
// never decode the same file. The lock screen leaves this off and keeps its
|
|
// own playback.
|
|
property bool deferVideo: false
|
|
// Bumped when the file behind an unchanged path may have been replaced.
|
|
// Images cache-bust through version; a video is rebuilt, since FFmpeg
|
|
// would read a query as part of the filename.
|
|
property int reloads: 0
|
|
property bool reloading: false
|
|
readonly property var current: video ? videoLoader.item : 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. FFmpeg treats the
|
|
// query as part of a local filename, so videos must keep their plain URL.
|
|
// Each URL is empty for the other kind, so a switch never hands the still
|
|
// loader a video, or the player a still, in the moment before it unloads.
|
|
// Both test the path directly: going through `video` lets a URL evaluate
|
|
// against the stale flag and leak the wrong file for one pass.
|
|
readonly property url imageUrl: path && !Util.isVideoPath(path) ? Util.fileUrl(path) + (version ? "?v=" + version : "") : ""
|
|
readonly property url videoUrl: path && Util.isVideoPath(path) ? Util.fileUrl(path) : ""
|
|
|
|
Loader {
|
|
id: imageLoader
|
|
anchors.fill: parent
|
|
active: root.path !== "" && !root.video
|
|
sourceComponent: imageComponent
|
|
}
|
|
|
|
// Loaded by URL rather than from a Component here, so QtMultimedia and its
|
|
// audio dependency closure never map into a session that only shows images.
|
|
Loader {
|
|
id: videoLoader
|
|
anchors.fill: parent
|
|
active: root.path !== "" && root.video && !root.reloading && !root.deferVideo
|
|
source: "BackgroundVideo.qml"
|
|
}
|
|
|
|
onReloadsChanged: {
|
|
if (!video) return
|
|
reloading = true
|
|
Qt.callLater(function() { root.reloading = false })
|
|
}
|
|
|
|
// A player on its way out keeps its source: pushing an empty one starts a
|
|
// load of nothing that its destructor then cancels, which FFmpeg logs.
|
|
Binding {
|
|
target: videoLoader.item
|
|
property: "mediaSource"
|
|
value: root.videoUrl
|
|
when: videoLoader.item !== null && Util.isVideoPath(root.path)
|
|
restoreMode: Binding.RestoreNone
|
|
}
|
|
|
|
Binding {
|
|
target: videoLoader.item
|
|
property: "playbackEnabled"
|
|
value: root.playbackEnabled
|
|
when: videoLoader.item !== null
|
|
}
|
|
|
|
Binding {
|
|
target: videoLoader.item
|
|
property: "audioEnabled"
|
|
value: root.audioEnabled
|
|
when: videoLoader.item !== null
|
|
}
|
|
|
|
Component {
|
|
id: imageComponent
|
|
|
|
Image {
|
|
readonly property bool ready: status === Image.Ready
|
|
source: root.imageUrl
|
|
fillMode: Image.PreserveAspectCrop
|
|
asynchronous: true
|
|
cache: root.version === 0
|
|
sourceSize.width: root.version > 0 ? width : 0
|
|
sourceSize.height: root.version > 0 ? height : 0
|
|
}
|
|
}
|
|
}
|