pragma Singleton import QtQuick import Quickshell import Quickshell.Io import qs.Commons import qs.Services.UI Singleton { id: root property bool isInhibited: false property string reason: I18n.tr("system.user-requested") property var activeInhibitors: [] property var timeout: null // in seconds // Different inhibitor strategies property string strategy: "systemd" // "systemd", "wayland", or "auto" function init() { Logger.i("IdleInhibitor", "Service started") detectStrategy() } // Auto-detect the best strategy function detectStrategy() { if (strategy === "auto") { // Check if systemd-inhibit is available try { var systemdResult = Quickshell.execDetached(["which", "systemd-inhibit"]) strategy = "systemd" Logger.d("IdleInhibitor", "Using systemd-inhibit strategy") return } catch (e) { // systemd-inhibit not found, try Wayland tools } try { var waylandResult = Quickshell.execDetached(["which", "wayhibitor"]) strategy = "wayland" Logger.d("IdleInhibitor", "Using wayhibitor strategy") return } catch (e) { // wayhibitor not found } Logger.w("IdleInhibitor", "No suitable inhibitor found - will try systemd as fallback") strategy = "systemd" // Fallback to systemd even if not detected } } // Add an inhibitor function addInhibitor(id, reason = "Application request") { if (activeInhibitors.includes(id)) { Logger.w("IdleInhibitor", "Inhibitor already active:", id) return false } activeInhibitors.push(id) updateInhibition(reason) Logger.d("IdleInhibitor", "Added inhibitor:", id) return true } // Remove an inhibitor function removeInhibitor(id) { const index = activeInhibitors.indexOf(id) if (index === -1) { Logger.w("IdleInhibitor", "Inhibitor not found:", id) return false } activeInhibitors.splice(index, 1) updateInhibition() Logger.d("IdleInhibitor", "Removed inhibitor:", id) return true } // Update the actual system inhibition function updateInhibition(newReason = reason) { const shouldInhibit = activeInhibitors.length > 0 if (shouldInhibit === isInhibited) { return // No change needed } if (shouldInhibit) { startInhibition(newReason) } else { stopInhibition() } } // Start system inhibition function startInhibition(newReason) { reason = newReason if (strategy === "systemd") { startSystemdInhibition() } else if (strategy === "wayland") { startWaylandInhibition() } else { Logger.w("IdleInhibitor", "No inhibition strategy available") return } isInhibited = true Logger.i("IdleInhibitor", "Started inhibition:", reason) } // Stop system inhibition function stopInhibition() { if (!isInhibited) return if (inhibitorProcess.running) { inhibitorProcess.signal(15) // SIGTERM } isInhibited = false Logger.i("IdleInhibitor", "Stopped inhibition") } // Systemd inhibition using systemd-inhibit function startSystemdInhibition() { inhibitorProcess.command = ["systemd-inhibit", "--what=idle", "--why=" + reason, "--mode=block", "sleep", "infinity"] inhibitorProcess.running = true } // Wayland inhibition using wayhibitor or similar function startWaylandInhibition() { inhibitorProcess.command = ["wayhibitor"] inhibitorProcess.running = true } // Process for maintaining the inhibition Process { id: inhibitorProcess running: false onExited: function (exitCode, exitStatus) { if (isInhibited) { Logger.w("IdleInhibitor", "Inhibitor process exited unexpectedly:", exitCode) isInhibited = false } } onStarted: function () { Logger.d("IdleInhibitor", "Inhibitor process started successfully") } } Timer { id: inhibitorTimeout repeat: true interval: 1000 // 1 second onTriggered: function () { if (timeout == null) { inhibitorTimeout.stop() return } timeout -= 1 if (timeout <= 0) { removeManualInhibitor() return } } } // Manual toggle for user control function manualToggle() { // clear any existing timeout timeout = null if (activeInhibitors.includes("manual")) { removeManualInhibitor() return false } else { addManualInhibitor(null) return true } } function changeTimeout(delta) { if (timeout == null && delta < 0) { // no inhibitor, ignored return } if (timeout == null && delta > 0) { // enable manual inhibitor and set timeout addManualInhibitor(timeout + delta) return } if (timeout + delta <= 0) { // disable manual inhibitor removeManualInhibitor() return } if (timeout + delta > 0) { // change timeout addManualInhibitor(timeout + delta) return } } function removeManualInhibitor() { if (timeout !== null) { timeout = null if (inhibitorTimeout.running) { inhibitorTimeout.stop() } } if (activeInhibitors.includes("manual")) { removeInhibitor("manual") ToastService.showNotice(I18n.tr("tooltips.keep-awake"), I18n.tr("toast.keep-awake.disabled"), "keep-awake-off") Logger.i("IdleInhibitor", "Manual inhibition disabled") } } function addManualInhibitor(timeoutSec) { if (!activeInhibitors.includes("manual")) { addInhibitor("manual", "Manually activated by user") ToastService.showNotice(I18n.tr("tooltips.keep-awake"), I18n.tr("toast.keep-awake.enabled"), "keep-awake-on") } if (timeoutSec === null && timeout === null) { Logger.i("IdleInhibitor", "Manual inhibition enabled") return } else if (timeoutSec !== null && timeout === null) { timeout = timeoutSec inhibitorTimeout.start() Logger.i("IdleInhibitor", "Manual inhibition enabled with timeout:", timeoutSec) return } else if (timeoutSec !== null && timeout !== null) { timeout = timeoutSec Logger.i("IdleInhibitor", "Manual inhibition timeout changed to:", timeoutSec) return } else if (timeoutSec === null && timeout !== null) { timeout = null inhibitorTimeout.stop() Logger.i("IdleInhibitor", "Manual inhibition timeout cleared") return } } // Clean up on shutdown Component.onDestruction: { stopInhibition() } }