1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-06 05:25:41 -05:00

Add support for linux-wallpaperengine wallpapers (#191)

* Add support for linux-wallpaperengine wallpapers

* Remove unnecessary onCompleted hook
This commit is contained in:
Aziz Hasanain
2025-09-12 19:13:14 +03:00
committed by GitHub
parent c3759dc542
commit c8cb5dc146
6 changed files with 262 additions and 28 deletions

View File

@@ -30,6 +30,32 @@ Singleton {
readonly property string wallpaperPath: { readonly property string wallpaperPath: {
if (typeof SessionData === "undefined") return "" if (typeof SessionData === "undefined") return ""
if (SessionData.perMonitorWallpaper) {
// Use first monitor's wallpaper for dynamic theming
var screens = Quickshell.screens
if (screens.length > 0) {
var firstMonitorWallpaper = SessionData.getMonitorWallpaper(screens[0].name)
var wallpaperPath = firstMonitorWallpaper || SessionData.wallpaperPath
if (wallpaperPath && wallpaperPath.startsWith("we:")) {
return stateDir + "/we_screenshots/" + wallpaperPath.substring(3) + ".jpg"
}
return wallpaperPath
}
}
var wallpaperPath = SessionData.wallpaperPath
var screens = Quickshell.screens
if (screens.length > 0 && wallpaperPath && wallpaperPath.startsWith("we:")) {
return stateDir + "/we_screenshots/" + wallpaperPath.substring(3) + ".jpg"
}
return wallpaperPath
}
readonly property string rawWallpaperPath: {
if (typeof SessionData === "undefined") return ""
if (SessionData.perMonitorWallpaper) { if (SessionData.perMonitorWallpaper) {
// Use first monitor's wallpaper for dynamic theming // Use first monitor's wallpaper for dynamic theming
var screens = Quickshell.screens var screens = Quickshell.screens
@@ -38,7 +64,7 @@ Singleton {
return firstMonitorWallpaper || SessionData.wallpaperPath return firstMonitorWallpaper || SessionData.wallpaperPath
} }
} }
return SessionData.wallpaperPath return SessionData.wallpaperPath
} }
@@ -383,7 +409,11 @@ Singleton {
function extractColors() { function extractColors() {
extractionRequested = true extractionRequested = true
if (matugenAvailable) if (matugenAvailable)
fileChecker.running = true if (rawWallpaperPath.startsWith("we:")) {
fileCheckerTimer.start()
} else {
fileChecker.running = true
}
else else
matugenCheck.running = true matugenCheck.running = true
} }
@@ -418,7 +448,15 @@ Singleton {
Quickshell.execDetached(["sh", "-c", `mkdir -p '${stateDir}' && cat > '${desiredPath}' << 'EOF'\n${json}\nEOF`]) Quickshell.execDetached(["sh", "-c", `mkdir -p '${stateDir}' && cat > '${desiredPath}' << 'EOF'\n${json}\nEOF`])
workerRunning = true workerRunning = true
systemThemeGenerator.command = [shellDir + "/scripts/matugen-worker.sh", stateDir, shellDir, "--run"] if (rawWallpaperPath.startsWith("we:")) {
console.log("calling matugen worker")
systemThemeGenerator.command = [
"sh", "-c",
`sleep 1 && ${shellDir}/scripts/matugen-worker.sh '${stateDir}' '${shellDir}' --run`
]
} else {
systemThemeGenerator.command = [shellDir + "/scripts/matugen-worker.sh", stateDir, shellDir, "--run"]
}
systemThemeGenerator.running = true systemThemeGenerator.running = true
} }
@@ -552,7 +590,11 @@ Singleton {
return return
} }
if (extractionRequested) { if (extractionRequested) {
fileChecker.running = true if (rawWallpaperPath.startsWith("we:")) {
fileCheckerTimer.start()
} else {
fileChecker.running = true
}
} }
const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode) const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode)
@@ -597,6 +639,15 @@ Singleton {
} }
} }
Timer {
id: fileCheckerTimer
interval: 1000
repeat: false
onTriggered: {
fileChecker.running = true
}
}
Process { Process {
id: matugenProcess id: matugenProcess
command: ["matugen", "image", wallpaperPath, "--json", "hex"] command: ["matugen", "image", wallpaperPath, "--json", "hex"]

View File

@@ -439,8 +439,8 @@ DankModal {
width: parent.width width: parent.width
height: parent.height - 80 height: parent.height - 80
clip: true clip: true
cellWidth: 150 cellWidth: fileBrowserModal.browserType === "we" ? 255 : 150
cellHeight: 130 cellHeight: fileBrowserModal.browserType === "we" ? 215 : 130
cacheBuffer: 260 cacheBuffer: 260
model: folderModel model: folderModel
currentIndex: selectedIndex currentIndex: selectedIndex
@@ -466,8 +466,8 @@ DankModal {
required property url fileURL required property url fileURL
required property int index required property int index
width: 140 width: fileBrowserModal.browserType === "we" ? 245 : 140
height: 120 height: fileBrowserModal.browserType === "we" ? 205 : 120
radius: Theme.cornerRadius radius: Theme.cornerRadius
color: { color: {
if (keyboardNavigationActive && delegateRoot.index === selectedIndex) if (keyboardNavigationActive && delegateRoot.index === selectedIndex)
@@ -498,16 +498,31 @@ DankModal {
spacing: Theme.spacingXS spacing: Theme.spacingXS
Item { Item {
width: 80 width: fileBrowserModal.browserType === "we" ? 225 : 80
height: 60 height: fileBrowserModal.browserType === "we" ? 165 : 60
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
CachingImage { CachingImage {
anchors.fill: parent anchors.fill: parent
source: (!delegateRoot.fileIsDir && isImageFile(delegateRoot.fileName)) ? ("file://" + delegateRoot.filePath) : "" property var weExtensions: [".jpg", ".png", ".webp", ".gif", ".jpeg"]
property int weExtIndex: 0
source: {
if (fileBrowserModal.browserType === "we" && delegateRoot.fileIsDir) {
return "file://" + delegateRoot.filePath + "/preview" + weExtensions[weExtIndex]
}
return (!delegateRoot.fileIsDir && isImageFile(delegateRoot.fileName)) ? ("file://" + delegateRoot.filePath) : ""
}
onStatusChanged: {
if (fileBrowserModal.browserType === "we" && delegateRoot.fileIsDir && status === Image.Error) {
if (weExtIndex < weExtensions.length - 1) {
weExtIndex++
source = "file://" + delegateRoot.filePath + "/preview" + weExtensions[weExtIndex]
}
}
}
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
visible: !delegateRoot.fileIsDir && isImageFile(delegateRoot.fileName) visible: (!delegateRoot.fileIsDir && isImageFile(delegateRoot.fileName)) || (fileBrowserModal.browserType === "we" && delegateRoot.fileIsDir)
maxCacheSize: 80 maxCacheSize: fileBrowserModal.browserType === "we" ? 225 : 80
} }
DankIcon { DankIcon {
@@ -523,7 +538,7 @@ DankModal {
name: "folder" name: "folder"
size: Theme.iconSizeLarge size: Theme.iconSizeLarge
color: Theme.primary color: Theme.primary
visible: delegateRoot.fileIsDir visible: delegateRoot.fileIsDir && fileBrowserModal.browserType !== "we"
} }
} }
@@ -550,7 +565,11 @@ DankModal {
// Update selected file info and index first // Update selected file info and index first
selectedIndex = delegateRoot.index selectedIndex = delegateRoot.index
setSelectedFileData(delegateRoot.filePath, delegateRoot.fileName, delegateRoot.fileIsDir) setSelectedFileData(delegateRoot.filePath, delegateRoot.fileName, delegateRoot.fileIsDir)
if (delegateRoot.fileIsDir) { if (fileBrowserModal.browserType === "we" && delegateRoot.fileIsDir) {
// Select this folder instead of navigating inside
fileSelected(delegateRoot.filePath)
fileBrowserModal.close()
} if (delegateRoot.fileIsDir) {
navigateTo(delegateRoot.filePath) navigateTo(delegateRoot.filePath)
} else { } else {
fileSelected(delegateRoot.filePath) fileSelected(delegateRoot.filePath)

View File

@@ -1,3 +1,4 @@
import QtCore
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Effects import QtQuick.Effects
@@ -75,6 +76,12 @@ Item {
anchors.fill: parent anchors.fill: parent
source: { source: {
var currentWallpaper = SessionData.getMonitorWallpaper(screenName) var currentWallpaper = SessionData.getMonitorWallpaper(screenName)
if (screenName && currentWallpaper && currentWallpaper.startsWith("we:")) {
const cacheHome = StandardPaths.writableLocation(StandardPaths.CacheLocation).toString()
const baseDir = cacheHome.startsWith("file://") ? cacheHome.substring(7) : cacheHome
const screenshotPath = baseDir + "/dankshell/we_screenshots" + "/" + currentWallpaper.substring(3) + ".jpg"
return screenshotPath
}
return (currentWallpaper && !currentWallpaper.startsWith("#")) ? currentWallpaper : "" return (currentWallpaper && !currentWallpaper.startsWith("#")) ? currentWallpaper : ""
} }
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop

View File

@@ -1,3 +1,4 @@
import QtCore
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Effects import QtQuick.Effects
@@ -141,10 +142,28 @@ Item {
CachingImage { CachingImage {
anchors.fill: parent anchors.fill: parent
anchors.margins: 1 anchors.margins: 1
property var weExtensions: [".jpg", ".png", ".webp", ".gif", ".jpeg"]
property int weExtIndex: 0
source: { source: {
var currentWallpaper = SessionData.perMonitorWallpaper ? SessionData.getMonitorWallpaper(selectedMonitorName) : SessionData.wallpaperPath var currentWallpaper = SessionData.perMonitorWallpaper ? SessionData.getMonitorWallpaper(selectedMonitorName) : SessionData.wallpaperPath
if (currentWallpaper && currentWallpaper.startsWith("we:")) {
var sceneId = currentWallpaper.substring(3)
return StandardPaths.writableLocation(StandardPaths.HomeLocation)
+ "/.local/share/Steam/steamapps/workshop/content/431960/"
+ sceneId + "/preview" + weExtensions[weExtIndex]
}
return (currentWallpaper !== "" && !currentWallpaper.startsWith("#")) ? "file://" + currentWallpaper : "" return (currentWallpaper !== "" && !currentWallpaper.startsWith("#")) ? "file://" + currentWallpaper : ""
} }
onStatusChanged: {
var currentWallpaper = SessionData.perMonitorWallpaper ? SessionData.getMonitorWallpaper(selectedMonitorName) : SessionData.wallpaperPath
if (currentWallpaper.startsWith("we:") && status === Image.Error && weExtIndex < weExtensions.length - 1) {
weExtIndex++
source = StandardPaths.writableLocation(StandardPaths.HomeLocation)
+ "/.local/share/Steam/steamapps/workshop/content/431960/"
+ currentWallpaper.substring(3)
+ "/preview" + weExtensions[weExtIndex]
}
}
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
visible: { visible: {
var currentWallpaper = SessionData.perMonitorWallpaper ? SessionData.getMonitorWallpaper(selectedMonitorName) : SessionData.wallpaperPath var currentWallpaper = SessionData.perMonitorWallpaper ? SessionData.getMonitorWallpaper(selectedMonitorName) : SessionData.wallpaperPath
@@ -234,6 +253,30 @@ Item {
} }
} }
Rectangle {
width: 32; height: 32; radius: 16
color: Qt.rgba(255, 255, 255, 0.9)
DankIcon {
anchors.centerIn: parent
name: "movie" // 🎬 icon for WE
size: 18
color: "black"
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
if (parentModal) {
parentModal.allowFocusOverride = true
parentModal.shouldHaveFocus = false
}
weBrowser.open()
}
}
}
Rectangle { Rectangle {
width: 32 width: 32
height: 32 height: 32
@@ -347,11 +390,11 @@ Item {
iconSize: Theme.iconSizeSmall iconSize: Theme.iconSizeSmall
enabled: { enabled: {
var currentWallpaper = SessionData.perMonitorWallpaper ? SessionData.getMonitorWallpaper(selectedMonitorName) : SessionData.wallpaperPath var currentWallpaper = SessionData.perMonitorWallpaper ? SessionData.getMonitorWallpaper(selectedMonitorName) : SessionData.wallpaperPath
return currentWallpaper && !currentWallpaper.startsWith("#") return currentWallpaper && !currentWallpaper.startsWith("#") && !currentWallpaper.startsWith("we")
} }
opacity: { opacity: {
var currentWallpaper = SessionData.perMonitorWallpaper ? SessionData.getMonitorWallpaper(selectedMonitorName) : SessionData.wallpaperPath var currentWallpaper = SessionData.perMonitorWallpaper ? SessionData.getMonitorWallpaper(selectedMonitorName) : SessionData.wallpaperPath
return (currentWallpaper && !currentWallpaper.startsWith("#")) ? 1 : 0.5 return (currentWallpaper && !currentWallpaper.startsWith("#") && !currentWallpaper.startsWith("we")) ? 1 : 0.5
} }
backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5) backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
@@ -370,11 +413,11 @@ Item {
iconSize: Theme.iconSizeSmall iconSize: Theme.iconSizeSmall
enabled: { enabled: {
var currentWallpaper = SessionData.perMonitorWallpaper ? SessionData.getMonitorWallpaper(selectedMonitorName) : SessionData.wallpaperPath var currentWallpaper = SessionData.perMonitorWallpaper ? SessionData.getMonitorWallpaper(selectedMonitorName) : SessionData.wallpaperPath
return currentWallpaper && !currentWallpaper.startsWith("#") return currentWallpaper && !currentWallpaper.startsWith("#") && !currentWallpaper.startsWith("we")
} }
opacity: { opacity: {
var currentWallpaper = SessionData.perMonitorWallpaper ? SessionData.getMonitorWallpaper(selectedMonitorName) : SessionData.wallpaperPath var currentWallpaper = SessionData.perMonitorWallpaper ? SessionData.getMonitorWallpaper(selectedMonitorName) : SessionData.wallpaperPath
return (currentWallpaper && !currentWallpaper.startsWith("#")) ? 1 : 0.5 return (currentWallpaper && !currentWallpaper.startsWith("#") && !currentWallpaper.startsWith("we")) ? 1 : 0.5
} }
backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5) backgroundColor: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
iconColor: Theme.surfaceText iconColor: Theme.surfaceText
@@ -1424,6 +1467,38 @@ Item {
} }
} }
FileBrowserModal {
id: weBrowser
browserTitle: "Select Wallpaper Engine Scene"
browserIcon: "movie"
browserType: "we"
fileExtensions: [] // only show dirs
// override homeDir to point to Steam workshop path
homeDir: StandardPaths.writableLocation(StandardPaths.HomeLocation)
+ "/.local/share/Steam/steamapps/workshop/content/431960"
// ensure we start there
Component.onCompleted: currentPath = homeDir
function getLastPath() {
return homeDir
}
onFileSelected: folderPath => {
var sceneId = folderPath.split("/").pop()
var weSource = "we:" + sceneId
if (SessionData.perMonitorWallpaper) {
SessionData.setMonitorWallpaper(selectedMonitorName, weSource)
} else {
SessionData.setWallpaper(weSource)
}
close()
}
}
DankColorPicker { DankColorPicker {
id: colorPicker id: colorPicker

View File

@@ -2,8 +2,10 @@ import QtQuick
import Quickshell import Quickshell
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell.Widgets import Quickshell.Widgets
import Quickshell.Io
import qs.Common import qs.Common
import qs.Widgets import qs.Widgets
import qs.Modules
LazyLoader { LazyLoader {
active: true active: true
@@ -36,20 +38,38 @@ LazyLoader {
property bool isColorSource: source.startsWith("#") property bool isColorSource: source.startsWith("#")
property Image current: one property Image current: one
WallpaperEngineProc {
id: weProc
monitor: modelData.name
}
Component.onDestruction: {
weProc.stop()
}
onSourceChanged: { onSourceChanged: {
if (!source) { const isWE = source.startsWith("we:")
current = null if (isWE) {
one.source = ""
two.source = ""
} else if (isColorSource) {
current = null current = null
one.source = "" one.source = ""
two.source = "" two.source = ""
weProc.start(source.substring(3)) // strip "we:"
} else { } else {
if (current === one) weProc.stop()
two.update() if (!source) {
else current = null
one.update() one.source = ""
two.source = ""
} else if (isColorSource) {
current = null
one.source = ""
two.source = ""
} else {
if (current === one)
two.update()
else
one.update()
}
} }
} }

View File

@@ -0,0 +1,62 @@
import QtCore
import QtQuick
import Quickshell.Io
import Quickshell
Item {
id: root
property string monitor: ""
property string sceneId: ""
property string pendingSceneId: ""
Process {
id: weProcess
running: false
command: []
}
Process {
id: killer
running: false
command: []
onExited: (code) => {
if (pendingSceneId !== "") {
const cacheHome = StandardPaths.writableLocation(StandardPaths.CacheLocation).toString()
const baseDir = cacheHome.startsWith("file://") ? cacheHome.substring(7) : cacheHome
const outDir = baseDir + "/dankshell/we_screenshots"
const outPath = outDir + "/" + pendingSceneId + ".jpg"
Quickshell.execDetached(["mkdir", "-p", outDir])
// only spawn after killer has done its job
weProcess.command = [
"linux-wallpaperengine",
"--screen-root", monitor,
"--screenshot",
outPath,
pendingSceneId,
"--silent"
]
weProcess.running = true
pendingSceneId = ""
}
}
}
function start(newSceneId) {
sceneId = newSceneId
pendingSceneId = newSceneId
stop()
}
function stop() {
if (weProcess.running) {
weProcess.running = false
}
killer.command = [
"pkill", "-f",
"linux-wallpaperengine --screen-root " + monitor
]
killer.running = true
}
}