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:
parent
fbc8c28c0e
commit
54bc3aa4f5
@ -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
|
||||
|
190
src/shell-app.c
190
src/shell-app.c
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user