1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-05-11 23:09:42 -04:00

quickshell: drop support for 0.2, require 0.3+

- Remove all compat code
- Rewire LegacyNetworkService to use Quickshell.Networking
- Add parentWindow to settings child windows
This commit is contained in:
bbedward
2026-05-11 13:04:29 -04:00
parent 3989c7f801
commit b8f4c350a8
52 changed files with 1472 additions and 2064 deletions

View File

@@ -22,17 +22,30 @@ Singleton {
property string currentSoundTheme: ""
property var soundFilePaths: ({})
property var volumeChangeSound: null
property var powerPlugSound: null
property var powerUnplugSound: null
property var normalNotificationSound: null
property var criticalNotificationSound: null
property var loginSound: null
readonly property var volumeChangeSound: soundsLoader.item?.volumeChangeSound ?? null
readonly property var powerPlugSound: soundsLoader.item?.powerPlugSound ?? null
readonly property var powerUnplugSound: soundsLoader.item?.powerUnplugSound ?? null
readonly property var normalNotificationSound: soundsLoader.item?.normalNotificationSound ?? null
readonly property var criticalNotificationSound: soundsLoader.item?.criticalNotificationSound ?? null
readonly property var loginSound: soundsLoader.item?.loginSound ?? null
readonly property var mediaDevices: soundsLoader.item?.mediaDevices ?? null
property real notificationsVolume: 1.0
property bool notificationsAudioMuted: false
property var mediaDevices: null
property var mediaDevicesConnections: null
Loader {
id: soundsLoader
active: root.soundsAvailable
source: "AudioSoundPlayers.qml"
onLoaded: {
item.volume = Qt.binding(() => root.notificationsVolume);
item.volumeChangeSource = Qt.binding(() => root.getSoundPath("audio-volume-change"));
item.powerPlugSource = Qt.binding(() => root.getSoundPath("power-plug"));
item.powerUnplugSource = Qt.binding(() => root.getSoundPath("power-unplug"));
item.normalNotificationSource = Qt.binding(() => root.getSoundPath("message"));
item.criticalNotificationSource = Qt.binding(() => root.getSoundPath("message-new-instant"));
item.loginSource = Qt.binding(() => root.getSoundPath("desktop-login"));
}
}
property var deviceAliases: ({})
property string wireplumberConfigPath: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation)) + "/wireplumber/wireplumber.conf.d/51-dms-audio-aliases.conf"
@@ -452,10 +465,6 @@ EOFCONFIG
function discoverSoundFiles(themeName) {
if (!themeName) {
soundFilePaths = {};
if (soundsAvailable) {
destroySoundPlayers();
createSoundPlayers();
}
return;
}
@@ -514,11 +523,6 @@ EOFCONFIG
}
}
soundFilePaths = paths;
if (soundsAvailable) {
destroySoundPlayers();
createSoundPlayers();
}
}, 0);
}
@@ -559,159 +563,6 @@ EOFCONFIG
discoverSoundFiles(currentSoundTheme);
} else {
soundFilePaths = {};
if (soundsAvailable) {
destroySoundPlayers();
createSoundPlayers();
}
}
}
function setupMediaDevices() {
if (!soundsAvailable || mediaDevices) {
return;
}
try {
mediaDevices = Qt.createQmlObject(`
import QtQuick
import QtMultimedia
MediaDevices {
id: devices
Component.onCompleted: {
log.debug("MediaDevices initialized, default output:", defaultAudioOutput?.description)
}
}
`, root, "AudioService.MediaDevices");
if (mediaDevices) {
mediaDevicesConnections = Qt.createQmlObject(`
import QtQuick
Connections {
target: root.mediaDevices
function onDefaultAudioOutputChanged() {
log.debug("Default audio output changed, recreating sound players")
root.destroySoundPlayers()
root.createSoundPlayers()
}
}
`, root, "AudioService.MediaDevicesConnections");
}
} catch (e) {
log.debug("MediaDevices not available, using default audio output");
mediaDevices = null;
}
}
function destroySoundPlayers() {
if (volumeChangeSound) {
volumeChangeSound.destroy();
volumeChangeSound = null;
}
if (powerPlugSound) {
powerPlugSound.destroy();
powerPlugSound = null;
}
if (powerUnplugSound) {
powerUnplugSound.destroy();
powerUnplugSound = null;
}
if (normalNotificationSound) {
normalNotificationSound.destroy();
normalNotificationSound = null;
}
if (criticalNotificationSound) {
criticalNotificationSound.destroy();
criticalNotificationSound = null;
}
if (loginSound) {
loginSound.destroy();
loginSound = null;
}
}
function createSoundPlayers() {
if (!soundsAvailable) {
return;
}
setupMediaDevices();
try {
const deviceProperty = mediaDevices ? `device: root.mediaDevices.defaultAudioOutput\n ` : "";
const volumeChangePath = getSoundPath("audio-volume-change");
volumeChangeSound = Qt.createQmlObject(`
import QtQuick
import QtMultimedia
MediaPlayer {
source: "${volumeChangePath}"
audioOutput: AudioOutput {
${deviceProperty}volume: notificationsVolume
}
}
`, root, "AudioService.VolumeChangeSound");
const powerPlugPath = getSoundPath("power-plug");
powerPlugSound = Qt.createQmlObject(`
import QtQuick
import QtMultimedia
MediaPlayer {
source: "${powerPlugPath}"
audioOutput: AudioOutput {
${deviceProperty}volume: notificationsVolume
}
}
`, root, "AudioService.PowerPlugSound");
const powerUnplugPath = getSoundPath("power-unplug");
powerUnplugSound = Qt.createQmlObject(`
import QtQuick
import QtMultimedia
MediaPlayer {
source: "${powerUnplugPath}"
audioOutput: AudioOutput {
${deviceProperty}volume: notificationsVolume
}
}
`, root, "AudioService.PowerUnplugSound");
const messagePath = getSoundPath("message");
normalNotificationSound = Qt.createQmlObject(`
import QtQuick
import QtMultimedia
MediaPlayer {
source: "${messagePath}"
audioOutput: AudioOutput {
${deviceProperty}volume: notificationsVolume
}
}
`, root, "AudioService.NormalNotificationSound");
const messageNewInstantPath = getSoundPath("message-new-instant");
criticalNotificationSound = Qt.createQmlObject(`
import QtQuick
import QtMultimedia
MediaPlayer {
source: "${messageNewInstantPath}"
audioOutput: AudioOutput {
${deviceProperty}volume: notificationsVolume
}
}
`, root, "AudioService.CriticalNotificationSound");
const loginPath = getSoundPath("desktop-login");
loginSound = Qt.createQmlObject(`
import QtQuick
import QtMultimedia
MediaPlayer {
source: "${loginPath}"
audioOutput: AudioOutput {
${deviceProperty}volume: notificationsVolume
}
}
`, root, "AudioService.LoginSound");
} catch (e) {
log.warn("Error creating sound players:", e);
}
}
@@ -955,16 +806,6 @@ EOFCONFIG
objects: Pipewire.nodes.values.filter(node => node.audio && !node.isStream)
}
Connections {
target: Pipewire
function onDefaultAudioSinkChanged() {
if (soundsAvailable) {
Qt.callLater(root.destroySoundPlayers);
Qt.callLater(root.createSoundPlayers);
}
}
}
function setVolume(percentage) {
if (!root.sink?.audio)
return "No audio sink available";
@@ -1127,10 +968,8 @@ EOFCONFIG
Component.onCompleted: {
rebuildTypedNodeLists();
if (soundsAvailable) {
if (soundsAvailable)
checkGsettings();
Qt.callLater(createSoundPlayers);
}
loadDeviceAliases();
}

View File

@@ -0,0 +1,80 @@
import QtQuick
import QtMultimedia
Item {
id: root
property real volume: 1.0
property url volumeChangeSource
property url powerPlugSource
property url powerUnplugSource
property url normalNotificationSource
property url criticalNotificationSource
property url loginSource
readonly property alias mediaDevices: devices
readonly property alias volumeChangeSound: volumeChangePlayer
readonly property alias powerPlugSound: powerPlugPlayer
readonly property alias powerUnplugSound: powerUnplugPlayer
readonly property alias normalNotificationSound: normalNotificationPlayer
readonly property alias criticalNotificationSound: criticalNotificationPlayer
readonly property alias loginSound: loginPlayer
MediaDevices {
id: devices
}
MediaPlayer {
id: volumeChangePlayer
source: root.volumeChangeSource
audioOutput: AudioOutput {
device: devices.defaultAudioOutput
volume: root.volume
}
}
MediaPlayer {
id: powerPlugPlayer
source: root.powerPlugSource
audioOutput: AudioOutput {
device: devices.defaultAudioOutput
volume: root.volume
}
}
MediaPlayer {
id: powerUnplugPlayer
source: root.powerUnplugSource
audioOutput: AudioOutput {
device: devices.defaultAudioOutput
volume: root.volume
}
}
MediaPlayer {
id: normalNotificationPlayer
source: root.normalNotificationSource
audioOutput: AudioOutput {
device: devices.defaultAudioOutput
volume: root.volume
}
}
MediaPlayer {
id: criticalNotificationPlayer
source: root.criticalNotificationSource
audioOutput: AudioOutput {
device: devices.defaultAudioOutput
volume: root.volume
}
}
MediaPlayer {
id: loginPlayer
source: root.loginSource
audioOutput: AudioOutput {
device: devices.defaultAudioOutput
volume: root.volume
}
}
}

View File

@@ -4,7 +4,6 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Wayland // ! Import is needed despite what qmlls says
import qs.Common
import qs.Services
@@ -12,9 +11,8 @@ Singleton {
id: root
readonly property var log: Log.scoped("BlurService")
property bool quickshellSupported: false
property bool compositorSupported: false
property bool available: quickshellSupported && compositorSupported
readonly property bool available: compositorSupported
readonly property bool enabled: available && (SettingsData.blurEnabled ?? false)
readonly property color borderColor: {
@@ -42,41 +40,6 @@ Singleton {
return Theme.withAlpha(baseColor, hoverAlpha ?? 0.15);
}
function createBlurRegion(targetWindow) {
if (!available)
return null;
try {
const region = Qt.createQmlObject(`
import Quickshell
Region {}
`, targetWindow, "BlurRegion");
targetWindow.BackgroundEffect.blurRegion = region;
return region;
} catch (e) {
log.warn("Failed to create blur region:", e);
return null;
}
}
function reapplyBlurRegion(targetWindow, region) {
if (!region || !available)
return;
try {
targetWindow.BackgroundEffect.blurRegion = region;
region.changed();
} catch (e) {}
}
function destroyBlurRegion(targetWindow, region) {
if (!region)
return;
try {
targetWindow.BackgroundEffect.blurRegion = null;
} catch (e) {}
region.destroy();
}
Process {
id: blurProbe
running: false
@@ -98,18 +61,5 @@ Singleton {
}
}
Component.onCompleted: {
try {
const test = Qt.createQmlObject(`
import Quickshell
Region { radius: 0 }
`, root, "BlurAvailabilityTest");
test.destroy();
quickshellSupported = true;
log.info("Quickshell blur support available");
blurProbe.running = true;
} catch (e) {
log.info("BackgroundEffect not available - blur disabled. Requires a newer version of Quickshell.");
}
}
Component.onCompleted: blurProbe.running = true
}

View File

@@ -11,25 +11,8 @@ Singleton {
id: root
readonly property var log: Log.scoped("IdleService")
readonly property bool idleMonitorAvailable: {
try {
return typeof IdleMonitor !== "undefined";
} catch (e) {
return false;
}
}
readonly property bool idleInhibitorAvailable: {
try {
return typeof IdleInhibitor !== "undefined";
} catch (e) {
return false;
}
}
property bool enabled: true
property bool respectInhibitors: true
property bool _enableGate: true
readonly property bool externalInhibitActive: DMSService.screensaverInhibited
@@ -43,17 +26,28 @@ Singleton {
readonly property bool mediaPlaying: MprisController.activePlayer !== null && MprisController.activePlayer.isPlaying
onEnabledChanged: _applyMonitorEnableds()
onPostLockMonitorActiveChanged: _applyMonitorEnableds()
onMonitorTimeoutChanged: _rearmIdleMonitors()
onLockTimeoutChanged: _rearmIdleMonitors()
onSuspendTimeoutChanged: _rearmIdleMonitors()
onPostLockMonitorTimeoutChanged: _rearmIdleMonitors()
onIsShellLockedChanged: _rearmIdleMonitors()
function _applyMonitorEnableds() {
const base = enabled;
monitorOffMonitor.enabled = base && monitorTimeout > 0 && !postLockMonitorActive;
postLockMonitorOffMonitor.enabled = base && postLockMonitorActive;
lockMonitor.enabled = base && lockTimeout > 0;
suspendMonitor.enabled = base && suspendTimeout > 0;
}
function _rearmIdleMonitors() {
_enableGate = false;
Qt.callLater(() => {
_enableGate = true;
});
monitorOffMonitor.enabled = false;
postLockMonitorOffMonitor.enabled = false;
lockMonitor.enabled = false;
suspendMonitor.enabled = false;
Qt.callLater(_applyMonitorEnableds);
}
signal lockRequested
@@ -65,10 +59,6 @@ Singleton {
signal requestMonitorOn
signal requestSuspend
property var monitorOffMonitor: null
property var postLockMonitorOffMonitor: null
property var lockMonitor: null
property var suspendMonitor: null
property var lockComponent: null
property bool monitorsOff: false
property bool isShellLocked: false
@@ -82,84 +72,69 @@ Singleton {
CompositorService.powerOffMonitors();
}
function createIdleMonitors() {
if (!idleMonitorAvailable) {
log.info("IdleMonitor not available, skipping creation");
return;
}
try {
const qmlString = `
import QtQuick
import Quickshell.Wayland
IdleMonitor {
enabled: false
respectInhibitors: true
timeout: 0
}
`;
monitorOffMonitor = Qt.createQmlObject(qmlString, root, "IdleService.MonitorOffMonitor");
monitorOffMonitor.timeout = Qt.binding(() => root.monitorTimeout > 0 ? root.monitorTimeout : 86400);
monitorOffMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors);
monitorOffMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.monitorTimeout > 0 && !root.postLockMonitorActive);
monitorOffMonitor.isIdleChanged.connect(function () {
if (monitorOffMonitor.isIdle) {
if (SettingsData.fadeToDpmsEnabled) {
root.fadeToDpmsRequested();
} else {
root.requestMonitorOff();
}
IdleMonitor {
id: monitorOffMonitor
timeout: root.monitorTimeout > 0 ? root.monitorTimeout : 86400
respectInhibitors: root.respectInhibitors
enabled: false
onIsIdleChanged: {
if (isIdle) {
if (SettingsData.fadeToDpmsEnabled) {
root.fadeToDpmsRequested();
} else {
if (SettingsData.fadeToDpmsEnabled) {
root.cancelFadeToDpms();
}
root.requestMonitorOn();
}
});
postLockMonitorOffMonitor = Qt.createQmlObject(qmlString, root, "IdleService.PostLockMonitorOffMonitor");
postLockMonitorOffMonitor.timeout = Qt.binding(() => root.postLockMonitorTimeout > 0 ? root.postLockMonitorTimeout : 86400);
postLockMonitorOffMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors);
postLockMonitorOffMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.postLockMonitorActive);
postLockMonitorOffMonitor.isIdleChanged.connect(function () {
if (postLockMonitorOffMonitor.isIdle) {
root.requestMonitorOff();
} else {
root.requestMonitorOn();
}
});
} else {
if (SettingsData.fadeToDpmsEnabled) {
root.cancelFadeToDpms();
}
root.requestMonitorOn();
}
}
}
lockMonitor = Qt.createQmlObject(qmlString, root, "IdleService.LockMonitor");
lockMonitor.timeout = Qt.binding(() => root.lockTimeout > 0 ? root.lockTimeout : 86400);
lockMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors);
lockMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.lockTimeout > 0);
lockMonitor.isIdleChanged.connect(function () {
if (lockMonitor.isIdle) {
if (SettingsData.fadeToLockEnabled) {
root.fadeToLockRequested();
} else {
root.lockRequested();
}
} else {
if (SettingsData.fadeToLockEnabled) {
root.cancelFadeToLock();
}
}
});
IdleMonitor {
id: postLockMonitorOffMonitor
timeout: root.postLockMonitorTimeout > 0 ? root.postLockMonitorTimeout : 86400
respectInhibitors: root.respectInhibitors
enabled: false
onIsIdleChanged: {
if (isIdle) {
root.requestMonitorOff();
} else {
root.requestMonitorOn();
}
}
}
suspendMonitor = Qt.createQmlObject(qmlString, root, "IdleService.SuspendMonitor");
suspendMonitor.timeout = Qt.binding(() => root.suspendTimeout > 0 ? root.suspendTimeout : 86400);
suspendMonitor.respectInhibitors = Qt.binding(() => root.respectInhibitors);
suspendMonitor.enabled = Qt.binding(() => root._enableGate && root.enabled && root.idleMonitorAvailable && root.suspendTimeout > 0);
suspendMonitor.isIdleChanged.connect(function () {
if (suspendMonitor.isIdle) {
root.requestSuspend();
IdleMonitor {
id: lockMonitor
timeout: root.lockTimeout > 0 ? root.lockTimeout : 86400
respectInhibitors: root.respectInhibitors
enabled: false
onIsIdleChanged: {
if (isIdle) {
if (SettingsData.fadeToLockEnabled) {
root.fadeToLockRequested();
} else {
root.lockRequested();
}
});
} catch (e) {
log.warn("Error creating IdleMonitors:", e);
} else {
if (SettingsData.fadeToLockEnabled) {
root.cancelFadeToLock();
}
}
}
}
IdleMonitor {
id: suspendMonitor
timeout: root.suspendTimeout > 0 ? root.suspendTimeout : 86400
respectInhibitors: root.respectInhibitors
enabled: false
onIsIdleChanged: {
if (isIdle)
root.requestSuspend();
}
}
@@ -194,13 +169,7 @@ Singleton {
}
Component.onCompleted: {
if (!idleMonitorAvailable) {
log.warn("IdleMonitor not available - power management disabled. This requires a newer version of Quickshell.");
} else {
log.info("Initialized with idle monitoring support");
createIdleMonitors();
}
_applyMonitorEnableds();
if (externalInhibitActive) {
const apps = DMSService.screensaverInhibitors.map(i => i.appName).join(", ");
SessionService.idleInhibited = true;

View File

@@ -5,8 +5,6 @@ import QtCore
import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
// ! Even though qmlls says this is unused, it is wrong
import qs.Common
import qs.Services
import "../Common/KeybindActions.js" as Actions
@@ -15,21 +13,7 @@ Singleton {
id: root
readonly property var log: Log.scoped("KeybindsService")
Component.onCompleted: {
if (!shortcutInhibitorAvailable) {
log.warn("ShortcutInhibitor is not available in this environment, keybinds editor disabled.");
}
}
readonly property bool shortcutInhibitorAvailable: {
try {
return typeof ShortcutInhibitor !== "undefined";
} catch (e) {
return false;
}
}
property bool available: (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl) && shortcutInhibitorAvailable
property bool available: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl
property string currentProvider: {
if (CompositorService.isNiri)
return "niri";

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
import QtQuick
// qmllint disable unused-imports
import QtMultimedia
// qmllint enable unused-imports
Item {}

View File

@@ -8,30 +8,15 @@ Singleton {
id: root
readonly property var log: Log.scoped("MultimediaService")
property bool available: false
readonly property bool available: probeLoader.status === Loader.Ready
function detectAvailability() {
try {
const testObj = Qt.createQmlObject(`
import QtQuick
import QtMultimedia
import qs.Services
Item {}
`, root, "MultimediaService.TestComponent");
if (testObj) {
testObj.destroy();
}
available = true;
return true;
} catch (e) {
available = false;
return false;
}
}
Component.onCompleted: {
if (!detectAvailability()) {
log.warn("QtMultimedia not available");
Loader {
id: probeLoader
source: "MultimediaProbe.qml"
active: true
onStatusChanged: {
if (status === Loader.Error)
log.warn("QtMultimedia not available");
}
}
}

View File

@@ -5,6 +5,7 @@ import QtCore
import QtQuick
import Qt.labs.folderlistmodel
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
@@ -154,35 +155,43 @@ Singleton {
}
function loadPluginManifestFile(manifestPathNoScheme, sourceTag, mtimeEpochMs) {
const manifestId = "m_" + Math.random().toString(36).slice(2);
const qml = `
import QtQuick
import Quickshell.Io
FileView {
id: fv
property string absPath: ""
onLoaded: {
try {
let raw = text()
if (raw.charCodeAt(0) === 0xFEFF) raw = raw.slice(1)
const manifest = JSON.parse(raw)
root._onManifestParsed(absPath, manifest, "${sourceTag}", ${mtimeEpochMs})
} catch (e) {
log.error("bad manifest", absPath, e.message)
knownManifests[absPath] = { mtime: ${mtimeEpochMs}, source: "${sourceTag}", bad: true }
}
fv.destroy()
}
onLoadFailed: (err) => {
log.warn("manifest load failed", absPath, err)
fv.destroy()
}
}
`;
const loader = manifestFvComp.createObject(root, {
absPath: manifestPathNoScheme,
path: manifestPathNoScheme,
sourceTag: sourceTag,
mtimeEpochMs: mtimeEpochMs
});
}
const loader = Qt.createQmlObject(qml, root, "mf_" + manifestId);
loader.absPath = manifestPathNoScheme;
loader.path = manifestPathNoScheme;
Component {
id: manifestFvComp
FileView {
id: fv
property string absPath: ""
property string sourceTag: ""
property double mtimeEpochMs: 0
onLoaded: {
try {
let raw = text();
if (raw.charCodeAt(0) === 0xFEFF)
raw = raw.slice(1);
const manifest = JSON.parse(raw);
root._onManifestParsed(absPath, manifest, sourceTag, mtimeEpochMs);
} catch (e) {
root.log.error("bad manifest", absPath, e.message);
root.knownManifests[absPath] = {
mtime: mtimeEpochMs,
source: sourceTag,
bad: true
};
}
fv.destroy();
}
onLoadFailed: err => {
root.log.warn("manifest load failed", absPath, err);
fv.destroy();
}
}
}
function _onManifestParsed(absPath, manifest, sourceTag, mtimeEpochMs) {
@@ -670,10 +679,10 @@ Singleton {
_stateLoaded[pluginId] = true;
_ensureStateDir();
const path = getPluginStatePath(pluginId);
const escapedPath = path.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
try {
const qml = 'import QtQuick; import Quickshell.Io; FileView { path: "' + escapedPath + '"; blockLoading: true; blockWrites: true; atomicWrites: true }';
const fv = Qt.createQmlObject(qml, root, "sf_" + pluginId);
const fv = stateLoadFvComp.createObject(root, {
path: path
});
const raw = fv.text();
if (raw && raw.trim()) {
_stateCache[pluginId] = JSON.parse(raw);
@@ -694,10 +703,10 @@ Singleton {
return;
}
const path = getPluginStatePath(pluginId);
const escapedPath = path.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
try {
const qml = 'import QtQuick; import Quickshell.Io; FileView { path: "' + escapedPath + '"; blockWrites: true; atomicWrites: true }';
const fv = Qt.createQmlObject(qml, root, "sw_" + pluginId);
const fv = stateSaveFvComp.createObject(root, {
path: path
});
_stateWriters[pluginId] = fv;
fv.loaded.connect(function () {
fv.setText(content);
@@ -710,6 +719,23 @@ Singleton {
}
}
Component {
id: stateLoadFvComp
FileView {
blockLoading: true
blockWrites: true
atomicWrites: true
}
}
Component {
id: stateSaveFvComp
FileView {
blockWrites: true
atomicWrites: true
}
}
function _flushDirtyStates() {
const dirty = _stateDirtyPlugins;
_stateDirtyPlugins = {};
@@ -748,22 +774,8 @@ Singleton {
}
function createPluginDirectory() {
const mkdirProcess = Qt.createComponent("data:text/plain,import Quickshell.Io; Process { }");
if (mkdirProcess.status === Component.Ready) {
const process = mkdirProcess.createObject(root);
process.command = ["mkdir", "-p", pluginDirectory];
process.exited.connect(function (exitCode) {
if (exitCode !== 0) {
log.error("Failed to create plugin directory, exit code:", exitCode);
}
process.destroy();
});
process.running = true;
return true;
} else {
log.error("Failed to create mkdir process");
return false;
}
Quickshell.execDetached(["mkdir", "-p", pluginDirectory]);
return true;
}
// Launcher plugin helper functions

View File

@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Services.Polkit
Singleton {
id: root
@@ -10,33 +11,15 @@ Singleton {
readonly property bool disablePolkitIntegration: Quickshell.env("DMS_DISABLE_POLKIT") === "1"
property bool polkitAvailable: false
property var agent: null
readonly property bool polkitAvailable: !disablePolkitIntegration
readonly property alias agent: polkitAgentInstance
function createPolkitAgent() {
try {
const qmlString = `
import QtQuick
import Quickshell.Services.Polkit
import qs.Services
PolkitAgent {
}
`
agent = Qt.createQmlObject(qmlString, root, "PolkitService.Agent")
polkitAvailable = true
log.info("Initialized successfully")
} catch (e) {
polkitAvailable = false
log.warn("Polkit not available - authentication prompts disabled. This requires a newer version of Quickshell.")
}
PolkitAgent {
id: polkitAgentInstance
}
Component.onCompleted: {
if (disablePolkitIntegration) {
return
}
createPolkitAgent()
if (!disablePolkitIntegration)
log.info("Initialized successfully");
}
}

View File

@@ -6,7 +6,6 @@ import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import Quickshell.I3
import Quickshell.Wayland
import qs.Common
import qs.Services
@@ -22,14 +21,6 @@ Singleton {
property string inhibitReason: "Keep system awake"
property string nvidiaCommand: ""
readonly property bool nativeInhibitorAvailable: {
try {
return typeof IdleInhibitor !== "undefined";
} catch (e) {
return false;
}
}
property bool loginctlAvailable: false
property bool wtypeAvailable: false
property string sessionId: ""
@@ -66,8 +57,6 @@ Singleton {
detectHibernateProcess.running = true;
detectPrimeRunProcess.running = true;
detectWtypeProcess.running = true;
cleanupOrphanedInhibitors();
log.info("Native inhibitor available:", nativeInhibitorAvailable);
if (!SettingsData.loginctlLockIntegration) {
log.debug("loginctl lock integration disabled by user");
return;
@@ -396,19 +385,15 @@ Singleton {
signal inhibitorChanged
function enableIdleInhibit() {
if (idleInhibited) {
if (idleInhibited)
return;
}
log.debug("Enabling idle inhibit (native:", nativeInhibitorAvailable, ")");
idleInhibited = true;
inhibitorChanged();
}
function disableIdleInhibit() {
if (!idleInhibited) {
if (!idleInhibited)
return;
}
log.debug("Disabling idle inhibit (native:", nativeInhibitorAvailable, ")");
idleInhibited = false;
inhibitorChanged();
}
@@ -423,64 +408,6 @@ Singleton {
function setInhibitReason(reason) {
inhibitReason = reason;
if (idleInhibited && !nativeInhibitorAvailable) {
const wasActive = idleInhibited;
idleInhibited = false;
Qt.callLater(() => {
if (wasActive) {
idleInhibited = true;
}
});
}
}
Process {
id: idleInhibitProcess
command: {
if (!idleInhibited || nativeInhibitorAvailable) {
return ["true"];
}
log.debug("Starting systemd/elogind inhibit process");
return [isElogind ? "elogind-inhibit" : "systemd-inhibit", "--what=idle", "--who=quickshell", `--why=${inhibitReason}`, "--mode=block", "sleep", "infinity"];
}
running: idleInhibited && !nativeInhibitorAvailable
onRunningChanged: {
log.debug("Inhibit process running:", running, "(native:", nativeInhibitorAvailable, ")");
}
onExited: function (exitCode) {
if (idleInhibited && exitCode !== 0 && !nativeInhibitorAvailable) {
log.warn("Inhibitor process crashed with exit code:", exitCode);
idleInhibited = false;
ToastService.showWarning("Idle inhibitor failed");
}
}
}
// Kill orphaned idle inhibitor processes left behind by previous quickshell sessions.
// When quickshell crashes or is force-killed, the child systemd-inhibit process gets
// reparented to PID 1 and continues to block idle indefinitely.
function cleanupOrphanedInhibitors() {
if (nativeInhibitorAvailable) return;
orphanCleanupProcess.running = true;
}
Process {
id: orphanCleanupProcess
running: false
command: ["pkill", "-f", "systemd-inhibit --what=idle --who=quickshell.*sleep infinity"]
onExited: function (exitCode) {
if (exitCode === 0) {
log.info("Cleaned up orphaned idle inhibitor process(es) from a previous session");
}
}
}
Connections {