[gnome-session] shell: add support for shell logout/shutdown dialog



commit 1f9ed4829f56826b05c9d4f9fbab8fb7bca53e8c
Author: Ray Strode <rstrode redhat com>
Date:   Tue Oct 26 17:04:51 2010 -0400

    shell: add support for shell logout/shutdown dialog
    
    This commit changes gnome-session to use the shell for
    presenting the logout and shutdown dialogs.
    
    Coordination happens over the session bus.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=637188

 gnome-session/Makefile.am   |    2 +
 gnome-session/gsm-manager.c |  194 +++++++++++++--
 gnome-session/gsm-shell.c   |  569 +++++++++++++++++++++++++++++++++++++++++++
 gnome-session/gsm-shell.h   |   85 +++++++
 4 files changed, 828 insertions(+), 22 deletions(-)
---
diff --git a/gnome-session/Makefile.am b/gnome-session/Makefile.am
index 030052e..23855da 100644
--- a/gnome-session/Makefile.am
+++ b/gnome-session/Makefile.am
@@ -55,6 +55,8 @@ gnome_session_SOURCES =				\
 	gsm-session-fill.h			\
 	gsm-session-save.c			\
 	gsm-session-save.h			\
+	gsm-shell.c				\
+	gsm-shell.h				\
 	gsm-xsmp-server.c			\
 	gsm-xsmp-server.h
 
diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c
index 326d859..0198e12 100644
--- a/gnome-session/gsm-manager.c
+++ b/gnome-session/gsm-manager.c
@@ -48,6 +48,7 @@
 #include "gsm-store.h"
 #include "gsm-inhibitor.h"
 #include "gsm-presence.h"
+#include "gsm-shell.h"
 
 #include "gsm-xsmp-client.h"
 #include "gsm-dbus-client.h"
@@ -130,6 +131,11 @@ struct GsmManagerPrivate
 
         /* Interface with other parts of the system */
         UpClient               *up_client;
