[glib/wip/oholy/gappinfo-async: 1/3] gappinfo: Add launch_uris_async() and launch_uris_finish() vfuncs



commit 1795a0eac657e2019d41535d9daaa44fcfe9f685
Author: Ondrej Holy <oholy redhat com>
Date:   Tue Jan 22 15:39:15 2019 +0100

    gappinfo: Add launch_uris_async() and launch_uris_finish() vfuncs
    
    The g_app_info_launch_uris_async() and g_app_info_launch_uris_finish()
    functions are crucial to fix g_app_info_launch_default_for_uri_async()
    to be really asynchronous.
    
    This patch also adds GDesktopAppInfo implementation of that vfuncs.
    The implementation may still use some synchronous calls to local MIME DB.
    
    https://gitlab.gnome.org/GNOME/glib/issues/1347
    https://gitlab.gnome.org/GNOME/glib/issues/1249

 docs/reference/gio/gio-sections.txt |   2 +
 gio/gappinfo.c                      |  81 ++++++++++++++++++
 gio/gappinfo.h                      |  25 +++++-
 gio/gdesktopappinfo.c               | 159 ++++++++++++++++++++++++++++++++++--
 gio/gfile.c                         |   2 +-
 5 files changed, 258 insertions(+), 11 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index ea53d2624..67968538f 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -1442,6 +1442,8 @@ g_app_info_launch
 g_app_info_supports_files
 g_app_info_supports_uris
 g_app_info_launch_uris
+g_app_info_launch_uris_async
+g_app_info_launch_uris_finish
 g_app_info_should_show
 g_app_info_can_delete
 g_app_info_delete
diff --git a/gio/gappinfo.c b/gio/gappinfo.c
index 47cd73366..cf8a6d00d 100644
--- a/gio/gappinfo.c
+++ b/gio/gappinfo.c
@@ -24,6 +24,7 @@
 #include "gappinfoprivate.h"
 #include "gcontextspecificgroup.h"
 #include "gtask.h"
+#include "gcancellable.h"
 
 #include "glibintl.h"
 #include <gioerror.h>
@@ -662,6 +663,86 @@ g_app_info_launch_uris (GAppInfo           *appinfo,
   return (* iface->launch_uris) (appinfo, uris, launch_context, error);
 }
 
+/**
+ * g_app_info_launch_uris_async:
+ * @appinfo: a #GAppInfo
+ * @uris: (nullable) (element-type utf8): a #GList containing URIs to launch.
+ * @context: (nullable): a #GAppLaunchContext or %NULL
+ * @cancellable: (nullable): a #GCancellable
+ * @callback: (nullable): a #GAsyncReadyCallback to call when the request is done
+ * @user_data: (nullable): data to pass to @callback
+ *
+ * Async version of g_app_info_launch_uris().
+ *
+ * The @callback is invoked immediately after the application launch, but it
+ * waits for activation in case of DBus-activated applications and also provides
+ * extended error information for sandboxed applications, see notes for
+ * g_app_info_launch_default_for_uri_async().
+ *
+ * Since: 2.60
+ **/
+void
+g_app_info_launch_uris_async (GAppInfo           *appinfo,
+                              GList              *uris,
+                              GAppLaunchContext  *context,
+                              GCancellable       *cancellable,
+                              GAsyncReadyCallback callback,
+                              gpointer            user_data)
+{
+  GAppInfoIface *iface;
+
+  g_return_if_fail (G_IS_APP_INFO (appinfo));
+  g_return_if_fail (context == NULL || G_IS_APP_LAUNCH_CONTEXT (context));
+  g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+  if (iface->launch_uris_async == NULL)
+    {
+      GTask *task;
+
+      task = g_task_new (appinfo, cancellable, callback, user_data);
+      g_task_set_source_tag (task, g_app_info_launch_uris_async);
+      g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                               "Operation not supported for the current backend.");
+      g_object_unref (task);
+
+      return;
+    }
+
+  (* iface->launch_uris_async) (appinfo, uris, context, cancellable, callback, user_data);
+}
+
+/**
+ * g_app_info_launch_uris_finish:
+ * @appinfo: a #GAppInfo
+ * @result: a #GAsyncResult
+ * @error: (nullable): a #GError
+ *
+ * Finishes a g_app_info_launch_uris_async() operation.
+ *
+ * Returns: %TRUE on successful launch, %FALSE otherwise.
+ *
+ * Since: 2.60
+ */
+gboolean
+g_app_info_launch_uris_finish (GAppInfo     *appinfo,
+                               GAsyncResult *result,
+                               GError      **error)
+{
+  GAppInfoIface *iface;
+
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+  if (iface->launch_uris_finish == NULL)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                           "Operation not supported for the current backend.");
+      return FALSE;
+    }
+
+  return (* iface->launch_uris_finish) (appinfo, result, error);
+}
 
 /**
  * g_app_info_should_show:
diff --git a/gio/gappinfo.h b/gio/gappinfo.h
index 4889be923..d26d048a5 100644
--- a/gio/gappinfo.h
+++ b/gio/gappinfo.h
@@ -78,7 +78,9 @@ typedef struct _GAppLaunchContextPrivate GAppLaunchContextPrivate;
  * @get_display_name: Gets the display name for the #GAppInfo. Since 2.24
  * @set_as_last_used_for_type: Sets the application as the last used. See 
g_app_info_set_as_last_used_for_type().
  * @get_supported_types: Retrieves the list of content types that @app_info claims to support.
- *
+ * @launch_uris_async: Asynchronously launches an application with a list of URIs. (Since: 2.60)
+ * @launch_uris_finish: Finishes an operation started with @launch_uris_async. (Since: 2.60)
+
  * Application Information interface, for operating system portability.
  */
 typedef struct _GAppInfoIface    GAppInfoIface;
