[gtk+/wip/session: 1/6] GtkApplication: Add support for session management



commit eec5e34c31ee9637d83f25f6f21993d5b83d82b5
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue Jan 3 14:52:29 2012 -0500

    GtkApplication: Add support for session management
    
    The support is fairly basic, allowing applications to learn when
    the session manager is about to end the session, and possibly
    block this. The only implementation at this point is using the
    org.gnome.SessionManager D-Bus interface of gnome-session. It should
    be straightforward to port the EggSMClient implementations for
    Windows and OS X.

 gtk/gtk.symbols      |    1 +
 gtk/gtkapplication.c |  216 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gtk/gtkapplication.h |   10 ++-
 3 files changed, 226 insertions(+), 1 deletions(-)
---
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index 2006c17..8ed3adf 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -224,6 +224,7 @@ gtk_application_get_menubar
 gtk_application_get_type
 gtk_application_get_windows
 gtk_application_new
+gtk_application_quit_response
 gtk_application_remove_accelerator
 gtk_application_remove_window
 gtk_application_set_app_menu
diff --git a/gtk/gtkapplication.c b/gtk/gtkapplication.c
index adfd927..595266b 100644
--- a/gtk/gtkapplication.c
+++ b/gtk/gtkapplication.c
@@ -103,6 +103,9 @@
 enum {
   WINDOW_ADDED,
   WINDOW_REMOVED,
+  QUIT_REQUESTED,
+  QUIT_CANCELLED,
+  QUIT,
   LAST_SIGNAL
 };
 
@@ -118,6 +121,11 @@ struct _GtkApplicationPrivate
   GDBusConnection *session_bus;
   gchar *window_prefix;
   guint next_id;
+
+  GDBusProxy *sm_proxy;
+  GDBusProxy *client_proxy;
+  gchar *app_id;
+  gchar *client_path;
 #endif
 
 #ifdef GDK_WINDOWING_QUARTZ
@@ -187,6 +195,9 @@ window_prefix_from_appid (const gchar *appid)
   return appid_path;
 }
 
+static void gtk_application_startup_session_dbus (GtkApplication *app);
+
+
 static void
 gtk_application_startup_x11 (GtkApplication *application)
 {
@@ -195,6 +206,8 @@ gtk_application_startup_x11 (GtkApplication *application)
   application_id = g_application_get_application_id (G_APPLICATION (application));
   application->priv->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
   application->priv->window_prefix = window_prefix_from_appid (application_id);
+
+  gtk_application_startup_session_dbus (GTK_APPLICATION (application));
 }
 
 static void
@@ -203,6 +216,11 @@ gtk_application_shutdown_x11 (GtkApplication *application)
   g_free (application->priv->window_prefix);
   application->priv->window_prefix = NULL;
   g_clear_object (&application->priv->session_bus);
+
+  g_clear_object (&application->priv->sm_proxy);
+  g_clear_object (&application->priv->client_proxy);
+  g_free (application->priv->app_id);
+  g_free (application->priv->client_path);
 }
 #endif
 
@@ -534,6 +552,21 @@ gtk_application_class_init (GtkApplicationClass *class)
                   NULL, NULL,
                   g_cclosure_marshal_VOID__OBJECT,
                   G_TYPE_NONE, 1, GTK_TYPE_WINDOW);