+
+        GsmShell               *shell;
+        guint                   shell_end_session_dialog_canceled_id;
+        guint                   shell_end_session_dialog_open_failed_id;
+        guint                   shell_end_session_dialog_confirmed_id;
 };
 
 enum {
@@ -164,7 +170,8 @@ static void     _handle_client_end_session_response (GsmManager *manager,
                                                      gboolean    do_last,
                                                      gboolean    cancel,
                                                      const char *reason);
-
+static void     show_shell_end_session_dialog (GsmManager                   *manager,
+                                               GsmShellEndSessionDialogType  type);
 static gpointer manager_object = NULL;
 
 G_DEFINE_TYPE (GsmManager, gsm_manager, G_TYPE_OBJECT)
@@ -1100,24 +1107,10 @@ inhibit_dialog_response (GsmInhibitDialog *dialog,
 }
 
 static void
-query_end_session_complete (GsmManager *manager)
+end_session_or_show_fallback_dialog (GsmManager *manager)
 {
         GsmLogoutAction action;
 
-        g_debug ("GsmManager: query end session complete");
-
-        /* Remove the timeout since this can be called from outside the timer
-         * and we don't want to have it called twice */
-        if (manager->priv->query_timeout_id > 0) {
-                g_source_remove (manager->priv->query_timeout_id);
-                manager->priv->query_timeout_id = 0;
-        }
-
-        if (! gsm_manager_is_logout_inhibited (manager)) {
-                end_phase (manager);
-                return;
-        }
-
         if (manager->priv->inhibit_dialog != NULL) {
                 g_debug ("GsmManager: inhibit dialog already up");
                 gtk_window_present (GTK_WINDOW (manager->priv->inhibit_dialog));
@@ -1145,6 +1138,11 @@ query_end_session_complete (GsmManager *manager)
                 break;
         }
 
+        if (! gsm_manager_is_logout_inhibited (manager)) {
+                end_phase (manager);
+                return;
+        }
+
         /* Note: GSM_LOGOUT_ACTION_SHUTDOWN and GSM_LOGOUT_ACTION_REBOOT are
          * actually handled the same way as GSM_LOGOUT_ACTION_LOGOUT in the
          * inhibit dialog; the action, if the button is clicked, will be to
@@ -1158,6 +1156,69 @@ query_end_session_complete (GsmManager *manager)
                           G_CALLBACK (inhibit_dialog_response),
                           manager);
         gtk_widget_show (manager->priv->inhibit_dialog);
+}
+
+static void
+end_session_or_show_shell_dialog (GsmManager *manager)
+{
+        GsmShellEndSessionDialogType type;
+
+        switch (manager->priv->logout_type) {
+        case GSM_MANAGER_LOGOUT_LOGOUT:
+                type = GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT;
+                break;
+        case GSM_MANAGER_LOGOUT_REBOOT:
+        case GSM_MANAGER_LOGOUT_REBOOT_INTERACT:
+        case GSM_MANAGER_LOGOUT_REBOOT_GDM:
+                type = GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART;
+                break;
+        case GSM_MANAGER_LOGOUT_SHUTDOWN:
+        case GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT:
+        case GSM_MANAGER_LOGOUT_SHUTDOWN_GDM:
+                type = GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN;
+                break;
+        default:
+                g_warning ("Unexpected logout type %d when creating end session dialog",
+                           manager->priv->logout_type);
+                type = GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT;
+                break;
+        }
+
+        if (! gsm_manager_is_logout_inhibited (manager)) {
+                gboolean logout_prompt;
+
+                logout_prompt = g_settings_get_boolean (manager->priv->settings,
+                                                        KEY_LOGOUT_PROMPT);
+                if (logout_prompt) {
+                        show_shell_end_session_dialog (manager, type);
+                        return;
+                } else {
+                        end_phase (manager);
+                }
+                return;
+        }
+
+        show_shell_end_session_dialog (manager, type);
+}
+
+static void
+query_end_session_complete (GsmManager *manager)
+{
+
+        g_debug ("GsmManager: query end session complete");
+
+        /* Remove the timeout since this can be called from outside the timer
+         * and we don't want to have it called twice */
+        if (manager->priv->query_timeout_id > 0) {
+                g_source_remove (manager->priv->query_timeout_id);
+                manager->priv->query_timeout_id = 0;
+        }
+
+        if (gsm_shell_is_running (manager->priv->shell)) {
+                end_session_or_show_shell_dialog (manager);
+        } else {
+                end_session_or_show_fallback_dialog (manager);
+        }
 
 }
 
@@ -2241,6 +2302,11 @@ gsm_manager_dispose (GObject *object)
                 manager->priv->up_client = NULL;
         }
 
+        if (manager->priv->shell != NULL) {
+                g_object_unref (manager->priv->shell);
+                manager->priv->shell = NULL;
+        }
+
         G_OBJECT_CLASS (gsm_manager_parent_class)->dispose (object);
 }
 
@@ -2421,6 +2487,8 @@ gsm_manager_init (GsmManager *manager)
                           G_CALLBACK (on_setting_changed),
                           manager);
 
+        manager->priv->shell = gsm_get_shell ();
+
         fetch_idle_delay_setting (manager);
 }
 
@@ -2810,7 +2878,7 @@ logout_dialog_response (GsmLogoutDialog *logout_dialog,
 }
 
 static void
-show_shutdown_dialog (GsmManager *manager)
+show_fallback_shutdown_dialog (GsmManager *manager)
 {
         GtkWidget *dialog;
 
@@ -2830,7 +2898,7 @@ show_shutdown_dialog (GsmManager *manager)
 }
 
 static void
-show_logout_dialog (GsmManager *manager)
+show_fallback_logout_dialog (GsmManager *manager)
 {
         GtkWidget *dialog;
 
@@ -2850,27 +2918,101 @@ show_logout_dialog (GsmManager *manager)
 }
 
 static void
