diff --git a/surfaces/quickshell/services/SessionEvents.qml b/surfaces/quickshell/services/SessionEvents.qml index 63178f3..4034be0 100644 --- a/surfaces/quickshell/services/SessionEvents.qml +++ b/surfaces/quickshell/services/SessionEvents.qml @@ -76,38 +76,43 @@ Singleton { signal sleepInhibitorAcquired() // --- Session path resolution ------------------------------------------- - // The logind session D-Bus object path contains the session ID with dots - // replaced by underscores (session "3" -> /org/freedesktop/login1/session/_3, - // session "c1" -> /org/freedesktop/login1/session/_c1). We resolve it once - // at startup by asking logind for the current session's object path. + // Resolve the graphical session's REAL object path via the user's `Display` + // session. sessiond resolves exactly this way; see the two measured traps + // documented on `resolve_session_path` in src/sessiond/lockhint.rs: + // + // Not `auto`. `GetSession("auto")` resolves to the CALLER's session, and + // this shell is a systemd user unit under user@1000.service, outside any + // session scope — it has no session of its own to name. + // + // Not the `auto` path either. /org/freedesktop/login1/session/auto is an + // alias, not an object: PropertiesChanged only fires on the concrete + // path, so monitoring the alias subscribes to something never delivered. + // + // The user's `Display` session is the graphical one by definition, whoever + // asks. The previous probe here asked for a `-p ObjectPath` property that + // loginctl does not have, so it returned empty on every boot and external + // lock signals were never monitored at all. Process { id: sessionResolver running: true command: ["sh", "-c", - // loginctl list-sessions -p Name gives the first session's user; - // show-session with no argument operates on the caller's session. - // -p ObjectPath gives the D-Bus path directly, no encoding needed. - "loginctl show-session -p ObjectPath 2>/dev/null | head -1"] + "busctl --system get-property org.freedesktop.login1 " + + "/org/freedesktop/login1/user/_$(id -u) " + + "org.freedesktop.login1.User Display 2>/dev/null"] stdout: StdioCollector { onStreamFinished: { const raw = text.trim(); - // loginctl outputs ObjectPath=/org/freedesktop/login1/session/_3 - const match = raw.match(/^ObjectPath=(.+)$/); - if (match && match[1].length > 0) { - root.sessionPath = match[1]; + // `(so) "1" "/org/freedesktop/login1/session/_31"` — the object + // path is the second quoted field. + const path = raw.split('"')[3]; + if (path && path.startsWith("/org/freedesktop/login1/session/")) { + root.sessionPath = path; console.log("[session-events] session path:", root.sessionPath); sessionLockMonitor.running = true; } else if (raw.length > 0) { - // Some logind versions print just the path without the key. - if (raw.startsWith("/org/freedesktop/login1/session/")) { - root.sessionPath = raw; - console.log("[session-events] session path (bare):", root.sessionPath); - sessionLockMonitor.running = true; - } else { - console.log("[session-events] could not parse session path from:", raw); - } + console.log("[session-events] could not parse Display session from:", raw); } else { - console.log("[session-events] no session path returned; " + console.log("[session-events] no Display session for this user; " + "external lock signals will not be monitored"); } } diff --git a/surfaces/quickshell/services/SessiondBridge.qml b/surfaces/quickshell/services/SessiondBridge.qml index bbd0f57..faf92dc 100644 --- a/surfaces/quickshell/services/SessiondBridge.qml +++ b/surfaces/quickshell/services/SessiondBridge.qml @@ -28,6 +28,10 @@ Singleton { // Registered = shell_ready was answered ok on the CURRENT connection. property bool registered: false + // Latched copy of `registered` taken when the connection drops. The + // disconnect branch clears `registered` before the reconnect branch runs, + // so reconnect cannot read it directly to decide whether to re-register. + property bool wasRegistered: false // sessiond held the session lock when we registered; we owe it a lock // and a locked_ack. property bool oweLock: false @@ -112,8 +116,9 @@ Singleton { onConnectionStateChanged: { if (sock.connected) { console.log("[sessiond-bridge] connected"); - const wasRegistered = root.registered; + const wasRegistered = root.wasRegistered; root.registered = false; + root.wasRegistered = false; root.ackSent = false; if (wasRegistered) { root.shellReady(function(mustLock) { @@ -126,6 +131,7 @@ Singleton { } } else { console.log("[sessiond-bridge] disconnected"); + root.wasRegistered = root.registered; root.registered = false; root.pendingReady = null; }