loginctl terminate-session kills the login session scope, which holds the niri-session script, but niri.service runs in the user manager and survives it -- along with the cleanup line the script would have run next. The compositor stayed on seat0, greetd returned to the greeter, and every login after that died immediately. Quitting niri stops niri.service, lets the script finish and takes graphical-session.target down with it.
123 lines
4.4 KiB
QML
123 lines
4.4 KiB
QML
// ABOUTME: Session power popout — Lock, Logout, Hibernate, Reboot, Shutdown.
|
|
// ABOUTME: Replaces moonset; every action except Lock needs a second click to confirm.
|
|
pragma ComponentBehavior: Bound
|
|
import QtQuick
|
|
import Quickshell.Io
|
|
|
|
PopoutPanel {
|
|
id: root
|
|
property var popouts: null
|
|
title: qsTr("Session")
|
|
panelWidth: 220
|
|
|
|
// Row currently asking for confirmation ("" = none). A fresh open never starts armed.
|
|
property string pending: ""
|
|
readonly property bool shown: root.popouts && root.popouts.hasCurrent
|
|
onShownChanged: if (root.shown)
|
|
root.pending = ""
|
|
|
|
// One process for all five actions: the popout closes on activation, so two
|
|
// actions cannot be in flight at once. `swaylock -f` daemonises itself, the
|
|
// systemctl and loginctl calls are one-shot.
|
|
Process {
|
|
id: actionProc
|
|
}
|
|
|
|
function run(cmd) {
|
|
if (actionProc.running)
|
|
return;
|
|
actionProc.command = cmd;
|
|
actionProc.running = true;
|
|
if (root.popouts)
|
|
root.popouts.close();
|
|
}
|
|
|
|
function activate(entry) {
|
|
// First click on a destructive row arms it, second one runs it.
|
|
if (entry.confirm && root.pending !== entry.name) {
|
|
root.pending = entry.name;
|
|
return;
|
|
}
|
|
root.run(entry.cmd);
|
|
}
|
|
|
|
Repeater {
|
|
// `auto`, not `self`: the shell process does not always count as belonging
|
|
// to a session ("Caller does not belong to any known session"), while `auto`
|
|
// falls back to the user's graphical session — verified to resolve to the
|
|
// wayland session on systemd 261. Avoids plumbing XDG_SESSION_ID in.
|
|
// Glyph codepoints read out of UbuntuSansNerdFont-Regular.ttf, not guessed.
|
|
model: [
|
|
{
|
|
"name": "lock",
|
|
"label": qsTr("Lock"),
|
|
"icon": 0xf033e, // md-lock
|
|
"confirm": false,
|
|
"cmd": ["swaylock", "-f"]
|
|
},
|
|
{
|
|
"name": "logout",
|
|
"label": qsTr("Logout"),
|
|
"icon": 0xf0343, // md-logout
|
|
"confirm": true,
|
|
"cmd": ["niri", "msg", "action", "quit", "--skip-confirmation"]
|
|
},
|
|
{
|
|
"name": "hibernate",
|
|
"label": qsTr("Hibernate"),
|
|
"icon": 0xf0904, // md-power_sleep
|
|
"confirm": true,
|
|
"cmd": ["systemctl", "hibernate"]
|
|
},
|
|
{
|
|
"name": "reboot",
|
|
"label": qsTr("Reboot"),
|
|
"icon": 0xf0709, // md-restart
|
|
"confirm": true,
|
|
"cmd": ["systemctl", "reboot"]
|
|
},
|
|
{
|
|
"name": "shutdown",
|
|
"label": qsTr("Shutdown"),
|
|
"icon": 0xf0425, // md-power
|
|
"confirm": true,
|
|
"cmd": ["systemctl", "poweroff"]
|
|
}
|
|
]
|
|
delegate: ListRow {
|
|
id: actionRow
|
|
required property var modelData
|
|
readonly property bool armed: root.pending === actionRow.modelData.name
|
|
width: root.contentWidth
|
|
onClicked: root.activate(actionRow.modelData)
|
|
|
|
Row {
|
|
anchors.left: parent.left
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: 8
|
|
// Explicit height: children can only align vertically inside a Row
|
|
// that has one (see CLAUDE.md, quickshell geometry notes).
|
|
height: 20
|
|
|
|
Text {
|
|
width: 16
|
|
height: parent.height
|
|
text: String.fromCodePoint(actionRow.modelData.icon)
|
|
color: actionRow.armed ? Theme.red : Theme.text
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSize + 2
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
Text {
|
|
height: parent.height
|
|
text: actionRow.armed ? qsTr("%1 — click again").arg(actionRow.modelData.label) : actionRow.modelData.label
|
|
color: actionRow.armed ? Theme.red : Theme.text
|
|
font.family: Theme.fontFamily
|
|
font.pixelSize: Theme.fontSize
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|