Files
noctalia-shell/Services/Control/HooksService.qml
T
Oskar Grunning 7592d9cd77 refactor(hooks): unify shutdown and reboot hooks into session hook
Per PR #1177 review feedback. Single executeSessionHook function passes
action type (shutdown/reboot) as $1 to the script.
2026-01-11 21:53:07 +01:00

247 lines
6.4 KiB
QML

pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
import qs.Services.Power
import qs.Services.UI
Singleton {
id: root
// Hook connections for automatic script execution
Connections {
target: Settings.data.colorSchemes
function onDarkModeChanged() {
executeDarkModeHook(Settings.data.colorSchemes.darkMode);
}
}
Connections {
target: WallpaperService
function onWallpaperChanged(screenName, path) {
executeWallpaperHook(path, screenName);
}
}
// Track lock screen state for unlock hook
property bool wasLocked: false
Connections {
target: PanelService
function onLockScreenChanged() {
if (PanelService.lockScreen) {
lockScreenActiveConnection.target = PanelService.lockScreen;
}
}
}
Connections {
id: lockScreenActiveConnection
target: PanelService.lockScreen
function onActiveChanged() {
// Detect lock: was unlocked, now locked
if (!wasLocked && PanelService.lockScreen.active) {
executeLockHook();
}
// Detect unlock: was locked, now not locked
if (wasLocked && !PanelService.lockScreen.active) {
executeUnlockHook();
}
wasLocked = PanelService.lockScreen.active;
}
}
// Track performance mode state for hooks
property bool wasPerformanceModeEnabled: false
Connections {
target: PowerProfileService
function onNoctaliaPerformanceModeChanged() {
const isEnabled = PowerProfileService.noctaliaPerformanceMode;
// Detect enabled: was disabled, now enabled
if (!wasPerformanceModeEnabled && isEnabled) {
executePerformanceModeEnabledHook();
}
// Detect disabled: was enabled, now disabled
if (wasPerformanceModeEnabled && !isEnabled) {
executePerformanceModeDisabledHook();
}
wasPerformanceModeEnabled = isEnabled;
}
}
// Execute wallpaper change hook
function executeWallpaperHook(wallpaperPath, screenName) {
if (!Settings.data.hooks?.enabled) {
return;
}
const script = Settings.data.hooks?.wallpaperChange;
if (!script || script === "") {
return;
}
try {
let command = script.replace(/\$1/g, wallpaperPath);
command = command.replace(/\$2/g, screenName || "");
Quickshell.execDetached(["sh", "-lc", command]);
Logger.d("HooksService", `Executed wallpaper hook: ${command}`);
} catch (e) {
Logger.e("HooksService", `Failed to execute wallpaper hook: ${e}`);
}
}
// Execute dark mode change hook
function executeDarkModeHook(isDarkMode) {
if (!Settings.data.hooks?.enabled) {
return;
}
const script = Settings.data.hooks?.darkModeChange;
if (!script || script === "") {
return;
}
try {
const command = script.replace(/\$1/g, isDarkMode ? "true" : "false");
Quickshell.execDetached(["sh", "-lc", command]);
Logger.d("HooksService", `Executed dark mode hook: ${command}`);
} catch (e) {
Logger.e("HooksService", `Failed to execute dark mode hook: ${e}`);
}
}
// Execute screen lock hook
function executeLockHook() {
if (!Settings.data.hooks?.enabled) {
return;
}
const script = Settings.data.hooks?.screenLock;
if (!script || script === "") {
return;
}
try {
Quickshell.execDetached(["sh", "-lc", script]);
Logger.d("HooksService", `Executed screen lock hook: ${script}`);
} catch (e) {
Logger.e("HooksService", `Failed to execute screen lock hook: ${e}`);
}
}
// Execute screen unlock hook
function executeUnlockHook() {
if (!Settings.data.hooks?.enabled) {
return;
}
const script = Settings.data.hooks?.screenUnlock;
if (!script || script === "") {
return;
}
try {
Quickshell.execDetached(["sh", "-lc", script]);
Logger.d("HooksService", `Executed screen unlock hook: ${script}`);
} catch (e) {
Logger.e("HooksService", `Failed to execute screen unlock hook: ${e}`);
}
}
// Execute performance mode enabled hook
function executePerformanceModeEnabledHook() {
if (!Settings.data.hooks?.enabled) {
return;
}
const script = Settings.data.hooks?.performanceModeEnabled;
if (!script || script === "") {
return;
}
try {
Quickshell.execDetached(["sh", "-lc", script]);
} catch (e) {
Logger.e("HooksService", `Failed to execute performance mode enabled hook: ${e}`);
}
}
// Execute performance mode disabled hook
function executePerformanceModeDisabledHook() {
if (!Settings.data.hooks?.enabled) {
return;
}
const script = Settings.data.hooks?.performanceModeDisabled;
if (!script || script === "") {
return;
}
try {
Quickshell.execDetached(["sh", "-lc", script]);
} catch (e) {
Logger.e("HooksService", `Failed to execute performance mode disabled hook: ${e}`);
}
}
// Blocking power hook infrastructure
property var pendingPowerCallback: null
Process {
id: powerHookProcess
onExited: (exitCode, exitStatus) => {
if (exitCode !== 0) {
Logger.w("HooksService", `Power hook failed with exit code ${exitCode}`);
}
if (pendingPowerCallback !== null) {
const callback = pendingPowerCallback;
pendingPowerCallback = null;
callback();
}
}
}
function runPowerHook(script, callback) {
pendingPowerCallback = callback;
powerHookProcess.command = ["sh", "-c", script];
powerHookProcess.running = true;
}
function executeSessionHook(action, callback) {
if (!Settings.data.hooks?.enabled) {
callback();
return;
}
const script = Settings.data.hooks?.session;
if (!script) {
callback();
return;
}
Logger.i("HooksService", `Executing session hook for ${action}`);
runPowerHook(`${script} ${action}`, callback);
}
// Initialize the service
function init() {
Logger.i("HooksService", "Service started");
// Initialize lock screen state tracking
Qt.callLater(() => {
if (PanelService.lockScreen) {
wasLocked = PanelService.lockScreen.active;
lockScreenActiveConnection.target = PanelService.lockScreen;
}
// Initialize performance mode state tracking
wasPerformanceModeEnabled = PowerProfileService.noctaliaPerformanceMode;
});
}
}