[glib/application-replace: 158/159] GApplication: Add a way to replace a unique instance



commit 3a4b18f9038f316c617e229123b54ea9885ba1dc
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Aug 18 15:35:33 2018 -0400

    GApplication: Add a way to replace a unique instance
    
    While uniqueness is great, sometimes you want to restart
    a newer version of the same app. These two flags make that
    possible.
    
    We also add a ::name-lost signal, that is emitted when it
    happens. The default handler for this signal just calls
    g_application_quit(), but applications may want to connect
    and do cleanup or state-saving here.

 gio/gapplication.c          | 46 +++++++++++++++++++++++++++++++
 gio/gapplication.h          |  3 ++-
 gio/gapplicationimpl-dbus.c | 66 +++++++++++++++++++++++++++++++++++++++++----
 gio/gioenums.h              |  9 ++++++-
 4 files changed, 117 insertions(+), 7 deletions(-)
---
diff --git a/gio/gapplication.c b/gio/gapplication.c
index 5a2f5adf7..2d2ab48e3 100644
--- a/gio/gapplication.c
+++ b/gio/gapplication.c
@@ -212,6 +212,7 @@
  *     the @dbus_register vfunc. Since: 2.34
  * @handle_local_options: invoked locally after the parsing of the commandline
  *  options has occurred. Since: 2.40
+ * @name_lost: invoked when another instance is taking over the name. Since: 2.60
  *
  * Virtual function table for #GApplication.
  *
@@ -277,6 +278,7 @@ enum
   SIGNAL_ACTION,
   SIGNAL_COMMAND_LINE,
   SIGNAL_HANDLE_LOCAL_OPTIONS,
+  SIGNAL_NAME_LOST,
   NR_SIGNALS
 };
 
