Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
90aeebc
Modularize Wallpaper.qml into ImageWallpaper.qml and Wallpaper.qml
AteebXYZ Nov 9, 2025
5debdc1
Add support for videos to be seen in picker
AteebXYZ Nov 9, 2025
fc559f7
Modular system works perfectly with images only
AteebXYZ Nov 12, 2025
1f5e660
Video and image wallpapers function, with some transition side effects
AteebXYZ Nov 12, 2025
eee299d
Add .gif extension to isVideo check
AteebXYZ Nov 12, 2025
8143ee2
Fixed transitions between images and videos vice versa
AteebXYZ Nov 14, 2025
4d708a1
Video thumbnails visible in wallpaper selector, fixed black wallpaper…
AteebXYZ Dec 13, 2025
33ca3fa
Add config options for video wallpaper audio mute and volume
AteebXYZ Dec 13, 2025
8b39f8e
Cleanup
AteebXYZ Dec 13, 2025
07234cf
Fix aspect ratio scaling for video wallpaper
AteebXYZ Dec 14, 2025
a1a128d
Add placeholder screen when wallpaper doesnt exist
AteebXYZ Dec 14, 2025
56a0e47
experiments
AteebXYZ Dec 30, 2025
d1dd4fd
getting videothumbnailer to work
AteebXYZ Dec 30, 2025
02f3f20
VRAM spike fixed when opening wallpaper menu (unstable)
AteebXYZ Jan 4, 2026
3940172
rewrite videothumbnailer.cpp
AteebXYZ Jan 6, 2026
0326a72
Rewrite video hashing in videothumbnailer and cleanup
AteebXYZ Jan 14, 2026
52c442c
Make video wallpapers pause on shell lock
AteebXYZ Jan 14, 2026
496aa17
Remove failed signal in VideoWallpaper.qml
AteebXYZ Jan 14, 2026
ba5fd70
Pause video playback when game mode is enabled, and some cleanup for …
AteebXYZ Jan 14, 2026
ff7943b
Unload video when game mode is on and fix VRAM issues
AteebXYZ Jan 15, 2026
f0e5ea7
remove state transitions and bug fixes
AteebXYZ Jan 15, 2026
3185997
bug fixes
AteebXYZ Jan 16, 2026
2b80bb8
stability fix
AteebXYZ Jan 16, 2026
4230057
allow for video files to be picked from filedialog
AteebXYZ Jan 16, 2026
5723131
Update fork and resolve conflicts
AteebXYZ Jan 17, 2026
d755a29
Update WallpaperGrid in controlcenter to use videothumbnailer
AteebXYZ Jan 18, 2026
02165d3
Fix wallpaper config example in README
AteebXYZ Jan 18, 2026
c57849f
Restructure wallpaper config
AteebXYZ Jan 18, 2026
e4d9f9b
fix typo
AteebXYZ Jan 18, 2026
8b0d1fa
explicitly capture 'this' to comply with C++20
AteebXYZ Jan 18, 2026
42851fa
fix warning
AteebXYZ Jan 19, 2026
3b67716
fix opacity animations
AteebXYZ Jan 20, 2026
68be292
make loaders synchronous for hot reload to work
AteebXYZ Jan 20, 2026
b06de22
Merge branch 'upstream-main' into video-wallpapers
AteebXYZ Jan 21, 2026
72de99d
cache filepath to memory for faster lookup
AteebXYZ Jan 26, 2026
0b9d477
update fork
AteebXYZ Feb 2, 2026
64dcb64
remove unused imports and clean up warnings
AteebXYZ Feb 2, 2026
9dabb8a
check if file location in hash map exists before returning
AteebXYZ Feb 6, 2026
d7298a2
added config where video can be auto paused if applications are maxim…
AteebXYZ Feb 6, 2026
208426d
implement unloading videos in pausePlayVideo
AteebXYZ Feb 7, 2026
e6b39b1
remove trailing comma in shell config example
AteebXYZ Feb 20, 2026
03d156e
update fork
AteebXYZ Mar 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,15 @@ default, you must create it manually.
"autoHide": true,
"rounding": 1,
"spacing": 1
},
"wallpaper": {
"audio": {
"muteAudio": true,
"volume": 1.0
},
"video": {
"autoPause": false
}
}
},
"bar": {
Expand Down
15 changes: 15 additions & 0 deletions config/BackgroundConfig.qml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ JsonObject {
property bool wallpaperEnabled: true
property DesktopClock desktopClock: DesktopClock {}
property Visualiser visualiser: Visualiser {}
property Wallpaper wallpaper: Wallpaper {}

component DesktopClock: JsonObject {
property bool enabled: false
Expand Down Expand Up @@ -34,4 +35,18 @@ JsonObject {
property real rounding: 1
property real spacing: 1
}

component Wallpaper: JsonObject {
property WallpaperAudio audio: WallpaperAudio {}
property WallpaperVideo video: WallpaperVideo {}
}

component WallpaperAudio: JsonObject {
property bool muteAudio: true
property real volume: 1.0
}

component WallpaperVideo: JsonObject {
property bool autoPause: false
}
}
9 changes: 8 additions & 1 deletion modules/background/Background.qml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ import Quickshell.Wayland
import QtQuick

Loader {
id: backgroundLoader

active: Config.background.enabled

property var lock

sourceComponent: Variants {
model: Screens.screens

Expand Down Expand Up @@ -42,7 +46,10 @@ Loader {
anchors.fill: parent
active: Config.background.wallpaperEnabled

sourceComponent: Wallpaper {}
sourceComponent: Wallpaper {
screen: win.modelData
sessionLock: backgroundLoader.lock ? backgroundLoader.lock.lock : null
}
}

Visualiser {
Expand Down
51 changes: 51 additions & 0 deletions modules/background/ImageWallpaper.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
pragma ComponentBehavior: Bound

import qs.components
import qs.components.images
import qs.services
import QtQuick

Item {
id: root
anchors.fill: parent

property bool isCurrent: false

signal ready

function update(explicitSource) {
explicitSource = explicitSource.toString();
const target = explicitSource;

if (img.path === target && img.status === Image.Ready) {
Qt.callLater(() => root.ready());
return;
}

img.path = target;

img.onStatusChanged.connect(function handler() {
if (img.status === Image.Ready) {
Qt.callLater(() => root.ready());
img.onStatusChanged.disconnect(handler);
}
});
}

CachingImage {
id: img
anchors.fill: parent
asynchronous: true
fillMode: Image.PreserveAspectCrop
opacity: root.isCurrent ? 1 : 0
scale: (root.isCurrent ? 1 : Wallpapers.showPreview ? 1 : 0.8)

Behavior on opacity {
Anim {}
}

Behavior on scale {
Anim {}
}
}
}
167 changes: 167 additions & 0 deletions modules/background/VideoWallpaper.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
pragma ComponentBehavior: Bound

import qs.components
import qs.services
import qs.config
import QtQuick
import QtMultimedia

Item {
id: root
anchors.fill: parent
signal ready
property bool shouldPause: false
property bool isCurrent: false
property string source: ""
property bool gamemodeEnabled: GameMode.enabled
property bool pendingUnload: false
property string visualMode: !root.isCurrent ? "none" : gamemodeEnabled ? "placeholder" : "video"

function update(path) {
if (gamemodeEnabled) {
root.source = path;
Qt.callLater(root.ready);
return;
}
path = path.toString();
if (!path || path.trim() === "")
return;

player.source = path;
root.source = path;

player.onMediaStatusChanged.connect(function handler() {
if (player.mediaStatus === MediaPlayer.BufferedMedia || player.mediaStatus === MediaPlayer.LoadedMedia) {
pausePlayVideo(root.shouldPause);
Qt.callLater(root.ready);
player.onMediaStatusChanged.disconnect(handler);
}
});
}

function pausePlayVideo(shouldPause) {
if (gamemodeEnabled)
return;
if (isCurrent === false) {
if (player.mediaStatus !== MediaPlayer.NoMedia) {
player.pause();
root.pendingUnload = true;
}
return;
}
if (player.mediaStatus === MediaPlayer.NoMedia)
return;

if (shouldPause || root.shouldPause) {
if (player && player.mediaStatus !== MediaPlayer.NoMedia) {
player.pause();
}
} else {
if (player.source && player.mediaStatus !== MediaPlayer.PlayingState) {
player.play();
}
}
}

onShouldPauseChanged: {
pausePlayVideo(root.shouldPause);
}

onGamemodeEnabledChanged: {
if (gamemodeEnabled) {
player.pause();
pendingUnload = true;
} else if (root.isCurrent) {
update(root.source);
pausePlayVideo(root.shouldPause);
}
}

MediaPlayer {
id: player
autoPlay: false
loops: MediaPlayer.Infinite
videoOutput: video
audioOutput: AudioOutput {
muted: Config.background.wallpaper.audio.muteAudio
volume: Config.background.wallpaper.audio.volume
}
}

VideoOutput {
id: video
anchors.fill: parent
opacity: root.visualMode === "video" ? 1 : 0
scale: (root.isCurrent ? 1 : Wallpapers.showPreview ? 1 : 0.8)
fillMode: VideoOutput.PreserveAspectCrop

Behavior on opacity {
Anim {
onRunningChanged: {
if (running)
return;
if (root.pendingUnload && video.opacity === 0) {
player.stop();
Qt.callLater(() => {
if (root.gamemodeEnabled) {
player.source = "";
return;
}

if (!root.isCurrent) {
player.source = "";
}
});

root.pendingUnload = false;
}
}
}
}

Behavior on scale {
Anim {}
}
}

StyledRect {
id: gameModePlaceholder
opacity: root.visualMode === "placeholder" ? 1 : 0
anchors.fill: parent
color: Colours.palette.m3surfaceContainer

Behavior on opacity {
Anim {}
}

Row {
anchors.centerIn: parent
spacing: Appearance.spacing.large

MaterialIcon {
text: "stadia_controller"
color: Colours.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.extraLarge * 5
}

Column {
anchors.verticalCenter: parent.verticalCenter
spacing: Appearance.spacing.small

StyledText {
text: qsTr("Video wallpapers are disabled in game mode")
color: Colours.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.extraLarge
font.bold: true
}
}
}
}

Connections {
target: root
function onIsCurrentChanged() {
root.pausePlayVideo(root.shouldPause);
}
}
}
Loading