diff --git a/install/omarchy-base.packages b/install/omarchy-base.packages index 76e74110..b38c68b8 100644 --- a/install/omarchy-base.packages +++ b/install/omarchy-base.packages @@ -107,6 +107,8 @@ python-poetry-core ttfx qemu-user-static-binfmt qrencode +# TODO: add owe-wallpaper-engine here once it is packaged. The shell yields +# video backgrounds to it while its daemon runs. qt6-imageformats qt6-multimedia qt6-multimedia-ffmpeg diff --git a/manual/39-backgrounds.md b/manual/39-backgrounds.md index f2cd51eb..f0291653 100644 --- a/manual/39-backgrounds.md +++ b/manual/39-backgrounds.md @@ -4,6 +4,6 @@ Every theme ships with its own set of backgrounds, and you can add extras of you You can do this most easily by going to _Install > Style > Background_ in the Omarchy Menu. That'll bring up the folder where the backgrounds for that theme is stored. Hit `Super + Shift + F` to start another file manager, find your background, copy it over. Now it'll be included in the choices of backgrounds you can select between using `Super + Ctrl + Space`. -Backgrounds can be videos as well as stills. Drop an `mp4`, `m4v`, `mov`, `webm`, `mkv`, or `avi` file in the same folder and it appears alongside the images, playing on a loop. Only your first monitor's wallpaper plays a video's sound track, through the default audio output at the system volume, and the lock screen stays silent. Playback stops on its own whenever nothing can see it — while a fullscreen window covers that monitor, while the screensaver is up, and once a locked screen has gone dark — but a video wallpaper still costs far more power than a still one, and each monitor decodes its own copy. +Backgrounds can be videos as well as stills. Drop an `mp4`, `m4v`, `mov`, `webm`, `mkv`, or `avi` file in the same folder and it appears alongside the images, playing on a loop. Only your first monitor's wallpaper plays a video's sound track, through the default audio output at the system volume, and the lock screen stays silent. Playback stops on its own whenever nothing can see it — while a fullscreen window covers that monitor, while the screensaver is up, and once a locked screen has gone dark — but a video wallpaper still costs far more power than a still one, and each monitor decodes its own copy. When the OWE wallpaper engine is running, it takes over video backgrounds and the shell keeps only stills. You can find a huge collection of cool curated backgrounds on https://github.com/dharmx/walls. diff --git a/shell/Ui/BackgroundMedia.qml b/shell/Ui/BackgroundMedia.qml index b281ae22..f3ab7743 100644 --- a/shell/Ui/BackgroundMedia.qml +++ b/shell/Ui/BackgroundMedia.qml @@ -8,6 +8,10 @@ Item { 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. @@ -37,7 +41,7 @@ Item { Loader { id: videoLoader anchors.fill: parent - active: root.path !== "" && root.video && !root.reloading + active: root.path !== "" && root.video && !root.reloading && !root.deferVideo source: "BackgroundVideo.qml" } diff --git a/shell/plugins/background/Background.qml b/shell/plugins/background/Background.qml index dea93a4c..f8929fa9 100644 --- a/shell/plugins/background/Background.qml +++ b/shell/plugins/background/Background.qml @@ -32,6 +32,25 @@ Item { // services so playback can stop whenever nothing can see the wallpaper. property var shell: null + // When the OWE wallpaper engine is running, it owns video backgrounds. This + // plugin keeps stills, which OWE hands back to it, and the lock screen keeps + // its own playback. + property bool oweActive: false + + Process { + id: oweStatusProc + command: ["bash", "-c", "test -S \"${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/owe/owed.sock\""] + onExited: root.oweActive = (exitCode === 0) + } + + Timer { + id: oweStatusTimer + interval: 5000 + repeat: true + running: true + onTriggered: if (!oweStatusProc.running) oweStatusProc.running = true + } + // Stop a video wallpaper's decoding whenever it is covered. Qt's FFmpeg // engine drives its own clock, so an unseen player keeps decoding until it // is told not to — a locked laptop would otherwise decode until it died. @@ -204,7 +223,10 @@ Item { } } - Component.onCompleted: refreshBackground() + Component.onCompleted: { + oweStatusProc.running = true + refreshBackground() + } Variants { model: Quickshell.screens @@ -264,6 +286,7 @@ Item { anchors.fill: parent path: root.displayedBackground reloads: root.displayedReloads + deferVideo: root.oweActive playbackEnabled: !root.sessionObscured && !root.powerSaverActive && !panel.fullscreenHere audioEnabled: panel.firstScreen onReadyChanged: { diff --git a/test/shell.d/video-background-test.sh b/test/shell.d/video-background-test.sh index 7ebad86b..d3591b4f 100755 --- a/test/shell.d/video-background-test.sh +++ b/test/shell.d/video-background-test.sh @@ -87,6 +87,15 @@ assert( 'video switches bypass the image-only reveal stack and use the durable background path' ) assert(backgroundQml.includes('BackgroundMedia {') && lockQml.includes('BackgroundMedia {'), 'desktop and lock screen share video-capable media rendering') +assert( + backgroundQml.includes('property bool oweActive: false') && + backgroundQml.includes('/owe/owed.sock') && + /deferVideo: root\.oweActive/.test(backgroundQml) && + /property bool deferVideo: false/.test(mediaQml) && + /active: root\.path !== "" && root\.video && !root\.reloading && !root\.deferVideo/.test(mediaQml) && + !lockQml.includes('deferVideo'), + 'the desktop yields video backgrounds to OWE while it is running, and the lock keeps its own playback' +) assert( lockQml.includes('source: wallpaper.video ? null : wallpaper') && lockQml.includes('visible: !wallpaper.video') &&