From 6d791f70dc1b89fcbf15e6a4558e7ec2d04c183c Mon Sep 17 00:00:00 2001 From: lenemter Date: Wed, 24 Sep 2025 00:49:10 +0300 Subject: [PATCH 1/5] Register EndSessionDialog on start --- src/DBus/EndSessionDialogServer.vala | 4 +- src/DBus/SystemInterface.vala | 53 ++++++++++++----- src/Indicator.vala | 51 ++++++++++++++++ src/Services/UserManager.vala | 56 +++++++----------- src/Widgets/SessionBox.vala | 88 +++------------------------- 5 files changed, 121 insertions(+), 131 deletions(-) diff --git a/src/DBus/EndSessionDialogServer.vala b/src/DBus/EndSessionDialogServer.vala index 34a6dc8d..899c715e 100644 --- a/src/DBus/EndSessionDialogServer.vala +++ b/src/DBus/EndSessionDialogServer.vala @@ -30,7 +30,7 @@ public class QuickSettings.EndSessionDialogServer : Object { } [DBus (visible = false)] - public signal void show_dialog (uint type, uint32 triggering_event_timestamp); + public signal void show_dialog (uint type); public signal void confirmed_logout (); public signal void confirmed_reboot (); @@ -47,6 +47,6 @@ public class QuickSettings.EndSessionDialogServer : Object { throw new DBusError.NOT_SUPPORTED ("Hibernate, suspend and hybrid sleep are not supported actions yet"); } - show_dialog (type, timestamp); + show_dialog (type); } } diff --git a/src/DBus/SystemInterface.vala b/src/DBus/SystemInterface.vala index 6a61c080..ecf9f10f 100644 --- a/src/DBus/SystemInterface.vala +++ b/src/DBus/SystemInterface.vala @@ -1,22 +1,45 @@ /* * SPDX-License-Identifier: GPL-3.0-or-later - * SPDX-FileCopyrightText: 2024 elementary, Inc. (https://elementary.io) + * SPDX-FileCopyrightText: 2024-2025 elementary, Inc. (https://elementary.io) */ -struct UserInfo { - uint32 uid; - string user_name; - ObjectPath? user_object; -} +public class QuickSettings.Login1Manager : Object { + public struct UserInfo { + uint32 uid; + string user_name; + ObjectPath? user_object; + } + + [DBus (name = "org.freedesktop.login1.Manager")] + public interface Login1ManagerInterface : Object { + public abstract void suspend (bool interactive) throws GLib.Error; + public abstract void reboot (bool interactive) throws GLib.Error; + public abstract void power_off (bool interactive) throws GLib.Error; + public abstract void reboot_with_flags (uint64 flags) throws GLib.Error; + public abstract void power_off_with_flags (uint64 flags) throws GLib.Error; + + public abstract UserInfo[] list_users () throws GLib.Error; + public abstract string can_suspend () throws GLib.Error; + } + + public Login1ManagerInterface object; + + private static GLib.Once instance; + public static unowned Login1Manager get_default () { + return instance.once (() => { return new Login1Manager (); }); + } -[DBus (name = "org.freedesktop.login1.Manager")] -interface QuickSettings.SystemInterface : Object { - public abstract void suspend (bool interactive) throws GLib.Error; - public abstract void reboot (bool interactive) throws GLib.Error; - public abstract void power_off (bool interactive) throws GLib.Error; - public abstract void reboot_with_flags (uint64 flags) throws GLib.Error; - public abstract void power_off_with_flags (uint64 flags) throws GLib.Error; + private Login1Manager () {} - public abstract UserInfo[] list_users () throws GLib.Error; - public abstract string can_suspend () throws GLib.Error; + construct { + try { + object = Bus.get_proxy_sync ( + SYSTEM, + "org.freedesktop.login1", + "/org/freedesktop/login1" + ); + } catch (Error e) { + critical (e.message); + } + } } diff --git a/src/Indicator.vala b/src/Indicator.vala index 04642b1a..ba84e4c0 100644 --- a/src/Indicator.vala +++ b/src/Indicator.vala @@ -6,6 +6,7 @@ public class QuickSettings.Indicator : Wingpanel.Indicator { public Wingpanel.IndicatorManager.ServerType server_type { get; construct; } + private EndSessionDialog? current_dialog; private PopoverWidget? popover_widget; public Indicator (Wingpanel.IndicatorManager.ServerType server_type) { @@ -23,6 +24,56 @@ public class QuickSettings.Indicator : Wingpanel.Indicator { // Prevent a race that skips automatic resource loading // https://github.com/elementary/wingpanel-indicator-bluetooth/issues/203 Gtk.IconTheme.get_default ().add_resource_path ("/org/elementary/wingpanel/icons"); + + EndSessionDialogServer.init (); + EndSessionDialogServer.get_default ().show_dialog.connect ( + (type) => show_dialog ((EndSessionDialogType) type) + ); + } + + private void show_dialog (EndSessionDialogType type) { + unowned var popover = (Gtk.Popover) popover_widget?.get_ancestor (typeof (Gtk.Popover)); + popover?.popdown (); + + if (current_dialog != null) { + if (current_dialog.dialog_type != type) { + current_dialog.destroy (); + } else { + return; + } + } + + unowned var server = EndSessionDialogServer.get_default (); + + current_dialog = new EndSessionDialog (type); + current_dialog.destroy.connect (() => { + server.closed (); + current_dialog = null; + }); + + current_dialog.cancelled.connect (() => server.canceled ()); + current_dialog.logout.connect (() => server.confirmed_logout ()); + current_dialog.shutdown.connect (() => { + try { + // See https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.login1.html for flags values + // #define SD_LOGIND_ROOT_CHECK_INHIBITORS (UINT64_C(1) << 0) == 1 + Login1Manager.get_default ().object.power_off_with_flags (1); + } catch (Error e) { + warning ("Unable to shutdown: %s", e.message); + } + }); + + current_dialog.reboot.connect (() => { + try { + // See https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.login1.html for flags values + // #define SD_LOGIND_KEXEC_REBOOT (UINT64_C(1) << 1) == 2 + Login1Manager.get_default ().object.reboot_with_flags (2); + } catch (Error e) { + warning ("Unable to reboot: %s", e.message); + } + }); + + current_dialog.present (); } public override Gtk.Widget get_display_widget () { diff --git a/src/Services/UserManager.vala b/src/Services/UserManager.vala index fba800c8..d441c4ab 100644 --- a/src/Services/UserManager.vala +++ b/src/Services/UserManager.vala @@ -21,42 +21,34 @@ public enum UserState { } public class QuickSettings.UserManager : Object { - private const string LOGIN_IFACE = "org.freedesktop.login1"; - private const string LOGIN_PATH = "/org/freedesktop/login1"; - - private static string active_user_real_name; - private static SystemInterface? login_proxy; - - private static async void init_login_proxy () { - try { - login_proxy = yield Bus.get_proxy (BusType.SYSTEM, LOGIN_IFACE, LOGIN_PATH, DBusProxyFlags.NONE); - } catch (IOError e) { - critical ("Failed to create login1 dbus proxy: %s", e.message); - } - } + private static string? active_user_real_name; public static async UserState get_user_state (uid_t uuid) { - if (login_proxy == null) { - yield init_login_proxy (); - } - try { - UserInfo[] users = login_proxy.list_users (); + var users = Login1Manager.get_default ().object.list_users (); if (users == null) { return UserState.OFFLINE; } - foreach (UserInfo user in users) { - if (((uid_t) user.uid) == uuid) { - if (user.user_object == null) { - return UserState.OFFLINE; - } - UserInterface? user_interface = yield Bus.get_proxy (BusType.SYSTEM, LOGIN_IFACE, user.user_object, DBusProxyFlags.NONE); - if (user_interface == null) { - return UserState.OFFLINE; - } - return UserState.to_enum (user_interface.state); + foreach (var user in users) { + if (((uid_t) user.uid) != uuid) { + continue; } + + if (user.user_object == null) { + return UserState.OFFLINE; + } + + var user_interface = yield Bus.get_proxy ( + SYSTEM, + "org.freedesktop.login1", + user.user_object + ); + if (user_interface == null) { + return UserState.OFFLINE; + } + + return UserState.to_enum (user_interface.state); } } catch (GLib.Error e) { @@ -67,13 +59,9 @@ public class QuickSettings.UserManager : Object { } public static async UserState get_guest_state () { - if (login_proxy == null) { - return UserState.OFFLINE; - } - try { - UserInfo[] users = login_proxy.list_users (); - foreach (UserInfo user in users) { + var users = Login1Manager.get_default ().object.list_users (); + foreach (var user in users) { var state = yield get_user_state (user.uid); if (user.user_name.has_prefix ("guest-") && state == UserState.ACTIVE) { diff --git a/src/Widgets/SessionBox.vala b/src/Widgets/SessionBox.vala index adba17ab..dd193af9 100644 --- a/src/Widgets/SessionBox.vala +++ b/src/Widgets/SessionBox.vala @@ -6,9 +6,7 @@ public class QuickSettings.SessionBox : Gtk.Box { public Wingpanel.IndicatorManager.ServerType server_type { get; construct; } - private EndSessionDialog? current_dialog = null; private Gtk.Popover? popover; - private SystemInterface system_interface; public SessionBox (Wingpanel.IndicatorManager.ServerType server_type) { Object (server_type: server_type); @@ -66,21 +64,17 @@ public class QuickSettings.SessionBox : Gtk.Box { shutdown_button.clicked.connect (() => { popover.popdown (); - show_dialog (EndSessionDialogType.RESTART); + EndSessionDialogServer.get_default ().show_dialog (EndSessionDialogType.RESTART); }); - setup_system_interface.begin ((obj, res) => { - system_interface = setup_system_interface.end (res); - - suspend_button.clicked.connect (() => { - popover.popdown (); + suspend_button.clicked.connect (() => { + popover.popdown (); - try { - system_interface.suspend (true); - } catch (GLib.Error e) { - critical ("Unable to suspend: %s", e.message); - } - }); + try { + Login1Manager.get_default ().object.suspend (true); + } catch (GLib.Error e) { + critical ("Unable to suspend: %s", e.message); + } }); var keybinding_settings = new Settings ("org.gnome.settings-daemon.plugins.media-keys"); @@ -95,11 +89,6 @@ public class QuickSettings.SessionBox : Gtk.Box { ); }); - EndSessionDialogServer.init (); - EndSessionDialogServer.get_default ().show_dialog.connect ( - (type, timestamp) => show_dialog ((EndSessionDialogType) type) - ); - settings_button.clicked.connect (() => { popover.popdown (); @@ -111,15 +100,6 @@ public class QuickSettings.SessionBox : Gtk.Box { }); } - private async SystemInterface? setup_system_interface () { - try { - return yield Bus.get_proxy (BusType.SYSTEM, "org.freedesktop.login1", "/org/freedesktop/login1"); - } catch (IOError e) { - critical ("Unable to connect to the login interface: %s", e.message); - return null; - } - } - private async LockInterface? setup_lock_interface () { try { return yield Bus.get_proxy (BusType.SESSION, "org.gnome.ScreenSaver", "/org/gnome/ScreenSaver"); @@ -128,56 +108,4 @@ public class QuickSettings.SessionBox : Gtk.Box { return null; } } - - private void show_dialog (EndSessionDialogType type) { - popover.popdown (); - - if (current_dialog != null) { - if (current_dialog.dialog_type != type) { - current_dialog.destroy (); - } else { - return; - } - } - - unowned var server = EndSessionDialogServer.get_default (); - - current_dialog = new EndSessionDialog (type) { - transient_for = (Gtk.Window) get_toplevel () - }; - current_dialog.destroy.connect (() => { - server.closed (); - current_dialog = null; - }); - - current_dialog.cancelled.connect (() => { - server.canceled (); - }); - - current_dialog.logout.connect (() => { - server.confirmed_logout (); - }); - - current_dialog.shutdown.connect (() => { - try { - // See https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.login1.html for flags values - // #define SD_LOGIND_ROOT_CHECK_INHIBITORS (UINT64_C(1) << 0) == 1 - system_interface.power_off_with_flags (1); - } catch (Error e) { - warning ("Unable to shutdown: %s", e.message); - } - }); - - current_dialog.reboot.connect (() => { - try { - // See https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.login1.html for flags values - // #define SD_LOGIND_KEXEC_REBOOT (UINT64_C(1) << 1) == 2 - system_interface.reboot_with_flags (2); - } catch (Error e) { - warning ("Unable to reboot: %s", e.message); - } - }); - - current_dialog.present (); - } } From 8dbb625075c4a0b1393137eb40bfbe95a3b35b92 Mon Sep 17 00:00:00 2001 From: lenemter Date: Wed, 24 Sep 2025 00:50:25 +0300 Subject: [PATCH 2/5] Rename file --- src/DBus/{SystemInterface.vala => Login1Manager.vala} | 2 +- src/meson.build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/DBus/{SystemInterface.vala => Login1Manager.vala} (96%) diff --git a/src/DBus/SystemInterface.vala b/src/DBus/Login1Manager.vala similarity index 96% rename from src/DBus/SystemInterface.vala rename to src/DBus/Login1Manager.vala index ecf9f10f..5579f48c 100644 --- a/src/DBus/SystemInterface.vala +++ b/src/DBus/Login1Manager.vala @@ -25,7 +25,7 @@ public class QuickSettings.Login1Manager : Object { public Login1ManagerInterface object; private static GLib.Once instance; - public static unowned Login1Manager get_default () { + public static unowned Login1Manager get_default () { return instance.once (() => { return new Login1Manager (); }); } diff --git a/src/meson.build b/src/meson.build index 7a0cd829..c32beb5d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -12,10 +12,10 @@ sources = [ 'PopoverWidget.vala', 'DBus' / 'EndSessionDialogServer.vala', 'DBus' / 'LockInterface.vala', + 'DBus' / 'Login1Manager.vala', 'DBus' / 'SeatInterface.vala', 'DBus' / 'SensorProxy.vala', 'DBus' / 'SessionInterface.vala', - 'DBus' / 'SystemInterface.vala', 'DBus' / 'UserInterface.vala', 'Services' / 'UserManager.vala', 'Widgets' / 'AvatarButton.vala', From f9b753df9b8f370ce9632a7e0fef38834ac060f9 Mon Sep 17 00:00:00 2001 From: lenemter Date: Wed, 24 Sep 2025 00:51:48 +0300 Subject: [PATCH 3/5] Simplify --- src/Indicator.vala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Indicator.vala b/src/Indicator.vala index ba84e4c0..bc4519a2 100644 --- a/src/Indicator.vala +++ b/src/Indicator.vala @@ -32,8 +32,7 @@ public class QuickSettings.Indicator : Wingpanel.Indicator { } private void show_dialog (EndSessionDialogType type) { - unowned var popover = (Gtk.Popover) popover_widget?.get_ancestor (typeof (Gtk.Popover)); - popover?.popdown (); + ((Gtk.Popover?) popover_widget?.get_ancestor (typeof (Gtk.Popover)))?.popdown (); if (current_dialog != null) { if (current_dialog.dialog_type != type) { From 820d47c92db43afe6d451c1332c954e65a44cbb2 Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 24 Sep 2025 19:20:57 +0900 Subject: [PATCH 4/5] Update src/DBus/Login1Manager.vala --- src/DBus/Login1Manager.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DBus/Login1Manager.vala b/src/DBus/Login1Manager.vala index 5579f48c..e37b7775 100644 --- a/src/DBus/Login1Manager.vala +++ b/src/DBus/Login1Manager.vala @@ -26,7 +26,7 @@ public class QuickSettings.Login1Manager : Object { private static GLib.Once instance; public static unowned Login1Manager get_default () { - return instance.once (() => { return new Login1Manager (); }); + return instance.once (() => new Login1Manager ()); } private Login1Manager () {} From 4beb7fdf9423cb65c208910be3cb6b6eb0a2eddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danielle=20For=C3=A9?= Date: Mon, 26 Jan 2026 12:43:33 -0800 Subject: [PATCH 5/5] Add issue link for Ctrl+Alt+Del focus problem --- data/quick-settings.metainfo.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/quick-settings.metainfo.xml b/data/quick-settings.metainfo.xml index e1a40e93..a481cc08 100644 --- a/data/quick-settings.metainfo.xml +++ b/data/quick-settings.metainfo.xml @@ -40,6 +40,7 @@ End Session Dialog should pull visual focus + Ctrl+Alt+Del not available before opening Quick Settings for the first time