shell/app: Add method to activate an action via FDO DBus activation

There are multiple different places where we activate an app action,
but none set the correct activation token. Therefore this adds an async
method to to call `ActivateAction` on `org.freedesktop.Appliaction`
with the correct activation token/startup id as platform data.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3198>
This commit is contained in:
Julian Sparber 2024-02-19 09:30:25 +01:00
parent fbc8c28c0e
commit 54bc3aa4f5
3 changed files with 204 additions and 0 deletions

View File

@ -30,6 +30,7 @@ Gio._promisify(Gio.DBusProxy.prototype, 'init_async');
Gio._promisify(Gio.DBusProxy.prototype, 'call_with_unix_fd_list');
Gio._promisify(Gio.File.prototype, 'query_info_async');
Gio._promisify(Polkit.Permission, 'new');
Gio._promisify(Shell.App.prototype, 'activate_action');
// We can't import shell JS modules yet, because they may have
// variable initializations, etc, that depend on this file's

View File

@ -1447,6 +1447,196 @@ shell_app_launch_action (ShellApp *app,
g_object_unref (context);
}
static gchar *
object_path_from_app_id (const gchar *app_id)
{
gchar *app_id_path, *iter;
app_id_path = g_strconcat ("/", app_id, NULL);
for (iter = app_id_path; *iter; iter++)
{
if (*iter == '.')
*iter = '/';
if (*iter == '-')
*iter = '_';
}
return app_id_path;
}
static GVariant *
get_platform_data (ShellApp *app,
guint timestamp,
int workspace)
{
GVariantBuilder builder;
ShellGlobal *global;
g_autoptr (GAppLaunchContext) context = NULL;
gchar *startup_id;
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
if (!app->info)
return g_variant_builder_end (&builder);
global = shell_global_get ();
context = shell_global_create_app_launch_context (global, timestamp, workspace);
if (!context)
return g_variant_builder_end (&builder);
startup_id = g_app_launch_context_get_startup_notify_id (context, G_APP_INFO (app->info), NULL);
if (!startup_id)
return g_variant_builder_end (&builder);
g_variant_builder_add (&builder, "{sv}",
"desktop-startup-id", g_variant_new_string (startup_id));
g_variant_builder_add (&builder, "{sv}",
"activation-token", g_variant_new_take_string (g_steal_pointer (&startup_id)));
return g_variant_builder_end (&builder);
}
static void
on_activate_action_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
GTask *task = G_TASK (user_data);
g_autoptr (GVariant) value = NULL;
g_autoptr (GError) error = NULL;
value = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source),
res, &error);
if (error)
g_task_return_error (task, g_steal_pointer (&error));
else
g_task_return_boolean (task, TRUE);
}
static void
activate_action_get_bus_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
GTask *task = G_TASK (user_data);
ShellApp *app = NULL;
g_autoptr (GDBusConnection) session_bus = NULL;
g_autoptr (GError) error = NULL;
g_autofree gchar *object_path = NULL;
g_autofree gchar *app_id = NULL;
gchar *last_dot;
session_bus = g_bus_get_finish (result, &error);
if (error)
{
g_task_return_error (task, g_steal_pointer (&error));
return;
}
app = SHELL_APP (g_task_get_source_object (task));
app_id = g_strdup (g_app_info_get_id (G_APP_INFO (app->info)));
last_dot = strrchr (app_id, '.');
if (last_dot && g_str_equal (last_dot, ".desktop"))
*last_dot = '\0';
object_path = object_path_from_app_id (app_id);
g_dbus_connection_call (session_bus,
app_id, object_path,
"org.freedesktop.Application", "ActivateAction",
g_task_get_task_data (task),
NULL, G_DBUS_CALL_FLAGS_NONE, -1,
g_task_get_cancellable (task),
on_activate_action_cb, task);
}
/**
* shell_app_activate_action
* @app: the #ShellApp
* @action_name: the name of an action to activate
* @parameter: (nullable): the parameter to the activation
* @timestamp: Event timestamp, or 0 for current event timestamp
* @workspace: Start on this workspace, or -1 for default
* @cancellable: (nullable): a #GCancellable or %NULL
* @callback: (scope async): A #GAsyncReadyCallback to call when the request is satisfied.
* @user_data: (nullable): User data to pass to @callback
*
* This activates an action using 'org.freedesktop.Application' DBus interface.
*
* This function will fail if this #ShellApp doesn't have a valid #GDesktopAppInfo
* with a valid id.
*/
void
shell_app_activate_action (ShellApp *app,
const char *action_name,
GVariant *parameter,
guint timestamp,
int workspace,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr (GTask) task = NULL;
g_autoptr (GVariant) task_data = NULL;
g_return_if_fail (SHELL_IS_APP (app));
g_return_if_fail (G_IS_DESKTOP_APP_INFO (app->info));
g_return_if_fail (g_application_id_is_valid (g_app_info_get_id (G_APP_INFO (app->info))));
g_return_if_fail (action_name != NULL && action_name[0] != '\0');
g_return_if_fail (parameter == NULL || g_variant_is_of_type (parameter, G_VARIANT_TYPE ("av")));
g_return_if_fail (timestamp >= 0);
g_return_if_fail (workspace >= -1);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
task = g_task_new (app, cancellable, callback, user_data);
g_task_set_source_tag (task, shell_app_activate_action);
if (!parameter)
parameter = g_variant_new("av", NULL);
task_data = g_variant_ref_sink (g_variant_new ("(s@av@a{sv})", action_name, parameter,
get_platform_data (app, timestamp, workspace)));
g_task_set_task_data (task, g_steal_pointer (&task_data), (GDestroyNotify) g_variant_unref);
g_bus_get (G_BUS_TYPE_SESSION, cancellable, activate_action_get_bus_cb, g_steal_pointer (&task));
}
/**
* shell_app_activate_action_finish:
* @app: the #ShellApp
* @error: #GError for error reporting
*
* Finish the asynchronous operation started by shell_app_activate_action()
* and obtain its result.
*
* Returns: whether the operation was successful
*
*/
gboolean
shell_app_activate_action_finish (ShellApp *app,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (SHELL_IS_APP (app), FALSE);
g_return_val_if_fail (G_IS_TASK (result), FALSE);
g_return_val_if_fail (g_async_result_is_tagged (result,
shell_app_activate_action),
FALSE);
if (!g_task_propagate_boolean (G_TASK (result), error))
return FALSE;
return TRUE;
}
/**
* shell_app_get_app_info:
* @app: a #ShellApp

View File

@ -69,6 +69,19 @@ void shell_app_launch_action (ShellApp *app,
guint timestamp,
int workspace);
void shell_app_activate_action (ShellApp *app,
const char *action_name,
GVariant *parameter,
guint timestamp,
int workspace,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean
shell_app_activate_action_finish (ShellApp *app,
GAsyncResult *result,
GError **error);
int shell_app_compare_by_name (ShellApp *app, ShellApp *other);
int shell_app_compare (ShellApp *app, ShellApp *other);