summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Withnall <philip@tecnocode.co.uk>2019-01-28 16:27:07 +0000
committerPhilip Withnall <philip@tecnocode.co.uk>2019-01-28 16:27:07 +0000
commit08e5c89dfcb714985d15b0c91cb96c4572bb5a1c (patch)
tree8952adca11d4fd01caaffcae7a1a23ec7fb1a1fd
parent92881babba9f355f9de6bb21a416d279ff35d969 (diff)
parent051c6ba4e7111b04ab417403730b82de02a1c0d8 (diff)
downloadglib-08e5c89dfcb714985d15b0c91cb96c4572bb5a1c.tar.gz
Merge branch 'wip/oholy/gappinfo-async' into 'master'
Make `g_app_info_launch_uris_async()` really asynchronous Closes #1249 and #1347 See merge request GNOME/glib!609
-rw-r--r--docs/reference/gio/gio-sections.txt4
-rw-r--r--gio/gappinfo.c324
-rw-r--r--gio/gappinfo.h25
-rw-r--r--gio/gdesktopappinfo.c160
-rw-r--r--gio/gfile.c125
-rw-r--r--gio/gfile.h11
-rw-r--r--gio/gio-tool-open.c120
7 files changed, 605 insertions, 164 deletions
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index e61001f68..6aa07b462 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -125,6 +125,8 @@ g_file_query_filesystem_info
g_file_query_filesystem_info_async
g_file_query_filesystem_info_finish
g_file_query_default_handler
+g_file_query_default_handler_async
+g_file_query_default_handler_finish
g_file_measure_disk_usage
g_file_measure_disk_usage_async
g_file_measure_disk_usage_finish
@@ -1446,6 +1448,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..84e667575 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 D-Bus–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:
@@ -684,15 +765,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 D-Bus–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
@@ -712,56 +809,148 @@ 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
+{
+ 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)
{
- if (launch_default_for_uri (uri, launch_context, error))
- return TRUE;
+ 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);
+ GCancellable *cancellable = g_task_get_cancellable (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,
+ cancellable,
+ 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)
+{
+ GCancellable *cancellable = g_task_get_cancellable (task);
+ 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,
+ cancellable,
+ 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));
}
/**
@@ -779,6 +968,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 D-Bus–activated
+ * applications are really started before termination and if you are interested
+ * in receiving error information from their activation.
+ *
* Since: 2.50
*/
void
@@ -788,32 +981,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));
}
/**
@@ -831,11 +1037,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
}
/**
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..7d7425ea9 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,137 @@ 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);
+ GCancellable *cancellable = g_task_get_cancellable (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,
+ cancellable,
+ 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,
+ cancellable,
+ 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 +4016,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 a5709a4cc..1cc69166a 100644
--- a/gio/gfile.c
+++ b/gio/gfile.c
@@ -6851,6 +6851,8 @@ g_file_query_default_handler (GFile *file,
if (appinfo != NULL)
return appinfo;
}
+ else
+ g_free (uri_scheme);
info = g_file_query_info (file,
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
@@ -6883,6 +6885,129 @@ g_file_query_default_handler (GFile *file,
return NULL;
}
+static void
+query_default_handler_query_info_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GFile *file = G_FILE (object);
+ GTask *task = G_TASK (user_data);
+ GError *error = NULL;
+ GFileInfo *info;
+ const char *content_type;
+ GAppInfo *appinfo = NULL;
+
+ info = g_file_query_info_finish (file, result, &error);
+ if (info == NULL)
+ {
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
+ return;
+ }
+
+ content_type = g_file_info_get_content_type (info);
+ if (content_type)
+ {
+ char *path;
+
+ /* Don't use is_native(), as we want to support fuse paths if available */
+ path = g_file_get_path (file);
+
+ /* FIXME: The following still uses blocking calls. */
+ appinfo = g_app_info_get_default_for_type (content_type,
+ path == NULL);
+ g_free (path);
+ }
+
+ g_object_unref (info);
+
+ if (appinfo != NULL)
+ g_task_return_pointer (task, g_steal_pointer (&appinfo), g_object_unref);
+ else
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_SUPPORTED,
+ _("No application is registered as handling this file"));
+ g_object_unref (task);
+}
+
+/**
+ * g_file_query_default_handler_async:
+ * @file: a #GFile to open
+ * @cancellable: optional #GCancellable object, %NULL to ignore
+ * @callback: (nullable): a #GAsyncReadyCallback to call when the request is done
+ * @user_data: (nullable): data to pass to @callback
+ *
+ * Async version of g_file_query_default_handler().
+ *
+ * Since: 2.60
+ */
+void
+g_file_query_default_handler_async (GFile *file,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ char *uri_scheme;
+
+ task = g_task_new (file, cancellable, callback, user_data);
+ g_task_set_source_tag (task, g_file_query_default_handler_async);
+
+ uri_scheme = g_file_get_uri_scheme (file);
+ if (uri_scheme && uri_scheme[0] != '\0')
+ {
+ GAppInfo *appinfo;
+
+ /* FIXME: The following still uses blocking calls. */
+ appinfo = g_app_info_get_default_for_uri_scheme (uri_scheme);
+ g_free (uri_scheme);
+
+ if (appinfo != NULL)
+ {
+ g_task_return_pointer (task, g_steal_pointer (&appinfo), g_object_unref);
+ g_object_unref (task);
+ return;
+ }
+ }
+ else
+ g_free (uri_scheme);
+
+ g_file_query_info_async (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ 0,
+ io_priority,
+ cancellable,
+ query_default_handler_query_info_cb,
+ g_steal_pointer (&task));
+}
+
+/**
+ * g_file_query_default_handler_finish:
+ * @file: a #GFile to open
+ * @result: a #GAsyncResult
+ * @error: (nullable): a #GError
+ *
+ * Finishes a g_file_query_default_handler_async() operation.
+ *
+ * Returns: (transfer full): a #GAppInfo if the handle was found,
+ * %NULL if there were errors.
+ * When you are done with it, release it with g_object_unref()
+ *
+ * Since: 2.60
+ */
+GAppInfo *
+g_file_query_default_handler_finish (GFile *file,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+ g_return_val_if_fail (g_task_is_valid (result, file), NULL);
+
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
#define GET_CONTENT_BLOCK_SIZE 8192
/**
diff --git a/gio/gfile.h b/gio/gfile.h
index 4aff644c3..6e25b0de0 100644
--- a/gio/gfile.h
+++ b/gio/gfile.h
@@ -1183,6 +1183,17 @@ GLIB_AVAILABLE_IN_ALL
GAppInfo *g_file_query_default_handler (GFile *file,
GCancellable *cancellable,
GError **error);
+GLIB_AVAILABLE_IN_2_60
+void g_file_query_default_handler_async (GFile *file,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GLIB_AVAILABLE_IN_2_60
+GAppInfo *g_file_query_default_handler_finish (GFile *file,
+ GAsyncResult *result,
+ GError **error);
+
GLIB_AVAILABLE_IN_ALL
gboolean g_file_load_contents (GFile *file,
GCancellable *cancellable,
diff --git a/gio/gio-tool-open.c b/gio/gio-tool-open.c
index 73863c7c5..ac6764a97 100644
--- a/gio/gio-tool-open.c
+++ b/gio/gio-tool-open.c
@@ -29,73 +29,32 @@
#include "gio-tool.h"
+static int n_outstanding = 0;
+static gboolean success = TRUE;
static const GOptionEntry entries[] = {
{ NULL }
};
-#if defined(G_OS_UNIX) && !defined(HAVE_COCOA)
-static gboolean
-get_bus_name_and_path_from_uri (const char *uri,
- char **bus_name_out,
- char **object_path_out)
+static void
+launch_default_for_uri_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
{
- GAppInfo *app_info = NULL;
- char *bus_name = NULL;
- char *object_path = NULL;
- char *uri_scheme;
- const char *filename;
- char *basename = NULL;
- char *p;
- gboolean got_name = FALSE;
-
- uri_scheme = g_uri_parse_scheme (uri);
- if (uri_scheme && uri_scheme[0] != '\0')
- app_info = g_app_info_get_default_for_uri_scheme (uri_scheme);
- g_free (uri_scheme);
-
- if (app_info == NULL)
- {
- GFile *file;
+ GError *error = NULL;
+ gchar *uri = user_data;
- file = g_file_new_for_uri (uri);
- app_info = g_file_query_default_handler (file, NULL, NULL);
- g_object_unref (file);
+ if (!g_app_info_launch_default_for_uri_finish (res, &error))
+ {
+ print_error ("%s: %s", uri, error->message);
+ g_clear_error (&error);
+ success = FALSE;
}
- if (app_info == NULL || !G_IS_DESKTOP_APP_INFO (app_info) ||
- !g_desktop_app_info_get_boolean (G_DESKTOP_APP_INFO (app_info), "DBusActivatable"))
- goto out;
-
- filename = g_desktop_app_info_get_filename (G_DESKTOP_APP_INFO (app_info));
- if (filename == NULL)
- goto out;
-
- basename = g_path_get_basename (filename);
- if (!g_str_has_suffix (basename, ".desktop"))
- goto out;
-
- basename[strlen (basename) - strlen (".desktop")] = '\0';
- if (!g_dbus_is_name (basename))
- goto out;
-
- bus_name = g_strdup (basename);
- object_path = g_strdup_printf ("/%s", bus_name);
- for (p = object_path; *p != '\0'; p++)
- if (*p == '.')
- *p = '/';
+ n_outstanding--;
- *bus_name_out = g_steal_pointer (&bus_name);
- *object_path_out = g_steal_pointer (&object_path);
- got_name = TRUE;
-
-out:
- g_clear_object (&app_info);
- g_clear_pointer (&basename, g_free);
-
- return got_name;
+ g_free (uri);
}
-#endif
int
handle_open (int argc, char *argv[], gboolean do_help)
@@ -104,8 +63,6 @@ handle_open (int argc, char *argv[], gboolean do_help)
gchar *param;
GError *error = NULL;
int i;
- gboolean success;
- gboolean res;
g_set_prgname ("gio open");
@@ -143,7 +100,6 @@ handle_open (int argc, char *argv[], gboolean do_help)
g_option_context_free (context);
- success = TRUE;
for (i = 1; i < argc; i++)
{
char *uri = NULL;
@@ -162,47 +118,23 @@ handle_open (int argc, char *argv[], gboolean do_help)
uri = g_file_get_uri (file);
g_object_unref (file);
}
+ else
+ uri = g_strdup (argv[i]);
g_free (uri_scheme);
- res = g_app_info_launch_default_for_uri (uri ? uri : argv[i], NULL, &error);
- if (!res)
- {
- print_error ("%s: %s", uri ? uri : argv[i], error->message);
- g_clear_error (&error);
- success = FALSE;
- }
+ g_app_info_launch_default_for_uri_async (uri,
+ NULL,
+ NULL,
+ launch_default_for_uri_cb,
+ g_strdup (uri));
-#if defined(G_OS_UNIX) && !defined(HAVE_COCOA)
- /* FIXME: This chunk of madness is a workaround for a dbus-daemon bug.
- * See https://bugzilla.gnome.org/show_bug.cgi?id=780296
- */
- if (res)
- {
- char *bus_name = NULL;
- char *object_path = NULL;
-
- if (get_bus_name_and_path_from_uri (uri ? uri : argv[i], &bus_name, &object_path))
- {
- GDBusConnection *connection;
- connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
-
- if (connection)
- g_dbus_connection_call_sync (connection,
- bus_name,
- object_path,
- "org.freedesktop.DBus.Peer",
- "Ping",
- NULL, NULL,
- G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL);
- g_clear_object (&connection);
- g_free (bus_name);
- g_free (object_path);
- }
- }
-#endif
+ n_outstanding++;
g_free (uri);
}
+ while (n_outstanding > 0)
+ g_main_context_iteration (NULL, TRUE);
+
return success ? 0 : 2;
}