Add async variant of g_app_info_launch_default_for_uri

This is useful in the portalized case, when the portal may
present an app chooser dialog to the user.

https://bugzilla.gnome.org/show_bug.cgi?id=768752
This commit is contained in:
Matthias Clasen 2016-07-16 11:45:44 -04:00
parent f885c4dd0d
commit c1e8f705dd
3 changed files with 219 additions and 50 deletions

View File

@ -1427,6 +1427,8 @@ g_app_info_get_default_for_uri_scheme
g_app_info_get_fallback_for_type g_app_info_get_fallback_for_type
g_app_info_get_recommended_for_type g_app_info_get_recommended_for_type
g_app_info_launch_default_for_uri g_app_info_launch_default_for_uri
g_app_info_launch_default_for_uri_async
g_app_info_launch_default_for_uri_finish
g_app_launch_context_setenv g_app_launch_context_setenv
g_app_launch_context_unsetenv g_app_launch_context_unsetenv
g_app_launch_context_get_environment g_app_launch_context_get_environment

View File

@ -36,7 +36,6 @@
#endif #endif
/** /**
* SECTION:gappinfo * SECTION:gappinfo
* @short_description: Application information and launch contexts * @short_description: Application information and launch contexts
@ -678,20 +677,95 @@ g_app_info_should_show (GAppInfo *appinfo)
} }
#ifdef G_OS_UNIX #ifdef G_OS_UNIX
static gboolean static void
launch_default_with_portal (const char *uri, response_received (GDBusConnection *connection,
GAppLaunchContext *context, const char *sender_name,
GError **error) const char *object_path,
const char *interface_name,
const char *signal_name,
GVariant *parameters,
gpointer user_data)
{
GTask *task = user_data;
guint32 response;
guint signal_id;
signal_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (task), "signal-id"));
g_dbus_connection_signal_unsubscribe (connection, signal_id);
g_variant_get (parameters, "(u@a{sv})", &response, NULL);
if (response == 0)
g_task_return_boolean (task, TRUE);
else if (response == 1)
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Launch cancelled");
else
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "Launch failed");
g_object_unref (task);
}
static void
open_uri_done (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GDBusConnection *connection = G_DBUS_CONNECTION (source);
GTask *task = user_data;
GVariant *res;
GError *error = NULL;
const char *path;
guint signal_id;
res = g_dbus_connection_call_finish (connection, result, &error);
if (res == NULL)
{
g_task_return_error (task, error);
g_object_unref (task);
return;
}
g_variant_get (res, "(&o)", &path);
signal_id =
g_dbus_connection_signal_subscribe (connection,
"org.freedesktop.portal.Desktop",
"org.freedesktop.portal.Request",
"Response",
path,
NULL,
G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
response_received,
task, NULL);
g_object_set_data (G_OBJECT (task), "signal-id", GINT_TO_POINTER (signal_id));
g_variant_unref (res);
}
static void
launch_default_with_portal_async (const char *uri,
GAppLaunchContext *context,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{ {
GDBusConnection *session_bus; GDBusConnection *session_bus;
GVariantBuilder opt_builder; GVariantBuilder opt_builder;
const char *parent_window = NULL; const char *parent_window = NULL;
GFile *file; GFile *file;
char *real_uri; char *real_uri;
GTask *task;
GAsyncReadyCallback dbus_callback;
GError *error = NULL;
session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error); session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
if (session_bus == NULL) if (session_bus == NULL)
return FALSE; {
g_task_report_error (context, callback, user_data, NULL, error);
return;
}
if (context && context->priv->envp) if (context && context->priv->envp)
parent_window = g_environ_getenv (context->priv->envp, "PARENT_WINDOW_ID"); parent_window = g_environ_getenv (context->priv->envp, "PARENT_WINDOW_ID");
@ -702,16 +776,13 @@ launch_default_with_portal (const char *uri,
if (g_file_is_native (file)) if (g_file_is_native (file))
{ {
GError *local_error = NULL; real_uri = g_document_portal_add_document (file, &error);
real_uri = g_document_portal_add_document (file, &local_error);
g_object_unref (file); g_object_unref (file);
if (real_uri == NULL) if (real_uri == NULL)
{ {
g_warning ("Can't register with document portal: %s", local_error->message); g_task_report_error (context, callback, user_data, NULL, error);
g_propagate_error (error, local_error); return;
return FALSE;
} }
} }
else else
@ -720,6 +791,17 @@ launch_default_with_portal (const char *uri,
real_uri = g_strdup (uri); real_uri = g_strdup (uri);
} }
if (callback)
{
task = g_task_new (context, cancellable, callback, user_data);
dbus_callback = open_uri_done;
}
else
{
task = NULL;
dbus_callback = NULL;
}
g_dbus_connection_call (session_bus, g_dbus_connection_call (session_bus,
"org.freedesktop.portal.Desktop", "org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop", "/org/freedesktop/portal/desktop",
@ -732,46 +814,35 @@ launch_default_with_portal (const char *uri,
NULL, NULL,
G_DBUS_CALL_FLAGS_NONE, G_DBUS_CALL_FLAGS_NONE,
G_MAXINT, G_MAXINT,
NULL, cancellable,
NULL, dbus_callback,
NULL); task);
g_dbus_connection_flush (session_bus, NULL, NULL, NULL); g_dbus_connection_flush (session_bus, cancellable, NULL, NULL);
g_object_unref (session_bus); g_object_unref (session_bus);
g_free (real_uri); g_free (real_uri);
}
static gboolean
launch_default_with_portal (const char *uri,
GAppLaunchContext *context,
GError **error)
{
launch_default_with_portal_async (uri, context, NULL, NULL, NULL);
return TRUE; return TRUE;
} }
#endif #endif
/** static gboolean
* g_app_info_launch_default_for_uri: launch_default_for_uri (const char *uri,
* @uri: the uri to show GAppLaunchContext *context,
* @launch_context: (allow-none): an optional #GAppLaunchContext. GError **error)
* @error: a #GError.
*
* 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)
{ {
char *uri_scheme; char *uri_scheme;
GAppInfo *app_info = NULL; GAppInfo *app_info = NULL;
GList l; GList l;
gboolean res; gboolean res;
#ifdef G_OS_UNIX
if (glib_should_use_portal ())
return launch_default_with_portal (uri, launch_context, error);
#endif
/* g_file_query_default_handler() calls /* g_file_query_default_handler() calls
* g_app_info_get_default_for_uri_scheme() too, but we have to do it * 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. * here anyway in case GFile can't parse @uri correctly.
@ -788,26 +859,111 @@ g_app_info_launch_default_for_uri (const char *uri,
file = g_file_new_for_uri (uri); file = g_file_new_for_uri (uri);
app_info = g_file_query_default_handler (file, NULL, error); app_info = g_file_query_default_handler (file, NULL, error);
g_object_unref (file); g_object_unref (file);
if (app_info == NULL)
return FALSE;
/* We still use the original @uri rather than calling
* g_file_get_uri(), because GFile might have modified the URI
* in ways we don't want (eg, removing the fragment identifier
* from a file: URI).
*/
} }
if (app_info == NULL)
return FALSE;
l.data = (char *)uri; l.data = (char *)uri;
l.next = l.prev = NULL; l.next = l.prev = NULL;
res = g_app_info_launch_uris (app_info, &l, res = g_app_info_launch_uris (app_info, &l, context, error);
launch_context, error);
g_object_unref (app_info); g_object_unref (app_info);
return res; return res;
} }
/**
* g_app_info_launch_default_for_uri:
* @uri: the uri to show
* @launch_context: (allow-none): 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)
{
#ifdef G_OS_UNIX
if (glib_should_use_portal ())
return launch_default_with_portal (uri, launch_context, error);
else
#endif
return launch_default_for_uri (uri, launch_context, error);
}
/**
* g_app_info_launch_default_for_uri_async:
* @uri: the uri to show
* @context: (allow-none): an optional #GAppLaunchContext
* cancellable: (allow-none): a #GCancellable
* @callback: (allow-none): a #GASyncReadyCallback to call when the request is done
* @user_data: (allow-none): data to pass to @callback
*
* Async version of g_app_info_launch_default_for_uri().
*
* This version is useful if you are interested in receiving
* error information in the case where the application is
* sandboxed and the portal may present an application chooser
* dialog to the user.
*
* Since: 2.50
*/
void
g_app_info_launch_default_for_uri_async (const char *uri,
GAppLaunchContext *context,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
gboolean res;
GError *error = NULL;
GTask *task;
#ifdef G_OS_UNIX
if (glib_should_use_portal ())
{
launch_default_with_portal_async (uri, context, cancellable, callback, user_data);
return;
}
#endif
res = launch_default_for_uri (uri, context, &error);
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);
}
/**
* g_app_info_launch_default_for_uri_finish:
* @result: a #GAsyncResult
* @error: (nullable): return location for an error, or %NULL
*
* Finishes an asynchronous launch-default-for-uri operation.
*
* Returns: %TRUE if the launch was successful, %FALSE if @error is set
*
* Since: 2.50
*/
gboolean
g_app_info_launch_default_for_uri_finish (GAsyncResult *result,
GError **error)
{
return g_task_propagate_boolean (G_TASK (result), error);
}
/** /**
* g_app_info_can_delete: * g_app_info_can_delete:
* @appinfo: a #GAppInfo * @appinfo: a #GAppInfo

View File

@ -230,6 +230,17 @@ gboolean g_app_info_launch_default_for_uri (const char *uri,
GAppLaunchContext *launch_context, GAppLaunchContext *launch_context,
GError **error); GError **error);
GLIB_AVAILABLE_IN_2_50
void g_app_info_launch_default_for_uri_async (const char *uri,
GAppLaunchContext *launch_context,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_2_50
gboolean g_app_info_launch_default_for_uri_finish (GAsyncResult *result,
GError **error);
/** /**
* GAppLaunchContext: * GAppLaunchContext:
* *