+on_shell_end_session_dialog_canceled (GsmShell   *shell,
+                                      GsmManager *manager)
+{
+        cancel_end_session (manager);
+        manager->priv->shell_end_session_dialog_canceled_id = 0;
+
+        if (manager->priv->shell_end_session_dialog_canceled_id != 0) {
+                g_signal_handler_disconnect (manager->priv->shell,
+                                             manager->priv->shell_end_session_dialog_canceled_id);
+                manager->priv->shell_end_session_dialog_canceled_id = 0;
+        }
+
+        if (manager->priv->shell_end_session_dialog_confirmed_id != 0) {
+                g_signal_handler_disconnect (manager->priv->shell,
+                                             manager->priv->shell_end_session_dialog_confirmed_id);
+                manager->priv->shell_end_session_dialog_confirmed_id = 0;
+        }
+
+        if (manager->priv->shell_end_session_dialog_open_failed_id != 0) {
+                g_signal_handler_disconnect (manager->priv->shell,
+                                             manager->priv->shell_end_session_dialog_open_failed_id);
+                manager->priv->shell_end_session_dialog_open_failed_id = 0;
+        }
+}
+
+static void
+on_shell_end_session_dialog_confirmed (GsmShell   *shell,
+                                       GsmManager *manager)
+{
+        manager->priv->forceful_logout = TRUE;
+        end_phase (manager);
+
+        manager->priv->shell_end_session_dialog_confirmed_id = 0;
+}
+
+static void
+show_shell_end_session_dialog (GsmManager                   *manager,
+                               GsmShellEndSessionDialogType  type)
+{
+        if (!gsm_shell_is_running (manager->priv->shell))
+                return;
+
+        gsm_shell_open_end_session_dialog (manager->priv->shell,
+                                           type,
+                                           manager->priv->inhibitors,
+                                           manager->priv->clients);
+        if (manager->priv->shell_end_session_dialog_canceled_id != 0)
+                return;
+
+        manager->priv->shell_end_session_dialog_canceled_id =
+                g_signal_connect (manager->priv->shell,
+                                  "end-session-dialog-canceled",
+                                  G_CALLBACK (on_shell_end_session_dialog_canceled),
+                                  manager);
+
+        manager->priv->shell_end_session_dialog_open_failed_id =
+                g_signal_connect (manager->priv->shell,
+                                  "end-session-dialog-open-failed",
+                                  G_CALLBACK (on_shell_end_session_dialog_canceled),
+                                  manager);
+
+        manager->priv->shell_end_session_dialog_confirmed_id =
+                g_signal_connect (manager->priv->shell,
+                                  "end-session-dialog-confirmed",
+                                  G_CALLBACK (on_shell_end_session_dialog_confirmed),
+                                  manager);
+}
+
+static void
 user_logout (GsmManager *manager,
              gboolean    show_confirmation,
              gboolean    forceful_logout)
 {
         gboolean logout_prompt;
+        gboolean shell_running;
 
         if (manager->priv->phase >= GSM_MANAGER_PHASE_QUERY_END_SESSION) {
                 /* Already shutting down, nothing more to do */
                 return;
         }
 
+        shell_running = gsm_shell_is_running (manager->priv->shell);
         logout_prompt = g_settings_get_boolean (manager->priv->settings,
                                                 KEY_LOGOUT_PROMPT);
 
         /* Global settings overides input parameter in order to disable confirmation
-         * dialog accordingly. If we're shutting down, we always show the confirmation
+         * dialog accordingly.  For the shell, we never show the confirmation dialog,
+         * since the dialog is merged with the inhibit dialog.
+         *
+         * If we're shutting down and the shell isn't running, we show the confirmation
          * dialog */
-        logout_prompt = (logout_prompt && show_confirmation);
+        logout_prompt = (logout_prompt && show_confirmation && !shell_running);
 
         if (logout_prompt) {
-                show_logout_dialog (manager);
+                show_fallback_logout_dialog (manager);
         } else {
                 request_logout (manager, forceful_logout);
         }
@@ -2896,6 +3038,8 @@ gboolean
 gsm_manager_shutdown (GsmManager *manager,
                       GError    **error)
 {
+        gboolean shell_running;
+
         g_debug ("GsmManager: Shutdown called");
 
         g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE);
@@ -2908,7 +3052,13 @@ gsm_manager_shutdown (GsmManager *manager,
                 return FALSE;
         }
 
-        show_shutdown_dialog (manager);
+        shell_running = gsm_shell_is_running (manager->priv->shell);
+
+        if (!shell_running) {
+                show_fallback_shutdown_dialog (manager);
+        } else {
+                request_shutdown (manager);
+        }
 
         return TRUE;
 }
