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.c889
1 files changed, 889 insertions, 0 deletions
diff --git a/src/session-menu-mgr.c b/src/session-menu-mgr.c
new file mode 100644
index 0000000..0fdaeb5
--- /dev/null
+++ b/src/session-menu-mgr.c
@@ -0,0 +1,889 @@
+/*
+Copyright 2011 Canonical Ltd.
+
+Authors:
+ 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
+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
+PURPOSE. See the GNU General Public License for more details.
+
+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 <glib.h>
+#include <gio/gio.h>
+
+#include <libdbusmenu-glib/client.h>
+#include <libdbusmenu-gtk/menuitem.h>
+
+#include "dbus-shared-names.h"
+#include "dbus-upower.h"
+#include "lock-helper.h"
+#include "session-menu-mgr.h"
+#include "settings-helper.h"
+#include "users-service-dbus.h"
+
+#define UP_ADDRESS "org.freedesktop.UPower"
+#define UP_OBJECT "/org/freedesktop/UPower"
+#define UP_INTERFACE "org.freedesktop.UPower"
+
+#define EXTRA_LAUNCHER_DIR "/usr/share/indicators/session/applications"
+
+struct _SessionMenuMgr
+{
+ GObject parent_instance;
+ DbusmenuMenuitem * parent_mi;
+ SessionDbus* session_dbus_interface;
+
+ GSettings *lockdown_settings;
+ GSettings * keybinding_settings;
+
+ DbusmenuMenuitem * hibernate_mi;
+ DbusmenuMenuitem * suspend_mi;
+ DbusmenuMenuitem * lock_mi;
+
+ gboolean can_hibernate;
+ gboolean can_suspend;
+ gboolean allow_hibernate;
+ gboolean allow_suspend;
+
+ IndicatorSessionUPower * upower_proxy;
+ GCancellable * cancellable;
+
+ UsersServiceDbus* users_dbus_interface;
+ DbusmenuMenuitem * guest_mi;
+ gboolean greeter_mode;
+};
+
+static void setup_up (SessionMenuMgr* self);
+static void session_menu_mgr_rebuild_items (SessionMenuMgr *self);
+static void screensaver_keybinding_changed (GSettings*, const gchar*, gpointer);
+
+static void activate_new_session (DbusmenuMenuitem * mi,
+ guint timestamp,
+ gpointer user_data);
+static void activate_user_session (DbusmenuMenuitem *mi,
+ guint timestamp,
+ gpointer user_data);
+static void activate_user_accounts (DbusmenuMenuitem *mi,
+ guint timestamp,
+ gpointer user_data);
+static gint compare_users_by_username (gconstpointer a,
+ gconstpointer b);
+static void activate_user_accounts (DbusmenuMenuitem *mi,
+ guint timestamp,
+ gpointer user_data);
+static void menu_mgr_rebuild_users (SessionMenuMgr *self);
+static void on_user_list_changed (UsersServiceDbus *service,
+ SessionMenuMgr * umm);
+
+static void on_guest_logged_in_changed (UsersServiceDbus * users_dbus,
+ SessionMenuMgr * self);
+
+static void on_user_logged_in_changed (UsersServiceDbus * users_dbus,
+ AccountsUser * user,
+ SessionMenuMgr * self);
+static gboolean is_this_guest_session (void);
+static void activate_guest_session (DbusmenuMenuitem * mi,
+ guint timestamp,
+ gpointer user_data);
+
+
+
+G_DEFINE_TYPE (SessionMenuMgr, session_menu_mgr, G_TYPE_OBJECT);
+
+static void
+session_menu_mgr_init (SessionMenuMgr *self)
+{
+ self->can_hibernate = TRUE;
+ self->can_suspend = TRUE;
+ self->allow_hibernate = TRUE;
+ self->allow_suspend = TRUE;
+
+ self->lockdown_settings = g_settings_new (LOCKDOWN_SCHEMA);
+ g_signal_connect_swapped (self->lockdown_settings, "changed::" LOCKDOWN_KEY_USER, G_CALLBACK(session_menu_mgr_rebuild_items), self);
+ g_signal_connect_swapped (self->lockdown_settings, "changed::" LOCKDOWN_KEY_SCREENSAVER, G_CALLBACK(session_menu_mgr_rebuild_items), self);
+
+ self->keybinding_settings = g_settings_new (KEYBINDING_SCHEMA);
+ g_signal_connect (self->keybinding_settings, "changed::" KEY_LOCK_SCREEN, G_CALLBACK(screensaver_keybinding_changed), self);
+
+ setup_up(self);
+
+ /* users */
+ self->users_dbus_interface = g_object_new (USERS_SERVICE_DBUS_TYPE, NULL);
+ g_signal_connect (self->users_dbus_interface, "user-list-changed",
+ G_CALLBACK (on_user_list_changed), self);
+ g_signal_connect (self->users_dbus_interface, "user-logged-in-changed",
+ G_CALLBACK(on_user_logged_in_changed), self);
+ g_signal_connect (self->users_dbus_interface, "guest-logged-in-changed",
+ G_CALLBACK(on_guest_logged_in_changed), self);
+
+ g_idle_add(lock_screen_setup, NULL);
+}
+
+static void
+session_menu_mgr_dispose (GObject *object)
+{
+ SessionMenuMgr * self = SESSION_MENU_MGR (object);
+ g_clear_object (&self->lockdown_settings);
+ g_clear_object (&self->keybinding_settings);
+ g_clear_object (&self->upower_proxy);
+ g_clear_object (&self->cancellable);
+ g_clear_object (&self->users_dbus_interface);
+
+ 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);
+}
+
+static void
+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;
+}
+
+/***
+****
+***/
+
+static void
+update_screensaver_shortcut (DbusmenuMenuitem * menuitem, GSettings * settings)
+{
+ if (menuitem != NULL)
+ {
+ gchar * val = g_settings_get_string (settings, KEY_LOCK_SCREEN);
+ g_debug ("Keybinding changed to: %s", val);
+ dbusmenu_menuitem_property_set_shortcut_string (menuitem, val);
+ g_free (val);
+ }
+}
+
+static void
+screensaver_keybinding_changed (GSettings * settings,
+ const gchar * key G_GNUC_UNUSED,
+ gpointer userdata)
+{
+ update_screensaver_shortcut (SESSION_MENU_MGR(userdata)->lock_mi, settings);
+}
+
+
+static void
+machine_sleep_from_suspend (DbusmenuMenuitem * mi G_GNUC_UNUSED,
+ guint timestamp G_GNUC_UNUSED,
+ gpointer userdata)
+{
+ GError * error = NULL;
+ SessionMenuMgr * mgr = SESSION_MENU_MGR (userdata);
+
+ indicator_session_upower_call_suspend_sync (mgr->upower_proxy, mgr->cancellable, &error);
+ if (error != NULL)
+ {
+ g_warning ("%s: %s", G_STRFUNC, error->message);
+ g_clear_error (&error);
+ }
+}
+
+static void
+machine_sleep_from_hibernate (DbusmenuMenuitem * mi G_GNUC_UNUSED,
+ guint timestamp G_GNUC_UNUSED,
+ gpointer userdata)
+{
+ GError * error = NULL;
+ SessionMenuMgr * mgr = SESSION_MENU_MGR (userdata);
+
+ indicator_session_upower_call_hibernate_sync (mgr->upower_proxy, mgr->cancellable, &error);
+ if (error != NULL)
+ {
+ g_warning ("%s: %s", G_STRFUNC, error->message);
+ g_clear_error (&error);
+ }
+}
+
+/***
+****
+***/
+
+/* When allow-suspend changes, rebuild the menus */
+static void
+allowed_suspend_cb (GObject * source, GAsyncResult * res, gpointer userdata)
+{
+ gboolean allowed;
+ GError * error = NULL;
+ SessionMenuMgr * mgr = SESSION_MENU_MGR (userdata);
+
+ indicator_session_upower_call_suspend_allowed_finish (mgr->upower_proxy, &allowed, res, &error);
+ if (error != NULL)
+ {
+ g_warning ("%s: %s", G_STRFUNC, error->message);
+ g_error_free (error);
+ }
+ else if (mgr->allow_suspend != allowed)
+ {
+ mgr->allow_suspend = allowed;
+ session_menu_mgr_rebuild_items (mgr);
+ }
+}
+
+/* When allow-hibernate changes, rebuild the menus */
+static void
+allowed_hibernate_cb (GObject * source, GAsyncResult * res, gpointer userdata)
+{
+ gboolean allowed;
+ GError * error = NULL;
+ SessionMenuMgr * mgr = SESSION_MENU_MGR (userdata);
+
+ indicator_session_upower_call_hibernate_allowed_finish (mgr->upower_proxy, &allowed, res, &error);
+ if (error != NULL)
+ {
+ g_warning ("%s: %s", G_STRFUNC, error->message);
+ g_error_free (error);
+ }
+ else if (mgr->allow_hibernate != allowed)
+ {
+ mgr->allow_hibernate = allowed;
+ session_menu_mgr_rebuild_items (mgr);
+ }
+}
+
+
+static void
+on_upower_properties_changed (IndicatorSessionUPower * upower_proxy, SessionMenuMgr * mgr)
+{
+ gboolean b;
+ gboolean refresh = FALSE;
+
+ /* suspend */
+ b = indicator_session_upower_get_can_suspend (upower_proxy);
+ if (mgr->can_suspend != b)
+ {
+ mgr->can_suspend = b;
+ refresh = TRUE;
+ }
+
+ /* hibernate */
+ b = indicator_session_upower_get_can_hibernate (upower_proxy);
+ if (mgr->can_hibernate != b)
+ {
+ mgr->can_hibernate = b;
+ refresh = TRUE;
+ }
+
+ if (refresh)
+ {
+ session_menu_mgr_rebuild_items (mgr);
+ }
+}
+
+/* This function goes through and sets up what we need for DKp checking.
+ We're even setting up the calls for the props we need */
+static void
+setup_up (SessionMenuMgr* self)
+{
+ self->cancellable = g_cancellable_new ();
+
+ GError * error = NULL;
+ self->upower_proxy = indicator_session_upower_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+ 0,
+ UP_ADDRESS,
+ UP_OBJECT,
+ NULL,
+ &error);
+ if (error != NULL)
+ {
+ g_warning ("Error creating cups notify handler: %s", error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ /* Check to see if these are getting blocked by PolicyKit */
+ indicator_session_upower_call_suspend_allowed (self->upower_proxy, self->cancellable, allowed_suspend_cb, self);
+ indicator_session_upower_call_hibernate_allowed (self->upower_proxy, self->cancellable, allowed_hibernate_cb, self);
+
+ g_signal_connect (self->upower_proxy, "changed", G_CALLBACK(on_upower_properties_changed), self);
+
+ /* trigger an initial "changed" event */
+ on_upower_properties_changed (self->upower_proxy, self);
+ }
+
+
+}
+
+static void
+spawn_command_line_async (const char * fmt, ...)
+{
+ va_list marker;
+ 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))
+ {
+ g_warning ("Unable to show \"%s\": %s", cmd, error->message);
+ }
+
+ g_clear_error (&error);
+ g_free (cmd);
+}
+
+
+/* This is the function to show a dialog on actions that
+ can destroy data. Currently it just calls the GTK version
+ but it seems that in the future it should figure out
+ what's going on and something better. */
+static void
+show_dialog (DbusmenuMenuitem * mi, guint timestamp, gchar * type)
+{
+#ifdef HAVE_GTKLOGOUTHELPER
+ gchar * helper = g_build_filename(LIBEXECDIR, "gtk-logout-helper", NULL);
+#else
+ gchar * helper = g_build_filename("gnome-session-quit", NULL);
+#endif /* HAVE_GTKLOGOUTHELPER */
+ spawn_command_line_async ("%s --%s", helper, type);
+ g_free (helper);
+}
+
+static void
+session_menu_mgr_build_static_items (SessionMenuMgr* self, gboolean greeter_mode)
+{
+ const char * name;
+ DbusmenuMenuitem * mi;
+ DbusmenuMenuitem * logout_mi = NULL;
+ DbusmenuMenuitem * shutdown_mi = NULL;
+
+ /***
+ **** Admin items
+ ***/
+
+ name = _("About This Computer");
+ mi = dbusmenu_menuitem_new ();
+ dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_LABEL, name);
+ dbusmenu_menuitem_child_append (self->parent_mi, mi);
+
+ g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+ G_CALLBACK(spawn_command_line_async), "gnome-control-center info");
+
+ name = _("Ubuntu Help");
+ mi = dbusmenu_menuitem_new ();
+ dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_LABEL, name);
+ dbusmenu_menuitem_child_append (self->parent_mi, mi);
+ g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+ G_CALLBACK(spawn_command_line_async), "yelp");
+
+ if (!greeter_mode)
+ {
+ mi = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR);
+ dbusmenu_menuitem_child_append (self->parent_mi, mi);
+
+ name = _("System Settings…");
+ mi = dbusmenu_menuitem_new ();
+ dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_LABEL, name);
+ dbusmenu_menuitem_child_append (self->parent_mi, mi);
+ g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+ G_CALLBACK(spawn_command_line_async), "gnome-control-center");
+ }
+
+ mi = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR);
+ dbusmenu_menuitem_child_append (self->parent_mi, mi);
+
+ /***
+ **** Account-switching items
+ ***/
+
+ /* TODO: FIXME */
+
+ const gboolean can_lockscreen = !g_settings_get_boolean (self->lockdown_settings, LOCKDOWN_KEY_SCREENSAVER);
+ if (can_lockscreen)
+ {
+ name = _("Lock Screen");
+ self->lock_mi = mi = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_LABEL, name);
+ update_screensaver_shortcut (mi, self->keybinding_settings);
+ dbusmenu_menuitem_child_append (self->parent_mi, mi);
+ g_signal_connect (G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+ G_CALLBACK(lock_screen), NULL);
+ }
+
+ /***
+ **** Session Items
+ ***/
+
+ mi = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR);
+ dbusmenu_menuitem_child_append (self->parent_mi, mi);
+
+ if (!greeter_mode)
+ {
+ name = supress_confirmations() ? _("Log Out") : _("Log Out\342\200\246");
+ logout_mi = mi = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_LABEL, name);
+ dbusmenu_menuitem_property_set_bool (mi, DBUSMENU_MENUITEM_PROP_VISIBLE, show_logout());
+ dbusmenu_menuitem_child_append(self->parent_mi, mi);
+ g_signal_connect (G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+ G_CALLBACK(show_dialog), "logout");
+ }
+
+ if (self->can_suspend && self->allow_suspend)
+ {
+ name = _("Suspend");
+ self->suspend_mi = mi = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_LABEL, name);
+ dbusmenu_menuitem_child_append (self->parent_mi, mi);
+ g_signal_connect (G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+ G_CALLBACK(machine_sleep_from_suspend), self);
+ }
+
+ if (self->can_hibernate && self->allow_hibernate)
+ {
+ name = _("Hibernate");
+ self->hibernate_mi = mi = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_LABEL, name);
+ dbusmenu_menuitem_child_append(self->parent_mi, mi);
+ g_signal_connect (G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+ G_CALLBACK(machine_sleep_from_hibernate), self);
+ }
+
+ name = _("Restart");
+ mi = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_LABEL, name);
+ dbusmenu_menuitem_child_append (self->parent_mi, mi);
+ /* FIXME: not implemented */
+
+ name = supress_confirmations() ? _("Shut Down") : _("Shut Down\342\200\246");
+ shutdown_mi = mi = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_LABEL, name);
+ dbusmenu_menuitem_property_set_bool (mi, DBUSMENU_MENUITEM_PROP_VISIBLE, show_shutdown());
+ dbusmenu_menuitem_child_append (self->parent_mi, mi);
+ g_signal_connect (G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+#ifdef HAVE_GTKLOGOUTHELPER
+ G_CALLBACK(show_dialog), "shutdown");
+#else
+ G_CALLBACK(show_dialog), "power-off");
+#endif /* HAVE_GTKLOGOUTHELPER */
+
+ RestartShutdownLogoutMenuItems * restart_shutdown_logout_mi = g_new0 (RestartShutdownLogoutMenuItems, 1);
+ restart_shutdown_logout_mi->logout_mi = logout_mi;
+ restart_shutdown_logout_mi->shutdown_mi = shutdown_mi;
+
+ update_menu_entries(restart_shutdown_logout_mi);
+}
+
+static void
+session_menu_mgr_rebuild_items (SessionMenuMgr* self)
+{
+ dbusmenu_menuitem_property_set_bool (self->hibernate_mi,
+ DBUSMENU_MENUITEM_PROP_VISIBLE,
+ self->can_hibernate && self->allow_hibernate);
+ dbusmenu_menuitem_property_set_bool (self->suspend_mi,
+ DBUSMENU_MENUITEM_PROP_VISIBLE,
+ self->can_suspend && self->allow_suspend);
+}
+
+/***
+**** User Menu
+***/
+
+struct ActivateUserSessionData
+{
+ SessionMenuMgr * menu_mgr;
+ AccountsUser * user;
+};
+
+/***
+****
+***/
+
+static GQuark
+get_menuitem_quark (void)
+{
+ static GQuark q = 0;
+
+ if (G_UNLIKELY(!q))
+ {
+ q = g_quark_from_static_string ("menuitem");
+ }
+
+ return q;
+}
+
+static DbusmenuMenuitem*
+user_get_menuitem (AccountsUser * user)
+{
+ return g_object_get_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);
+}
+
+static GQuark
+get_name_collision_quark (void)
+{
+ static GQuark q = 0;
+
+ if (G_UNLIKELY(!q))
+ {
+ q = g_quark_from_static_string ("name-collision");
+ }
+
+ return q;
+}
+
+static gboolean
+get_user_name_collision (AccountsUser * user)
+{
+ return g_object_get_qdata (G_OBJECT(user), get_name_collision_quark()) != NULL;
+}
+
+static void
+set_user_name_collision (AccountsUser * user, gboolean b)
+{
+ g_object_set_qdata (G_OBJECT(user), get_name_collision_quark(), GINT_TO_POINTER(b));
+}
+
+/***
+****
+***/
+
+static void
+update_menuitem_icon (DbusmenuMenuitem * mi, AccountsUser * user)
+{
+ const gchar * str = accounts_user_get_icon_file (user);
+
+ if (!str || !*str)
+ {
+ str = USER_ITEM_ICON_DEFAULT;
+ }
+
+ dbusmenu_menuitem_property_set (mi, USER_ITEM_PROP_ICON, str);
+}
+
+static void
+on_user_icon_file_changed (AccountsUser * user, GParamSpec * pspec, DbusmenuMenuitem * mi)
+{
+ update_menuitem_icon (mi, user);
+}
+
+static DbusmenuMenuitem*
+create_user_menuitem (SessionMenuMgr * menu_mgr, AccountsUser * user)
+{
+ DbusmenuMenuitem * mi = dbusmenu_menuitem_new ();
+ dbusmenu_menuitem_property_set (mi,
+ DBUSMENU_MENUITEM_PROP_TYPE,
+ USER_ITEM_TYPE);
+
+ /* set the name property */
+ const gchar * const real_name = accounts_user_get_real_name (user);
+ const gchar * const user_name = accounts_user_get_user_name (user);
+ char * str = get_user_name_collision (user)
+ ? g_strdup_printf ("%s (%s)", real_name, user_name)
+ : g_strdup (real_name);
+ dbusmenu_menuitem_property_set (mi, USER_ITEM_PROP_NAME, str);
+ g_free (str);
+
+ /* set the logged-in property */
+ const gboolean is_logged_in = users_service_dbus_is_user_logged_in (menu_mgr->users_dbus_interface, user);
+ dbusmenu_menuitem_property_set_bool (mi,
+ USER_ITEM_PROP_LOGGED_IN,
+ is_logged_in);
+
+ /* set the icon property */
+ update_menuitem_icon (mi, user);
+ g_signal_connect (user, "notify::icon-file", G_CALLBACK(on_user_icon_file_changed), mi);
+
+ /* set the is-current-user property */
+ dbusmenu_menuitem_property_set_bool (mi,
+ USER_ITEM_PROP_IS_CURRENT_USER,
+ !g_strcmp0 (user_name, g_get_user_name()));
+
+ /* set the activate callback */
+ struct ActivateUserSessionData * data = g_new (struct ActivateUserSessionData, 1);
+ data->user = user;
+ data->menu_mgr = menu_mgr;
+ g_signal_connect_data (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+ G_CALLBACK (activate_user_session),
+ data, (GClosureNotify)g_free,
+ 0);
+
+ /* give this AccountsUser a hook back to this menuitem */
+ user_set_menuitem (user, mi);
+
+ /* done */
+ return mi;
+}
+
+static void
+on_guest_logged_in_changed (UsersServiceDbus * users_service_dbus,
+ SessionMenuMgr * self)
+{
+ if (self->guest_mi != NULL)
+ {
+ const gboolean b = users_service_dbus_is_guest_logged_in (users_service_dbus);
+ dbusmenu_menuitem_property_set_bool (self->guest_mi, USER_ITEM_PROP_LOGGED_IN, b);
+ }
+}
+
+/* When a user's login state changes,
+ update the corresponding menuitem's LOGGED_IN property */
+static void
+on_user_logged_in_changed (UsersServiceDbus * users_service_dbus,
+ AccountsUser * user,
+ SessionMenuMgr * self)
+{
+ DbusmenuMenuitem * mi = user_get_menuitem (user);
+
+ if (mi != NULL)
+ {
+ const gboolean b = users_service_dbus_is_user_logged_in (users_service_dbus, user);
+ dbusmenu_menuitem_property_set_bool (mi, USER_ITEM_PROP_LOGGED_IN, b);
+ }
+}
+
+/* Builds up the menu for us */
+static void
+menu_mgr_rebuild_users (SessionMenuMgr *self)
+{
+ GList *u;
+ gboolean can_activate;
+
+ /* Check to see which menu items we're allowed to have */
+ can_activate = users_service_dbus_can_activate_session (self->users_dbus_interface) &&
+ !g_settings_get_boolean (self->lockdown_settings, LOCKDOWN_KEY_USER);
+
+ /* Remove the old menu items if that makes sense */
+#warning FIXME
+#if 0
+ GList * children = dbusmenu_menuitem_take_children (self->root_item);
+ g_list_foreach (children, (GFunc)g_object_unref, NULL);
+ g_list_free (children);
+#endif
+
+ /* Set to NULL in case we don't end up building one */
+ self->guest_mi = NULL;
+
+ /* Build all of the user switching items */
+ if (can_activate)
+ {
+ /* TODO: This needs to be updated once the ability to query guest session support is available */
+ GList * users = users_service_dbus_get_user_list (self->users_dbus_interface);
+ const gboolean guest_enabled = users_service_dbus_guest_session_enabled (self->users_dbus_interface);
+
+ /* TODO we should really return here if the menu is not going to be shown. */
+
+ DbusmenuMenuitem * switch_menuitem = dbusmenu_menuitem_new ();
+/*
+ dbusmenu_menuitem_property_set (switch_menuitem,
+ DBUSMENU_MENUITEM_PROP_TYPE,
+ MENU_SWITCH_TYPE);
+*/
+ dbusmenu_menuitem_property_set (switch_menuitem,
+ USER_ITEM_PROP_NAME,
+ _("Switch User Account…"));
+ dbusmenu_menuitem_child_append (self->parent_mi, switch_menuitem);
+g_message ("appending Switch button to %p", self->parent_mi);
+ g_signal_connect (G_OBJECT (switch_menuitem),
+ DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+ G_CALLBACK (activate_new_session),
+ self);
+
+ if ( !is_this_guest_session () && guest_enabled)
+ {
+ self->guest_mi = dbusmenu_menuitem_new ();
+ dbusmenu_menuitem_property_set (self->guest_mi,
+ DBUSMENU_MENUITEM_PROP_TYPE,
+ USER_ITEM_TYPE);
+ dbusmenu_menuitem_property_set (self->guest_mi,
+ USER_ITEM_PROP_NAME,
+ _("Guest Session"));
+ on_guest_logged_in_changed (self->users_dbus_interface, self);
+ dbusmenu_menuitem_child_append (self->parent_mi, self->guest_mi);
+ g_signal_connect (G_OBJECT (self->guest_mi),
+ DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+ G_CALLBACK (activate_guest_session),
+ self);
+ }
+ else{
+ session_dbus_set_users_real_name (self->session_dbus_interface,
+ _("Guest"));
+ }
+
+
+
+ users = g_list_sort (users, compare_users_by_username);
+
+ for (u = users; u != NULL; u = g_list_next (u))
+ {
+ AccountsUser * user = u->data;
+
+ DbusmenuMenuitem * mi = create_user_menuitem (self, user);
+ dbusmenu_menuitem_child_append (self->parent_mi, mi);
+
+ const char * const user_name = accounts_user_get_user_name (user);
+ if (!g_strcmp0 (user_name, g_get_user_name()))
+ {
+ const char * const real_name = accounts_user_get_real_name (user);
+ session_dbus_set_users_real_name (self->session_dbus_interface, real_name);
+ }
+ }
+
+ g_list_free(users);
+ }
+
+ // Add the user accounts and separator
+ DbusmenuMenuitem * separator1 = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set (separator1,
+ DBUSMENU_MENUITEM_PROP_TYPE,
+ DBUSMENU_CLIENT_TYPES_SEPARATOR);
+ dbusmenu_menuitem_child_append (self->parent_mi, separator1);
+
+ DbusmenuMenuitem * user_accounts_item = dbusmenu_menuitem_new();
+ dbusmenu_menuitem_property_set (user_accounts_item,
+ DBUSMENU_MENUITEM_PROP_TYPE,
+ DBUSMENU_CLIENT_TYPES_DEFAULT);
+ dbusmenu_menuitem_property_set (user_accounts_item,
+ DBUSMENU_MENUITEM_PROP_LABEL,
+ _("User Accounts…"));
+
+ g_signal_connect (G_OBJECT (user_accounts_item),
+ DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
+ G_CALLBACK (activate_user_accounts),
+ NULL);
+
+ dbusmenu_menuitem_child_append (self->parent_mi, user_accounts_item);
+
+
+}
+
+/* Check to see if the lockdown key is protecting from
+ locking the screen. If not, lock it. */
+static void
+lock_if_possible (SessionMenuMgr * menu_mgr)
+{
+ if (!g_settings_get_boolean (menu_mgr->lockdown_settings, LOCKDOWN_KEY_SCREENSAVER))
+ {
+ lock_screen(NULL, 0, NULL);
+ }
+}
+
+/* Starts a new generic session */
+static void
+activate_new_session (DbusmenuMenuitem * mi, guint timestamp, gpointer user_data)
+{
+ SessionMenuMgr * menu_mgr = SESSION_MENU_MGR (user_data);
+ g_return_if_fail (menu_mgr != NULL);
+
+ lock_if_possible (menu_mgr);
+ users_service_dbus_show_greeter (menu_mgr->users_dbus_interface);
+}
+
+/* Activates a session for a particular user. */
+static void
+activate_user_session (DbusmenuMenuitem *mi, guint timestamp, gpointer user_data)
+{
+ struct ActivateUserSessionData * data = user_data;
+
+ lock_if_possible (data->menu_mgr);
+ users_service_dbus_activate_user_session (data->menu_mgr->users_dbus_interface, data->user);
+}
+
+/* Comparison function to look into the UserData struct
+ to compare by using the username value */
+static gint
+compare_users_by_username (gconstpointer ga, gconstpointer gb)
+{
+ AccountsUser * a = ACCOUNTS_USER(ga);
+ AccountsUser * b = ACCOUNTS_USER(gb);
+
+ const int ret = g_strcmp0 (accounts_user_get_real_name (a),
+ accounts_user_get_real_name (b));
+
+ if (!ret) /* names are the same, so both have a name collision */
+ {
+ set_user_name_collision (a, TRUE);
+ set_user_name_collision (b, TRUE);
+ }
+
+ return ret;
+}
+
+static void
+activate_user_accounts (DbusmenuMenuitem *mi,
+ guint timestamp,
+ gpointer user_data)
+{
+ GError * error = NULL;
+ if (!g_spawn_command_line_async("gnome-control-center user-accounts", &error))
+ {
+ g_warning("Unable to show control centre: %s", error->message);
+ g_error_free(error);
+ }
+}
+
+/* Signal called when a user is added.
+ It updates the count and rebuilds the menu */
+static void
+on_user_list_changed (UsersServiceDbus * service G_GNUC_UNUSED,
+ SessionMenuMgr * menu_mgr)
+{
+ g_return_if_fail (IS_SESSION_MENU_MGR(menu_mgr));
+
+ menu_mgr_rebuild_users (menu_mgr);
+}
+
+
+/* Checks to see if we should show the guest session item.
+ System users shouldn't have guest account shown.
+ Mostly this would be the case of the guest user itself. */
+static gboolean
+is_this_guest_session (void)
+{
+ return geteuid() < 500;
+}
+
+/* Called when someone clicks on the guest session item. */
+static void
+activate_guest_session (DbusmenuMenuitem * mi, guint timestamp, gpointer user_data)
+{
+ SessionMenuMgr * menu_mgr = SESSION_MENU_MGR (user_data);
+ g_return_if_fail (menu_mgr != NULL);
+
+ lock_if_possible (menu_mgr);
+ users_service_dbus_activate_guest_session (menu_mgr->users_dbus_interface);
+}
+
+/***
+****
+***/
+
+SessionMenuMgr* session_menu_mgr_new (DbusmenuMenuitem * parent_mi,
+ SessionDbus * session_dbus,
+ gboolean greeter_mode)
+{
+ SessionMenuMgr* menu_mgr = g_object_new (SESSION_TYPE_MENU_MGR, NULL);
+ menu_mgr->parent_mi = parent_mi;
+ menu_mgr->greeter_mode = greeter_mode;
+ menu_mgr->session_dbus_interface = session_dbus;
+ session_menu_mgr_build_static_items (menu_mgr, greeter_mode);
+ menu_mgr_rebuild_users (menu_mgr);
+ return menu_mgr;
+}