+
+  gtk_application_signals[QUIT_REQUESTED] =
+    g_signal_new ("quit-requested", GTK_TYPE_APPLICATION, G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GtkApplicationClass, quit_requested),
+                  NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+  gtk_application_signals[QUIT_CANCELLED] =
+    g_signal_new ("quit-cancelled", GTK_TYPE_APPLICATION, G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GtkApplicationClass, quit_cancelled),
+                  NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+  gtk_application_signals[QUIT] =
+    g_signal_new ("quit", GTK_TYPE_APPLICATION, G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GtkApplicationClass, quit),
+                  NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
 }
 
 /**
@@ -847,3 +880,186 @@ gtk_application_get_menubar (GtkApplication *application)
 
   return menubar;
 }
+
+/* D-Bus Session Management */
+
+#ifdef GDK_WINDOWING_X11
+
+static void
+unregister_client (GtkApplication *app)
+{
+  GError *error = NULL;
+
+  g_debug ("Unregistering client\n");
+
+  g_dbus_proxy_call_sync (app->priv->sm_proxy,
+                          "UnregisterClient",
+                          g_variant_new ("(o)", app->priv->client_path),
+                          0,
+                          G_MAXINT,
+                          NULL,
+                          &error);
+
+  if (error)
+    {
+      g_warning ("Failed to unregister client: %s\n", error->message);
+      g_error_free (error);
+    }
+
+  g_clear_object (&app->priv->client_proxy);
+
+  g_free (app->priv->client_path);
+  app->priv->client_path = NULL;
+}
+
+static void
+gtk_application_emit_quit_requested (GtkApplication *app)
+{
+  if (!g_signal_has_handler_pending (app, gtk_application_signals[QUIT_REQUESTED], 0, FALSE) &&
+      GTK_APPLICATION_GET_CLASS (app)->quit_requested == NULL)
+    {
+      g_debug ("Not emitting quit_requested because no one is listening");
+      gtk_application_quit_response (app, TRUE, NULL);
+      return;
+    }
+
+  g_debug ("Emitting quit-requested");
+  g_signal_emit (app, gtk_application_signals[QUIT_REQUESTED], 0);
+}
+
+static void
+client_proxy_signal (GDBusProxy     *proxy,
+                     const gchar    *sender_name,
+                     const gchar    *signal_name,
+                     GVariant       *parameters,
+                     GtkApplication *app)
+{
+  if (strcmp (signal_name, "QueryEndSession") == 0)
+    {
+      g_debug ("Received QueryEndSession\n");
+      gtk_application_emit_quit_requested (app);
+    }
+  else if (strcmp (signal_name, "EndSession") == 0)
+    {
+      g_debug ("Received EndSession\n");
+      gtk_application_quit_response (app, TRUE, NULL);
+      unregister_client (app);
+      g_debug ("Emitting quit");
+      g_signal_emit (app, gtk_application_signals[QUIT], 0);
+    }
+  else if (strcmp (signal_name, "CancelEndSession") == 0)
+    {
+      g_debug ("Received CancelEndSession\n");
+      g_debug ("Emitting quit-cancelled");
+      g_signal_emit (app, gtk_application_signals[QUIT_CANCELLED], 0);
+    }
+  else if (strcmp (signal_name, "Stop") == 0)
+    {
+      g_debug ("Received Stop\n");
+      unregister_client (app);
+      g_debug ("Emitting quit");
+      g_signal_emit (app, gtk_application_signals[QUIT], 0);
+    }
+}
+
+static void
+gtk_application_startup_session_dbus (GtkApplication *app)
+{
+  static gchar *client_id;
+  GError *error = NULL;
+  GVariant *res;
+
+  if (app->priv->session_bus == NULL)
+    return;
+
+  if (client_id == NULL)
+    {
+      const gchar *desktop_autostart_id;
+
+      desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID");
+      /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to
+       * use the same client id.
+       */
+      g_unsetenv ("DESKTOP_AUTOSTART_ID");
+      client_id = g_strdup (desktop_autostart_id ? desktop_autostart_id : "");
+    }
+
+  g_debug ("Connecting to session manager\n");
+
+  app->priv->sm_proxy = g_dbus_proxy_new_sync (app->priv->session_bus, 0,
+                                               NULL, /* FIXME */
+                                               "org.gnome.SessionManager",
+                                               "/org/gnome/SessionManager",
+                                               "org.gnome.SessionManager",
+                                               NULL,
+                                               &error);
+  if (error)
+    {
+      g_warning ("Failed to get a session proxy: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  app->priv->app_id = g_strdup (g_get_prgname ());
+
+  g_debug ("Registering client '%s' '%s'\n", app->priv->app_id, client_id);
+
+  res = g_dbus_proxy_call_sync (app->priv->sm_proxy,
+                                "RegisterClient",
+                                g_variant_new ("(ss)", app->priv->app_id, client_id),
+                                0,
+                                -1,
+                                NULL,
+                                &error);
+
+  if (error)
+    {
+      g_warning ("Failed to register client: %s\n", error->message);
+      g_error_free (error);
+      g_clear_object (&app->priv->sm_proxy);
+      return;
+    }
+
+  g_variant_get (res, "(o)", &app->priv->client_path);
+  g_variant_unref (res);
+
+  g_debug ("Registered client at '%s'\n", app->priv->client_path);
+  app->priv->client_proxy = g_dbus_proxy_new_sync (app->priv->session_bus, 0,
+                                                   NULL, /* FIXME */
+                                                   "org.gnome.SessionManager",
+                                                   app->priv->client_path,
+                                                   "org.gnome.SessionManager.ClientPrivate",
+                                                   NULL,
+                                                   &error);
+  if (error)
+    {
+      g_warning ("Failed to get client proxy: %s\n", error->message);
+      g_error_free (error);
+      g_clear_object (&app->priv->sm_proxy);
+      g_free (app->priv->client_path);
+      app->priv->client_path = NULL;
+      return;
+    }
+
+  g_signal_connect (app->priv->client_proxy, "g-signal", G_CALLBACK (client_proxy_signal), app);
+}
+
+void
+gtk_application_quit_response (GtkApplication *application,
+                               gboolean        will_quit,
+                               const gchar    *reason)
+{
+  g_return_if_fail (GTK_IS_APPLICATION (application));
+  g_return_if_fail (!g_application_get_is_remote (G_APPLICATION (application)));
+  g_return_if_fail (application->priv->client_proxy != NULL);
+
+  g_debug ("Calling EndSessionResponse %d '%s'\n", will_quit, reason);
+
+  g_dbus_proxy_call (application->priv->client_proxy,
+                     "EndSessionResponse",
+                     g_variant_new ("(bs)", will_quit, reason ? reason : ""),
+                     0, G_MAXINT,
+                     NULL, NULL, NULL);
+}
+
+#endif
diff --git a/gtk/gtkapplication.h b/gtk/gtkapplication.h
index 2f87bb9..aeb4688 100644
--- a/gtk/gtkapplication.h
+++ b/gtk/gtkapplication.h
@@ -59,8 +59,12 @@ struct _GtkApplicationClass
   void (*window_removed) (GtkApplication *application,
                           GtkWindow      *window);
 
+  void (*quit_requested) (GtkApplication *application);
+  void (*quit_cancelled) (GtkApplication *application);
+  void (*quit)           (GtkApplication *application);
+
   /*< private >*/
-  gpointer padding[14];
+  gpointer padding[11];
 };
 
 GType            gtk_application_get_type      (void) G_GNUC_CONST;
@@ -91,6 +95,10 @@ void             gtk_application_remove_accelerator (GtkApplication *application
                                                      const gchar    *action_name,
                                                      GVariant       *parameter);
 
+void             gtk_application_quit_response      (GtkApplication *application,
+                                                     gboolean        will_quit,
+                                                     const gchar    *reason);
+
 G_END_DECLS
 
 #endif /* __GTK_APPLICATION_H__ */



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