@@ -131,6 +133,15 @@ struct _GAppInfoIface
                                                  const char         *content_type,
                                                  GError            **error);
   const char ** (* get_supported_types)         (GAppInfo           *appinfo);
+  void         (* launch_uris_async)            (GAppInfo           *appinfo,
+                                                 GList              *uris,
+                                                 GAppLaunchContext  *context,
+                                                 GCancellable       *cancellable,
+                                                 GAsyncReadyCallback callback,
+                                                 gpointer            user_data);
+  gboolean     (* launch_uris_finish)           (GAppInfo           *appinfo,
+                                                 GAsyncResult       *result,
+                                                 GError            **error);
 };
 
 GLIB_AVAILABLE_IN_ALL
@@ -173,6 +184,18 @@ gboolean    g_app_info_launch_uris                  (GAppInfo             *appin
                                                      GList                *uris,
                                                      GAppLaunchContext    *context,
                                                      GError              **error);
+GLIB_AVAILABLE_IN_2_60
+void        g_app_info_launch_uris_async            (GAppInfo             *appinfo,
+                                                     GList                *uris,
+                                                     GAppLaunchContext    *context,
+                                                     GCancellable         *cancellable,
+                                                     GAsyncReadyCallback   callback,
+                                                     gpointer              user_data);
+GLIB_AVAILABLE_IN_2_60
+gboolean    g_app_info_launch_uris_finish           (GAppInfo             *appinfo,
+                                                     GAsyncResult         *result,
+                                                     GError              **error);
+
 GLIB_AVAILABLE_IN_ALL
 gboolean    g_app_info_should_show                  (GAppInfo             *appinfo);
 
diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c
index 9ebfce4f0..75f731834 100644
--- a/gio/gdesktopappinfo.c
+++ b/gio/gdesktopappinfo.c
@@ -2870,7 +2870,10 @@ static void
 launch_uris_with_dbus (GDesktopAppInfo    *info,
                        GDBusConnection    *session_bus,
                        GList              *uris,
-                       GAppLaunchContext  *launch_context)
+                       GAppLaunchContext  *launch_context,
+                       GCancellable       *cancellable,
+                       GAsyncReadyCallback callback,
+                       gpointer            user_data)
 {
   GVariantBuilder builder;
   gchar *object_path;
@@ -2889,14 +2892,11 @@ launch_uris_with_dbus (GDesktopAppInfo    *info,
 
   g_variant_builder_add_value (&builder, g_desktop_app_info_make_platform_data (info, uris, launch_context));
 
-  /* This is non-blocking API.  Similar to launching via fork()/exec()
-   * we don't wait around to see if the program crashed during startup.
-   * This is what startup-notification's job is...
-   */
   object_path = object_path_from_appid (info->app_id);
   g_dbus_connection_call (session_bus, info->app_id, object_path, "org.freedesktop.Application",
                           uris ? "Open" : "Activate", g_variant_builder_end (&builder),
-                          NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+                          NULL, G_DBUS_CALL_FLAGS_NONE, -1,
+                          cancellable, callback, user_data);
   g_free (object_path);
 }
 
@@ -2904,7 +2904,10 @@ static gboolean
 g_desktop_app_info_launch_uris_with_dbus (GDesktopAppInfo    *info,
                                           GDBusConnection    *session_bus,
                                           GList              *uris,
-                                          GAppLaunchContext  *launch_context)
+                                          GAppLaunchContext  *launch_context,
+                                          GCancellable       *cancellable,
+                                          GAsyncReadyCallback callback,
+                                          gpointer            user_data)
 {
   GList *ruris = uris;
   char *app_id = NULL;
@@ -2921,7 +2924,8 @@ g_desktop_app_info_launch_uris_with_dbus (GDesktopAppInfo    *info,
     }
 #endif
 
-  launch_uris_with_dbus (info, session_bus, ruris, launch_context);
+  launch_uris_with_dbus (info, session_bus, ruris, launch_context,
+                         cancellable, callback, user_data);
 
   if (ruris != uris)
     g_list_free_full (ruris, g_free);
@@ -2952,7 +2956,12 @@ g_desktop_app_info_launch_uris_internal (GAppInfo                   *appinfo,
   session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
 
   if (session_bus && info->app_id)
-    g_desktop_app_info_launch_uris_with_dbus (info, session_bus, uris, launch_context);
+    /* This is non-blocking API. Similar to launching via fork()/exec()
+     * we don't wait around to see if the program crashed during startup.
+     * This is what startup-notification's job is...
+     */
+    g_desktop_app_info_launch_uris_with_dbus (info, session_bus, uris, launch_context,
+                                              NULL, NULL, NULL);
   else
     success = g_desktop_app_info_launch_uris_with_spawn (info, session_bus, info->exec, uris, launch_context,
                                                          spawn_flags, user_setup, user_setup_data,
@@ -2986,6 +2995,136 @@ g_desktop_app_info_launch_uris (GAppInfo           *appinfo,
                                                   error);
 }
 
+typedef struct
+{
+  GAppInfo *appinfo;
+  GList *uris;
+  GAppLaunchContext *context;
+} LaunchUrisData;
+
+static void
+launch_uris_data_free (LaunchUrisData *data)
+{
+  g_clear_object (&data->context);
+  g_list_free_full (data->uris, g_free);
+  g_free (data);
+}
+
+static void
+launch_uris_with_dbus_cb (GObject      *object,
+                          GAsyncResult *result,
+                          gpointer      user_data)
+{
+  GTask *task = G_TASK (user_data);
+  GError *error = NULL;
+
+  g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), result, &error);
+  if (error != NULL)
+    {
+      g_dbus_error_strip_remote_error (error);
+      g_task_return_error (task, g_steal_pointer (&error));
+    }
+  else
+    g_task_return_boolean (task, TRUE);
+
+  g_object_unref (task);
+}
+
+static void
+launch_uris_flush_cb (GObject      *object,
+                      GAsyncResult *result,
+                      gpointer      user_data)
+{
+  GTask *task = G_TASK (user_data);
+
+  g_dbus_connection_flush_finish (G_DBUS_CONNECTION (object), result, NULL);
+  g_task_return_boolean (task, TRUE);
+  g_object_unref (task);
+}
+
+static void
+launch_uris_bus_get_cb (GObject      *object,
+                        GAsyncResult *result,
+                        gpointer      user_data)
+{
+  GTask *task = G_TASK (user_data);
+  GDesktopAppInfo *info = G_DESKTOP_APP_INFO (g_task_get_source_object (task));
+  LaunchUrisData *data = g_task_get_task_data (task);
+  GDBusConnection *session_bus;
+  GError *error = NULL;
+
+  session_bus = g_bus_get_finish (result, NULL);
+
+  if (session_bus && info->app_id)
+    {
+      /* FIXME: The g_document_portal_add_documents() function, which is called
+       * from the g_desktop_app_info_launch_uris_with_dbus() function, still
+       * uses blocking calls.
+       */
+      g_desktop_app_info_launch_uris_with_dbus (info, session_bus,
+                                                data->uris, data->context,
+                                                g_task_get_cancellable (task),
+                                                launch_uris_with_dbus_cb,
+                                                g_steal_pointer (&task));
+    }
+  else
+    {
+      /* FIXME: The D-Bus message from the notify_desktop_launch() function
+       * can be still lost even if flush is called later. See:
+       * https://gitlab.freedesktop.org/dbus/dbus/issues/72
+       */
+      g_desktop_app_info_launch_uris_with_spawn (info, session_bus, info->exec,
+                                                 data->uris, data->context,
+                                                 _SPAWN_FLAGS_DEFAULT, NULL,
+                                                 NULL, NULL, NULL, -1, -1, -1,
+                                                 &error);
+      if (error != NULL)
+        {
+          g_task_return_error (task, g_steal_pointer (&error));
+          g_object_unref (task);
+        }
+      else
+        g_dbus_connection_flush (session_bus,
+                                 g_task_get_cancellable (task),
+                                 launch_uris_flush_cb,
+                                 g_steal_pointer (&task));
+    }
+
+  g_clear_object (&session_bus);
+}
+
+static void
+g_desktop_app_info_launch_uris_async (GAppInfo           *appinfo,
+                                      GList              *uris,
+                                      GAppLaunchContext  *context,
+                                      GCancellable       *cancellable,
+                                      GAsyncReadyCallback callback,
+                                      gpointer            user_data)
+{
+  GTask *task;
+  LaunchUrisData *data;
+
+  task = g_task_new (appinfo, cancellable, callback, user_data);
+  g_task_set_source_tag (task, g_desktop_app_info_launch_uris_async);
+
+  data = g_new0 (LaunchUrisData, 1);
+  data->uris = g_list_copy_deep (uris, (GCopyFunc) g_strdup, NULL);
+  data->context = (context != NULL) ? g_object_ref (context) : NULL;
+  g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) launch_uris_data_free);
+
+  g_bus_get (G_BUS_TYPE_SESSION, cancellable, launch_uris_bus_get_cb, task);
+}
+
+static gboolean
+g_desktop_app_info_launch_uris_finish (GAppInfo     *appinfo,
+                                       GAsyncResult *result,
+                                       GError      **error)
+{
+  g_return_val_if_fail (g_task_is_valid (result, appinfo), FALSE);
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
 static gboolean
 g_desktop_app_info_supports_uris (GAppInfo *appinfo)
 {
@@ -3876,6 +4015,8 @@ g_desktop_app_info_iface_init (GAppInfoIface *iface)
   iface->supports_uris = g_desktop_app_info_supports_uris;
   iface->supports_files = g_desktop_app_info_supports_files;
   iface->launch_uris = g_desktop_app_info_launch_uris;
+  iface->launch_uris_async = g_desktop_app_info_launch_uris_async;
+  iface->launch_uris_finish = g_desktop_app_info_launch_uris_finish;
   iface->should_show = g_desktop_app_info_should_show;
   iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type;
   iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension;
diff --git a/gio/gfile.c b/gio/gfile.c
index c7926478c..1cc69166a 100644
--- a/gio/gfile.c
+++ b/gio/gfile.c
@@ -6989,7 +6989,7 @@ g_file_query_default_handler_async (GFile              *file,
  * @result: a #GAsyncResult
  * @error: (nullable): a #GError
  *
- * Finishes g_file_query_default_handler_async() operation.
+ * Finishes a g_file_query_default_handler_async() operation.
  *
  * Returns: (transfer full): a #GAppInfo if the handle was found,
  *     %NULL if there were errors.


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