@@ -476,6 +478,7 @@ g_application_parse_command_line (GApplication   *application,
 {
   gboolean become_service = FALSE;
   gchar *app_id = NULL;
+  gboolean replace = FALSE;
   GVariantDict *dict = NULL;
   GOptionContext *context;
   GOptionGroup *gapplication_group;
@@ -557,6 +560,18 @@ g_application_parse_command_line (GApplication   *application,
       g_option_group_add_entries (gapplication_group, entries);
     }
 
+  /* Allow replacing if the application allows it */
+  if (application->priv->flags & G_APPLICATION_ALLOW_REPLACEMENT)
+    {
+      GOptionEntry entries[] = {
+        { "gapplication-replace", '\0', 0, G_OPTION_ARG_NONE, &replace,
+          N_("Replace the running instance") },
+        { NULL }
+      };
+
+      g_option_group_add_entries (gapplication_group, entries);
+    }
+
   /* Now we parse... */
   if (!g_option_context_parse_strv (context, arguments, error))
     goto out;
@@ -569,6 +584,10 @@ g_application_parse_command_line (GApplication   *application,
   if (app_id)
     g_application_set_application_id (application, app_id);
 
+  /* Check for --gapplication-replace */
+  if (replace)
+    application->priv->flags |= G_APPLICATION_REPLACE;
+
   dict = g_variant_dict_new (NULL);
   if (application->priv->packed_options)
     {
@@ -1177,6 +1196,13 @@ g_application_real_dbus_unregister (GApplication    *application,
 {
 }
 
+static gboolean
+g_application_real_name_lost (GApplication *application)
+{
+  g_application_quit (application);
+  return TRUE;
+}
+
 /* GObject implementation stuff {{{1 */
 static void
 g_application_set_property (GObject      *object,
@@ -1434,6 +1460,7 @@ g_application_class_init (GApplicationClass *class)
   class->add_platform_data = g_application_real_add_platform_data;
   class->dbus_register = g_application_real_dbus_register;
   class->dbus_unregister = g_application_real_dbus_unregister;
+  class->name_lost = g_application_real_name_lost;
 
   g_object_class_install_property (object_class, PROP_APPLICATION_ID,
     g_param_spec_string ("application-id",
@@ -1628,6 +1655,25 @@ g_application_class_init (GApplicationClass *class)
                   g_application_handle_local_options_accumulator, NULL, NULL,
                   G_TYPE_INT, 1, G_TYPE_VARIANT_DICT);
 
+  /**
+   * GApplication::name-lost:
+   * @application: the application
+   *
+   * The ::name-lost signal is emitted only on the registered primary instance
+   * when a new instance has taken over. This can only happen if the application
+   * is using the %G_APPLICATION_ALLOW_REPLACEMENT flag.
+   *
+   * The default handler for this signal calls g_application_quit().
+   *
+   * Returns: %TRUE if the signal has been handled
+   *
+   * Since: 2.60
+   */
+  g_application_signals[SIGNAL_NAME_LOST] =
+    g_signal_new (I_("name-lost"), G_TYPE_APPLICATION, G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GApplicationClass, name_lost),
+                  g_signal_accumulator_true_handled, NULL, NULL,
+                  G_TYPE_BOOLEAN, 0);
 }
 
 /* Application ID validity {{{1 */
diff --git a/gio/gapplication.h b/gio/gapplication.h
index cdb93655a..adc32ed44 100644
--- a/gio/gapplication.h
+++ b/gio/gapplication.h
@@ -112,9 +112,10 @@ struct _GApplicationClass
                                                      const gchar               *object_path);
   gint                      (* handle_local_options)(GApplication              *application,
                                                      GVariantDict              *options);
+  gboolean                  (* name_lost)           (GApplication              *application);
 
   /*< private >*/
-  gpointer padding[8];
+  gpointer padding[7];
 };
 
 GLIB_AVAILABLE_IN_ALL
diff --git a/gio/gapplicationimpl-dbus.c b/gio/gapplicationimpl-dbus.c
index f9e5e710d..76d67eec8 100644
--- a/gio/gapplicationimpl-dbus.c
+++ b/gio/gapplicationimpl-dbus.c
@@ -111,6 +111,7 @@ struct _GApplicationImpl
   GDBusConnection *session_bus;
   GActionGroup    *exported_actions;
   const gchar     *bus_name;
+  guint            name_lost_signal;
 
   gchar           *object_path;
   guint            object_id;
@@ -327,6 +328,25 @@ application_path_from_appid (const gchar *appid)
   return appid_path;
 }
 
+static void g_application_impl_stop_primary (GApplicationImpl *impl);
+
+static void
+name_lost (GDBusConnection *bus,
+           const char      *sender_name,
+           const char      *object_path,
+           const char      *interface_name,
+           const char      *signal_name,
+           GVariant        *parameters,
+           gpointer         user_data)
+{
+  GApplicationImpl *impl = user_data;
+  gboolean handled;
+
+  impl->primary = FALSE;
+  g_application_impl_stop_primary (impl);
+  g_signal_emit_by_name (impl->app, "name-lost", &handled);
+}
+
 /* Attempt to become the primary instance.
  *
  * Returns %TRUE if everything went OK, regardless of if we became the
@@ -347,6 +367,8 @@ g_application_impl_attempt_primary (GApplicationImpl  *impl,
     NULL /* set_property */
   };
   GApplicationClass *app_class = G_APPLICATION_GET_CLASS (impl->app);
+  GBusNameOwnerFlags name_owner_flags;
+  GApplicationFlags app_flags;
   GVariant *reply;
   guint32 rval;
 
@@ -426,11 +448,33 @@ g_application_impl_attempt_primary (GApplicationImpl  *impl,
    * the well-known name and fall back to remote mode (!is_primary)
    * in the case that we can't do that.
    */
-  reply = g_dbus_connection_call_sync (impl->session_bus, "org.freedesktop.DBus", "/org/freedesktop/DBus",
-                                       "org.freedesktop.DBus", "RequestName",
-                                       g_variant_new ("(su)",
-                                                      impl->bus_name,
-                                                      G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE),
+  name_owner_flags = G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE;
+  app_flags = g_application_get_flags (impl->app);
+
+  if (app_flags & G_APPLICATION_ALLOW_REPLACEMENT)
+    {
+      impl->name_lost_signal = g_dbus_connection_signal_subscribe (impl->session_bus,
+                                                                   "org.freedesktop.DBus",
+                                                                   "org.freedesktop.DBus",
+                                                                   "NameLost",
+                                                                   "/org/freedesktop/DBus",
+                                                                   impl->bus_name,
+                                                                   G_DBUS_SIGNAL_FLAGS_NONE,
+                                                                   name_lost,
+                                                                   impl,
+                                                                   NULL);
+
+      name_owner_flags |= G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
+    }
+  if (app_flags & G_APPLICATION_REPLACE)
+    name_owner_flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE;
+
+  reply = g_dbus_connection_call_sync (impl->session_bus,
+                                       "org.freedesktop.DBus",
+                                       "/org/freedesktop/DBus",
+                                       "org.freedesktop.DBus",
+                                       "RequestName",
+                                       g_variant_new ("(su)", impl->bus_name, name_owner_flags),
                                        G_VARIANT_TYPE ("(u)"),
                                        0, -1, cancellable, error);
 
@@ -443,6 +487,12 @@ g_application_impl_attempt_primary (GApplicationImpl  *impl,
   /* DBUS_REQUEST_NAME_REPLY_EXISTS: 3 */
   impl->primary = (rval != 3);
 
+  if (!impl->primary && impl->name_lost_signal)
+    {
+      g_dbus_connection_signal_unsubscribe (impl->session_bus, impl->name_lost_signal);
+      impl->name_lost_signal = 0;
+    }
+
   return TRUE;
 }
 
@@ -485,6 +535,12 @@ g_application_impl_stop_primary (GApplicationImpl *impl)
       impl->actions_id = 0;
     }
 
+  if (impl->name_lost_signal)
+    {
+      g_dbus_connection_signal_unsubscribe (impl->session_bus, impl->name_lost_signal);
+      impl->name_lost_signal = 0;
+    }
+
   if (impl->primary && impl->bus_name)
     {
       g_dbus_connection_call (impl->session_bus, "org.freedesktop.DBus",
diff --git a/gio/gioenums.h b/gio/gioenums.h
index a01532cfd..d6d1e59f1 100644
--- a/gio/gioenums.h
+++ b/gio/gioenums.h
@@ -1474,6 +1474,11 @@ typedef enum
  * @G_APPLICATION_CAN_OVERRIDE_APP_ID: Allow users to override the
  *     application ID from the command line with `--gapplication-app-id`.
  *     Since: 2.48
+ * @G_APPLICATION_ALLOW_REPLACEMENT: Allow another instance to take over
+ *     the bus name. Since: 2.60
+ * @G_APPLICATION_REPLACE: Take over from another instance. This flag is
+ *     usually set by passing `--gapplication-replace` on the commandline.
+ *     Since: 2.60
  *
  * Flags used to define the behaviour of a #GApplication.
  *
@@ -1491,7 +1496,9 @@ typedef enum
 
   G_APPLICATION_NON_UNIQUE =           (1 << 5),
 
-  G_APPLICATION_CAN_OVERRIDE_APP_ID =  (1 << 6)
+  G_APPLICATION_CAN_OVERRIDE_APP_ID =  (1 << 6),
+  G_APPLICATION_ALLOW_REPLACEMENT   =  (1 << 7),
+  G_APPLICATION_REPLACE             =  (1 << 8)
 } GApplicationFlags;
 
 /**


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