aboutsummaryrefslogtreecommitdiff
path: root/src/session-menu-mgr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/session-menu-mgr.c')
-rw-r--r--src/session-menu-mgr.c546
1 files changed, 333 insertions, 213 deletions
diff --git a/src/session-menu-mgr.c b/src/session-menu-mgr.c
index c6de0b1..752a6a6 100644
--- a/src/session-menu-mgr.c
+++ b/src/session-menu-mgr.c
@@ -2,26 +2,26 @@
Copyright 2011 Canonical Ltd.
Authors:
- Conor Curran <conor.curran@canonical.com>
Charles Kerr <charles.kerr@canonical.com>
+ Conor Curran <conor.curran@canonical.com>
-This program is free software: you can redistribute it and/or modify it
-under the terms of the GNU General Public License version 3, as published
+This program is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License version 3, as published
by the Free Software Foundation.
-This program is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranties of
-MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
PURPOSE. See the GNU General Public License for more details.
-You should have received a copy of the GNU General Public License along
+You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <sys/types.h>
-#include <pwd.h>
+#include <pwd.h> /* geteuid(), getpwuid() */
#include <glib.h>
#include <glib/gi18n.h>
@@ -29,11 +29,13 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include <libdbusmenu-glib/client.h>
#include <libdbusmenu-gtk/menuitem.h>
-#include "dbus-shared-names.h"
#include "dbus-upower.h"
#include "session-menu-mgr.h"
+#include "shared-names.h"
#include "users-service-dbus.h"
+#define DEBUG_SHOW_ALL FALSE
+
#define UPOWER_ADDRESS "org.freedesktop.UPower"
#define UPOWER_PATH "/org/freedesktop/UPower"
@@ -41,13 +43,15 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#define CMD_INFO "gnome-control-center info"
#define CMD_SYSTEM_SETTINGS "gnome-control-center"
#ifdef HAVE_GTKLOGOUTHELPER
+ #define HAVE_RESTART_CMD TRUE
+ #define CMD_RESTART LIBEXECDIR"/gtk-logout-helper --restart"
#define CMD_LOGOUT LIBEXECDIR"/gtk-logout-helper --logout"
#define CMD_SHUTDOWN LIBEXECDIR"/gtk-logout-helper --shutdown"
- #define CMD_RESTART LIBEXECDIR"/gtk-logout-helper --restart"
#else
+ #define HAVE_RESTART_CMD FALSE /* hmm, no gnome-session-quit --restart? */
+ #define CMD_RESTART ""
#define CMD_LOGOUT "gnome-session-quit --logout"
#define CMD_SHUTDOWN "gnome-session-quit --power-off"
- #define CMD_RESTART CMD_SHUTDOWN /* hmm, no gnome-session-quit --restart? */
#endif
/**
@@ -64,18 +68,23 @@ typedef enum
}
SwitcherMode;
-typedef struct
-{
- SessionMenuMgr * mgr;
- AccountsUser * user;
-}
-ActivateUserSessionData;
-
+/**
+ * Creates and manages the menumodel and associated actions for the
+ * session menu described at <https://wiki.ubuntu.com/SystemMenu>.
+ *
+ * This is a pretty straightforward class: it creates the menumodel
+ * and listens for events that can affect the model's properties.
+ *
+ * Simple event sources, such as GSettings and a UPower DBus proxy,
+ * are handled here. More involved event sources are delegated to
+ * UsersServiceDBus facade class.
+ */
struct _SessionMenuMgr
{
GObject parent_instance;
DbusmenuMenuitem * parent_mi;
+ DbusmenuMenuitem * screensaver_mi;
DbusmenuMenuitem * lock_mi;
DbusmenuMenuitem * lock_switch_mi;
DbusmenuMenuitem * guest_mi;
@@ -92,19 +101,22 @@ struct _SessionMenuMgr
GSettings * indicator_settings;
GSettings * keybinding_settings;
+ /* cached settings taken from the upower proxy */
gboolean can_hibernate;
gboolean can_suspend;
gboolean allow_hibernate;
gboolean allow_suspend;
+
gboolean greeter_mode;
GCancellable * cancellable;
DBusUPower * upower_proxy;
- SessionDbus * session_dbus;
+ SessionDbus * session_dbus;
UsersServiceDbus * users_dbus_facade;
};
static SwitcherMode get_switcher_mode (SessionMenuMgr *);
+
static void init_upower_proxy (SessionMenuMgr *);
static void update_screensaver_shortcut (SessionMenuMgr *);
@@ -118,21 +130,21 @@ static void action_func_hibernate (SessionMenuMgr *);
static void action_func_switch_to_lockscreen (SessionMenuMgr *);
static void action_func_switch_to_greeter (SessionMenuMgr *);
static void action_func_switch_to_guest (SessionMenuMgr *);
-static void action_func_switch_to_user (ActivateUserSessionData *);
+static void action_func_switch_to_user (AccountsUser *);
static void action_func_spawn_async (const char * fmt, ...);
-static gboolean is_this_guest_session (void);
-static gboolean is_this_live_session (void);
+static gboolean is_this_guest_session (void);
+static gboolean is_this_live_session (void);
-static void on_guest_logged_in_changed (UsersServiceDbus *,
- SessionMenuMgr *);
+static void on_guest_logged_in_changed (UsersServiceDbus *,
+ SessionMenuMgr *);
-static void on_user_logged_in_changed (UsersServiceDbus *,
- AccountsUser *,
- SessionMenuMgr *);
+static void on_user_logged_in_changed (UsersServiceDbus *,
+ AccountsUser *,
+ SessionMenuMgr *);
/**
-*** GObject init / dispose / finalize
+*** GObject init / dispose
**/
G_DEFINE_TYPE (SessionMenuMgr, session_menu_mgr, G_TYPE_OBJECT);
@@ -140,14 +152,9 @@ G_DEFINE_TYPE (SessionMenuMgr, session_menu_mgr, G_TYPE_OBJECT);
static void
session_menu_mgr_init (SessionMenuMgr *mgr)
{
- mgr->can_hibernate = TRUE;
- mgr->can_suspend = TRUE;
- mgr->allow_hibernate = TRUE;
- mgr->allow_suspend = TRUE;
-
/* Lockdown settings */
GSettings * s = g_settings_new ("org.gnome.desktop.lockdown");
- g_signal_connect_swapped (s, "changed",
+ g_signal_connect_swapped (s, "changed::disable-log-out",
G_CALLBACK(update_session_menuitems), mgr);
g_signal_connect_swapped (s, "changed::disable-lock-screen",
G_CALLBACK(update_user_menuitems), mgr);
@@ -159,7 +166,12 @@ session_menu_mgr_init (SessionMenuMgr *mgr)
s = g_settings_new ("com.canonical.indicator.session");
g_signal_connect_swapped (s, "changed::suppress-logout-restart-shutdown",
G_CALLBACK(update_confirmation_labels), mgr);
- g_signal_connect (s, "changed", G_CALLBACK(update_session_menuitems), mgr);
+ g_signal_connect_swapped (s, "changed::suppress-logout-menuitem",
+ G_CALLBACK(update_session_menuitems), mgr);
+ g_signal_connect_swapped (s, "changed::suppress-restart-menuitem",
+ G_CALLBACK(update_session_menuitems), mgr);
+ g_signal_connect_swapped (s, "changed::suppress-shutdown-menuitem",
+ G_CALLBACK(update_session_menuitems), mgr);
mgr->indicator_settings = s;
/* Keybinding settings */
@@ -168,7 +180,7 @@ session_menu_mgr_init (SessionMenuMgr *mgr)
G_CALLBACK(update_screensaver_shortcut), mgr);
mgr->keybinding_settings = s;
- /* listen for users who appear or log in or log out */
+ /* listen for user events */
mgr->users_dbus_facade = g_object_new (USERS_SERVICE_DBUS_TYPE, NULL);
g_signal_connect_swapped (mgr->users_dbus_facade, "user-list-changed",
G_CALLBACK (update_user_menuitems), mgr);
@@ -177,7 +189,7 @@ session_menu_mgr_init (SessionMenuMgr *mgr)
g_signal_connect (mgr->users_dbus_facade, "guest-logged-in-changed",
G_CALLBACK(on_guest_logged_in_changed), mgr);
- init_upower_proxy (mgr);
+ init_upower_proxy (mgr);
}
static void
@@ -197,13 +209,7 @@ session_menu_mgr_dispose (GObject *object)
g_clear_object (&mgr->upower_proxy);
g_clear_object (&mgr->users_dbus_facade);
- G_OBJECT_CLASS (session_menu_mgr_parent_class)->finalize (object);
-}
-
-static void
-session_menu_mgr_finalize (GObject *object)
-{
- G_OBJECT_CLASS (session_menu_mgr_parent_class)->finalize (object);
+ G_OBJECT_CLASS (session_menu_mgr_parent_class)->dispose (object);
}
static void
@@ -211,61 +217,16 @@ session_menu_mgr_class_init (SessionMenuMgrClass *klass)
{
GObjectClass* object_class = G_OBJECT_CLASS (klass);
object_class->dispose = session_menu_mgr_dispose;
- object_class->finalize = session_menu_mgr_finalize;
-}
-
-/***
-**** Menuitem Helpers
-***/
-
-static inline void
-mi_set_label (DbusmenuMenuitem * mi, const char * str)
-{
- dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_LABEL, str);
-}
-
-static inline void
-mi_set_type (DbusmenuMenuitem * mi, const char * str)
-{
- dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_TYPE, str);
-}
-
-static inline void
-mi_set_visible (DbusmenuMenuitem * mi, gboolean b)
-{
- dbusmenu_menuitem_property_set_bool (mi, DBUSMENU_MENUITEM_PROP_VISIBLE, b);
-}
-
-static inline void
-mi_set_logged_in (DbusmenuMenuitem * mi, gboolean b)
-{
- dbusmenu_menuitem_property_set_bool (mi, USER_ITEM_PROP_LOGGED_IN, b);
-}
-
-static DbusmenuMenuitem*
-mi_new_separator (void)
-{
- DbusmenuMenuitem * mi = dbusmenu_menuitem_new ();
- mi_set_type (mi, DBUSMENU_CLIENT_TYPES_SEPARATOR);
- return mi;
-}
-
-static DbusmenuMenuitem*
-mi_new (const char * label)
-{
- DbusmenuMenuitem * mi = dbusmenu_menuitem_new ();
- mi_set_label (mi, label);
- return mi;
}
/***
**** UPower Proxy:
****
**** 1. While bootstrapping, we invoke the AllowSuspend and AllowHibernate
-**** methods to find out whether or not those functions are allowed.
+**** methods to find out whether or not those features are allowed.
**** 2. While bootstrapping, we get the CanSuspend and CanHibernate properties
**** and also listen for property changes.
-**** 3. These four values are used to set suspend and hibernate's visibility
+**** 3. These four values are used to set suspend and hibernate's visibility.
****
***/
@@ -273,14 +234,14 @@ static void
on_upower_properties_changed (SessionMenuMgr * mgr)
{
gboolean b;
- gboolean refresh = FALSE;
+ gboolean need_refresh = FALSE;
/* suspend */
b = dbus_upower_get_can_suspend (mgr->upower_proxy);
if (mgr->can_suspend != b)
{
mgr->can_suspend = b;
- refresh = TRUE;
+ need_refresh = TRUE;
}
/* hibernate */
@@ -288,10 +249,10 @@ on_upower_properties_changed (SessionMenuMgr * mgr)
if (mgr->can_hibernate != b)
{
mgr->can_hibernate = b;
- refresh = TRUE;
+ need_refresh = TRUE;
}
- if (refresh)
+ if (need_refresh)
{
update_session_menuitems (mgr);
}
@@ -300,6 +261,12 @@ on_upower_properties_changed (SessionMenuMgr * mgr)
static void
init_upower_proxy (SessionMenuMgr * mgr)
{
+ /* default values */
+ mgr->can_suspend = TRUE;
+ mgr->can_hibernate = TRUE;
+ mgr->allow_suspend = TRUE;
+ mgr->allow_hibernate = TRUE;
+
mgr->cancellable = g_cancellable_new ();
GError * error = NULL;
@@ -344,44 +311,92 @@ init_upower_proxy (SessionMenuMgr * mgr)
}
/***
-**** Admin Menuitems
+**** Menuitem Helpers
***/
-#define DEBUG_SHOW_ALL 1
+static inline void
+mi_set_label (DbusmenuMenuitem * mi, const char * str)
+{
+ dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_LABEL, str);
+}
-static void
-build_admin_menuitems (SessionMenuMgr * mgr)
+static inline void
+mi_set_type (DbusmenuMenuitem * mi, const char * str)
{
- DbusmenuMenuitem * mi;
- const gboolean show_settings = DEBUG_SHOW_ALL || !mgr->greeter_mode;
+ dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_TYPE, str);
+}
- mi = mi_new (_("About This Computer"));
- dbusmenu_menuitem_child_append (mgr->parent_mi, mi);
- g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
- G_CALLBACK(action_func_spawn_async), CMD_INFO);
+static inline void
+mi_set_visible (DbusmenuMenuitem * mi, gboolean b)
+{
+ dbusmenu_menuitem_property_set_bool (mi, DBUSMENU_MENUITEM_PROP_VISIBLE,
+ b || DEBUG_SHOW_ALL);
+}
- mi = mi_new (_("Ubuntu Help"));
- dbusmenu_menuitem_child_append (mgr->parent_mi, mi);
- g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
- G_CALLBACK(action_func_spawn_async), CMD_HELP);
+static inline void
+mi_set_logged_in (DbusmenuMenuitem * mi, gboolean b)
+{
+ dbusmenu_menuitem_property_set_bool (mi, USER_ITEM_PROP_LOGGED_IN, b);
+}
- mi = mi_new_separator ();
- mi_set_visible (mi, show_settings);
- dbusmenu_menuitem_child_append (mgr->parent_mi, mi);
+static DbusmenuMenuitem*
+mi_new_separator (void)
+{
+ DbusmenuMenuitem * mi = dbusmenu_menuitem_new ();
+ mi_set_type (mi, DBUSMENU_CLIENT_TYPES_SEPARATOR);
+ return mi;
+}
- mi = mi_new (_("System Settings\342\200\246"));
- mi_set_visible (mi, show_settings);
- dbusmenu_menuitem_child_append (mgr->parent_mi, mi);
- g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
- G_CALLBACK(action_func_spawn_async),
- CMD_SYSTEM_SETTINGS);
+static DbusmenuMenuitem*
+mi_new (const char * label)
+{
+ DbusmenuMenuitem * mi = dbusmenu_menuitem_new ();
+ mi_set_label (mi, label);
+ return mi;
+}
- mi = mi_new_separator ();
- dbusmenu_menuitem_child_append (mgr->parent_mi, mi);
+/***
+**** Admin Menuitems
+**** <https://wiki.ubuntu.com/SystemMenu#Admin_items>
+***/
+
+static void
+build_admin_menuitems (SessionMenuMgr * mgr)
+{
+ if (!mgr->greeter_mode)
+ {
+ DbusmenuMenuitem * mi;
+ const gboolean show_settings = !mgr->greeter_mode;
+
+ mi = mi_new (_("About This Computer"));
+ dbusmenu_menuitem_child_append (mgr->parent_mi, mi);
+ g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+ G_CALLBACK(action_func_spawn_async), CMD_INFO);
+
+ mi = mi_new (_("Ubuntu Help"));
+ dbusmenu_menuitem_child_append (mgr->parent_mi, mi);
+ g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+ G_CALLBACK(action_func_spawn_async), CMD_HELP);
+
+ mi = mi_new_separator ();
+ mi_set_visible (mi, show_settings);
+ dbusmenu_menuitem_child_append (mgr->parent_mi, mi);
+
+ mi = mi_new (_("System Settings"));
+ mi_set_visible (mi, show_settings);
+ dbusmenu_menuitem_child_append (mgr->parent_mi, mi);
+ g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+ G_CALLBACK(action_func_spawn_async),
+ CMD_SYSTEM_SETTINGS);
+
+ mi = mi_new_separator ();
+ dbusmenu_menuitem_child_append (mgr->parent_mi, mi);
+ }
}
/***
**** Session Menuitems
+**** <https://wiki.ubuntu.com/SystemMenu#Session_items>
***/
static void
@@ -392,7 +407,7 @@ update_session_menuitems (SessionMenuMgr * mgr)
v = !mgr->greeter_mode
&& !is_this_live_session()
- && !g_settings_get_boolean (mgr->lockdown_settings, "disable-logout")
+ && !g_settings_get_boolean (mgr->lockdown_settings, "disable-log-out")
&& !g_settings_get_boolean (s, "suppress-logout-menuitem");
mi_set_visible (mgr->logout_mi, v);
@@ -404,15 +419,20 @@ update_session_menuitems (SessionMenuMgr * mgr)
&& mgr->allow_hibernate;
mi_set_visible (mgr->hibernate_mi, v);
- v = !g_settings_get_boolean (s, "suppress-restart-menuitem");
+ v = HAVE_RESTART_CMD
+ && !g_settings_get_boolean (s, "suppress-restart-menuitem");
mi_set_visible (mgr->restart_mi, v);
v = !g_settings_get_boolean (s, "suppress-shutdown-menuitem");
mi_set_visible (mgr->shutdown_mi, v);
}
-/* if confirmation is enabled,
- add ellipsis to the labels of items whose actions need confirmation */
+/* Update the ellipses when the confirmation setting changes.
+ *
+ * <http://developer.gnome.org/hig-book/3.0/menus-design.html.en>:
+ * "Label the menu item with a trailing ellipsis ("...") only if the
+ * command requires further input from the user before it can be performed."
+ */
static void
update_confirmation_labels (SessionMenuMgr * mgr)
{
@@ -436,9 +456,6 @@ build_session_menuitems (SessionMenuMgr* mgr)
{
DbusmenuMenuitem * mi;
- mi = mi_new_separator ();
- dbusmenu_menuitem_child_append (mgr->parent_mi, mi);
-
mi = mgr->logout_mi = mi_new (_("Log Out\342\200\246"));
dbusmenu_menuitem_child_append (mgr->parent_mi, mi);
g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
@@ -454,24 +471,28 @@ build_session_menuitems (SessionMenuMgr* mgr)
g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
G_CALLBACK(action_func_hibernate), mgr);
- mi = mgr->restart_mi = mi_new (_("Restart\342\200\246"));
+ mi = mgr->restart_mi = dbusmenu_menuitem_new ();
+ mi_set_type (mi, RESTART_ITEM_TYPE);
+ dbusmenu_menuitem_property_set (mi, RESTART_ITEM_LABEL, _("Restart\342\200\246"));
dbusmenu_menuitem_child_append (mgr->parent_mi, mi);
g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
G_CALLBACK(action_func_spawn_async), CMD_RESTART);
-
+
mi = mgr->shutdown_mi = mi_new (_("Switch Off\342\200\246"));
dbusmenu_menuitem_child_append (mgr->parent_mi, mi);
g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
G_CALLBACK(action_func_spawn_async), CMD_SHUTDOWN);
update_confirmation_labels (mgr);
+ update_session_menuitems (mgr);
}
/****
***** User Menuitems
+***** https://wiki.ubuntu.com/SystemMenu#Account-switching_items
****/
-/* Local Extensions to AccountsUser */
+/* Local extensions to AccountsUser */
static GQuark
get_menuitem_quark (void)
@@ -493,14 +514,48 @@ user_get_menuitem (AccountsUser * user)
}
static void
+user_clear_menuitem (AccountsUser * user)
+{
+ g_object_steal_qdata (G_OBJECT(user), get_menuitem_quark());
+}
+
+static void
user_set_menuitem (AccountsUser * user, DbusmenuMenuitem * mi)
{
- g_message ("%s %s() associating user %s with mi %p",
- G_STRLOC, G_STRFUNC, accounts_user_get_user_name(user), mi);
- g_object_set_qdata_full (G_OBJECT(user), get_menuitem_quark(),
- g_object_ref(G_OBJECT(mi)), g_object_unref);
+ g_object_set_qdata (G_OBJECT(user), get_menuitem_quark(), mi);
+
+ g_object_weak_ref (G_OBJECT(mi), (GWeakNotify)user_clear_menuitem, user);
}
+/***/
+
+static GQuark
+get_mgr_quark (void)
+{
+ static GQuark q = 0;
+
+ if (G_UNLIKELY(!q))
+ {
+ q = g_quark_from_static_string ("session-menu-mgr");
+ }
+
+ return q;
+}
+
+static SessionMenuMgr*
+user_get_mgr (AccountsUser * user)
+{
+ return g_object_get_qdata (G_OBJECT(user), get_mgr_quark());
+}
+
+static void
+user_set_mgr (AccountsUser * user, SessionMenuMgr * mgr)
+{
+ g_object_set_qdata (G_OBJECT(user), get_mgr_quark(), mgr);
+}
+
+/***/
+
static GQuark
get_collision_quark (void)
{
@@ -534,8 +589,11 @@ static void
on_guest_logged_in_changed (UsersServiceDbus * usd,
SessionMenuMgr * mgr)
{
- mi_set_logged_in (mgr->guest_mi,
- users_service_dbus_is_guest_logged_in (usd));
+ if (mgr->guest_mi != NULL)
+ {
+ mi_set_logged_in (mgr->guest_mi,
+ users_service_dbus_is_guest_logged_in (usd));
+ }
}
/* When a user's login state changes,
@@ -557,19 +615,31 @@ static void
update_screensaver_shortcut (SessionMenuMgr * mgr)
{
gchar * s = g_settings_get_string (mgr->keybinding_settings, "screensaver");
- g_debug ("Keybinding changed to: %s", s);
- dbusmenu_menuitem_property_set_shortcut_string (mgr->lock_mi, s);
- dbusmenu_menuitem_property_set_shortcut_string (mgr->lock_switch_mi, s);
+ g_debug ("%s Screensaver shortcut changed to: '%s'", G_STRLOC, s);
+
+ if (mgr->lock_mi != NULL)
+ {
+ dbusmenu_menuitem_property_set_shortcut_string (mgr->lock_mi, s);
+ }
+
+ if (mgr->lock_switch_mi != NULL)
+ {
+ dbusmenu_menuitem_property_set_shortcut_string (mgr->lock_switch_mi, s);
+ }
+
+ if (mgr->screensaver_mi != NULL)
+ {
+ dbusmenu_menuitem_property_set_shortcut_string (mgr->screensaver_mi, s);
+ }
+
g_free (s);
}
static void
-on_user_icon_file_changed (AccountsUser * user,
- GParamSpec * pspec G_GNUC_UNUSED,
- DbusmenuMenuitem * mi)
+update_user_menuitem_icon (DbusmenuMenuitem * mi, AccountsUser * user)
{
const gchar * str = accounts_user_get_icon_file (user);
-
+
if (!str || !*str)
{
str = USER_ITEM_ICON_DEFAULT;
@@ -578,19 +648,62 @@ on_user_icon_file_changed (AccountsUser * user,
dbusmenu_menuitem_property_set (mi, USER_ITEM_PROP_ICON, str);
}
+static void
+update_user_menuitem_name (DbusmenuMenuitem * mi, AccountsUser * user)
+{
+ GString * gstr = g_string_new (accounts_user_get_real_name (user));
+
+ if (user_has_name_collision (user))
+ {
+ g_string_append_printf (gstr, " (%s)", accounts_user_get_user_name(user));
+ }
+
+ dbusmenu_menuitem_property_set (mi, USER_ITEM_PROP_NAME, gstr->str);
+ g_string_free (gstr, TRUE);
+}
+
+static void
+on_user_property_changed (AccountsUser * user,
+ GParamSpec * pspec,
+ DbusmenuMenuitem * mi)
+{
+ static const char * interned_icon_file = NULL;
+ static const char * interned_real_name = NULL;
+ static const char * interned_user_name = NULL;
+
+ if (G_UNLIKELY (interned_icon_file == NULL))
+ {
+ interned_icon_file = g_intern_static_string ("icon-file");
+ interned_user_name = g_intern_static_string ("user-name");
+ interned_real_name = g_intern_static_string ("real-name");
+ }
+
+ if (pspec->name == interned_icon_file)
+ {
+ update_user_menuitem_icon (mi, user);
+ }
+ else if ((pspec->name == interned_real_name)
+ || (pspec->name == interned_user_name))
+ {
+ /* name changing can affect other menuitems too by invalidating
+ the sort order or name collision flags... so let's rebuild */
+ update_user_menuitems (user_get_mgr (user));
+ }
+}
+
typedef struct
{
- AccountsUser * user;
+ gpointer instance;
gulong handler_id;
}
-UserChangeListenerData;
+SignalHandlerData;
-/* when the menuitem is destroyed,
- it should stop listening for changes to the UserAccount properties :) */
+/* when a user menuitem is destroyed,
+ it should stop listening for its UserAccount's property changes */
static void
-on_user_menuitem_destroyed (UserChangeListenerData * data)
+on_user_menuitem_destroyed (SignalHandlerData * data)
{
- g_signal_handler_disconnect (data->user, data->handler_id);
+ g_signal_handler_disconnect (data->instance, data->handler_id);
g_free (data);
}
@@ -600,27 +713,20 @@ user_menuitem_new (AccountsUser * user, SessionMenuMgr * mgr)
DbusmenuMenuitem * mi = dbusmenu_menuitem_new ();
mi_set_type (mi, USER_ITEM_TYPE);
- /* set the name property */
- GString * gstr = g_string_new (accounts_user_get_real_name (user));
- if (user_has_name_collision (user))
- {
- g_string_append_printf (gstr, " (%s)", accounts_user_get_user_name(user));
- }
- dbusmenu_menuitem_property_set (mi, USER_ITEM_PROP_NAME, gstr->str);
- g_string_free (gstr, TRUE);
+ /* set the name & icon and listen for property changes */
+ update_user_menuitem_name (mi, user);
+ update_user_menuitem_icon (mi, user);
+ SignalHandlerData * hd = g_new0 (SignalHandlerData, 1);
+ hd->instance = user;
+ hd->handler_id = g_signal_connect (user, "notify",
+ G_CALLBACK(on_user_property_changed), mi);
+ g_object_weak_ref (G_OBJECT(mi), (GWeakNotify)on_user_menuitem_destroyed, hd);
+
/* set the logged-in property */
mi_set_logged_in (mi,
users_service_dbus_is_user_logged_in (mgr->users_dbus_facade, user));
- /* set the icon property & listen for changes */
- UserChangeListenerData * cd = g_new0 (UserChangeListenerData, 1);
- cd->user = user;
- cd->handler_id = g_signal_connect (user, "notify::icon-file",
- G_CALLBACK(on_user_icon_file_changed), mi);
- g_object_weak_ref (G_OBJECT(mi), (GWeakNotify)on_user_menuitem_destroyed, cd);
- on_user_icon_file_changed (user, NULL, mi);
-
/* set the is-current-user property */
const gboolean is_current_user =
!g_strcmp0 (g_get_user_name(), accounts_user_get_user_name(user));
@@ -628,17 +734,13 @@ user_menuitem_new (AccountsUser * user, SessionMenuMgr * mgr)
USER_ITEM_PROP_IS_CURRENT_USER,
is_current_user);
- /* set the activate callback */
- ActivateUserSessionData * data = g_new (ActivateUserSessionData, 1);
- data->user = user;
- data->mgr = mgr;
- g_signal_connect_data (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
- G_CALLBACK (action_func_switch_to_user),
- data, (GClosureNotify)g_free,
- G_CONNECT_SWAPPED);
+ /* set the switch-to-user action */
+ g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+ G_CALLBACK (action_func_switch_to_user), user);
/* give this AccountsUser a hook back to this menuitem */
user_set_menuitem (user, mi);
+ user_set_mgr (user, mgr);
return mi;
}
@@ -654,7 +756,7 @@ compare_users_by_login_frequency (gconstpointer a, gconstpointer b)
return 0;
}
-/* for sorting AccountsUsers by name */
+/* for sorting AccountsUsers alphabetically */
static gint
compare_users_by_username (gconstpointer ga, gconstpointer gb)
{
@@ -690,6 +792,8 @@ is_user_switching_allowed (SessionMenuMgr * mgr)
static void
build_user_menuitems (SessionMenuMgr * mgr)
{
+ g_return_if_fail (!mgr->greeter_mode);
+
DbusmenuMenuitem * mi;
GSList * items = NULL;
gint pos = mgr->user_menuitem_index;
@@ -702,63 +806,78 @@ build_user_menuitems (SessionMenuMgr * mgr)
*** Lock / Switch Account...
**/
- const gboolean show_all = DEBUG_SHOW_ALL;
const SwitcherMode mode = get_switcher_mode (mgr);
- mi = mi_new (_("Start Screen Saver"));
- mi_set_visible (mi, show_all || (mode == SWITCHER_MODE_SCREENSAVER));
+ mi = mgr->screensaver_mi = mi_new (_("Start Screen Saver"));
+ mi_set_visible (mi, mode == SWITCHER_MODE_SCREENSAVER);
dbusmenu_menuitem_child_add_position (mgr->parent_mi, mi, pos++);
items = g_slist_prepend (items, mi);
g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
G_CALLBACK (action_func_lock), mgr);
mi = mi_new (_("Switch User Account\342\200\246"));
- mi_set_visible (mi, show_all || (mode == SWITCHER_MODE_SWITCH));
+ mi_set_visible (mi, mode == SWITCHER_MODE_SWITCH);
dbusmenu_menuitem_child_add_position (mgr->parent_mi, mi, pos++);
items = g_slist_prepend (items, mi);
g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
G_CALLBACK (action_func_switch_to_greeter), mgr);
mi = mgr->lock_mi = mi_new (_("Lock"));
- mi_set_visible (mi, show_all || (mode == SWITCHER_MODE_LOCK));
+ mi_set_visible (mi, mode == SWITCHER_MODE_LOCK);
dbusmenu_menuitem_child_add_position (mgr->parent_mi, mi, pos++);
items = g_slist_prepend (items, mi);
g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
G_CALLBACK (action_func_switch_to_lockscreen), mgr);
mi = mgr->lock_switch_mi = mi_new (_("Lock/Switch Account\342\200\246"));
- mi_set_visible (mi, show_all || (mode == SWITCHER_MODE_SWITCH_OR_LOCK));
+ mi_set_visible (mi, mode == SWITCHER_MODE_SWITCH_OR_LOCK);
dbusmenu_menuitem_child_add_position (mgr->parent_mi, mi, pos++);
items = g_slist_prepend (items, mi);
g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
G_CALLBACK (action_func_switch_to_lockscreen), mgr);
- const gboolean guest_allowed =
- users_service_dbus_guest_session_enabled (mgr->users_dbus_facade);
- const gboolean is_guest = is_this_guest_session();
- mi = mi_new (_("Guest Session"));
+ const gboolean is_guest = is_this_guest_session ();
+ const gboolean guest_allowed = users_service_dbus_guest_session_enabled (mgr->users_dbus_facade);
+ mi = mgr->guest_mi = dbusmenu_menuitem_new ();
mi_set_type (mi, USER_ITEM_TYPE);
- mi_set_visible (mi, guest_allowed && !is_guest);
+ mi_set_visible (mi, !is_guest && guest_allowed);
+ dbusmenu_menuitem_property_set (mi, USER_ITEM_PROP_NAME, _("Guest Session"));
dbusmenu_menuitem_child_add_position (mgr->parent_mi, mi, pos++);
on_guest_logged_in_changed (mgr->users_dbus_facade, mgr);
items = g_slist_prepend (items, mi);
g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
G_CALLBACK (action_func_switch_to_guest), mgr);
- mgr->guest_mi = mi;
+
if (guest_allowed && is_guest)
{
current_real_name = _("Guest");
}
-
+
/***
**** Users
***/
/* if we can switch to another user account, show them here */
- if (is_user_switching_allowed (mgr))
+ const char * const username = g_get_user_name();
+ GList * users = users_service_dbus_get_user_list (mgr->users_dbus_facade);
+
+ /* since we're building (or rebuilding) from scratch,
+ clear the name collision flags */
+ GList * u;
+ for (u=users; u!=NULL; u=u->next)
{
- GList * users = users_service_dbus_get_user_list (mgr->users_dbus_facade);
+ AccountsUser * user = ACCOUNTS_USER(u->data);
+
+ user_set_name_collision (user, FALSE);
+
+ if (!g_strcmp0 (username, accounts_user_get_user_name(user)))
+ {
+ current_real_name = accounts_user_get_real_name (user);
+ }
+ }
+ if (is_user_switching_allowed (mgr))
+ {
/* pick the most frequently used accounts */
const int MAX_USERS = 12; /* this limit comes from the spec */
if (g_list_length(users) > MAX_USERS)
@@ -776,21 +895,14 @@ build_user_menuitems (SessionMenuMgr * mgr)
/* Create menuitems for them */
int i;
- GList * u;
- const char * const username = g_get_user_name();
for (i=0, u=users; i<MAX_USERS && u!=NULL; u=u->next, i++)
{
AccountsUser * user = u->data;
DbusmenuMenuitem * mi = user_menuitem_new (user, mgr);
dbusmenu_menuitem_child_add_position (mgr->parent_mi, mi, pos++);
items = g_slist_prepend (items, mi);
-
- if (!g_strcmp0 (username, accounts_user_get_user_name(user)))
- {
- current_real_name = accounts_user_get_real_name (user);
- }
}
- g_list_free(users);
+ g_list_free (users);
}
/* separator */
@@ -821,11 +933,14 @@ update_user_menuitems (SessionMenuMgr * mgr)
mgr->user_menuitems = NULL;
/* add fresh user menuitems */
- build_user_menuitems (mgr);
+ if (!mgr->greeter_mode)
+ {
+ build_user_menuitems (mgr);
+ }
}
/***
-****
+**** Actions!
***/
static void
@@ -835,14 +950,15 @@ action_func_spawn_async (const char * fmt, ...)
va_start (marker, fmt);
gchar * cmd = g_strdup_vprintf (fmt, marker);
va_end (marker);
-
- GError * error = NULL;
- if (!g_spawn_command_line_async (cmd, &error))
+
+ GError * error = NULL;
+ g_spawn_command_line_async (cmd, &error);
+ if (error != NULL)
{
- g_warning ("Unable to show \"%s\": %s", cmd, error->message);
+ g_warning ("Unable to execute \"%s\": %s", cmd, error->message);
+ g_clear_error (&error);
}
- g_clear_error (&error);
g_free (cmd);
}
@@ -908,11 +1024,12 @@ action_func_switch_to_greeter (SessionMenuMgr * mgr)
}
static void
-action_func_switch_to_user (ActivateUserSessionData * data)
+action_func_switch_to_user (AccountsUser * user)
{
- action_func_lock (data->mgr);
- users_service_dbus_activate_user_session (data->mgr->users_dbus_facade,
- data->user);
+ SessionMenuMgr * mgr = user_get_mgr (user);
+ g_return_if_fail (mgr != NULL);
+ action_func_lock (mgr);
+ users_service_dbus_activate_user_session (mgr->users_dbus_facade, user);
}
static void
@@ -961,6 +1078,8 @@ action_func_hibernate (SessionMenuMgr * mgr)
static gboolean
is_this_guest_session (void)
{
+ /* FIXME: this test has been here awhile and seems to work,
+ but seems brittle to me */
return geteuid() < 500;
}
@@ -996,15 +1115,16 @@ get_switcher_mode (SessionMenuMgr * mgr)
{
mode = SWITCHER_MODE_SWITCH;
}
- else
+ else /* both locking & switching are allowed */
{
GList * l = users_service_dbus_get_user_list (mgr->users_dbus_facade);
const size_t user_count = g_list_length (l);
g_list_free (l);
- mode = user_count < 2
- ? SWITCHER_MODE_LOCK /* you can't switch if no other users */
- : SWITCHER_MODE_SWITCH_OR_LOCK;
+ /* only show switch mode if we have users to switch to */
+ mode = user_count > (is_this_guest_session() ? 0 : 1)
+ ? SWITCHER_MODE_SWITCH_OR_LOCK
+ : SWITCHER_MODE_LOCK;
}
return mode;
@@ -1026,7 +1146,7 @@ SessionMenuMgr* session_menu_mgr_new (DbusmenuMenuitem * parent_mi,
build_admin_menuitems (mgr);
const guint n = g_list_length (dbusmenu_menuitem_get_children (parent_mi));
mgr->user_menuitem_index = n;
- build_user_menuitems (mgr);
+ update_user_menuitems (mgr);
build_session_menuitems (mgr);
return mgr;
}