[glib/wip/oholy/gappinfo-async: 3/4] gappinfo: Use g_app_info_launch_uris_async() for async calls
- From: Ondrej Holy <oholy src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/wip/oholy/gappinfo-async: 3/4] gappinfo: Use g_app_info_launch_uris_async() for async calls
- Date: Mon, 28 Jan 2019 10:21:01 +0000 (UTC)
commit f9583a33fdb70c9524eea1ca5c60d296da942c2b
Author: Ondrej Holy <oholy redhat com>
Date: Tue Jan 22 15:44:10 2019 +0100
gappinfo: Use g_app_info_launch_uris_async() for async calls
Currently, the g_app_info_launch_default_for_uri_async() function uses
g_app_info_launch_uris(), which is not fully asynchronous and may cause
for example Nautilus freezes. Let's use the g_app_info_launch_uris_async()
function to prevent the freezes.
Closes: https://gitlab.gnome.org/GNOME/glib/issues/1347
https://gitlab.gnome.org/GNOME/glib/issues/1249
gio/gappinfo.c | 241 +++++++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 181 insertions(+), 60 deletions(-)
---
diff --git a/gio/gappinfo.c b/gio/gappinfo.c
index f93c837fa..92d8eeb28 100644
--- a/gio/gappinfo.c
+++ b/gio/gappinfo.c
@@ -764,15 +764,31 @@ g_app_info_should_show (GAppInfo *appinfo)
return (* iface->should_show) (appinfo);
}
-static gboolean
-launch_default_for_uri (const char *uri,
- GAppLaunchContext *context,
- GError **error)
+/**
+ * g_app_info_launch_default_for_uri:
+ * @uri: the uri to show
+ * @context: (nullable): an optional #GAppLaunchContext
+ * @error: (nullable): return location for an error, or %NULL
+ *
+ * Utility function that launches the default application
+ * registered to handle the specified uri. Synchronous I/O
+ * is done on the uri to detect the type of the file if
+ * required.
+ *
+ * The DBus-activated applications don't have to be started if your application
+ * terminates too soon after this function. To prevent this, use
+ * g_app_info_launch_default_for_uri() instead.
+ *
+ * Returns: %TRUE on success, %FALSE on error.
+ **/
+gboolean
+g_app_info_launch_default_for_uri (const char *uri,
+ GAppLaunchContext *launch_context,
+ GError **error)
{
char *uri_scheme;
GAppInfo *app_info = NULL;
- GList l;
- gboolean res;
+ gboolean res = FALSE;
/* g_file_query_default_handler() calls
* g_app_info_get_default_for_uri_scheme() too, but we have to do it
@@ -792,56 +808,146 @@ launch_default_for_uri (const char *uri,
g_object_unref (file);
}
- if (app_info == NULL)
- return FALSE;
+ if (app_info)
+ {
+ GList l;
- l.data = (char *)uri;
- l.next = l.prev = NULL;
- res = g_app_info_launch_uris (app_info, &l, context, error);
+ l.data = (char *)uri;
+ l.next = l.prev = NULL;
+ res = g_app_info_launch_uris (app_info, &l, launch_context, error);
+ g_object_unref (app_info);
+ }
- g_object_unref (app_info);
+#ifdef G_OS_UNIX
+ if (!res && glib_should_use_portal ())
+ {
+ const char *parent_window = NULL;
+
+ /* Reset any error previously set by launch_default_for_uri */
+ g_clear_error (error);
+
+ if (launch_context && launch_context->priv->envp)
+ parent_window = g_environ_getenv (launch_context->priv->envp, "PARENT_WINDOW_ID");
+
+ return g_openuri_portal_open_uri (uri, parent_window, error);
+ }
+#endif
return res;
}
-/**
- * g_app_info_launch_default_for_uri:
- * @uri: the uri to show
- * @context: (nullable): an optional #GAppLaunchContext
- * @error: (nullable): return location for an error, or %NULL
- *
- * Utility function that launches the default application
- * registered to handle the specified uri. Synchronous I/O
- * is done on the uri to detect the type of the file if
- * required.
- *
- * Returns: %TRUE on success, %FALSE on error.
- **/
-gboolean
-g_app_info_launch_default_for_uri (const char *uri,
- GAppLaunchContext *launch_context,
- GError **error)
+typedef struct
{
- if (launch_default_for_uri (uri, launch_context, error))
- return TRUE;
+ gchar *uri;
+ GAppLaunchContext *context;
+} LaunchDefaultForUriData;
+
+static void
+launch_default_for_uri_data_free (LaunchDefaultForUriData *data)
+{
+ g_free (data->uri);
+ g_clear_object (&data->context);
+ g_free (data);
+}
#ifdef G_OS_UNIX
+static void
+launch_default_for_uri_portal_open_uri_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GTask *task = G_TASK (user_data);
+ GError *error = NULL;
+
+ if (g_openuri_portal_open_uri_finish (result, &error))
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
+}
+#endif
+
+static void
+launch_default_for_uri_portal_open_uri (GTask *task, GError *error)
+{
+#ifdef G_OS_UNIX
+ LaunchDefaultForUriData *data = g_task_get_task_data (task);
+
if (glib_should_use_portal ())
{
const char *parent_window = NULL;
/* Reset any error previously set by launch_default_for_uri */
- g_clear_error (error);
+ g_error_free (error);
- if (launch_context && launch_context->priv->envp)
- parent_window = g_environ_getenv (launch_context->priv->envp, "PARENT_WINDOW_ID");
-
- return g_openuri_portal_open_uri (uri, parent_window, error);
+ if (data->context && data->context->priv->envp)
+ parent_window = g_environ_getenv (data->context->priv->envp,
+ "PARENT_WINDOW_ID");
+ g_openuri_portal_open_uri_async (data->uri,
+ parent_window,
+ g_task_get_cancellable (task),
+ launch_default_for_uri_portal_open_uri_cb,
+ g_steal_pointer (&task));
+ return;
}
#endif
- return FALSE;
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
+}
+
+static void
+launch_default_for_uri_launch_uris_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GAppInfo *app_info = G_APP_INFO (object);
+ GTask *task = G_TASK (user_data);
+ GError *error = NULL;
+
+ if (g_app_info_launch_uris_finish (app_info, result, &error))
+ {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ }
+ else
+ launch_default_for_uri_portal_open_uri (g_steal_pointer (&task), g_steal_pointer (&error));
+}
+
+static void
+launch_default_for_uri_launch_uris (GTask *task,
+ GAppInfo *app_info)
+{
+ GList l;
+ LaunchDefaultForUriData *data = g_task_get_task_data (task);
+
+ l.data = (char *)data->uri;
+ l.next = l.prev = NULL;
+ g_app_info_launch_uris_async (app_info,
+ &l,
+ data->context,
+ g_task_get_cancellable (task),
+ launch_default_for_uri_launch_uris_cb,
+ g_steal_pointer (&task));
+ g_object_unref (app_info);
+}
+
+static void
+launch_default_for_uri_default_handler_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GFile *file = G_FILE (object);
+ GTask *task = G_TASK (user_data);
+ GAppInfo *app_info = NULL;
+ GError *error = NULL;
+
+ app_info = g_file_query_default_handler_finish (file, result, &error);
+ if (app_info)
+ launch_default_for_uri_launch_uris (g_steal_pointer (&task), g_steal_pointer (&app_info));
+ else
+ launch_default_for_uri_portal_open_uri (g_steal_pointer (&task), g_steal_pointer (&error));
}
/**
@@ -859,6 +965,10 @@ g_app_info_launch_default_for_uri (const char *uri,
* sandboxed and the portal may present an application chooser
* dialog to the user.
*
+ * This is also useful if you want to be sure that the DBus-activated
+ * applications are really started before termination and if you are interested
+ * in receiving error information from their activation.
+ *
* Since: 2.50
*/
void
@@ -868,32 +978,45 @@ g_app_info_launch_default_for_uri_async (const char *uri,
GAsyncReadyCallback callback,
gpointer user_data)
{
- gboolean res;
- GError *error = NULL;
GTask *task;
+ char *uri_scheme;
+ GAppInfo *app_info = NULL;
+ LaunchDefaultForUriData *data;
- res = launch_default_for_uri (uri, context, &error);
+ g_return_if_fail (uri != NULL);
-#ifdef G_OS_UNIX
- if (!res && glib_should_use_portal ())
- {
- const char *parent_window = NULL;
+ task = g_task_new (NULL, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_app_info_launch_default_for_uri_async);
- if (context && context->priv->envp)
- parent_window = g_environ_getenv (context->priv->envp, "PARENT_WINDOW_ID");
+ data = g_new (LaunchDefaultForUriData, 1);
+ data->uri = g_strdup (uri);
+ data->context = (context != NULL) ? g_object_ref (context) : NULL;
+ g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) launch_default_for_uri_data_free);
- g_openuri_portal_open_uri_async (uri, parent_window, cancellable, callback, user_data);
- return;
- }
-#endif
+ /* g_file_query_default_handler_async() calls
+ * g_app_info_get_default_for_uri_scheme() too, but we have to do it
+ * here anyway in case GFile can't parse @uri correctly.
+ */
+ uri_scheme = g_uri_parse_scheme (uri);
+ if (uri_scheme && uri_scheme[0] != '\0')
+ /* FIXME: The following still uses blocking calls. */
+ app_info = g_app_info_get_default_for_uri_scheme (uri_scheme);
+ g_free (uri_scheme);
- task = g_task_new (context, cancellable, callback, user_data);
- if (!res)
- g_task_return_error (task, error);
- else
- g_task_return_boolean (task, TRUE);
+ if (!app_info)
+ {
+ GFile *file;
- g_object_unref (task);
+ file = g_file_new_for_uri (uri);
+ g_file_query_default_handler_async (file,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ launch_default_for_uri_default_handler_cb,
+ g_steal_pointer (&task));
+ g_object_unref (file);
+ }
+ else
+ launch_default_for_uri_launch_uris (g_steal_pointer (&task), g_steal_pointer (&app_info));
}
/**
@@ -911,11 +1034,9 @@ gboolean
g_app_info_launch_default_for_uri_finish (GAsyncResult *result,
GError **error)
{
-#ifdef G_OS_UNIX
- return g_openuri_portal_open_uri_finish (result, error);
-#else
+ g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE);
+
return g_task_propagate_boolean (G_TASK (result), error);
-#endif
}
/**
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]