1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-29 16:02:51 -05:00

osd/audio: bind audio change to pipewire, suppress OSDs on startup and

resume from suspend
This commit is contained in:
bbedward
2025-11-28 11:05:53 -05:00
parent 94a1aebe2b
commit dd409b4d1c
4 changed files with 418 additions and 396 deletions

View File

@@ -21,6 +21,22 @@ Singleton {
property bool isLightMode: false property bool isLightMode: false
property bool doNotDisturb: false property bool doNotDisturb: false
property bool isSwitchingMode: false property bool isSwitchingMode: false
property bool suppressOSD: true
Timer {
id: osdSuppressTimer
interval: 2000
running: true
onTriggered: root.suppressOSD = false
}
Connections {
target: SessionService
function onSessionResumed() {
root.suppressOSD = true;
osdSuppressTimer.restart();
}
}
property string wallpaperPath: "" property string wallpaperPath: ""
property bool perMonitorWallpaper: false property bool perMonitorWallpaper: false

View File

@@ -1,5 +1,4 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtCore import QtCore
@@ -15,7 +14,6 @@ Singleton {
readonly property PwNode sink: Pipewire.defaultAudioSink readonly property PwNode sink: Pipewire.defaultAudioSink
readonly property PwNode source: Pipewire.defaultAudioSource readonly property PwNode source: Pipewire.defaultAudioSource
property bool suppressOSD: true
property bool soundsAvailable: false property bool soundsAvailable: false
property bool gsettingsAvailable: false property bool gsettingsAvailable: false
property var availableSoundThemes: [] property var availableSoundThemes: []
@@ -33,12 +31,14 @@ Singleton {
signal micMuteChanged signal micMuteChanged
Timer { Connections {
id: startupTimer target: root.sink?.audio ?? null
interval: 500
repeat: false function onVolumeChanged() {
running: true if (SessionData.suppressOSD)
onTriggered: root.suppressOSD = false return;
root.playVolumeChangeSoundIfEnabled();
}
} }
function detectSoundsAvailability() { function detectSoundsAvailability() {
@@ -47,35 +47,33 @@ Singleton {
import QtQuick import QtQuick
import QtMultimedia import QtMultimedia
Item {} Item {}
`, root, "AudioService.TestComponent") `, root, "AudioService.TestComponent");
if (testObj) { if (testObj) {
testObj.destroy() testObj.destroy();
} }
soundsAvailable = true soundsAvailable = true;
return true return true;
} catch (e) { } catch (e) {
soundsAvailable = false soundsAvailable = false;
return false return false;
} }
} }
function checkGsettings() { function checkGsettings() {
Proc.runCommand("checkGsettings", ["sh", "-c", "gsettings get org.gnome.desktop.sound theme-name 2>/dev/null"], (output, exitCode) => { Proc.runCommand("checkGsettings", ["sh", "-c", "gsettings get org.gnome.desktop.sound theme-name 2>/dev/null"], (output, exitCode) => {
gsettingsAvailable = (exitCode === 0) gsettingsAvailable = (exitCode === 0);
if (gsettingsAvailable) { if (gsettingsAvailable) {
scanSoundThemes() scanSoundThemes();
getCurrentSoundTheme() getCurrentSoundTheme();
} }
}, 0) }, 0);
} }
function scanSoundThemes() { function scanSoundThemes() {
const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS") const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS");
const searchPaths = xdgDataDirs && xdgDataDirs.trim() !== "" const searchPaths = xdgDataDirs && xdgDataDirs.trim() !== "" ? xdgDataDirs.split(":").concat(Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation))) : ["/usr/share", "/usr/local/share", Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation))];
? xdgDataDirs.split(":").concat(Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation)))
: ["/usr/share", "/usr/local/share", Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation))]
const basePaths = searchPaths.map(p => p + "/sounds").join(" ") const basePaths = searchPaths.map(p => p + "/sounds").join(" ");
const script = ` const script = `
for base_dir in ${basePaths}; do for base_dir in ${basePaths}; do
[ -d "$base_dir" ] || continue [ -d "$base_dir" ] || continue
@@ -84,65 +82,63 @@ Singleton {
basename "$theme_dir" basename "$theme_dir"
done done
done | sort -u done | sort -u
` `;
Proc.runCommand("scanSoundThemes", ["sh", "-c", script], (output, exitCode) => { Proc.runCommand("scanSoundThemes", ["sh", "-c", script], (output, exitCode) => {
if (exitCode === 0 && output.trim()) { if (exitCode === 0 && output.trim()) {
const themes = output.trim().split('\n').filter(t => t && t.length > 0) const themes = output.trim().split('\n').filter(t => t && t.length > 0);
availableSoundThemes = themes availableSoundThemes = themes;
} else { } else {
availableSoundThemes = [] availableSoundThemes = [];
} }
}, 0) }, 0);
} }
function getCurrentSoundTheme() { function getCurrentSoundTheme() {
Proc.runCommand("getCurrentSoundTheme", ["sh", "-c", "gsettings get org.gnome.desktop.sound theme-name 2>/dev/null | sed \"s/'//g\""], (output, exitCode) => { Proc.runCommand("getCurrentSoundTheme", ["sh", "-c", "gsettings get org.gnome.desktop.sound theme-name 2>/dev/null | sed \"s/'//g\""], (output, exitCode) => {
if (exitCode === 0 && output.trim()) { if (exitCode === 0 && output.trim()) {
currentSoundTheme = output.trim() currentSoundTheme = output.trim();
console.log("AudioService: Current system sound theme:", currentSoundTheme) console.log("AudioService: Current system sound theme:", currentSoundTheme);
if (SettingsData.useSystemSoundTheme) { if (SettingsData.useSystemSoundTheme) {
discoverSoundFiles(currentSoundTheme) discoverSoundFiles(currentSoundTheme);
} }
} else { } else {
currentSoundTheme = "" currentSoundTheme = "";
console.log("AudioService: No system sound theme found") console.log("AudioService: No system sound theme found");
} }
}, 0) }, 0);
} }
function setSoundTheme(themeName) { function setSoundTheme(themeName) {
if (!themeName || themeName === currentSoundTheme) { if (!themeName || themeName === currentSoundTheme) {
return return;
} }
Proc.runCommand("setSoundTheme", ["sh", "-c", `gsettings set org.gnome.desktop.sound theme-name '${themeName}'`], (output, exitCode) => { Proc.runCommand("setSoundTheme", ["sh", "-c", `gsettings set org.gnome.desktop.sound theme-name '${themeName}'`], (output, exitCode) => {
if (exitCode === 0) { if (exitCode === 0) {
currentSoundTheme = themeName currentSoundTheme = themeName;
if (SettingsData.useSystemSoundTheme) { if (SettingsData.useSystemSoundTheme) {
discoverSoundFiles(themeName) discoverSoundFiles(themeName);
} }
} }
}, 0) }, 0);
} }
function discoverSoundFiles(themeName) { function discoverSoundFiles(themeName) {
if (!themeName) { if (!themeName) {
soundFilePaths = {} soundFilePaths = {};
if (soundsAvailable) { if (soundsAvailable) {
destroySoundPlayers() destroySoundPlayers();
createSoundPlayers() createSoundPlayers();
} }
return return;
} }
const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS") const xdgDataDirs = Quickshell.env("XDG_DATA_DIRS");
const searchPaths = xdgDataDirs && xdgDataDirs.trim() !== "" const searchPaths = xdgDataDirs && xdgDataDirs.trim() !== "" ? xdgDataDirs.split(":").concat(Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation))) : ["/usr/share", "/usr/local/share", Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation))];
? xdgDataDirs.split(":").concat(Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation)))
: ["/usr/share", "/usr/local/share", Paths.strip(StandardPaths.writableLocation(StandardPaths.GenericDataLocation))]
const extensions = ["oga", "ogg", "wav", "mp3", "flac"] const extensions = ["oga", "ogg", "wav", "mp3", "flac"];
const themesToSearch = themeName !== "freedesktop" ? `${themeName} freedesktop` : themeName const themesToSearch = themeName !== "freedesktop" ? `${themeName} freedesktop` : themeName;
const script = ` const script = `
for event_key in audio-volume-change power-plug power-unplug message message-new-instant; do for event_key in audio-volume-change power-plug power-unplug message message-new-instant; do
@@ -179,26 +175,26 @@ Singleton {
[ $found -eq 1 ] && break [ $found -eq 1 ] && break
done done
done done
` `;
Proc.runCommand("discoverSoundFiles", ["sh", "-c", script], (output, exitCode) => { Proc.runCommand("discoverSoundFiles", ["sh", "-c", script], (output, exitCode) => {
const paths = {} const paths = {};
if (exitCode === 0 && output.trim()) { if (exitCode === 0 && output.trim()) {
const lines = output.trim().split('\n') const lines = output.trim().split('\n');
for (let line of lines) { for (let line of lines) {
const parts = line.split('=') const parts = line.split('=');
if (parts.length === 2) { if (parts.length === 2) {
paths[parts[0]] = "file://" + parts[1] paths[parts[0]] = "file://" + parts[1];
} }
} }
} }
soundFilePaths = paths soundFilePaths = paths;
if (soundsAvailable) { if (soundsAvailable) {
destroySoundPlayers() destroySoundPlayers();
createSoundPlayers() createSoundPlayers();
} }
}, 0) }, 0);
} }
function getSoundPath(soundEvent) { function getSoundPath(soundEvent) {
@@ -208,45 +204,45 @@ Singleton {
"power-unplug": "../assets/sounds/plasma/power-unplug.wav", "power-unplug": "../assets/sounds/plasma/power-unplug.wav",
"message": "../assets/sounds/freedesktop/message.wav", "message": "../assets/sounds/freedesktop/message.wav",
"message-new-instant": "../assets/sounds/freedesktop/message-new-instant.wav" "message-new-instant": "../assets/sounds/freedesktop/message-new-instant.wav"
} };
const specialConditions = { const specialConditions = {
"smooth": ["audio-volume-change"] "smooth": ["audio-volume-change"]
} };
const themeLower = currentSoundTheme.toLowerCase() const themeLower = currentSoundTheme.toLowerCase();
if (SettingsData.useSystemSoundTheme && specialConditions[themeLower]?.includes(soundEvent)) { if (SettingsData.useSystemSoundTheme && specialConditions[themeLower]?.includes(soundEvent)) {
const bundledPath = Qt.resolvedUrl(soundMap[soundEvent] || "../assets/sounds/freedesktop/message.wav") const bundledPath = Qt.resolvedUrl(soundMap[soundEvent] || "../assets/sounds/freedesktop/message.wav");
console.log("AudioService: Using bundled sound (special condition) for", soundEvent, ":", bundledPath) console.log("AudioService: Using bundled sound (special condition) for", soundEvent, ":", bundledPath);
return bundledPath return bundledPath;
} }
if (SettingsData.useSystemSoundTheme && soundFilePaths[soundEvent]) { if (SettingsData.useSystemSoundTheme && soundFilePaths[soundEvent]) {
console.log("AudioService: Using system sound for", soundEvent, ":", soundFilePaths[soundEvent]) console.log("AudioService: Using system sound for", soundEvent, ":", soundFilePaths[soundEvent]);
return soundFilePaths[soundEvent] return soundFilePaths[soundEvent];
} }
const bundledPath = Qt.resolvedUrl(soundMap[soundEvent] || "../assets/sounds/freedesktop/message.wav") const bundledPath = Qt.resolvedUrl(soundMap[soundEvent] || "../assets/sounds/freedesktop/message.wav");
console.log("AudioService: Using bundled sound for", soundEvent, ":", bundledPath) console.log("AudioService: Using bundled sound for", soundEvent, ":", bundledPath);
return bundledPath return bundledPath;
} }
function reloadSounds() { function reloadSounds() {
console.log("AudioService: Reloading sounds, useSystemSoundTheme:", SettingsData.useSystemSoundTheme, "currentSoundTheme:", currentSoundTheme) console.log("AudioService: Reloading sounds, useSystemSoundTheme:", SettingsData.useSystemSoundTheme, "currentSoundTheme:", currentSoundTheme);
if (SettingsData.useSystemSoundTheme && currentSoundTheme) { if (SettingsData.useSystemSoundTheme && currentSoundTheme) {
discoverSoundFiles(currentSoundTheme) discoverSoundFiles(currentSoundTheme);
} else { } else {
soundFilePaths = {} soundFilePaths = {};
if (soundsAvailable) { if (soundsAvailable) {
destroySoundPlayers() destroySoundPlayers();
createSoundPlayers() createSoundPlayers();
} }
} }
} }
function setupMediaDevices() { function setupMediaDevices() {
if (!soundsAvailable || mediaDevices) { if (!soundsAvailable || mediaDevices) {
return return;
} }
try { try {
@@ -259,7 +255,7 @@ Singleton {
console.log("AudioService: MediaDevices initialized, default output:", defaultAudioOutput?.description) console.log("AudioService: MediaDevices initialized, default output:", defaultAudioOutput?.description)
} }
} }
`, root, "AudioService.MediaDevices") `, root, "AudioService.MediaDevices");
if (mediaDevices) { if (mediaDevices) {
mediaDevicesConnections = Qt.createQmlObject(` mediaDevicesConnections = Qt.createQmlObject(`
@@ -272,48 +268,48 @@ Singleton {
root.createSoundPlayers() root.createSoundPlayers()
} }
} }
`, root, "AudioService.MediaDevicesConnections") `, root, "AudioService.MediaDevicesConnections");
} }
} catch (e) { } catch (e) {
console.log("AudioService: MediaDevices not available, using default audio output") console.log("AudioService: MediaDevices not available, using default audio output");
mediaDevices = null mediaDevices = null;
} }
} }
function destroySoundPlayers() { function destroySoundPlayers() {
if (volumeChangeSound) { if (volumeChangeSound) {
volumeChangeSound.destroy() volumeChangeSound.destroy();
volumeChangeSound = null volumeChangeSound = null;
} }
if (powerPlugSound) { if (powerPlugSound) {
powerPlugSound.destroy() powerPlugSound.destroy();
powerPlugSound = null powerPlugSound = null;
} }
if (powerUnplugSound) { if (powerUnplugSound) {
powerUnplugSound.destroy() powerUnplugSound.destroy();
powerUnplugSound = null powerUnplugSound = null;
} }
if (normalNotificationSound) { if (normalNotificationSound) {
normalNotificationSound.destroy() normalNotificationSound.destroy();
normalNotificationSound = null normalNotificationSound = null;
} }
if (criticalNotificationSound) { if (criticalNotificationSound) {
criticalNotificationSound.destroy() criticalNotificationSound.destroy();
criticalNotificationSound = null criticalNotificationSound = null;
} }
} }
function createSoundPlayers() { function createSoundPlayers() {
if (!soundsAvailable) { if (!soundsAvailable) {
return return;
} }
setupMediaDevices() setupMediaDevices();
try { try {
const deviceProperty = mediaDevices ? `device: root.mediaDevices.defaultAudioOutput\n ` : "" const deviceProperty = mediaDevices ? `device: root.mediaDevices.defaultAudioOutput\n ` : "";
const volumeChangePath = getSoundPath("audio-volume-change") const volumeChangePath = getSoundPath("audio-volume-change");
volumeChangeSound = Qt.createQmlObject(` volumeChangeSound = Qt.createQmlObject(`
import QtQuick import QtQuick
import QtMultimedia import QtMultimedia
@@ -323,9 +319,9 @@ Singleton {
${deviceProperty}volume: 1.0 ${deviceProperty}volume: 1.0
} }
} }
`, root, "AudioService.VolumeChangeSound") `, root, "AudioService.VolumeChangeSound");
const powerPlugPath = getSoundPath("power-plug") const powerPlugPath = getSoundPath("power-plug");
powerPlugSound = Qt.createQmlObject(` powerPlugSound = Qt.createQmlObject(`
import QtQuick import QtQuick
import QtMultimedia import QtMultimedia
@@ -335,9 +331,9 @@ Singleton {
${deviceProperty}volume: 1.0 ${deviceProperty}volume: 1.0
} }
} }
`, root, "AudioService.PowerPlugSound") `, root, "AudioService.PowerPlugSound");
const powerUnplugPath = getSoundPath("power-unplug") const powerUnplugPath = getSoundPath("power-unplug");
powerUnplugSound = Qt.createQmlObject(` powerUnplugSound = Qt.createQmlObject(`
import QtQuick import QtQuick
import QtMultimedia import QtMultimedia
@@ -347,9 +343,9 @@ Singleton {
${deviceProperty}volume: 1.0 ${deviceProperty}volume: 1.0
} }
} }
`, root, "AudioService.PowerUnplugSound") `, root, "AudioService.PowerUnplugSound");
const messagePath = getSoundPath("message") const messagePath = getSoundPath("message");
normalNotificationSound = Qt.createQmlObject(` normalNotificationSound = Qt.createQmlObject(`
import QtQuick import QtQuick
import QtMultimedia import QtMultimedia
@@ -359,9 +355,9 @@ Singleton {
${deviceProperty}volume: 1.0 ${deviceProperty}volume: 1.0
} }
} }
`, root, "AudioService.NormalNotificationSound") `, root, "AudioService.NormalNotificationSound");
const messageNewInstantPath = getSoundPath("message-new-instant") const messageNewInstantPath = getSoundPath("message-new-instant");
criticalNotificationSound = Qt.createQmlObject(` criticalNotificationSound = Qt.createQmlObject(`
import QtQuick import QtQuick
import QtMultimedia import QtMultimedia
@@ -371,114 +367,114 @@ Singleton {
${deviceProperty}volume: 1.0 ${deviceProperty}volume: 1.0
} }
} }
`, root, "AudioService.CriticalNotificationSound") `, root, "AudioService.CriticalNotificationSound");
} catch (e) { } catch (e) {
console.warn("AudioService: Error creating sound players:", e) console.warn("AudioService: Error creating sound players:", e);
} }
} }
function playVolumeChangeSound() { function playVolumeChangeSound() {
if (soundsAvailable && volumeChangeSound) { if (soundsAvailable && volumeChangeSound) {
volumeChangeSound.play() volumeChangeSound.play();
} }
} }
function playPowerPlugSound() { function playPowerPlugSound() {
if (soundsAvailable && powerPlugSound) { if (soundsAvailable && powerPlugSound) {
powerPlugSound.play() powerPlugSound.play();
} }
} }
function playPowerUnplugSound() { function playPowerUnplugSound() {
if (soundsAvailable && powerUnplugSound) { if (soundsAvailable && powerUnplugSound) {
powerUnplugSound.play() powerUnplugSound.play();
} }
} }
function playNormalNotificationSound() { function playNormalNotificationSound() {
if (soundsAvailable && normalNotificationSound && !SessionData.doNotDisturb) { if (soundsAvailable && normalNotificationSound && !SessionData.doNotDisturb) {
normalNotificationSound.play() normalNotificationSound.play();
} }
} }
function playCriticalNotificationSound() { function playCriticalNotificationSound() {
if (soundsAvailable && criticalNotificationSound && !SessionData.doNotDisturb) { if (soundsAvailable && criticalNotificationSound && !SessionData.doNotDisturb) {
criticalNotificationSound.play() criticalNotificationSound.play();
} }
} }
function playVolumeChangeSoundIfEnabled() { function playVolumeChangeSoundIfEnabled() {
if (SettingsData.soundsEnabled && SettingsData.soundVolumeChanged) { if (SettingsData.soundsEnabled && SettingsData.soundVolumeChanged) {
playVolumeChangeSound() playVolumeChangeSound();
} }
} }
function displayName(node) { function displayName(node) {
if (!node) { if (!node) {
return "" return "";
} }
if (node.properties && node.properties["device.description"]) { if (node.properties && node.properties["device.description"]) {
return node.properties["device.description"] return node.properties["device.description"];
} }
if (node.description && node.description !== node.name) { if (node.description && node.description !== node.name) {
return node.description return node.description;
} }
if (node.nickname && node.nickname !== node.name) { if (node.nickname && node.nickname !== node.name) {
return node.nickname return node.nickname;
} }
if (node.name.includes("analog-stereo")) { if (node.name.includes("analog-stereo")) {
return "Built-in Speakers" return "Built-in Speakers";
} }
if (node.name.includes("bluez")) { if (node.name.includes("bluez")) {
return "Bluetooth Audio" return "Bluetooth Audio";
} }
if (node.name.includes("usb")) { if (node.name.includes("usb")) {
return "USB Audio" return "USB Audio";
} }
if (node.name.includes("hdmi")) { if (node.name.includes("hdmi")) {
return "HDMI Audio" return "HDMI Audio";
} }
return node.name return node.name;
} }
function subtitle(name) { function subtitle(name) {
if (!name) { if (!name) {
return "" return "";
} }
if (name.includes('usb-')) { if (name.includes('usb-')) {
if (name.includes('SteelSeries')) { if (name.includes('SteelSeries')) {
return "USB Gaming Headset" return "USB Gaming Headset";
} }
if (name.includes('Generic')) { if (name.includes('Generic')) {
return "USB Audio Device" return "USB Audio Device";
} }
return "USB Audio" return "USB Audio";
} }
if (name.includes('pci-')) { if (name.includes('pci-')) {
if (name.includes('01_00.1') || name.includes('01:00.1')) { if (name.includes('01_00.1') || name.includes('01:00.1')) {
return "NVIDIA GPU Audio" return "NVIDIA GPU Audio";
} }
return "PCI Audio" return "PCI Audio";
} }
if (name.includes('bluez')) { if (name.includes('bluez')) {
return "Bluetooth Audio" return "Bluetooth Audio";
} }
if (name.includes('analog')) { if (name.includes('analog')) {
return "Built-in Audio" return "Built-in Audio";
} }
if (name.includes('hdmi')) { if (name.includes('hdmi')) {
return "HDMI Audio" return "HDMI Audio";
} }
return "" return "";
} }
PwObjectTracker { PwObjectTracker {
@@ -487,136 +483,134 @@ Singleton {
function setVolume(percentage) { function setVolume(percentage) {
if (!root.sink?.audio) { if (!root.sink?.audio) {
return "No audio sink available" return "No audio sink available";
} }
const clampedVolume = Math.max(0, Math.min(100, percentage)) const clampedVolume = Math.max(0, Math.min(100, percentage));
root.sink.audio.volume = clampedVolume / 100 root.sink.audio.volume = clampedVolume / 100;
return `Volume set to ${clampedVolume}%` return `Volume set to ${clampedVolume}%`;
} }
function toggleMute() { function toggleMute() {
if (!root.sink?.audio) { if (!root.sink?.audio) {
return "No audio sink available" return "No audio sink available";
} }
root.sink.audio.muted = !root.sink.audio.muted root.sink.audio.muted = !root.sink.audio.muted;
return root.sink.audio.muted ? "Audio muted" : "Audio unmuted" return root.sink.audio.muted ? "Audio muted" : "Audio unmuted";
} }
function setMicVolume(percentage) { function setMicVolume(percentage) {
if (!root.source?.audio) { if (!root.source?.audio) {
return "No audio source available" return "No audio source available";
} }
const clampedVolume = Math.max(0, Math.min(100, percentage)) const clampedVolume = Math.max(0, Math.min(100, percentage));
root.source.audio.volume = clampedVolume / 100 root.source.audio.volume = clampedVolume / 100;
return `Microphone volume set to ${clampedVolume}%` return `Microphone volume set to ${clampedVolume}%`;
} }
function toggleMicMute() { function toggleMicMute() {
if (!root.source?.audio) { if (!root.source?.audio) {
return "No audio source available" return "No audio source available";
} }
root.source.audio.muted = !root.source.audio.muted root.source.audio.muted = !root.source.audio.muted;
return root.source.audio.muted ? "Microphone muted" : "Microphone unmuted" return root.source.audio.muted ? "Microphone muted" : "Microphone unmuted";
} }
IpcHandler { IpcHandler {
target: "audio" target: "audio"
function setvolume(percentage: string): string { function setvolume(percentage: string): string {
return root.setVolume(parseInt(percentage)) return root.setVolume(parseInt(percentage));
} }
function increment(step: string): string { function increment(step: string): string {
if (!root.sink?.audio) { if (!root.sink?.audio) {
return "No audio sink available" return "No audio sink available";
} }
if (root.sink.audio.muted) { if (root.sink.audio.muted) {
root.sink.audio.muted = false root.sink.audio.muted = false;
} }
const currentVolume = Math.round(root.sink.audio.volume * 100) const currentVolume = Math.round(root.sink.audio.volume * 100);
const stepValue = parseInt(step || "5") const stepValue = parseInt(step || "5");
const newVolume = Math.max(0, Math.min(100, currentVolume + stepValue)) const newVolume = Math.max(0, Math.min(100, currentVolume + stepValue));
root.sink.audio.volume = newVolume / 100 root.sink.audio.volume = newVolume / 100;
root.playVolumeChangeSoundIfEnabled() return `Volume increased to ${newVolume}%`;
return `Volume increased to ${newVolume}%`
} }
function decrement(step: string): string { function decrement(step: string): string {
if (!root.sink?.audio) { if (!root.sink?.audio) {
return "No audio sink available" return "No audio sink available";
} }
if (root.sink.audio.muted) { if (root.sink.audio.muted) {
root.sink.audio.muted = false root.sink.audio.muted = false;
} }
const currentVolume = Math.round(root.sink.audio.volume * 100) const currentVolume = Math.round(root.sink.audio.volume * 100);
const stepValue = parseInt(step || "5") const stepValue = parseInt(step || "5");
const newVolume = Math.max(0, Math.min(100, currentVolume - stepValue)) const newVolume = Math.max(0, Math.min(100, currentVolume - stepValue));
root.sink.audio.volume = newVolume / 100 root.sink.audio.volume = newVolume / 100;
root.playVolumeChangeSoundIfEnabled() return `Volume decreased to ${newVolume}%`;
return `Volume decreased to ${newVolume}%`
} }
function mute(): string { function mute(): string {
return root.toggleMute() return root.toggleMute();
} }
function setmic(percentage: string): string { function setmic(percentage: string): string {
return root.setMicVolume(parseInt(percentage)) return root.setMicVolume(parseInt(percentage));
} }
function micmute(): string { function micmute(): string {
const result = root.toggleMicMute() const result = root.toggleMicMute();
root.micMuteChanged() root.micMuteChanged();
return result return result;
} }
function status(): string { function status(): string {
let result = "Audio Status:\n" let result = "Audio Status:\n";
if (root.sink?.audio) { if (root.sink?.audio) {
const volume = Math.round(root.sink.audio.volume * 100) const volume = Math.round(root.sink.audio.volume * 100);
const muteStatus = root.sink.audio.muted ? " (muted)" : "" const muteStatus = root.sink.audio.muted ? " (muted)" : "";
result += `Output: ${volume}%${muteStatus}\n` result += `Output: ${volume}%${muteStatus}\n`;
} else { } else {
result += "Output: No sink available\n" result += "Output: No sink available\n";
} }
if (root.source?.audio) { if (root.source?.audio) {
const micVolume = Math.round(root.source.audio.volume * 100) const micVolume = Math.round(root.source.audio.volume * 100);
const muteStatus = root.source.audio.muted ? " (muted)" : "" const muteStatus = root.source.audio.muted ? " (muted)" : "";
result += `Input: ${micVolume}%${muteStatus}` result += `Input: ${micVolume}%${muteStatus}`;
} else { } else {
result += "Input: No source available" result += "Input: No source available";
} }
return result return result;
} }
} }
Connections { Connections {
target: SettingsData target: SettingsData
function onUseSystemSoundThemeChanged() { function onUseSystemSoundThemeChanged() {
reloadSounds() reloadSounds();
} }
} }
Component.onCompleted: { Component.onCompleted: {
if (!detectSoundsAvailability()) { if (!detectSoundsAvailability()) {
console.warn("AudioService: QtMultimedia not available - sound effects disabled") console.warn("AudioService: QtMultimedia not available - sound effects disabled");
} else { } else {
console.info("AudioService: Sound effects enabled") console.info("AudioService: Sound effects enabled");
checkGsettings() checkGsettings();
Qt.callLater(createSoundPlayers) Qt.callLater(createSoundPlayers);
} }
} }
} }

View File

@@ -1,5 +1,4 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick import QtQuick
@@ -23,9 +22,9 @@ Singleton {
readonly property bool nativeInhibitorAvailable: { readonly property bool nativeInhibitorAvailable: {
try { try {
return typeof IdleInhibitor !== "undefined" return typeof IdleInhibitor !== "undefined";
} catch (e) { } catch (e) {
return false return false;
} }
} }
@@ -42,10 +41,10 @@ Singleton {
property string seat: "" property string seat: ""
property string display: "" property string display: ""
signal sessionLocked() signal sessionLocked
signal sessionUnlocked() signal sessionUnlocked
signal prepareForSleep() signal sessionResumed
signal loginctlStateChanged() signal loginctlStateChanged
property bool stateInitialized: false property bool stateInitialized: false
@@ -57,30 +56,29 @@ Singleton {
running: true running: true
repeat: false repeat: false
onTriggered: { onTriggered: {
detectElogindProcess.running = true detectElogindProcess.running = true;
detectHibernateProcess.running = true detectHibernateProcess.running = true;
detectPrimeRunProcess.running = true detectPrimeRunProcess.running = true;
console.info("SessionService: Native inhibitor available:", nativeInhibitorAvailable) console.info("SessionService: Native inhibitor available:", nativeInhibitorAvailable);
if (!SettingsData.loginctlLockIntegration) { if (!SettingsData.loginctlLockIntegration) {
console.log("SessionService: loginctl lock integration disabled by user") console.log("SessionService: loginctl lock integration disabled by user");
return return;
} }
if (socketPath && socketPath.length > 0) { if (socketPath && socketPath.length > 0) {
checkDMSCapabilities() checkDMSCapabilities();
} else { } else {
console.log("SessionService: DMS_SOCKET not set") console.log("SessionService: DMS_SOCKET not set");
} }
} }
} }
Process { Process {
id: detectUwsmProcess id: detectUwsmProcess
running: false running: false
command: ["which", "uwsm"] command: ["which", "uwsm"]
onExited: function (exitCode) { onExited: function (exitCode) {
hasUwsm = (exitCode === 0) hasUwsm = (exitCode === 0);
} }
} }
@@ -90,8 +88,8 @@ Singleton {
command: ["sh", "-c", "ps -eo comm= | grep -E '^(elogind|elogind-daemon)$'"] command: ["sh", "-c", "ps -eo comm= | grep -E '^(elogind|elogind-daemon)$'"]
onExited: function (exitCode) { onExited: function (exitCode) {
console.log("SessionService: Elogind detection exited with code", exitCode) console.log("SessionService: Elogind detection exited with code", exitCode);
isElogind = (exitCode === 0) isElogind = (exitCode === 0);
} }
} }
@@ -101,7 +99,7 @@ Singleton {
command: ["grep", "-q", "disk", "/sys/power/state"] command: ["grep", "-q", "disk", "/sys/power/state"]
onExited: function (exitCode) { onExited: function (exitCode) {
hibernateSupported = (exitCode === 0) hibernateSupported = (exitCode === 0);
} }
} }
@@ -111,7 +109,7 @@ Singleton {
command: ["which", "prime-run"] command: ["which", "prime-run"]
onExited: function (exitCode) { onExited: function (exitCode) {
hasPrimeRun = (exitCode === 0) hasPrimeRun = (exitCode === 0);
} }
} }
@@ -124,164 +122,167 @@ Singleton {
splitMarker: "\n" splitMarker: "\n"
onRead: data => { onRead: data => {
if (data.trim().toLowerCase().includes("not running")) { if (data.trim().toLowerCase().includes("not running")) {
_logout() _logout();
} }
} }
} }
onExited: function (exitCode) { onExited: function (exitCode) {
if (exitCode === 0) { if (exitCode === 0) {
return return;
} }
_logout() _logout();
} }
} }
function escapeShellArg(arg) { function escapeShellArg(arg) {
return "'" + arg.replace(/'/g, "'\\''") + "'" return "'" + arg.replace(/'/g, "'\\''") + "'";
} }
function needsShellExecution(prefix) { function needsShellExecution(prefix) {
if (!prefix || prefix.length === 0) return false if (!prefix || prefix.length === 0)
return /[;&|<>()$`\\"']/.test(prefix) return false;
return /[;&|<>()$`\\"']/.test(prefix);
} }
function launchDesktopEntry(desktopEntry, usePrimeRun) { function launchDesktopEntry(desktopEntry, usePrimeRun) {
let cmd = desktopEntry.command let cmd = desktopEntry.command;
if (usePrimeRun && hasPrimeRun) { if (usePrimeRun && hasPrimeRun) {
cmd = ["prime-run"].concat(cmd) cmd = ["prime-run"].concat(cmd);
} }
const userPrefix = SettingsData.launchPrefix?.trim() || "" const userPrefix = SettingsData.launchPrefix?.trim() || "";
const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || "" const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || "";
const prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix const prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix;
if (prefix.length > 0 && needsShellExecution(prefix)) { if (prefix.length > 0 && needsShellExecution(prefix)) {
const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" ") const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" ");
const shellCmd = `${prefix} ${escapedCmd}` const shellCmd = `${prefix} ${escapedCmd}`;
Quickshell.execDetached({ Quickshell.execDetached({
command: ["sh", "-c", shellCmd], command: ["sh", "-c", shellCmd],
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME"), workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME")
}) });
} else { } else {
if (prefix.length > 0) { if (prefix.length > 0) {
const launchPrefix = prefix.split(" ") const launchPrefix = prefix.split(" ");
cmd = launchPrefix.concat(cmd) cmd = launchPrefix.concat(cmd);
} }
Quickshell.execDetached({ Quickshell.execDetached({
command: cmd, command: cmd,
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME"), workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME")
}) });
} }
} }
function launchDesktopAction(desktopEntry, action, usePrimeRun) { function launchDesktopAction(desktopEntry, action, usePrimeRun) {
let cmd = action.command let cmd = action.command;
if (usePrimeRun && hasPrimeRun) { if (usePrimeRun && hasPrimeRun) {
cmd = ["prime-run"].concat(cmd) cmd = ["prime-run"].concat(cmd);
} }
const userPrefix = SettingsData.launchPrefix?.trim() || "" const userPrefix = SettingsData.launchPrefix?.trim() || "";
const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || "" const defaultPrefix = Quickshell.env("DMS_DEFAULT_LAUNCH_PREFIX") || "";
const prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix const prefix = userPrefix.length > 0 ? userPrefix : defaultPrefix;
if (prefix.length > 0 && needsShellExecution(prefix)) { if (prefix.length > 0 && needsShellExecution(prefix)) {
const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" ") const escapedCmd = cmd.map(arg => escapeShellArg(arg)).join(" ");
const shellCmd = `${prefix} ${escapedCmd}` const shellCmd = `${prefix} ${escapedCmd}`;
Quickshell.execDetached({ Quickshell.execDetached({
command: ["sh", "-c", shellCmd], command: ["sh", "-c", shellCmd],
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME"), workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME")
}) });
} else { } else {
if (prefix.length > 0) { if (prefix.length > 0) {
const launchPrefix = prefix.split(" ") const launchPrefix = prefix.split(" ");
cmd = launchPrefix.concat(cmd) cmd = launchPrefix.concat(cmd);
} }
Quickshell.execDetached({ Quickshell.execDetached({
command: cmd, command: cmd,
workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME"), workingDirectory: desktopEntry.workingDirectory || Quickshell.env("HOME")
}) });
} }
} }
// * Session management // * Session management
function logout() { function logout() {
if (hasUwsm) { if (hasUwsm) {
uwsmLogout.running = true uwsmLogout.running = true;
} }
_logout() _logout();
} }
function _logout() { function _logout() {
if (SettingsData.customPowerActionLogout.length === 0) { if (SettingsData.customPowerActionLogout.length === 0) {
if (CompositorService.isNiri) { if (CompositorService.isNiri) {
NiriService.quit() NiriService.quit();
return return;
} }
if (CompositorService.isDwl) { if (CompositorService.isDwl) {
DwlService.quit() DwlService.quit();
return return;
} }
if (CompositorService.isSway) { if (CompositorService.isSway) {
try { I3.dispatch("exit") } catch(_){} try {
return I3.dispatch("exit");
} catch (_) {}
return;
} }
Hyprland.dispatch("exit") Hyprland.dispatch("exit");
} else { } else {
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionLogout]) Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionLogout]);
} }
} }
function suspend() { function suspend() {
if (SettingsData.customPowerActionSuspend.length === 0) { if (SettingsData.customPowerActionSuspend.length === 0) {
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "suspend"]) Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "suspend"]);
} else { } else {
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionSuspend]) Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionSuspend]);
} }
} }
function hibernate() { function hibernate() {
if (SettingsData.customPowerActionHibernate.length === 0) { if (SettingsData.customPowerActionHibernate.length === 0) {
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "hibernate"]) Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "hibernate"]);
} else { } else {
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionHibernate]) Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionHibernate]);
} }
} }
function suspendThenHibernate() { function suspendThenHibernate() {
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "suspend-then-hibernate"]) Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "suspend-then-hibernate"]);
} }
function suspendWithBehavior(behavior) { function suspendWithBehavior(behavior) {
if (behavior === SettingsData.SuspendBehavior.Hibernate) { if (behavior === SettingsData.SuspendBehavior.Hibernate) {
hibernate() hibernate();
} else if (behavior === SettingsData.SuspendBehavior.SuspendThenHibernate) { } else if (behavior === SettingsData.SuspendBehavior.SuspendThenHibernate) {
suspendThenHibernate() suspendThenHibernate();
} else { } else {
suspend() suspend();
} }
} }
function reboot() { function reboot() {
if (SettingsData.customPowerActionReboot.length === 0) { if (SettingsData.customPowerActionReboot.length === 0) {
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "reboot"]) Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "reboot"]);
} else { } else {
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionReboot]) Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionReboot]);
} }
} }
function poweroff() { function poweroff() {
if (SettingsData.customPowerActionPowerOff.length === 0) { if (SettingsData.customPowerActionPowerOff.length === 0) {
Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "poweroff"]) Quickshell.execDetached([isElogind ? "loginctl" : "systemctl", "poweroff"]);
} else { } else {
Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionPowerOff]) Quickshell.execDetached(["sh", "-c", SettingsData.customPowerActionPowerOff]);
} }
} }
@@ -290,42 +291,42 @@ Singleton {
function enableIdleInhibit() { function enableIdleInhibit() {
if (idleInhibited) { if (idleInhibited) {
return return;
} }
console.log("SessionService: Enabling idle inhibit (native:", nativeInhibitorAvailable, ")") console.log("SessionService: Enabling idle inhibit (native:", nativeInhibitorAvailable, ")");
idleInhibited = true idleInhibited = true;
inhibitorChanged() inhibitorChanged();
} }
function disableIdleInhibit() { function disableIdleInhibit() {
if (!idleInhibited) { if (!idleInhibited) {
return return;
} }
console.log("SessionService: Disabling idle inhibit (native:", nativeInhibitorAvailable, ")") console.log("SessionService: Disabling idle inhibit (native:", nativeInhibitorAvailable, ")");
idleInhibited = false idleInhibited = false;
inhibitorChanged() inhibitorChanged();
} }
function toggleIdleInhibit() { function toggleIdleInhibit() {
if (idleInhibited) { if (idleInhibited) {
disableIdleInhibit() disableIdleInhibit();
} else { } else {
enableIdleInhibit() enableIdleInhibit();
} }
} }
function setInhibitReason(reason) { function setInhibitReason(reason) {
inhibitReason = reason inhibitReason = reason;
if (idleInhibited && !nativeInhibitorAvailable) { if (idleInhibited && !nativeInhibitorAvailable) {
const wasActive = idleInhibited const wasActive = idleInhibited;
idleInhibited = false idleInhibited = false;
Qt.callLater(() => { Qt.callLater(() => {
if (wasActive) { if (wasActive) {
idleInhibited = true idleInhibited = true;
} }
}) });
} }
} }
@@ -334,24 +335,24 @@ Singleton {
command: { command: {
if (!idleInhibited || nativeInhibitorAvailable) { if (!idleInhibited || nativeInhibitorAvailable) {
return ["true"] return ["true"];
} }
console.log("SessionService: Starting systemd/elogind inhibit process") console.log("SessionService: Starting systemd/elogind inhibit process");
return [isElogind ? "elogind-inhibit" : "systemd-inhibit", "--what=idle", "--who=quickshell", `--why=${inhibitReason}`, "--mode=block", "sleep", "infinity"] return [isElogind ? "elogind-inhibit" : "systemd-inhibit", "--what=idle", "--who=quickshell", `--why=${inhibitReason}`, "--mode=block", "sleep", "infinity"];
} }
running: idleInhibited && !nativeInhibitorAvailable running: idleInhibited && !nativeInhibitorAvailable
onRunningChanged: { onRunningChanged: {
console.log("SessionService: Inhibit process running:", running, "(native:", nativeInhibitorAvailable, ")") console.log("SessionService: Inhibit process running:", running, "(native:", nativeInhibitorAvailable, ")");
} }
onExited: function (exitCode) { onExited: function (exitCode) {
if (idleInhibited && exitCode !== 0 && !nativeInhibitorAvailable) { if (idleInhibited && exitCode !== 0 && !nativeInhibitorAvailable) {
console.warn("SessionService: Inhibitor process crashed with exit code:", exitCode) console.warn("SessionService: Inhibitor process crashed with exit code:", exitCode);
idleInhibited = false idleInhibited = false;
ToastService.showWarning("Idle inhibitor failed") ToastService.showWarning("Idle inhibitor failed");
} }
} }
} }
@@ -361,12 +362,12 @@ Singleton {
function onConnectionStateChanged() { function onConnectionStateChanged() {
if (DMSService.isConnected) { if (DMSService.isConnected) {
checkDMSCapabilities() checkDMSCapabilities();
} }
} }
function onCapabilitiesReceived() { function onCapabilitiesReceived() {
syncSleepInhibitor() syncSleepInhibitor();
} }
} }
@@ -375,7 +376,7 @@ Singleton {
enabled: DMSService.isConnected enabled: DMSService.isConnected
function onCapabilitiesChanged() { function onCapabilitiesChanged() {
checkDMSCapabilities() checkDMSCapabilities();
} }
} }
@@ -386,22 +387,22 @@ Singleton {
if (SettingsData.loginctlLockIntegration) { if (SettingsData.loginctlLockIntegration) {
if (socketPath && socketPath.length > 0 && loginctlAvailable) { if (socketPath && socketPath.length > 0 && loginctlAvailable) {
if (!stateInitialized) { if (!stateInitialized) {
stateInitialized = true stateInitialized = true;
getLoginctlState() getLoginctlState();
syncLockBeforeSuspend() syncLockBeforeSuspend();
} }
} }
} else { } else {
stateInitialized = false stateInitialized = false;
} }
syncSleepInhibitor() syncSleepInhibitor();
} }
function onLockBeforeSuspendChanged() { function onLockBeforeSuspendChanged() {
if (SettingsData.loginctlLockIntegration) { if (SettingsData.loginctlLockIntegration) {
syncLockBeforeSuspend() syncLockBeforeSuspend();
} }
syncSleepInhibitor() syncSleepInhibitor();
} }
} }
@@ -410,109 +411,114 @@ Singleton {
enabled: SettingsData.loginctlLockIntegration enabled: SettingsData.loginctlLockIntegration
function onLoginctlStateUpdate(data) { function onLoginctlStateUpdate(data) {
updateLoginctlState(data) updateLoginctlState(data);
} }
function onLoginctlEvent(event) { function onLoginctlEvent(event) {
handleLoginctlEvent(event) handleLoginctlEvent(event);
} }
} }
function checkDMSCapabilities() { function checkDMSCapabilities() {
if (!DMSService.isConnected) { if (!DMSService.isConnected) {
return return;
} }
if (DMSService.capabilities.length === 0) { if (DMSService.capabilities.length === 0) {
return return;
} }
if (DMSService.capabilities.includes("loginctl")) { if (DMSService.capabilities.includes("loginctl")) {
loginctlAvailable = true loginctlAvailable = true;
if (SettingsData.loginctlLockIntegration && !stateInitialized) { if (SettingsData.loginctlLockIntegration && !stateInitialized) {
stateInitialized = true stateInitialized = true;
getLoginctlState() getLoginctlState();
syncLockBeforeSuspend() syncLockBeforeSuspend();
} }
} else { } else {
loginctlAvailable = false loginctlAvailable = false;
console.log("SessionService: loginctl capability not available in DMS") console.log("SessionService: loginctl capability not available in DMS");
} }
} }
function getLoginctlState() { function getLoginctlState() {
if (!loginctlAvailable) return if (!loginctlAvailable)
return;
DMSService.sendRequest("loginctl.getState", null, response => { DMSService.sendRequest("loginctl.getState", null, response => {
if (response.result) { if (response.result) {
updateLoginctlState(response.result) updateLoginctlState(response.result);
} }
}) });
} }
function syncLockBeforeSuspend() { function syncLockBeforeSuspend() {
if (!loginctlAvailable) return if (!loginctlAvailable)
return;
DMSService.sendRequest("loginctl.setLockBeforeSuspend", { DMSService.sendRequest("loginctl.setLockBeforeSuspend", {
enabled: SettingsData.lockBeforeSuspend enabled: SettingsData.lockBeforeSuspend
}, response => { }, response => {
if (response.error) { if (response.error) {
console.warn("SessionService: Failed to sync lock before suspend:", response.error) console.warn("SessionService: Failed to sync lock before suspend:", response.error);
} else { } else {
console.log("SessionService: Synced lock before suspend:", SettingsData.lockBeforeSuspend) console.log("SessionService: Synced lock before suspend:", SettingsData.lockBeforeSuspend);
} }
}) });
} }
function syncSleepInhibitor() { function syncSleepInhibitor() {
if (!loginctlAvailable) return if (!loginctlAvailable)
return;
if (!DMSService.apiVersion || DMSService.apiVersion < 4) return if (!DMSService.apiVersion || DMSService.apiVersion < 4)
return;
DMSService.sendRequest("loginctl.setSleepInhibitorEnabled", { DMSService.sendRequest("loginctl.setSleepInhibitorEnabled", {
enabled: SettingsData.loginctlLockIntegration && SettingsData.lockBeforeSuspend enabled: SettingsData.loginctlLockIntegration && SettingsData.lockBeforeSuspend
}, response => { }, response => {
if (response.error) { if (response.error) {
console.warn("SessionService: Failed to sync sleep inhibitor:", response.error) console.warn("SessionService: Failed to sync sleep inhibitor:", response.error);
} else { } else {
console.log("SessionService: Synced sleep inhibitor:", SettingsData.loginctlLockIntegration) console.log("SessionService: Synced sleep inhibitor:", SettingsData.loginctlLockIntegration);
} }
}) });
} }
function updateLoginctlState(state) { function updateLoginctlState(state) {
const wasLocked = locked const wasLocked = locked;
const wasSleeping = preparingForSleep;
sessionId = state.sessionId || "" sessionId = state.sessionId || "";
sessionPath = state.sessionPath || "" sessionPath = state.sessionPath || "";
locked = state.locked || false locked = state.locked || false;
active = state.active || false active = state.active || false;
idleHint = state.idleHint || false idleHint = state.idleHint || false;
lockedHint = state.lockedHint || false lockedHint = state.lockedHint || false;
sessionType = state.sessionType || "" preparingForSleep = state.preparingForSleep || false;
userName = state.userName || "" sessionType = state.sessionType || "";
seat = state.seat || "" userName = state.userName || "";
display = state.display || "" seat = state.seat || "";
display = state.display || "";
if (locked && !wasLocked) { if (locked && !wasLocked) {
sessionLocked() sessionLocked();
} else if (!locked && wasLocked) { } else if (!locked && wasLocked) {
sessionUnlocked() sessionUnlocked();
} }
loginctlStateChanged() if (wasSleeping && !preparingForSleep) {
sessionResumed();
}
loginctlStateChanged();
} }
function handleLoginctlEvent(event) { function handleLoginctlEvent(event) {
if (event.event === "Lock") { if (event.event === "Lock") {
locked = true locked = true;
lockedHint = true lockedHint = true;
sessionLocked() sessionLocked();
} else if (event.event === "Unlock") { } else if (event.event === "Unlock") {
locked = false locked = false;
lockedHint = false lockedHint = false;
sessionUnlocked() sessionUnlocked();
} }
} }
} }