diff --git a/gnome-session/gsm-shell.c b/gnome-session/gsm-shell.c
new file mode 100644
index 0000000..0144e17
--- /dev/null
+++ b/gnome-session/gsm-shell.c
@@ -0,0 +1,569 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gi18n.h>
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "gsm-inhibitor.h"
+#include "gsm-shell.h"
+
+#define SHELL_NAME      "org.gnome.Shell"
+#define SHELL_PATH      "/org/gnome/Shell"
+#define SHELL_INTERFACE "org.gnome.Shell"
+
+#define SHELL_END_SESSION_DIALOG_PATH      "/org/gnome/SessionManager/EndSessionDialog"
+#define SHELL_END_SESSION_DIALOG_INTERFACE "org.gnome.SessionManager.EndSessionDialog"
+
+#define GSM_SHELL_DBUS_TYPE_G_OBJECT_PATH_ARRAY (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH))
+
+#define AUTOMATIC_ACTION_TIMEOUT 60
+
+#define GSM_SHELL_GET_PRIVATE(o)                                   \
+        (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_SHELL, GsmShellPrivate))
+
+struct _GsmShellPrivate
+{
+        DBusGConnection *bus_connection;
+        DBusGProxy      *bus_proxy;
+        DBusGProxy      *proxy;
+        DBusGProxy      *end_session_dialog_proxy;
+
+        guint32          is_running : 1;
+
+        DBusGProxyCall  *end_session_open_call;
+        GsmShellEndSessionDialogType end_session_dialog_type;
+};
+
+enum {
+        PROP_0,
+        PROP_IS_RUNNING
+};
+
+enum {
+        END_SESSION_DIALOG_OPENED = 0,
+        END_SESSION_DIALOG_OPEN_FAILED,
+        END_SESSION_DIALOG_CANCELED,
+        END_SESSION_DIALOG_CONFIRMED,
+        NUMBER_OF_SIGNALS
+};
+
+static guint signals[NUMBER_OF_SIGNALS] = { 0 };
+
+static void     gsm_shell_class_init   (GsmShellClass *klass);
+static void     gsm_shell_init         (GsmShell      *ck);
+static void     gsm_shell_finalize     (GObject            *object);
+
+static void     gsm_shell_disconnect_from_bus    (GsmShell      *shell);
+
+static DBusHandlerResult gsm_shell_bus_filter (DBusConnection *connection,
+                                                DBusMessage    *message,
+                                                void           *user_data);
+
+static void     gsm_shell_on_name_owner_changed (DBusGProxy *bus_proxy,
+                                                 const char *name,
+                                                 const char *prev_owner,
+                                                 const char *new_owner,
+                                                 GsmShell   *shell);
+
+G_DEFINE_TYPE (GsmShell, gsm_shell, G_TYPE_OBJECT);
+
+static void
+gsm_shell_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+        GsmShell *shell = GSM_SHELL (object);
+
+        switch (prop_id) {
+        case PROP_IS_RUNNING:
+                g_value_set_boolean (value,
+                                     shell->priv->is_running);
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
+                                                   prop_id,
+                                                   pspec);
+        }
+}
+
+static void
+gsm_shell_class_init (GsmShellClass *shell_class)
+{
+        GObjectClass *object_class;
+        GParamSpec   *param_spec;
+
+        object_class = G_OBJECT_CLASS (shell_class);
+
+        object_class->finalize = gsm_shell_finalize;
+        object_class->get_property = gsm_shell_get_property;
+
+        param_spec = g_param_spec_boolean ("is-running",
+                                           "Is running",
+                                           "Whether the session is running to ConsoleKit",
+                                           FALSE,
+                                           G_PARAM_READABLE);
+
+        g_object_class_install_property (object_class, PROP_IS_RUNNING,
+                                         param_spec);
+
+        signals [END_SESSION_DIALOG_OPENED] =
+                g_signal_new ("end-session-dialog-opened",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GsmShellClass, end_session_dialog_opened),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+
+        signals [END_SESSION_DIALOG_OPEN_FAILED] =
+                g_signal_new ("end-session-dialog-open-failed",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GsmShellClass, end_session_dialog_open_failed),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+
+        signals [END_SESSION_DIALOG_CANCELED] =
+                g_signal_new ("end-session-dialog-canceled",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GsmShellClass, end_session_dialog_canceled),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+
+        signals [END_SESSION_DIALOG_CONFIRMED] =
+                g_signal_new ("end-session-dialog-confirmed",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GsmShellClass, end_session_dialog_confirmed),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+
+        g_type_class_add_private (shell_class, sizeof (GsmShellPrivate));
+}
+
+static DBusHandlerResult
+gsm_shell_bus_filter (DBusConnection *connection,
+                       DBusMessage    *message,
+                       void           *user_data)
+{
+        GsmShell *shell;
+
+        shell = GSM_SHELL (user_data);
+
+        if (dbus_message_is_signal (message,
+                                    DBUS_INTERFACE_LOCAL, "Disconnected") &&
+            strcmp (dbus_message_get_path (message), DBUS_PATH_LOCAL) == 0) {
+                gsm_shell_disconnect_from_bus (shell);
+                return DBUS_HANDLER_RESULT_HANDLED;
+        }
+
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static gboolean
+gsm_shell_ensure_connection (GsmShell  *shell,
+                             GError   **error)
+{
+        GError  *connection_error;
+        gboolean is_running;
+
+        connection_error = NULL;
+
+        if (shell->priv->bus_connection == NULL) {
+                DBusConnection *connection;
+
+                shell->priv->bus_connection = dbus_g_bus_get (DBUS_BUS_SESSION,
+                                                               &connection_error);
+
+                if (shell->priv->bus_connection == NULL) {
+                        g_propagate_error (error, connection_error);
+                        is_running = FALSE;
+                        goto out;
+                }
+
+                connection = dbus_g_connection_get_connection (shell->priv->bus_connection);
+                dbus_connection_set_exit_on_disconnect (connection, FALSE);
+                dbus_connection_add_filter (connection,
+                                            gsm_shell_bus_filter,
+                                            shell, NULL);
+        }
+
+        if (shell->priv->bus_proxy == NULL) {
+                shell->priv->bus_proxy =
+                        dbus_g_proxy_new_for_name_owner (shell->priv->bus_connection,
+                                                         DBUS_SERVICE_DBUS,
+                                                         DBUS_PATH_DBUS,
+                                                         DBUS_INTERFACE_DBUS,
+                                                         &connection_error);
+
+                if (shell->priv->bus_proxy == NULL) {
+                        g_propagate_error (error, connection_error);
+                        is_running = FALSE;
+                        goto out;
+                }
+
+                dbus_g_proxy_add_signal (shell->priv->bus_proxy,
+                                         "NameOwnerChanged",
+                                         G_TYPE_STRING,
+                                         G_TYPE_STRING,
+                                         G_TYPE_STRING,
+                                         G_TYPE_INVALID);
+
+                dbus_g_proxy_connect_signal (shell->priv->bus_proxy,
+                                             "NameOwnerChanged",
+                                             G_CALLBACK (gsm_shell_on_name_owner_changed),
+                                             shell, NULL);
+        }
+
+        if (shell->priv->proxy == NULL) {
+                shell->priv->proxy =
+                        dbus_g_proxy_new_for_name_owner (shell->priv->bus_connection,
+                                                         SHELL_NAME,
+                                                         SHELL_PATH,
+                                                         SHELL_INTERFACE,
+                                                         &connection_error);
+
+                if (shell->priv->proxy == NULL) {
+                        g_propagate_error (error, connection_error);
+                        is_running = FALSE;
+                        goto out;
+                }
+        }
+
+        is_running = TRUE;
+
+ out:
+        if (shell->priv->is_running != is_running) {
+                shell->priv->is_running = is_running;
+                g_object_notify (G_OBJECT (shell), "is-running");
+        }
+
+        if (!is_running) {
+                if (shell->priv->bus_connection == NULL) {
+                        if (shell->priv->bus_proxy != NULL) {
+                                g_object_unref (shell->priv->bus_proxy);
+                                shell->priv->bus_proxy = NULL;
+                        }
+
+                        if (shell->priv->proxy != NULL) {
+                                g_object_unref (shell->priv->proxy);
+                                shell->priv->proxy = NULL;
+                        }
+                } else if (shell->priv->bus_proxy == NULL) {
+                        if (shell->priv->proxy != NULL) {
+                                g_object_unref (shell->priv->proxy);
+                                shell->priv->proxy = NULL;
+                        }
+                }
+        }
+
+        return is_running;
+}
+
+static void
+gsm_shell_on_name_owner_changed (DBusGProxy    *bus_proxy,
+                                 const char    *name,
+                                 const char    *prev_owner,
+                                 const char    *new_owner,
+                                 GsmShell      *shell)
+{
+        if (name != NULL && strcmp (name, SHELL_NAME) != 0) {
+                return;
+        }
+
+        if (shell->priv->proxy != NULL) {
+                g_object_unref (shell->priv->proxy);
+                shell->priv->proxy = NULL;
+        }
+
+        gsm_shell_ensure_connection (shell, NULL);
+}
+
+static void
+gsm_shell_init (GsmShell *shell)
+{
+        GError *error;
+
+        shell->priv = GSM_SHELL_GET_PRIVATE (shell);
+
+        error = NULL;
+
+        if (!gsm_shell_ensure_connection (shell, &error)) {
+                g_warning ("Could not connect to the shell: %s",
+                           error->message);
+                g_error_free (error);
+        }
+}
+
+static void
+gsm_shell_disconnect_from_bus (GsmShell *shell)
+{
+        if (shell->priv->bus_proxy != NULL) {
+                g_object_unref (shell->priv->bus_proxy);
+                shell->priv->bus_proxy = NULL;
+        }
+
+        if (shell->priv->proxy != NULL) {
+                g_object_unref (shell->priv->proxy);
+                shell->priv->proxy = NULL;
+        }
+
+        if (shell->priv->bus_connection != NULL) {
+                DBusConnection *connection;
+                connection = dbus_g_connection_get_connection (shell->priv->bus_connection);
+                dbus_connection_remove_filter (connection,
+                                               gsm_shell_bus_filter,
+                                               shell);
+
+                dbus_g_connection_unref (shell->priv->bus_connection);
+                shell->priv->bus_connection = NULL;
+        }
+}
+
+static void
+gsm_shell_finalize (GObject *object)
+{
+        GsmShell *shell;
+        GObjectClass  *parent_class;
+
+        shell = GSM_SHELL (object);
+
+        parent_class = G_OBJECT_CLASS (gsm_shell_parent_class);
+
+        gsm_shell_disconnect_from_bus (shell);
+
+        if (parent_class->finalize != NULL) {
+                parent_class->finalize (object);
+        }
+}
+
+GsmShell *
+gsm_shell_new (void)
+{
+        GsmShell *shell;
+
+        shell = g_object_new (GSM_TYPE_SHELL, NULL);
+
+        return shell;
+}
+
+GsmShell *
+gsm_get_shell (void)
+{
+        static GsmShell *shell = NULL;
+
+        if (shell == NULL) {
+                shell = gsm_shell_new ();
+        }
+
+        return g_object_ref (shell);
+}
+
+gboolean
+gsm_shell_is_running (GsmShell *shell)
+{
+        gsm_shell_ensure_connection (shell, NULL);
+
+        return shell->priv->is_running;
+}
+
+static gboolean
+add_inhibitor_to_array (const char   *id,
+                        GsmInhibitor *inhibitor,
+                        GPtrArray    *array)
+{
+
+        g_ptr_array_add (array,
+                         g_strdup (gsm_inhibitor_peek_id (inhibitor)));
+        return FALSE;
+}
+
+static GPtrArray *
+get_array_from_store (GsmStore *inhibitors)
+{
+        GPtrArray *array;
+
+        array = g_ptr_array_new ();
+
+        gsm_store_foreach (inhibitors,
+                           (GsmStoreFunc) add_inhibitor_to_array,
+                           array);
+
+        return array;
+}
+
+static void
+on_open_finished (DBusGProxy     *proxy,
+                  DBusGProxyCall *call,
+                  GsmShell       *shell)
+{
+        GError         *error;
+        gboolean        res;
+
+        g_assert (shell->priv->end_session_open_call == call);
+
+        error = NULL;
+        res = dbus_g_proxy_end_call (proxy,
+                                     call,
+                                     &error,
+                                     G_TYPE_INVALID);
+        shell->priv->end_session_open_call = NULL;
+
+        if (!res) {
+                g_warning ("Unable to open shell end session dialog");
+                g_signal_emit (G_OBJECT (shell), signals[END_SESSION_DIALOG_OPEN_FAILED], 0);
+                return;
+        }
+
+        g_signal_emit (G_OBJECT (shell), signals[END_SESSION_DIALOG_OPENED], 0);
+}
+
+static void
+on_end_session_dialog_canceled (DBusGProxy *proxy,
+                                GsmShell   *shell)
+{
+        g_signal_emit (G_OBJECT (shell), signals[END_SESSION_DIALOG_CANCELED], 0);
+}
+
+static void
+on_end_session_dialog_confirmed (DBusGProxy *proxy,
+                                 GsmShell   *shell)
+{
+        g_signal_emit (G_OBJECT (shell), signals[END_SESSION_DIALOG_CONFIRMED], 0);
+}
+
+static void
+on_end_session_dialog_proxy_destroyed (DBusGProxy *proxy,
+                                       GsmShell   *shell)
+{
+        if (shell->priv->end_session_dialog_proxy != NULL) {
+                g_object_unref (shell->priv->proxy);
+                shell->priv->end_session_dialog_proxy = NULL;
+        }
+}
+
+gboolean
+gsm_shell_open_end_session_dialog (GsmShell *shell,
+                                   GsmShellEndSessionDialogType type,
+                                   GsmStore *inhibitors,
+                                   GsmStore *clients)
+{
+        DBusGProxyCall  *call;
+        GPtrArray *inhibitor_array;
+        GError *error;
+
+        error = NULL;
+        if (!gsm_shell_ensure_connection (shell, &error)) {
+                g_warning ("Could not connect to the shell: %s",
+                           error->message);
+                g_error_free (error);
+                return FALSE;
+        }
+
+        if (shell->priv->end_session_open_call != NULL) {
+                g_return_val_if_fail (shell->priv->end_session_dialog_type == type,
+                                      FALSE);
+
+                return TRUE;
+        }
+
+        if (shell->priv->end_session_dialog_proxy == NULL) {
+                DBusGProxy *proxy;
+
+                proxy = dbus_g_proxy_new_for_name (shell->priv->bus_connection,
+                                                   SHELL_NAME,
+                                                   SHELL_END_SESSION_DIALOG_PATH,
+                                                   SHELL_END_SESSION_DIALOG_INTERFACE);
+
+                g_assert (proxy != NULL);
+
+                shell->priv->end_session_dialog_proxy = proxy;
+
+                g_signal_connect (proxy, "destroy",
+                                  G_CALLBACK (on_end_session_dialog_proxy_destroyed),
+                                  shell);
+
+                dbus_g_proxy_add_signal (shell->priv->end_session_dialog_proxy,
+                                         "Canceled", G_TYPE_INVALID);
+                dbus_g_proxy_connect_signal (shell->priv->end_session_dialog_proxy,
+                                             "Canceled",
+                                             G_CALLBACK (on_end_session_dialog_canceled),
+                                             shell, NULL);
+
+                dbus_g_proxy_add_signal (shell->priv->end_session_dialog_proxy,
+                                         "Confirmed", G_TYPE_INVALID);
+                dbus_g_proxy_connect_signal (shell->priv->end_session_dialog_proxy,
+                                             "Confirmed",
+                                             G_CALLBACK (on_end_session_dialog_confirmed),
+                                             shell, NULL);
+
+        }
+
+        inhibitor_array = get_array_from_store (inhibitors);
+
+        call = dbus_g_proxy_begin_call_with_timeout (shell->priv->end_session_dialog_proxy,
+                                                     "Open",
+                                                     (DBusGProxyCallNotify)
+                                                     on_open_finished,
+                                                     shell,
+                                                     NULL,
+                                                     INT_MAX,
+                                                     G_TYPE_UINT,
+                                                     (guint) type,
+                                                     G_TYPE_UINT,
+                                                     (guint) 0,
+                                                     G_TYPE_UINT,
+                                                     AUTOMATIC_ACTION_TIMEOUT,
+                                                     GSM_SHELL_DBUS_TYPE_G_OBJECT_PATH_ARRAY,
+                                                     inhibitor_array,
+                                                     G_TYPE_INVALID);
+        g_ptr_array_foreach (inhibitor_array, (GFunc) g_free, NULL);
+        g_ptr_array_free (inhibitor_array, TRUE);
+
+        if (call == NULL) {
+                g_warning ("Unable to make Open call");
+                return FALSE;
+        }
+
+        shell->priv->end_session_open_call = call;
+        shell->priv->end_session_dialog_type = type;
+
+        return TRUE;
+}
diff --git a/gnome-session/gsm-shell.h b/gnome-session/gsm-shell.h
new file mode 100644
index 0000000..7908696
--- /dev/null
+++ b/gnome-session/gsm-shell.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Authors:
+ *    Ray Strode <rstrode redhat com>
+ */
+
+#ifndef __GSM_SHELL_H__
+#define __GSM_SHELL_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "gsm-store.h"
+
+G_BEGIN_DECLS
+
+#define GSM_TYPE_SHELL             (gsm_shell_get_type ())
+#define GSM_SHELL(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_SHELL, GsmShell))
+#define GSM_SHELL_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_SHELL, GsmShellClass))
+#define GSM_IS_SHELL(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_SHELL))
+#define GSM_IS_SHELL_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_SHELL))
+#define GSM_SHELL_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj), GSM_TYPE_SHELL, GsmShellClass))
+#define GSM_SHELL_ERROR            (gsm_shell_error_quark ())
+
+typedef struct _GsmShell        GsmShell;
+typedef struct _GsmShellClass   GsmShellClass;
+typedef struct _GsmShellPrivate GsmShellPrivate;
+
+typedef enum
+{
+    GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT = 0,
+    GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN,
+    GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART,
+} GsmShellEndSessionDialogType;
+
+struct _GsmShell
+{
+        GObject               parent;
+
+        GsmShellPrivate *priv;
+};
+
+struct _GsmShellClass
+{
+        GObjectClass parent_class;
+
+        void (* end_session_dialog_opened)        (GsmShell *shell);
+        void (* end_session_dialog_open_failed)   (GsmShell *shell);
+        void (* end_session_dialog_canceled)      (GsmShell *shell);
+        void (* end_session_dialog_confirmed)     (GsmShell *shell);
+
+};
+
+GType            gsm_shell_get_type           (void);
+
+GsmShell        *gsm_shell_new                (void);
+
+GsmShell        *gsm_get_shell                (void);
+gboolean         gsm_shell_is_running         (GsmShell *shell);
+
+gboolean         gsm_shell_open_end_session_dialog (GsmShell *shell,
+                                                    GsmShellEndSessionDialogType type,
+                                                    GsmStore *inhibitors,
+                                                    GsmStore *clients);
+
+G_END_DECLS
+
+#endif /* __GSM_SHELL_H__ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]