diff --git a/quickshell/DMSShell.qml b/quickshell/DMSShell.qml index a768cada1..577dbf2f6 100644 --- a/quickshell/DMSShell.qml +++ b/quickshell/DMSShell.qml @@ -1015,6 +1015,9 @@ Item { case "reboot": SessionService.reboot(); break; + case "softreboot": + SessionService.softReboot(); + break; case "poweroff": SessionService.poweroff(); break; diff --git a/quickshell/Modals/PowerMenuModal.qml b/quickshell/Modals/PowerMenuModal.qml index b01385c7d..668b232d3 100644 --- a/quickshell/Modals/PowerMenuModal.qml +++ b/quickshell/Modals/PowerMenuModal.qml @@ -175,6 +175,8 @@ DankModal { visibleActions = allActions.filter(action => { if (action === "hibernate" && !SessionService.hibernateSupported) return false; + if (action === "softreboot" && !SessionService.softRebootSupported) + return false; return true; }); @@ -223,6 +225,12 @@ DankModal { "label": I18n.tr("Reboot"), "key": "R" }; + case "softreboot": + return { + "icon": "autorenew", + "label": I18n.tr("Soft Reboot"), + "key": "B" + }; case "logout": return { "icon": "logout", @@ -370,7 +378,7 @@ DankModal { function handleListNavigation(event, isPressed) { if (!isPressed) { - if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter || event.key === Qt.Key_R || event.key === Qt.Key_X || event.key === Qt.Key_L || event.key === Qt.Key_S || event.key === Qt.Key_H || event.key === Qt.Key_D || (event.key === Qt.Key_P && !(event.modifiers & Qt.ControlModifier))) { + if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter || event.key === Qt.Key_R || event.key === Qt.Key_B || event.key === Qt.Key_X || event.key === Qt.Key_L || event.key === Qt.Key_S || event.key === Qt.Key_H || event.key === Qt.Key_D || (event.key === Qt.Key_P && !(event.modifiers & Qt.ControlModifier))) { cancelHold(); event.accepted = true; } @@ -429,6 +437,12 @@ DankModal { event.accepted = true; } break; + case Qt.Key_B: + if (visibleActions.includes("softreboot")) { + startHold("softreboot", visibleActions.indexOf("softreboot")); + event.accepted = true; + } + break; case Qt.Key_X: if (visibleActions.includes("logout")) { startHold("logout", visibleActions.indexOf("logout")); @@ -464,7 +478,7 @@ DankModal { function handleGridNavigation(event, isPressed) { if (!isPressed) { - if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter || event.key === Qt.Key_R || event.key === Qt.Key_X || event.key === Qt.Key_L || event.key === Qt.Key_S || event.key === Qt.Key_H || event.key === Qt.Key_D || (event.key === Qt.Key_P && !(event.modifiers & Qt.ControlModifier))) { + if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter || event.key === Qt.Key_R || event.key === Qt.Key_B || event.key === Qt.Key_X || event.key === Qt.Key_L || event.key === Qt.Key_S || event.key === Qt.Key_H || event.key === Qt.Key_D || (event.key === Qt.Key_P && !(event.modifiers & Qt.ControlModifier))) { cancelHold(); event.accepted = true; } @@ -539,6 +553,12 @@ DankModal { event.accepted = true; } break; + case Qt.Key_B: + if (visibleActions.includes("softreboot")) { + startHold("softreboot", visibleActions.indexOf("softreboot")); + event.accepted = true; + } + break; case Qt.Key_X: if (visibleActions.includes("logout")) { startHold("logout", visibleActions.indexOf("logout")); @@ -597,7 +617,7 @@ DankModal { readonly property var actionData: root.getActionData(modelData) readonly property bool isSelected: root.selectedIndex === index - readonly property bool showWarning: modelData === "reboot" || modelData === "poweroff" + readonly property bool showWarning: modelData === "reboot" || modelData === "softreboot" || modelData === "poweroff" readonly property bool isHolding: root.holdActionIndex === index && root.holdProgress > 0 width: (root.modalWidth - Theme.spacingL * 2 - Theme.spacingS * (root.gridColumns - 1)) / root.gridColumns @@ -627,7 +647,7 @@ DankModal { color: { if (gridButtonRect.modelData === "poweroff") return Theme.errorSelected; - if (gridButtonRect.modelData === "reboot") + if (gridButtonRect.modelData === "reboot" || gridButtonRect.modelData === "softreboot") return Theme.withAlpha(Theme.warning, 0.3); return Theme.primarySelected; } @@ -722,7 +742,7 @@ DankModal { readonly property var actionData: root.getActionData(modelData) readonly property bool isSelected: root.selectedIndex === index - readonly property bool showWarning: modelData === "reboot" || modelData === "poweroff" + readonly property bool showWarning: modelData === "reboot" || modelData === "softreboot" || modelData === "poweroff" readonly property bool isHolding: root.holdActionIndex === index && root.holdProgress > 0 width: parent.width @@ -752,7 +772,7 @@ DankModal { color: { if (listButtonRect.modelData === "poweroff") return Theme.errorSelected; - if (listButtonRect.modelData === "reboot") + if (listButtonRect.modelData === "reboot" || listButtonRect.modelData === "softreboot") return Theme.withAlpha(Theme.warning, 0.3); return Theme.primarySelected; } diff --git a/quickshell/Modules/Settings/PowerSleepTab.qml b/quickshell/Modules/Settings/PowerSleepTab.qml index 69c9c47fa..e2f9606cb 100644 --- a/quickshell/Modules/Settings/PowerSleepTab.qml +++ b/quickshell/Modules/Settings/PowerSleepTab.qml @@ -410,8 +410,8 @@ Item { settingKey: "powerMenuDefaultAction" tags: ["power", "menu", "default", "action", "reboot", "logout", "shutdown"] text: I18n.tr("Default selected action") - options: [I18n.tr("Reboot"), I18n.tr("Log Out"), I18n.tr("Power Off"), I18n.tr("Lock"), I18n.tr("Suspend"), I18n.tr("Restart DMS"), I18n.tr("Hibernate")] - property var actionValues: ["reboot", "logout", "poweroff", "lock", "suspend", "restart", "hibernate"] + options: [I18n.tr("Reboot"), I18n.tr("Log Out"), I18n.tr("Power Off"), I18n.tr("Lock"), I18n.tr("Suspend"), I18n.tr("Restart DMS"), I18n.tr("Hibernate"), I18n.tr("Soft Reboot")] + property var actionValues: ["reboot", "logout", "poweroff", "lock", "suspend", "restart", "hibernate", "softreboot"] Component.onCompleted: { const currentAction = SettingsData.powerMenuDefaultAction || "logout"; @@ -475,6 +475,12 @@ Item { label: I18n.tr("Show Hibernate"), desc: I18n.tr("Only visible if hibernate is supported by your system"), hibernate: true + }, + { + key: "softreboot", + label: I18n.tr("Show Soft Reboot"), + desc: I18n.tr("Restart userspace without rebooting the kernel, requires systemd"), + softreboot: true } ] @@ -484,7 +490,13 @@ Item { tags: ["power", "menu", "action", "show", modelData.key] text: modelData.label description: modelData.desc || "" - visible: !modelData.hibernate || SessionService.hibernateSupported + visible: { + if (modelData.hibernate) + return SessionService.hibernateSupported; + if (modelData.softreboot) + return SessionService.softRebootSupported; + return true; + } checked: SettingsData.powerMenuActions.includes(modelData.key) onToggled: checked => { let actions = [...SettingsData.powerMenuActions]; diff --git a/quickshell/Services/SessionService.qml b/quickshell/Services/SessionService.qml index 4a37dc646..71c0fb7d0 100644 --- a/quickshell/Services/SessionService.qml +++ b/quickshell/Services/SessionService.qml @@ -19,6 +19,7 @@ Singleton { property bool loginctlCommandAvailable: false property bool systemctlCommandAvailable: false property bool hibernateSupported: false + readonly property bool softRebootSupported: systemctlCommandAvailable property bool inhibitorAvailable: true property bool idleInhibited: false property string inhibitReason: "Keep system awake" @@ -470,6 +471,10 @@ Singleton { } } + function softReboot() { + Quickshell.execDetached(["systemctl", "soft-reboot"]); + } + function poweroff() { if (SettingsData.customPowerActionPowerOff.length === 0) { Quickshell.execDetached(powerManagerCommand("poweroff"));