View File

@@ -27,39 +27,41 @@ PanelWindow {
signal osdHidden signal osdHidden
function show() { function show() {
OSDManager.showOSD(root) if (SessionData.suppressOSD)
closeTimer.stop() return;
shouldBeVisible = true OSDManager.showOSD(root);
visible = true closeTimer.stop();
hideTimer.restart() shouldBeVisible = true;
osdShown() visible = true;
hideTimer.restart();
osdShown();
} }
function hide() { function hide() {
shouldBeVisible = false shouldBeVisible = false;
closeTimer.restart() closeTimer.restart();
} }
function resetHideTimer() { function resetHideTimer() {
if (shouldBeVisible) { if (shouldBeVisible) {
hideTimer.restart() hideTimer.restart();
} }
} }
function updateHoverState() { function updateHoverState() {
let isHovered = (enableMouseInteraction && mouseArea.containsMouse) || osdContainer.childHovered let isHovered = (enableMouseInteraction && mouseArea.containsMouse) || osdContainer.childHovered;
if (enableMouseInteraction) { if (enableMouseInteraction) {
if (isHovered) { if (isHovered) {
hideTimer.stop() hideTimer.stop();
} else if (shouldBeVisible) { } else if (shouldBeVisible) {
hideTimer.restart() hideTimer.restart();
} }
} }
} }
function setChildHovered(hovered) { function setChildHovered(hovered) {
osdContainer.childHovered = hovered osdContainer.childHovered = hovered;
updateHoverState() updateHoverState();
} }
screen: modelData screen: modelData
@@ -78,88 +80,92 @@ PanelWindow {
readonly property bool isVerticalLayout: SettingsData.osdPosition === SettingsData.Position.LeftCenter || SettingsData.osdPosition === SettingsData.Position.RightCenter readonly property bool isVerticalLayout: SettingsData.osdPosition === SettingsData.Position.LeftCenter || SettingsData.osdPosition === SettingsData.Position.RightCenter
readonly property real barThickness: { readonly property real barThickness: {
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default") const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default");
if (!defaultBar || !(defaultBar.visible ?? true)) return 0 if (!defaultBar || !(defaultBar.visible ?? true))
const innerPadding = defaultBar.innerPadding ?? 4 return 0;
const widgetThickness = Math.max(20, 26 + innerPadding * 0.6) const innerPadding = defaultBar.innerPadding ?? 4;
return Math.max(widgetThickness + innerPadding + 4, Theme.barHeight - 4 - (8 - innerPadding)) const widgetThickness = Math.max(20, 26 + innerPadding * 0.6);
return Math.max(widgetThickness + innerPadding + 4, Theme.barHeight - 4 - (8 - innerPadding));
} }
readonly property real barOffset: { readonly property real barOffset: {
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default") const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default");
if (!defaultBar || !(defaultBar.visible ?? true)) return 0 if (!defaultBar || !(defaultBar.visible ?? true))
const spacing = defaultBar.spacing ?? 4 return 0;
const bottomGap = defaultBar.bottomGap ?? 0 const spacing = defaultBar.spacing ?? 4;
return barThickness + spacing + bottomGap const bottomGap = defaultBar.bottomGap ?? 0;
return barThickness + spacing + bottomGap;
} }
readonly property real dockThickness: { readonly property real dockThickness: {
if (!SettingsData.showDock) return 0 if (!SettingsData.showDock)
return SettingsData.dockIconSize + SettingsData.dockSpacing * 2 + 10 return 0;
return SettingsData.dockIconSize + SettingsData.dockSpacing * 2 + 10;
} }
readonly property real dockOffset: { readonly property real dockOffset: {
if (!SettingsData.showDock || SettingsData.dockAutoHide) return 0 if (!SettingsData.showDock || SettingsData.dockAutoHide)
return dockThickness + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin return 0;
return dockThickness + SettingsData.dockSpacing + SettingsData.dockBottomGap + SettingsData.dockMargin;
} }
readonly property real alignedX: { readonly property real alignedX: {
const margin = Theme.spacingM const margin = Theme.spacingM;
const centerX = (screenWidth - alignedWidth) / 2 const centerX = (screenWidth - alignedWidth) / 2;
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default") const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default");
const barPos = defaultBar?.position ?? SettingsData.Position.Top const barPos = defaultBar?.position ?? SettingsData.Position.Top;
switch (SettingsData.osdPosition) { switch (SettingsData.osdPosition) {
case SettingsData.Position.Left: case SettingsData.Position.Left:
case SettingsData.Position.Bottom: case SettingsData.Position.Bottom:
const leftBarOffset = barPos === SettingsData.Position.Left ? barOffset : 0 const leftBarOffset = barPos === SettingsData.Position.Left ? barOffset : 0;
const leftDockOffset = SettingsData.dockPosition === SettingsData.Position.Left ? dockOffset : 0 const leftDockOffset = SettingsData.dockPosition === SettingsData.Position.Left ? dockOffset : 0;
return Theme.snap(margin + Math.max(leftBarOffset, leftDockOffset), dpr) return Theme.snap(margin + Math.max(leftBarOffset, leftDockOffset), dpr);
case SettingsData.Position.Top: case SettingsData.Position.Top:
case SettingsData.Position.Right: case SettingsData.Position.Right:
const rightBarOffset = barPos === SettingsData.Position.Right ? barOffset : 0 const rightBarOffset = barPos === SettingsData.Position.Right ? barOffset : 0;
const rightDockOffset = SettingsData.dockPosition === SettingsData.Position.Right ? dockOffset : 0 const rightDockOffset = SettingsData.dockPosition === SettingsData.Position.Right ? dockOffset : 0;
return Theme.snap(screenWidth - alignedWidth - margin - Math.max(rightBarOffset, rightDockOffset), dpr) return Theme.snap(screenWidth - alignedWidth - margin - Math.max(rightBarOffset, rightDockOffset), dpr);
case SettingsData.Position.LeftCenter: case SettingsData.Position.LeftCenter:
const leftCenterBarOffset = barPos === SettingsData.Position.Left ? barOffset : 0 const leftCenterBarOffset = barPos === SettingsData.Position.Left ? barOffset : 0;
const leftCenterDockOffset = SettingsData.dockPosition === SettingsData.Position.Left ? dockOffset : 0 const leftCenterDockOffset = SettingsData.dockPosition === SettingsData.Position.Left ? dockOffset : 0;
return Theme.snap(margin + Math.max(leftCenterBarOffset, leftCenterDockOffset), dpr) return Theme.snap(margin + Math.max(leftCenterBarOffset, leftCenterDockOffset), dpr);
case SettingsData.Position.RightCenter: case SettingsData.Position.RightCenter:
const rightCenterBarOffset = barPos === SettingsData.Position.Right ? barOffset : 0 const rightCenterBarOffset = barPos === SettingsData.Position.Right ? barOffset : 0;
const rightCenterDockOffset = SettingsData.dockPosition === SettingsData.Position.Right ? dockOffset : 0 const rightCenterDockOffset = SettingsData.dockPosition === SettingsData.Position.Right ? dockOffset : 0;
return Theme.snap(screenWidth - alignedWidth - margin - Math.max(rightCenterBarOffset, rightCenterDockOffset), dpr) return Theme.snap(screenWidth - alignedWidth - margin - Math.max(rightCenterBarOffset, rightCenterDockOffset), dpr);
case SettingsData.Position.TopCenter: case SettingsData.Position.TopCenter:
case SettingsData.Position.BottomCenter: case SettingsData.Position.BottomCenter:
default: default:
return Theme.snap(centerX, dpr) return Theme.snap(centerX, dpr);
} }
} }
readonly property real alignedY: { readonly property real alignedY: {
const margin = Theme.spacingM const margin = Theme.spacingM;
const centerY = (screenHeight - alignedHeight) / 2 const centerY = (screenHeight - alignedHeight) / 2;
const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default") const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default");
const barPos = defaultBar?.position ?? SettingsData.Position.Top const barPos = defaultBar?.position ?? SettingsData.Position.Top;
switch (SettingsData.osdPosition) { switch (SettingsData.osdPosition) {
case SettingsData.Position.Top: case SettingsData.Position.Top:
case SettingsData.Position.Left: case SettingsData.Position.Left:
case SettingsData.Position.TopCenter: case SettingsData.Position.TopCenter:
const topBarOffset = barPos === SettingsData.Position.Top ? barOffset : 0 const topBarOffset = barPos === SettingsData.Position.Top ? barOffset : 0;
const topDockOffset = SettingsData.dockPosition === SettingsData.Position.Top ? dockOffset : 0 const topDockOffset = SettingsData.dockPosition === SettingsData.Position.Top ? dockOffset : 0;
return Theme.snap(margin + Math.max(topBarOffset, topDockOffset), dpr) return Theme.snap(margin + Math.max(topBarOffset, topDockOffset), dpr);
case SettingsData.Position.Right: case SettingsData.Position.Right:
case SettingsData.Position.Bottom: case SettingsData.Position.Bottom:
case SettingsData.Position.BottomCenter: case SettingsData.Position.BottomCenter:
const bottomBarOffset = barPos === SettingsData.Position.Bottom ? barOffset : 0 const bottomBarOffset = barPos === SettingsData.Position.Bottom ? barOffset : 0;
const bottomDockOffset = SettingsData.dockPosition === SettingsData.Position.Bottom ? dockOffset : 0 const bottomDockOffset = SettingsData.dockPosition === SettingsData.Position.Bottom ? dockOffset : 0;
return Theme.snap(screenHeight - alignedHeight - margin - Math.max(bottomBarOffset, bottomDockOffset), dpr) return Theme.snap(screenHeight - alignedHeight - margin - Math.max(bottomBarOffset, bottomDockOffset), dpr);
case SettingsData.Position.LeftCenter: case SettingsData.Position.LeftCenter:
case SettingsData.Position.RightCenter: case SettingsData.Position.RightCenter:
default: default:
return Theme.snap(centerY, dpr) return Theme.snap(centerY, dpr);
} }
} }
@@ -177,9 +183,9 @@ PanelWindow {
repeat: false repeat: false
onTriggered: { onTriggered: {
if (!enableMouseInteraction || !mouseArea.containsMouse) { if (!enableMouseInteraction || !mouseArea.containsMouse) {
hide() hide();
} else { } else {
hideTimer.restart() hideTimer.restart();
} }
} }
} }
@@ -189,8 +195,8 @@ PanelWindow {
interval: animationDuration + 50 interval: animationDuration + 50
onTriggered: { onTriggered: {
if (!shouldBeVisible) { if (!shouldBeVisible) {
visible = false visible = false;
osdHidden() osdHidden();
} }
} }
} }
@@ -239,8 +245,8 @@ PanelWindow {
shadowBlur: Math.max(0, Math.min(1, osdContainer.shadowBlurPx / bgShadowLayer.blurMax)) shadowBlur: Math.max(0, Math.min(1, osdContainer.shadowBlurPx / bgShadowLayer.blurMax))
shadowScale: 1 + (2 * osdContainer.shadowSpreadPx) / Math.max(1, Math.min(bgShadowLayer.width, bgShadowLayer.height)) shadowScale: 1 + (2 * osdContainer.shadowSpreadPx) / Math.max(1, Math.min(bgShadowLayer.width, bgShadowLayer.height))
shadowColor: { shadowColor: {
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest;
return Theme.withAlpha(baseColor, osdContainer.effectiveShadowAlpha) return Theme.withAlpha(baseColor, osdContainer.effectiveShadowAlpha);
} }
} }