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:
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
|
||||
|
Reference in New Issue
Block a user