mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-02-04 10:16:17 +01:00
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
This commit is contained in:
commit
08e5c89dfc
@ -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
|
||||
|
330
gio/gappinfo.c
330
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,41 +809,18 @@ 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);
|
||||
|
||||
g_object_unref (app_info);
|
||||
|
||||
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)
|
||||
{
|
||||
if (launch_default_for_uri (uri, launch_context, error))
|
||||
return TRUE;
|
||||
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);
|
||||
}
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
if (glib_should_use_portal ())
|
||||
if (!res && glib_should_use_portal ())
|
||||
{
|
||||
const char *parent_window = NULL;
|
||||
|
||||
@ -757,11 +831,126 @@ g_app_info_launch_default_for_uri (const char *uri,
|
||||
parent_window = g_environ_getenv (launch_context->priv->envp, "PARENT_WINDOW_ID");
|
||||
|
||||
return g_openuri_portal_open_uri (uri, parent_window, error);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
return FALSE;
|
||||
return res;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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_error_free (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
|
||||
|
||||
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 ())
|
||||
task = g_task_new (NULL, cancellable, callback, user_data);
|
||||
g_task_set_source_tag (task, g_app_info_launch_default_for_uri_async);
|
||||
|
||||
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_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);
|
||||
|
||||
if (!app_info)
|
||||
{
|
||||
const char *parent_window = NULL;
|
||||
GFile *file;
|
||||
|
||||
if (context && context->priv->envp)
|
||||
parent_window = g_environ_getenv (context->priv->envp, "PARENT_WINDOW_ID");
|
||||
|
||||
g_openuri_portal_open_uri_async (uri, parent_window, cancellable, callback, user_data);
|
||||
return;
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
|
||||
task = g_task_new (context, cancellable, callback, user_data);
|
||||
if (!res)
|
||||
g_task_return_error (task, error);
|
||||
else
|
||||
g_task_return_boolean (task, TRUE);
|
||||
|
||||
g_object_unref (task);
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
125
gio/gfile.c
125
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
|
||||
|
||||
/**
|
||||
|
11
gio/gfile.h
11
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,
|
||||
|
@ -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;
|
||||
GError *error = NULL;
|
||||
gchar *uri = user_data;
|
||||
|
||||
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)
|
||||
if (!g_app_info_launch_default_for_uri_finish (res, &error))
|
||||
{
|
||||
GFile *file;
|
||||
|
||||
file = g_file_new_for_uri (uri);
|
||||
app_info = g_file_query_default_handler (file, NULL, NULL);
|
||||
g_object_unref (file);
|
||||
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;
|
||||
n_outstanding--;
|
||||
|
||||
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 = '/';
|
||||
|
||||
*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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user