From a46227b5daf184beda0c4f07087e5defb597870b Mon Sep 17 00:00:00 2001 From: dcz Date: Wed, 26 Jun 2024 08:56:12 +0000 Subject: [PATCH 1/9] wip globalshortcuts --- libportal/globalshortcuts.c | 1029 +++++++++++++++++++++++++++ libportal/globalshortcuts.h | 84 +++ libportal/meson.build | 2 + portal-test/gtk3/portal-test-win.ui | 47 +- 4 files changed, 1161 insertions(+), 1 deletion(-) create mode 100644 libportal/globalshortcuts.c create mode 100644 libportal/globalshortcuts.h diff --git a/libportal/globalshortcuts.c b/libportal/globalshortcuts.c new file mode 100644 index 00000000..2c84ed24 --- /dev/null +++ b/libportal/globalshortcuts.c @@ -0,0 +1,1029 @@ +/* + * Copyright (C) 2022, Red Hat, Inc. + * + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, version 3.0 of the + * License. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see . + * + * SPDX-License-Identifier: LGPL-3.0-only + */ + +#include "config.h" + +#include +#include +#include + +#include "globalshortcuts.h" +#include "portal-private.h" +#include "session-private.h" + +/** + * XdpGlobalShortcutsSession + * + * A representation of a long-lived global shortcuts portal interaction. + * + * The [class@GlobalShortcutsSession] object is used to represent portal + * interactions with the global shortcuts desktop portal that extend over + * multiple portal calls. Usually a caller creates a global shortcuts session, + * binds shortcuts, and then starts listening to activations. + * + * To list current assignments after [method@GlobalShortcutsSession.bind_shortcuts] returns, + * call [method@GlobalShortcutsSession.list_shortcuts]. + * + * The [class@GlobalShortcutsSession] wraps a [class@Session] object. + */ + +enum { + SIGNAL_CLOSED, + SIGNAL_ACTIVATED, + SIGNAL_DEACTIVATED, + SIGNAL_SHORTCUTS_CHANGED, + SIGNAL_LAST_SIGNAL, +}; + +static guint signals[SIGNAL_LAST_SIGNAL]; + +struct _XdpGlobalShortcutsSession +{ + GObject parent_instance; + XdpSession *parent_session; /* strong ref */ + + GList *zones; + + guint signal_ids[SIGNAL_LAST_SIGNAL]; + guint zone_serial; + guint zone_set; +}; + +G_DEFINE_TYPE (XdpGlobalShortcutsSession, xdp_global_shortcuts_session, G_TYPE_OBJECT) + +static gboolean +_xdp_global_shortcuts_session_is_valid (XdpGlobalShortcutsSession *session) +{ + return XDP_IS_INPUT_CAPTURE_SESSION (session) && session->parent_session != NULL; +} + +static void +parent_session_destroy (gpointer data, GObject *old_session) +{ + XdpGlobalShortcutsSession *session = XDP_GLOBAL_SHORTCUTS_SESSION (data); + + g_critical ("XdpSession destroyed before XdpGlobalShortcutsSesssion, you lost count of your session refs"); + + session->parent_session = NULL; +} + +static void +xdp_global_shortcuts_session_finalize (GObject *object) +{ + XdpGlobalShortcutsSession *session = XDP_GLOBAL_SHORTCUTS_SESSION (object); + XdpSession *parent_session = session->parent_session; + + if (parent_session == NULL) + { + g_critical ("XdpSession destroyed before XdpGlobalShortcutsSesssion, you lost count of your session refs"); + } + else + { + for (guint i = 0; i < SIGNAL_LAST_SIGNAL; i++) + { + guint signal_id = session->signal_ids[i]; + if (signal_id > 0) + g_dbus_connection_signal_unsubscribe (parent_session->portal->bus, signal_id); + } + + g_object_weak_unref (G_OBJECT (parent_session), parent_session_destroy, session); + session->parent_session->input_capture_session = NULL; + g_clear_pointer (&session->parent_session, g_object_unref); + } + + g_list_free_full (g_steal_pointer (&session->zones), g_object_unref); + + G_OBJECT_CLASS (xdp_global_shortcuts_session_parent_class)->finalize (object); +} + +static void +xdp_global_shortcuts_session_class_init (XdpGlobalShortcutsSessionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = xdp_global_shortcuts_session_finalize; + + /** + * XdpGlobalShortcutsSession::shortcuts-changed: + * @session: the [class@GlobalShortcutsSession] + * @options: a GVariant with the signal options + * + * Emitted when an GlobalShortcuts session's shortcuts have changed. This + * signal is emitted after new shortcuts have already become effective. + */ + signals[SIGNAL_SHORTCUTS_CHANGED] = + g_signal_new ("shortcuts-changed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 1, + G_TYPE_VARIANT); + /** + * XdpGlobalShortcutsSession::activated: + * @session: the [class@GlobalShortcutsSession] + * @name: shortcut ID + * @timestamp: measured since epoch + * + * Emitted when a GlobalShortcuts shortcut of this session was activated. + */ + signals[SIGNAL_ACTIVATED] = + g_signal_new ("activated", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 2, + G_TYPE_STRING, + G_TYPE_UINT); + /** + * XdpGlobalShortcutsSession::deactivated: + * @session: the [class@GlobalShortcutsSession] + * @name: shortcut ID + * @timestamp: measured since epoch + * + * Emitted when a GlobalShortcuts shortcut of this session was deactivated. + */ + signals[SIGNAL_DEACTIVATED] = + g_signal_new ("deactivated", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 2, + G_TYPE_STRING, + G_TYPE_UINT); +} + +static void +xdp_global_shortcuts_session_init (XdpGlobalShortcutsSession *session) +{ + session->parent_session = NULL; + session->zones = NULL; + session->zone_set = 0; + for (guint i = 0; i < SIGNAL_LAST_SIGNAL; i++) + session->signal_ids[i] = 0; +} + +/* A request-based method call */ +typedef struct { + XdpPortal *portal; + char *session_path; /* object path for session */ + GTask *task; + guint signal_id; /* Request::Response signal */ + char *request_path; /* object path for request */ + guint cancelled_id; /* signal id for cancelled gobject signal */ + + /* CreateSession only */ + XdpParent *parent; + char *parent_handle; + + /* GetZones only */ + XdpGlobalShortcutsSession *session; + + /* SetPointerBarrier only */ + GList *barriers; + +} Call; + +static void create_session (Call *call); +static void get_zones (Call *call); + +static void +call_free (Call *call) +{ + /* CreateSesssion */ + if (call->parent) + { + call->parent->parent_unexport (call->parent); + xdp_parent_free (call->parent); + } + g_free (call->parent_handle); + + /* Generic */ + if (call->signal_id) + g_dbus_connection_signal_unsubscribe (call->portal->bus, call->signal_id); + + if (call->cancelled_id) + g_signal_handler_disconnect (g_task_get_cancellable (call->task), call->cancelled_id); + + g_free (call->request_path); + + g_clear_object (&call->portal); + g_clear_object (&call->task); + g_clear_object (&call->session); + + g_free (call->session_path); + + g_free (call); +} + +static void +call_returned (GObject *object, + GAsyncResult *result, + gpointer data) +{ + Call *call = data; + GError *error = NULL; + g_autoptr(GVariant) ret; + + ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), result, &error); + if (error) + { + if (call->cancelled_id) + { + g_signal_handler_disconnect (g_task_get_cancellable (call->task), call->cancelled_id); + call->cancelled_id = 0; + } + g_task_return_error (call->task, error); + call_free (call); + } +} + +static gboolean +handle_matches_session (XdpGlobalShortcutsSession *session, const char *id) +{ + const char *sid = session->parent_session->id; + + return g_str_equal (sid, id); +} + + +static void +prep_call (Call *call, GDBusSignalCallback callback, GVariantBuilder *options, void *userdata) +{ + g_autofree char *token = NULL; + + token = g_strdup_printf ("portal%d", g_random_int_range (0, G_MAXINT)); + call->request_path = g_strconcat (REQUEST_PATH_PREFIX, call->portal->sender, "/", token, NULL); + call->signal_id = g_dbus_connection_signal_subscribe (call->portal->bus, + PORTAL_BUS_NAME, + REQUEST_INTERFACE, + "Response", + call->request_path, + NULL, + G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, + callback, + call, + userdata); + + g_variant_builder_init (options, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (options, "{sv}", "handle_token", g_variant_new_string (token)); +} + +static void +shortcuts_changed_emit_signal (GObject *source_object, + GAsyncResult *res, + gpointer data) +{ + XdpGlobalShortcutsSession *session = XDP_GLOBAL_SHORTCUTS_SESSION (data); + GVariantBuilder options; + + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&options, "{sv}", "zone_set", g_variant_new_uint32 (session->zone_set - 1)); + + g_signal_emit (session, signals[SIGNAL_SHORTCUTS_CHANGED], 0, g_variant_new ("a{sv}", &options)); +} + +static void +shortcuts_changed (GDBusConnection *bus, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer data) +{ + XdpGlobalShortcutsSession *session = XDP_GLOBAL_SHORTCUTS_SESSION (data); + XdpPortal *portal = session->parent_session->portal; + g_autoptr(GVariant) options = NULL; + const char *handle = NULL; + Call *call; + + g_variant_get(parameters, "(o@a{sv})", &handle, &options); + + if (!handle_matches_session (session, handle)) + return; + + /* Zones have changed, but let's fetch the new zones before we notify the + * caller so they're already available by the time they get notified */ + call = g_new0 (Call, 1); + call->portal = g_object_ref (portal); + call->task = g_task_new (portal, NULL, shortcuts_changed_emit_signal, session); + call->session = g_object_ref (session); + + get_zones (call); +} + +static void +activated (GDBusConnection *bus, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer data) +{ + XdpGlobalShortcutsSession *session = XDP_GLOBAL_SHORTCUTS_SESSION (data); + g_autoptr(GVariant) options = NULL; + guint32 activation_id = 0; + const char *handle = NULL; + + g_variant_get (parameters, "(o@a{sv})", &handle, &options); + + /* FIXME: we should remove the activation_id from options, but ... meh? */ + if (!g_variant_lookup (options, "activation_id", "u", &activation_id)) + g_warning ("Portal bug: activation_id missing from Activated signal"); + + if (!handle_matches_session (session, handle)) + return; + + g_signal_emit (session, signals[SIGNAL_ACTIVATED], 0, activation_id, options); +} + +static void +deactivated (GDBusConnection *bus, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer data) +{ + XdpGlobalShortcutsSession *session = XDP_GLOBAL_SHORTCUTS_SESSION (data); + g_autoptr(GVariant) options = NULL; + guint32 activation_id = 0; + const char *handle = NULL; + + g_variant_get(parameters, "(o@a{sv})", &handle, &options); + + /* FIXME: we should remove the activation_id from options, but ... meh? */ + if (!g_variant_lookup (options, "activation_id", "u", &activation_id)) + g_warning ("Portal bug: activation_id missing from Deactivated signal"); + + if (!handle_matches_session (session, handle)) + return; + + g_signal_emit (session, signals[SIGNAL_DEACTIVATED], 0, activation_id, options); +} + +static XdpGlobalShortcutsSession * +_xdp_global_shortcuts_session_new (XdpPortal *portal, const char *session_path) +{ + g_autoptr(XdpSession) parent_session = _xdp_session_new (portal, session_path, XDP_SESSION_INPUT_CAPTURE); + g_autoptr(XdpGlobalShortcutsSession) session = g_object_new (XDP_TYPE_INPUT_CAPTURE_SESSION, NULL); + + //parent_session->input_capture_session = session; /* weak ref */ + g_object_weak_ref (G_OBJECT (parent_session), parent_session_destroy, session); + session->parent_session = g_object_ref(parent_session); /* strong ref */ + + return g_object_ref(session); +} + +static void +get_zones_done (GDBusConnection *bus, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer data) +{ + Call *call = data; + guint32 response; + g_autoptr(GVariant) ret = NULL; + + g_variant_get (parameters, "(u@a{sv})", &response, &ret); + + if (response != 0 && call->cancelled_id) + { + g_signal_handler_disconnect (g_task_get_cancellable (call->task), call->cancelled_id); + call->cancelled_id = 0; + } + + if (response == 0) + { + GVariant *zones = NULL; + guint32 zone_set; + XdpGlobalShortcutsSession *session = call->session; + + g_dbus_connection_signal_unsubscribe (call->portal->bus, call->signal_id); + call->signal_id = 0; + + if (session == NULL) + { + session = _xdp_global_shortcuts_session_new (call->portal, call->session_path); + session->signal_ids[SIGNAL_SHORTCUTS_CHANGED] = + g_dbus_connection_signal_subscribe (bus, + PORTAL_BUS_NAME, + "org.freedesktop.portal.GlobalShortcuts", + "ShortcutsChanged", + PORTAL_OBJECT_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, + shortcuts_changed, + session, + NULL); + + session->signal_ids[SIGNAL_ACTIVATED] = + g_dbus_connection_signal_subscribe (bus, + PORTAL_BUS_NAME, + "org.freedesktop.portal.GlobalShortcuts", + "Activated", + PORTAL_OBJECT_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, + activated, + session, + NULL); + + session->signal_ids[SIGNAL_DEACTIVATED] = + g_dbus_connection_signal_subscribe (bus, + PORTAL_BUS_NAME, + "org.freedesktop.portal.GlobalShortcuts", + "Deactivated", + PORTAL_OBJECT_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, + deactivated, + session, + NULL); + } + } + + if (response == 1) + g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "InputCapture GetZones() canceled"); + else if (response == 2) + g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_FAILED, "InputCapture GetZones() failed"); + + if (response != 0) + call_free (call); +} + +static void +get_zones (Call *call) +{ + GVariantBuilder options; + const char *session_id; + + /* May be called after CreateSession before we have an XdpGlobalShortcutsSession, or by the + * ZoneChanged signal when we do have a session */ + session_id = call->session ? call->session->parent_session->id : call->session_path; + + prep_call (call, get_zones_done, &options, NULL); + g_dbus_connection_call (call->portal->bus, + PORTAL_BUS_NAME, + PORTAL_OBJECT_PATH, + "org.freedesktop.portal.GlobalShortcuts", + "BindShortcuts", + g_variant_new ("(oa{sv})", session_id, &options), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + g_task_get_cancellable (call->task), + call_returned, + call); +} + +static void +session_created (GDBusConnection *bus, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer data) +{ + Call *call = data; + guint32 response; + g_autoptr(GVariant) ret = NULL; + + g_variant_get (parameters, "(u@a{sv})", &response, &ret); + + if (response != 0 && call->cancelled_id) + { + g_signal_handler_disconnect (g_task_get_cancellable (call->task), call->cancelled_id); + call->cancelled_id = 0; + } + + if (response == 0) + { + g_dbus_connection_signal_unsubscribe (call->portal->bus, call->signal_id); + call->signal_id = 0; + + if (!g_variant_lookup (ret, "session_handle", "o", &call->session_path)) + { + g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_FAILED, "CreateSession failed to return a session handle"); + response = 2; + } + else + get_zones (call); + } + else if (response == 1) + g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "CreateSession canceled"); + else if (response == 2) + g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_FAILED, "CreateSession failed"); + + if (response != 0) + call_free (call); +} + +static void +call_cancelled_cb (GCancellable *cancellable, + gpointer data) +{ + Call *call = data; + + g_dbus_connection_call (call->portal->bus, + PORTAL_BUS_NAME, + call->request_path, + REQUEST_INTERFACE, + "Close", + NULL, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, NULL, NULL); +} + +static void +parent_exported (XdpParent *parent, + const char *handle, + gpointer data) +{ + Call *call = data; + call->parent_handle = g_strdup (handle); + create_session (call); +} + +static void +create_session (Call *call) +{ + GVariantBuilder options; + g_autofree char *session_token = NULL; + GCancellable *cancellable; + + if (call->parent_handle == NULL) + { + call->parent->parent_export (call->parent, parent_exported, call); + return; + } + + cancellable = g_task_get_cancellable (call->task); + if (cancellable) + call->cancelled_id = g_signal_connect (cancellable, "cancelled", G_CALLBACK (call_cancelled_cb), call); + + session_token = g_strdup_printf ("portal%d", g_random_int_range (0, G_MAXINT)); + + prep_call (call, session_created, &options, NULL); + g_variant_builder_add (&options, "{sv}", "session_handle_token", g_variant_new_string (session_token)); + + g_dbus_connection_call (call->portal->bus, + PORTAL_BUS_NAME, + PORTAL_OBJECT_PATH, + "org.freedesktop.portal.InputCapture", + "CreateSession", + g_variant_new ("(sa{sv})", call->parent_handle, &options), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + call_returned, + call); +} + +/** + * xdp_portal_create_global_shortcuts_session: + * @portal: a [class@Portal] + * @parent: (nullable): parent window information + * @cancellable: (nullable): optional [class@Gio.Cancellable] + * @callback: (scope async): a callback to call when the request is done + * @data: (closure): data to pass to @callback + * + * Creates a session for global shortcuts + * + * When the request is done, @callback will be called. You can then + * call [method@Portal.create_global_shortcuts_session_finish] to get the results. + */ +void +xdp_portal_create_global_shortcuts_session (XdpPortal *portal, + XdpParent *parent, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer data) +{ + Call *call; + + g_return_if_fail (XDP_IS_PORTAL (portal)); + + call = g_new0 (Call, 1); + call->portal = g_object_ref (portal); + call->task = g_task_new (portal, cancellable, callback, data); + + if (parent) + call->parent = xdp_parent_copy (parent); + else + call->parent_handle = g_strdup (""); + + create_session (call); +} + +/** + * xdp_portal_create_global_shortcuts_session_finish: + * @portal: a [class@Portal] + * @result: a [iface@Gio.AsyncResult] + * @error: return location for an error + * + * Finishes the GlobalShortcuts CreateSession request, and returns a + * [class@GlobalShortcutsSession]. To get to the [class@Session] within use + * xdp_global_shortcuts_session_get_session(). + * + * Returns: (transfer full): a [class@GlobalShortcutsSession] + */ +XdpGlobalShortcutsSession * +xdp_portal_create_global_shortcuts_session_finish (XdpPortal *portal, + GAsyncResult *result, + GError **error) +{ + XdpGlobalShortcutsSession *session; + + g_return_val_if_fail (XDP_IS_PORTAL (portal), NULL); + g_return_val_if_fail (g_task_is_valid (result, portal), NULL); + + session = g_task_propagate_pointer (G_TASK (result), error); + + if (session) + return session; + else + return NULL; +} + +/** + * xdp_global_shortcuts_session_get_session: + * @session: a [class@XdpGlobalShortcutsSession] + * + * Return the [class@XdpSession] for this GlobalShortcuts session. + * + * Returns: (transfer none): a [class@Session] object + */ +XdpSession * +xdp_global_shortcuts_session_get_session (XdpGlobalShortcutsSession *session) +{ + return session->parent_session; +} + +/** + * xdp_global_shortcuts_session_connect_to_eis: + * @session: a [class@InputCaptureSession] + * @error: return location for a #GError pointer + * + * Connect this session to an EIS implementation and return the fd. + * This fd can be passed into ei_setup_backend_fd(). See the libei + * documentation for details. + * + * This is a sync DBus invocation. + * + * Returns: a socket to the EIS implementation for this input capture + * session or a negative errno on failure. + */ +int +xdp_global_shortcuts_session_connect_to_eis (XdpGlobalShortcutsSession *session, + GError **error) +{ + GVariantBuilder options; + g_autoptr(GVariant) ret = NULL; + g_autoptr(GUnixFDList) fd_list = NULL; + int fd_out; + XdpPortal *portal; + XdpSession *parent_session = session->parent_session; + + if (!_xdp_global_shortcuts_session_is_valid (session)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Session is not an InputCapture session"); + return -1; + } + + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); + + portal = parent_session->portal; + ret = g_dbus_connection_call_with_unix_fd_list_sync (portal->bus, + PORTAL_BUS_NAME, + PORTAL_OBJECT_PATH, + "org.freedesktop.portal.InputCapture", + "ConnectToEIS", + g_variant_new ("(oa{sv})", + parent_session->id, + &options), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &fd_list, + NULL, + error); + + if (!ret) + return -1; + + g_variant_get (ret, "(h)", &fd_out); + + return g_unix_fd_list_get (fd_list, fd_out, NULL); +} + +static void +free_barrier_list (GList *list) +{ + g_list_free_full (list, g_object_unref); +} + +static void +list_shortcuts_done (GDBusConnection *bus, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer data) +{ + Call *call = data; + guint32 response; + g_autoptr(GVariant) ret = NULL; + GVariant *failed = NULL; + GList *failed_list = NULL; + + g_variant_get (parameters, "(u@a{sv})", &response, &ret); + + if (g_variant_lookup (ret, "failed_barriers", "@au", &failed)) + { + const guint *failed_barriers = NULL; + gsize n_elements; + GList *it = call->barriers; + + failed_barriers = g_variant_get_fixed_array (failed, &n_elements, sizeof (guint32)); + + } + + /* all failed barriers have an extra ref in failed_list, so we can unref all barriers + in our original list */ + free_barrier_list (call->barriers); + call->barriers = NULL; + g_task_return_pointer (call->task, failed_list, (GDestroyNotify)free_barrier_list); +} + + +static void +list_shortcuts (Call *call) +{ + GVariantBuilder options; + GVariantBuilder barriers; + g_autoptr(GVariantType) vtype; + + prep_call (call, list_shortcuts_done, &options, NULL); + + vtype = g_variant_type_new ("aa{sv}"); + + g_variant_builder_init (&barriers, vtype); + + + g_dbus_connection_call (call->portal->bus, + PORTAL_BUS_NAME, + PORTAL_OBJECT_PATH, + "org.freedesktop.portal.GlobalShortcuts", + "ListShortcuts", + g_variant_new ("(oa{sv}aa{sv}u)", + call->session->parent_session->id, + &options, + &barriers, + call->session->zone_set), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + g_task_get_cancellable (call->task), + call_returned, + call); +} + +static void +gobject_ref_wrapper (gpointer data, gpointer user_data) +{ + g_object_ref (G_OBJECT (data)); +} + +/** + * xdp_global_shortcuts_session_list_shortcuts: + * @session: a [class@GlobalShortcutsSession] + * + * List currently registered shortcuts and triggers. + */ +void +xdp_global_shortcuts_session_list_shortcuts (XdpGlobalShortcutsSession *session, + GList *barriers, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer data) +{ + Call *call; + XdpPortal *portal; + + g_return_if_fail (_xdp_global_shortcuts_session_is_valid (session)); + g_return_if_fail (barriers != NULL); + + portal = session->parent_session->portal; + + /* The list is ours, but we ref each object so we can create the list for the + * returned barriers during _finish*/ + g_list_foreach (barriers, gobject_ref_wrapper, NULL); + + call = g_new0 (Call, 1); + call->portal = g_object_ref (portal); + call->session = g_object_ref (session); + call->task = g_task_new (session, cancellable, callback, data); + call->barriers = barriers; + + list_shortcuts (call); +} + +/** + * xdp_global_shortcuts_session_list_shortcuts_finish: + * @session: a [class@GlobalShortcutsSession] + * @result: a [iface@Gio.AsyncResult] + * @error: return location for an error + * + * Finishes the list-shortcuts request, and returns a GList + * with the shortcuts. + * + * Returns: (element-type XdpGlobalShortcutsAssigned) (transfer full): a list of failed pointer barriers + */ + +GList * +xdp_global_shortcuts_session_list_shortcuts_finish (XdpGlobalShortcutsSession *session, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (_xdp_global_shortcuts_session_is_valid (session), NULL); + g_return_val_if_fail (g_task_is_valid (result, session), NULL); + + return g_task_propagate_pointer (G_TASK (result), error); +} + +/** + * xdp_global_shortcuts_session_enable: + * @session: a [class@InputCaptureSession] + * + * Enables this input capture session. In the future, this client may receive + * input events. + */ +void +xdp_global_shortcuts_session_enable (XdpGlobalShortcutsSession *session) +{ + XdpPortal *portal; + GVariantBuilder options; + + g_return_if_fail (_xdp_global_shortcuts_session_is_valid (session)); + + portal = session->parent_session->portal; + + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); + + g_dbus_connection_call (portal->bus, + PORTAL_BUS_NAME, + PORTAL_OBJECT_PATH, + "org.freedesktop.portal.InputCapture", + "Enable", + g_variant_new ("(oa{sv})", + session->parent_session->id, + &options), + NULL, + G_DBUS_CALL_FLAGS_NONE, + 1, + NULL, + NULL, + NULL); +} + +/** + * xdp_global_shortcuts_session_disable: + * @session: a [class@InputCaptureSession] + * + * Disables this input capture session. + */ +void +xdp_global_shortcuts_session_disable (XdpGlobalShortcutsSession *session) +{ + XdpPortal *portal; + GVariantBuilder options; + + g_return_if_fail (_xdp_global_shortcuts_session_is_valid (session)); + + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); + + portal = session->parent_session->portal; + g_dbus_connection_call (portal->bus, + PORTAL_BUS_NAME, + PORTAL_OBJECT_PATH, + "org.freedesktop.portal.InputCapture", + "Disable", + g_variant_new ("(oa{sv})", + session->parent_session->id, + &options), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +static void +release_session (XdpGlobalShortcutsSession *session, + guint activation_id, + gboolean with_position, + gdouble x, + gdouble y) +{ + XdpPortal *portal; + GVariantBuilder options; + + g_return_if_fail (_xdp_global_shortcuts_session_is_valid (session)); + + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&options, "{sv}", "activation_id", g_variant_new_uint32 (activation_id)); + + if (with_position) + { + g_variant_builder_add (&options, + "{sv}", + "cursor_position", + g_variant_new ("(dd)", x, y)); + } + + portal = session->parent_session->portal; + g_dbus_connection_call (portal->bus, + PORTAL_BUS_NAME, + PORTAL_OBJECT_PATH, + "org.freedesktop.portal.InputCapture", + "Release", + g_variant_new ("(oa{sv})", + session->parent_session->id, + &options), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); +} + +/** + * xdp_global_shortcuts_session_release: + * @session: a [class@InputCaptureSession] + * + * Releases this input capture session without a suggested cursor position. + */ +void +xdp_global_shortcuts_session_release (XdpGlobalShortcutsSession *session, + guint activation_id) +{ + g_return_if_fail (_xdp_global_shortcuts_session_is_valid (session)); + + release_session (session, activation_id, FALSE, 0, 0); +} + +/** + * xdp_global_shortcuts_session_release_at: + * @session: a [class@InputCaptureSession] + * @cursor_x_position: the suggested cursor x position once capture has been released + * @cursor_y_position: the suggested cursor y position once capture has been released + * + * Releases this input capture session with a suggested cursor position. + * Note that the implementation is not required to honour this position. + */ +void +xdp_global_shortcuts_session_release_at (XdpGlobalShortcutsSession *session, + guint activation_id, + gdouble cursor_x_position, + gdouble cursor_y_position) +{ + g_return_if_fail (_xdp_global_shortcuts_session_is_valid (session)); + + release_session (session, activation_id, TRUE, cursor_x_position, cursor_y_position); +} diff --git a/libportal/globalshortcuts.h b/libportal/globalshortcuts.h new file mode 100644 index 00000000..1c812939 --- /dev/null +++ b/libportal/globalshortcuts.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2018, Matthias Clasen + * + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, version 3.0 of the + * License. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see . + * + * SPDX-License-Identifier: LGPL-3.0-only + */ + +#pragma once + +#include +#include +#include + +G_BEGIN_DECLS + +#define XDP_TYPE_GLOBAL_SHORTCUTS_SESSION (xdp_global_shortcuts_session_get_type ()) + +XDP_PUBLIC +G_DECLARE_FINAL_TYPE (XdpGlobalShortcutsSession, xdp_global_shortcuts_session, XDP, GLOBAL_SHORTCUTS_SESSION, GObject) + + +XDP_PUBLIC +void xdp_portal_create_global_shortcuts_session (XdpPortal *portal, + XdpParent *parent, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer data); + +XDP_PUBLIC +XdpGlobalShortcutsSession * xdp_portal_create_global_shortucuts_session_finish (XdpPortal *portal, + GAsyncResult *result, + GError **error); + +XDP_PUBLIC +XdpSession *xdp_global_shortcuts_session_get_session (XdpGlobalShortcutsSession *session); + +XDP_PUBLIC +GList * xdp_global_shortcuts_session_get_zones (XdpGlobalShortcutsSession *session); + +XDP_PUBLIC +void xdp_global_shortcuts_session_set_pointer_barriers (XdpGlobalShortcutsSession *session, + GList *barriers, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer data); + +XDP_PUBLIC +GList * xdp_global_shortcuts_session_set_pointer_barriers_finish (XdpGlobalShortcutsSession *session, + GAsyncResult *result, + GError **error); + +XDP_PUBLIC +void xdp_global_shortcuts_session_enable (XdpGlobalShortcutsSession *session); + +XDP_PUBLIC +void xdp_global_shortcuts_session_disable (XdpGlobalShortcutsSession *session); + +XDP_PUBLIC +void xdp_global_shortcuts_session_release_at (XdpGlobalShortcutsSession *session, + guint activation_id, + gdouble cursor_x_position, + gdouble cursor_y_position); + +XDP_PUBLIC +void xdp_global_shortcuts_session_release (XdpGlobalShortcutsSession *session, + guint activation_id); + +XDP_PUBLIC +int xdp_global_shortcuts_session_connect_to_eis (XdpGlobalShortcutsSession *session, + GError **error); + +G_END_DECLS diff --git a/libportal/meson.build b/libportal/meson.build index 4e67f409..065b33e0 100644 --- a/libportal/meson.build +++ b/libportal/meson.build @@ -11,6 +11,7 @@ headers = [ 'dynamic-launcher.h', 'email.h', 'filechooser.h', + 'globalshortcuts.h', 'inhibit.h', 'inputcapture.h', 'inputcapture-zone.h', @@ -47,6 +48,7 @@ src = [ 'dynamic-launcher.c', 'email.c', 'filechooser.c', + 'globalshortcuts.c', 'inhibit.c', 'inputcapture.c', 'inputcapture-zone.c', diff --git a/portal-test/gtk3/portal-test-win.ui b/portal-test/gtk3/portal-test-win.ui index 656bd8ac..46cf01b1 100644 --- a/portal-test/gtk3/portal-test-win.ui +++ b/portal-test/gtk3/portal-test-win.ui @@ -838,7 +838,52 @@ - + + + 1 + end + Global Shortcuts + + + 0 + 22 + + + + + 1 + 0 + Request + + + + 1 + 22 + + + + + 1 + 1 + Release + + + + 2 + 22 + + + + + 1 + end + activations + + + 1 + 23 + + From e5f59bff9058fd63631d4ff10bafb737526f81dd Mon Sep 17 00:00:00 2001 From: dcz Date: Wed, 26 Jun 2024 15:34:19 +0000 Subject: [PATCH 2/9] session creates without crashing --- libportal/globalshortcuts.c | 217 +++------------------------- libportal/globalshortcuts.h | 23 +-- libportal/portal.h | 1 + libportal/session.h | 2 + portal-test/gtk3/portal-test-win.c | 78 ++++++++++ portal-test/gtk3/portal-test-win.ui | 6 +- 6 files changed, 110 insertions(+), 217 deletions(-) diff --git a/libportal/globalshortcuts.c b/libportal/globalshortcuts.c index 2c84ed24..08915c54 100644 --- a/libportal/globalshortcuts.c +++ b/libportal/globalshortcuts.c @@ -70,7 +70,7 @@ G_DEFINE_TYPE (XdpGlobalShortcutsSession, xdp_global_shortcuts_session, G_TYPE_O static gboolean _xdp_global_shortcuts_session_is_valid (XdpGlobalShortcutsSession *session) { - return XDP_IS_INPUT_CAPTURE_SESSION (session) && session->parent_session != NULL; + return XDP_IS_GLOBAL_SHORTCUTS_SESSION (session) && session->parent_session != NULL; } static void @@ -103,7 +103,7 @@ xdp_global_shortcuts_session_finalize (GObject *object) } g_object_weak_unref (G_OBJECT (parent_session), parent_session_destroy, session); - session->parent_session->input_capture_session = NULL; + //session->parent_session->input_capture_session = NULL; g_clear_pointer (&session->parent_session, g_object_unref); } @@ -193,10 +193,6 @@ typedef struct { char *request_path; /* object path for request */ guint cancelled_id; /* signal id for cancelled gobject signal */ - /* CreateSession only */ - XdpParent *parent; - char *parent_handle; - /* GetZones only */ XdpGlobalShortcutsSession *session; @@ -206,19 +202,10 @@ typedef struct { } Call; static void create_session (Call *call); -static void get_zones (Call *call); static void call_free (Call *call) { - /* CreateSesssion */ - if (call->parent) - { - call->parent->parent_unexport (call->parent); - xdp_parent_free (call->parent); - } - g_free (call->parent_handle); - /* Generic */ if (call->signal_id) g_dbus_connection_signal_unsubscribe (call->portal->bus, call->signal_id); @@ -330,8 +317,6 @@ shortcuts_changed (GDBusConnection *bus, call->portal = g_object_ref (portal); call->task = g_task_new (portal, NULL, shortcuts_changed_emit_signal, session); call->session = g_object_ref (session); - - get_zones (call); } static void @@ -389,8 +374,8 @@ deactivated (GDBusConnection *bus, static XdpGlobalShortcutsSession * _xdp_global_shortcuts_session_new (XdpPortal *portal, const char *session_path) { - g_autoptr(XdpSession) parent_session = _xdp_session_new (portal, session_path, XDP_SESSION_INPUT_CAPTURE); - g_autoptr(XdpGlobalShortcutsSession) session = g_object_new (XDP_TYPE_INPUT_CAPTURE_SESSION, NULL); + g_autoptr(XdpSession) parent_session = _xdp_session_new (portal, session_path, XDP_SESSION_GLOBAL_SHORTCUTS); + g_autoptr(XdpGlobalShortcutsSession) session = g_object_new (XDP_TYPE_GLOBAL_SHORTCUTS_SESSION, NULL); //parent_session->input_capture_session = session; /* weak ref */ g_object_weak_ref (G_OBJECT (parent_session), parent_session_destroy, session); @@ -399,6 +384,13 @@ _xdp_global_shortcuts_session_new (XdpPortal *portal, const char *session_path) return g_object_ref(session); } + +void +xdp_global_shortcuts_session_close (XdpGlobalShortcutsSession *session) +{ + _xdp_session_close (session->parent_session); +} + static void get_zones_done (GDBusConnection *bus, const char *sender_name, @@ -471,16 +463,16 @@ get_zones_done (GDBusConnection *bus, } if (response == 1) - g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "InputCapture GetZones() canceled"); + g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "GlobalShortcuts CreateSession() canceled"); else if (response == 2) - g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_FAILED, "InputCapture GetZones() failed"); + g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_FAILED, "GlobalShortcuts CreateSession() failed"); if (response != 0) call_free (call); } static void -get_zones (Call *call) +bind_shortcuts (Call *call) { GVariantBuilder options; const char *session_id; @@ -530,13 +522,16 @@ session_created (GDBusConnection *bus, g_dbus_connection_signal_unsubscribe (call->portal->bus, call->signal_id); call->signal_id = 0; - if (!g_variant_lookup (ret, "session_handle", "o", &call->session_path)) + if (!g_variant_lookup (ret, "session_handle", "s", &call->session_path)) { g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_FAILED, "CreateSession failed to return a session handle"); response = 2; } - else - get_zones (call); + else + { + call->session = _xdp_global_shortcuts_session_new (call->portal, call->session_path); + g_task_return_pointer (call->task, call->session, g_object_unref); + } } else if (response == 1) g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "CreateSession canceled"); @@ -565,16 +560,6 @@ call_cancelled_cb (GCancellable *cancellable, NULL, NULL, NULL); } -static void -parent_exported (XdpParent *parent, - const char *handle, - gpointer data) -{ - Call *call = data; - call->parent_handle = g_strdup (handle); - create_session (call); -} - static void create_session (Call *call) { @@ -582,12 +567,6 @@ create_session (Call *call) g_autofree char *session_token = NULL; GCancellable *cancellable; - if (call->parent_handle == NULL) - { - call->parent->parent_export (call->parent, parent_exported, call); - return; - } - cancellable = g_task_get_cancellable (call->task); if (cancellable) call->cancelled_id = g_signal_connect (cancellable, "cancelled", G_CALLBACK (call_cancelled_cb), call); @@ -600,9 +579,9 @@ create_session (Call *call) g_dbus_connection_call (call->portal->bus, PORTAL_BUS_NAME, PORTAL_OBJECT_PATH, - "org.freedesktop.portal.InputCapture", + "org.freedesktop.portal.GlobalShortcuts", "CreateSession", - g_variant_new ("(sa{sv})", call->parent_handle, &options), + g_variant_new ("(a{sv})", &options), NULL, G_DBUS_CALL_FLAGS_NONE, -1, @@ -614,7 +593,6 @@ create_session (Call *call) /** * xdp_portal_create_global_shortcuts_session: * @portal: a [class@Portal] - * @parent: (nullable): parent window information * @cancellable: (nullable): optional [class@Gio.Cancellable] * @callback: (scope async): a callback to call when the request is done * @data: (closure): data to pass to @callback @@ -626,7 +604,6 @@ create_session (Call *call) */ void xdp_portal_create_global_shortcuts_session (XdpPortal *portal, - XdpParent *parent, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data) @@ -639,11 +616,6 @@ xdp_portal_create_global_shortcuts_session (XdpPortal *portal, call->portal = g_object_ref (portal); call->task = g_task_new (portal, cancellable, callback, data); - if (parent) - call->parent = xdp_parent_copy (parent); - else - call->parent_handle = g_strdup (""); - create_session (call); } @@ -718,7 +690,7 @@ xdp_global_shortcuts_session_connect_to_eis (XdpGlobalShortcutsSession *session if (!_xdp_global_shortcuts_session_is_valid (session)) { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Session is not an InputCapture session"); + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Session is not a GlobalShortcuts session"); return -1; } @@ -884,146 +856,3 @@ xdp_global_shortcuts_session_list_shortcuts_finish (XdpGlobalShortcutsSession *s return g_task_propagate_pointer (G_TASK (result), error); } - -/** - * xdp_global_shortcuts_session_enable: - * @session: a [class@InputCaptureSession] - * - * Enables this input capture session. In the future, this client may receive - * input events. - */ -void -xdp_global_shortcuts_session_enable (XdpGlobalShortcutsSession *session) -{ - XdpPortal *portal; - GVariantBuilder options; - - g_return_if_fail (_xdp_global_shortcuts_session_is_valid (session)); - - portal = session->parent_session->portal; - - g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); - - g_dbus_connection_call (portal->bus, - PORTAL_BUS_NAME, - PORTAL_OBJECT_PATH, - "org.freedesktop.portal.InputCapture", - "Enable", - g_variant_new ("(oa{sv})", - session->parent_session->id, - &options), - NULL, - G_DBUS_CALL_FLAGS_NONE, - 1, - NULL, - NULL, - NULL); -} - -/** - * xdp_global_shortcuts_session_disable: - * @session: a [class@InputCaptureSession] - * - * Disables this input capture session. - */ -void -xdp_global_shortcuts_session_disable (XdpGlobalShortcutsSession *session) -{ - XdpPortal *portal; - GVariantBuilder options; - - g_return_if_fail (_xdp_global_shortcuts_session_is_valid (session)); - - g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); - - portal = session->parent_session->portal; - g_dbus_connection_call (portal->bus, - PORTAL_BUS_NAME, - PORTAL_OBJECT_PATH, - "org.freedesktop.portal.InputCapture", - "Disable", - g_variant_new ("(oa{sv})", - session->parent_session->id, - &options), - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - NULL, - NULL); -} - -static void -release_session (XdpGlobalShortcutsSession *session, - guint activation_id, - gboolean with_position, - gdouble x, - gdouble y) -{ - XdpPortal *portal; - GVariantBuilder options; - - g_return_if_fail (_xdp_global_shortcuts_session_is_valid (session)); - - g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); - g_variant_builder_add (&options, "{sv}", "activation_id", g_variant_new_uint32 (activation_id)); - - if (with_position) - { - g_variant_builder_add (&options, - "{sv}", - "cursor_position", - g_variant_new ("(dd)", x, y)); - } - - portal = session->parent_session->portal; - g_dbus_connection_call (portal->bus, - PORTAL_BUS_NAME, - PORTAL_OBJECT_PATH, - "org.freedesktop.portal.InputCapture", - "Release", - g_variant_new ("(oa{sv})", - session->parent_session->id, - &options), - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - NULL, - NULL); -} - -/** - * xdp_global_shortcuts_session_release: - * @session: a [class@InputCaptureSession] - * - * Releases this input capture session without a suggested cursor position. - */ -void -xdp_global_shortcuts_session_release (XdpGlobalShortcutsSession *session, - guint activation_id) -{ - g_return_if_fail (_xdp_global_shortcuts_session_is_valid (session)); - - release_session (session, activation_id, FALSE, 0, 0); -} - -/** - * xdp_global_shortcuts_session_release_at: - * @session: a [class@InputCaptureSession] - * @cursor_x_position: the suggested cursor x position once capture has been released - * @cursor_y_position: the suggested cursor y position once capture has been released - * - * Releases this input capture session with a suggested cursor position. - * Note that the implementation is not required to honour this position. - */ -void -xdp_global_shortcuts_session_release_at (XdpGlobalShortcutsSession *session, - guint activation_id, - gdouble cursor_x_position, - gdouble cursor_y_position) -{ - g_return_if_fail (_xdp_global_shortcuts_session_is_valid (session)); - - release_session (session, activation_id, TRUE, cursor_x_position, cursor_y_position); -} diff --git a/libportal/globalshortcuts.h b/libportal/globalshortcuts.h index 1c812939..ffe214b2 100644 --- a/libportal/globalshortcuts.h +++ b/libportal/globalshortcuts.h @@ -33,13 +33,12 @@ G_DECLARE_FINAL_TYPE (XdpGlobalShortcutsSession, xdp_global_shortcuts_session, X XDP_PUBLIC void xdp_portal_create_global_shortcuts_session (XdpPortal *portal, - XdpParent *parent, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data); XDP_PUBLIC -XdpGlobalShortcutsSession * xdp_portal_create_global_shortucuts_session_finish (XdpPortal *portal, +XdpGlobalShortcutsSession * xdp_portal_create_global_shortcuts_session_finish (XdpPortal *portal, GAsyncResult *result, GError **error); @@ -47,7 +46,7 @@ XDP_PUBLIC XdpSession *xdp_global_shortcuts_session_get_session (XdpGlobalShortcutsSession *session); XDP_PUBLIC -GList * xdp_global_shortcuts_session_get_zones (XdpGlobalShortcutsSession *session); +void xdp_global_shortcuts_session_close (XdpGlobalShortcutsSession *session); XDP_PUBLIC void xdp_global_shortcuts_session_set_pointer_barriers (XdpGlobalShortcutsSession *session, @@ -62,23 +61,7 @@ GList * xdp_global_shortcuts_session_set_pointer_barriers_finish (XdpGlobalS GError **error); XDP_PUBLIC -void xdp_global_shortcuts_session_enable (XdpGlobalShortcutsSession *session); +void xdp_global_shortcuts_session_release (XdpGlobalShortcutsSession *session); -XDP_PUBLIC -void xdp_global_shortcuts_session_disable (XdpGlobalShortcutsSession *session); - -XDP_PUBLIC -void xdp_global_shortcuts_session_release_at (XdpGlobalShortcutsSession *session, - guint activation_id, - gdouble cursor_x_position, - gdouble cursor_y_position); - -XDP_PUBLIC -void xdp_global_shortcuts_session_release (XdpGlobalShortcutsSession *session, - guint activation_id); - -XDP_PUBLIC -int xdp_global_shortcuts_session_connect_to_eis (XdpGlobalShortcutsSession *session, - GError **error); G_END_DECLS diff --git a/libportal/portal.h b/libportal/portal.h index 3618b81e..97c25790 100644 --- a/libportal/portal.h +++ b/libportal/portal.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/libportal/session.h b/libportal/session.h index e9f02145..fcb8140c 100644 --- a/libportal/session.h +++ b/libportal/session.h @@ -33,6 +33,7 @@ G_DECLARE_FINAL_TYPE (XdpSession, xdp_session, XDP, SESSION, GObject) * @XDP_SESSION_SCREENCAST: a screencast session. * @XDP_SESSION_REMOTE_DESKTOP: a remote desktop session. * @XDP_SESSION_INPUT_CAPTURE: an input capture session. + * @XDP_SESSION_GLOBAL_SHORTCUTS: a global shortcuts session. * * The type of a session. */ @@ -40,6 +41,7 @@ typedef enum { XDP_SESSION_SCREENCAST, XDP_SESSION_REMOTE_DESKTOP, XDP_SESSION_INPUT_CAPTURE, + XDP_SESSION_GLOBAL_SHORTCUTS, } XdpSessionType; XDP_PUBLIC diff --git a/portal-test/gtk3/portal-test-win.c b/portal-test/gtk3/portal-test-win.c index e95c1c13..d9f80640 100644 --- a/portal-test/gtk3/portal-test-win.c +++ b/portal-test/gtk3/portal-test-win.c @@ -62,6 +62,7 @@ struct _PortalTestWin XdpPortal *portal; XdpSession *session; + XdpGlobalShortcutsSession *gs_session; GNetworkMonitor *monitor; GProxyResolver *resolver; @@ -91,6 +92,8 @@ struct _PortalTestWin GtkWidget *screencast_label; GtkWidget *screencast_toggle; + GtkWidget *globalshortcuts_activations; + GtkWidget *inputcapture_label; GtkWidget *inputcapture_toggle; @@ -715,6 +718,77 @@ capture_input_release (GtkButton *button, /* FIXME */ } + +static void +globalshortcuts_session_created (GObject *source, + GAsyncResult *result, + gpointer data) +{ + XdpPortal *portal = XDP_PORTAL (source); + PortalTestWin *win = data; + g_autoptr(GError) error = NULL; + GList *zones; + g_autoptr (GString) s = NULL; + XdpGlobalShortcutsSession *session; + + session = xdp_portal_create_global_shortcuts_session_finish (portal, result, &error); + if (session == NULL) + { + g_warning ("Failed to create GlobalShortcuts session: %s", error->message); + return; + } + win->gs_session = session; + +// zones = xdp_input_capture_session_get_zones (XDP_INPUT_CAPTURE_SESSION (win->session)); + s = g_string_new ("session"); +/* for (GList *elem = g_list_first (zones); elem; elem = g_list_next (elem)) + { + XdpInputCaptureZone *zone = elem->data; + guint w, h; + gint x, y; + + g_object_get (zone, + "width", &w, + "height", &h, + "x", &x, + "y", &y, + NULL); + + g_string_append_printf (s, "%ux%u@%d,%d ", w, h, x, y); + }*/ + gtk_label_set_label (GTK_LABEL (win->inputcapture_label), s->str); +} + +static void +globalshortcuts_session_start (PortalTestWin *win) +{ + g_clear_object (&win->session); + + xdp_portal_create_global_shortcuts_session (win->portal, + NULL, + globalshortcuts_session_created, + win); +} + + +static void +globalshortcuts_session_stop (PortalTestWin *win) +{ + xdp_global_shortcuts_session_close (win->gs_session); + g_clear_object (&win->gs_session); + gtk_label_set_label (GTK_LABEL (win->globalshortcuts_activations), ""); +} + +static void +global_shortcuts_request (GtkButton *button, + PortalTestWin *win) +{ + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) + globalshortcuts_session_start (win); + else + globalshortcuts_session_stop (win); +} + static void session_started (GObject *source, GAsyncResult *result, @@ -737,6 +811,8 @@ session_started (GObject *source, return; } + + s = g_string_new (""); iter = g_variant_iter_new (xdp_session_get_streams (session)); @@ -1401,6 +1477,7 @@ portal_test_win_class_init (PortalTestWinClass *class) gtk_widget_class_bind_template_callback (widget_class, open_directory); gtk_widget_class_bind_template_callback (widget_class, open_local); gtk_widget_class_bind_template_callback (widget_class, take_screenshot); + gtk_widget_class_bind_template_callback (widget_class, global_shortcuts_request); gtk_widget_class_bind_template_callback (widget_class, capture_input); gtk_widget_class_bind_template_callback (widget_class, capture_input_release); gtk_widget_class_bind_template_callback (widget_class, screencast_toggled); @@ -1426,6 +1503,7 @@ portal_test_win_class_init (PortalTestWinClass *class) gtk_widget_class_bind_template_child (widget_class, PortalTestWin, inhibit_logout); gtk_widget_class_bind_template_child (widget_class, PortalTestWin, inhibit_suspend); gtk_widget_class_bind_template_child (widget_class, PortalTestWin, inhibit_switch); + gtk_widget_class_bind_template_child (widget_class, PortalTestWin, globalshortcuts_activations); gtk_widget_class_bind_template_child (widget_class, PortalTestWin, inputcapture_label); gtk_widget_class_bind_template_child (widget_class, PortalTestWin, inputcapture_toggle); gtk_widget_class_bind_template_child (widget_class, PortalTestWin, username); diff --git a/portal-test/gtk3/portal-test-win.ui b/portal-test/gtk3/portal-test-win.ui index 46cf01b1..cd80c98b 100644 --- a/portal-test/gtk3/portal-test-win.ui +++ b/portal-test/gtk3/portal-test-win.ui @@ -850,11 +850,11 @@ - + 1 0 Request - + 1 @@ -874,7 +874,7 @@ - + 1 end activations From 4b3aca4869a88dd80adce8b55a2adbbb66298484 Mon Sep 17 00:00:00 2001 From: dcz Date: Wed, 26 Jun 2024 15:36:34 +0000 Subject: [PATCH 3/9] session closes without crashing --- libportal/globalshortcuts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libportal/globalshortcuts.c b/libportal/globalshortcuts.c index 08915c54..6ee8e554 100644 --- a/libportal/globalshortcuts.c +++ b/libportal/globalshortcuts.c @@ -388,7 +388,7 @@ _xdp_global_shortcuts_session_new (XdpPortal *portal, const char *session_path) void xdp_global_shortcuts_session_close (XdpGlobalShortcutsSession *session) { - _xdp_session_close (session->parent_session); + xdp_session_close (session->parent_session); } static void From 388e116a04057d41dd77451eea9e11a5e9c77a9b Mon Sep 17 00:00:00 2001 From: dcz Date: Thu, 27 Jun 2024 08:26:29 +0000 Subject: [PATCH 4/9] calls bindshortcuts --- libportal/globalshortcuts.c | 131 +++++++++++++++++++++++------ libportal/globalshortcuts.h | 17 +++- portal-test/gtk3/portal-test-win.c | 63 +++++++++++++- 3 files changed, 179 insertions(+), 32 deletions(-) diff --git a/libportal/globalshortcuts.c b/libportal/globalshortcuts.c index 6ee8e554..e7071d03 100644 --- a/libportal/globalshortcuts.c +++ b/libportal/globalshortcuts.c @@ -392,7 +392,7 @@ xdp_global_shortcuts_session_close (XdpGlobalShortcutsSession *session) } static void -get_zones_done (GDBusConnection *bus, +bind_shortcuts_done (GDBusConnection *bus, const char *sender_name, const char *object_path, const char *interface_name, @@ -471,31 +471,6 @@ get_zones_done (GDBusConnection *bus, call_free (call); } -static void -bind_shortcuts (Call *call) -{ - GVariantBuilder options; - const char *session_id; - - /* May be called after CreateSession before we have an XdpGlobalShortcutsSession, or by the - * ZoneChanged signal when we do have a session */ - session_id = call->session ? call->session->parent_session->id : call->session_path; - - prep_call (call, get_zones_done, &options, NULL); - g_dbus_connection_call (call->portal->bus, - PORTAL_BUS_NAME, - PORTAL_OBJECT_PATH, - "org.freedesktop.portal.GlobalShortcuts", - "BindShortcuts", - g_variant_new ("(oa{sv})", session_id, &options), - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - g_task_get_cancellable (call->task), - call_returned, - call); -} - static void session_created (GDBusConnection *bus, const char *sender_name, @@ -800,6 +775,109 @@ gobject_ref_wrapper (gpointer data, gpointer user_data) g_object_ref (G_OBJECT (data)); } +/** + * xdp_global_shortcuts_session_bind_shortcuts: + * @session: a [class@GlobalShortcutsSession] + * @shortcuts: GArray + * + * Bind shortcuts and list triggers. + */ +void xdp_global_shortcuts_session_bind_shortcuts(XdpGlobalShortcutsSession *session, + // Contains XdpGlobalShortcut elements + GArray *shortcuts, + char *parent_window, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer data) +{ + Call *call; + XdpPortal *portal; + guint i; + GVariantBuilder options; + GVariantBuilder shortcuts_builder; + g_autoptr(GVariantType) vtype; + + g_return_if_fail (_xdp_global_shortcuts_session_is_valid (session)); + g_return_if_fail (shortcuts != NULL); + + g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); + + portal = session->parent_session->portal; + + call = g_new0 (Call, 1); + call->portal = g_object_ref (portal); + call->task = g_task_new (portal, cancellable, callback, data); + + prep_call (call, bind_shortcuts_done, &options, NULL); + + vtype = g_variant_type_new ("a(sa{sv})"); + + g_variant_builder_init (&shortcuts_builder, vtype); + + for (i = 0; i < shortcuts->len; i++) { + GVariantDict dict; + g_auto (GStrv) combos = NULL; + struct XdpGlobalShortcut shortcut = ((struct XdpGlobalShortcut*)shortcuts->data)[i]; + + g_variant_dict_init (&dict, NULL); + if (shortcut.preferred_trigger) + g_variant_dict_insert (&dict, "preferred_trigger", "s", shortcut.preferred_trigger); + g_variant_dict_insert (&dict, "description", "s", shortcut.description); + + g_variant_builder_add (&shortcuts_builder, "(s@a{sv})", shortcut.name, g_variant_dict_end (&dict)); + } + + if (parent_window == NULL) + parent_window = ""; + + GVariant *v = g_variant_new ("(o@a(sa{sv})s@a{sv})", + session->parent_session->id, + g_variant_builder_end(&shortcuts_builder), + parent_window, + g_variant_builder_end(&options)); + + g_dbus_connection_call (call->portal->bus, + PORTAL_BUS_NAME, + PORTAL_OBJECT_PATH, + "org.freedesktop.portal.GlobalShortcuts", + "BindShortcuts", + v, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + g_task_get_cancellable (call->task), + call_returned, + call); +} + + +/** + * xdp_global_shortcuts_session_bind_shortcuts_finish: + * @session: a [class@GlobalShortcutsSession] + * @result: a [iface@Gio.AsyncResult] + * @error: return location for an error + * + * Finishes the GlobalShortcuts BindShortcuts request. + * + * Returns: (transfer full): an array of [GlobalShortcutAssigned]. + */ +GArray * +xdp_global_shortcuts_session_bind_shortcuts_finish (XdpGlobalShortcutsSession *session, + GAsyncResult *result, + GError **error) +{ + GVariant *r; + g_return_val_if_fail (XDP_IS_GLOBAL_SHORTCUTS_SESSION (session), NULL); + g_return_val_if_fail (g_task_is_valid (result, session), NULL); + + r = g_task_propagate_pointer (G_TASK (result), error); + + if (session) + return session; + else + return NULL; +} + /** * xdp_global_shortcuts_session_list_shortcuts: * @session: a [class@GlobalShortcutsSession] @@ -856,3 +934,4 @@ xdp_global_shortcuts_session_list_shortcuts_finish (XdpGlobalShortcutsSession *s return g_task_propagate_pointer (G_TASK (result), error); } + diff --git a/libportal/globalshortcuts.h b/libportal/globalshortcuts.h index ffe214b2..327d1011 100644 --- a/libportal/globalshortcuts.h +++ b/libportal/globalshortcuts.h @@ -31,6 +31,16 @@ XDP_PUBLIC G_DECLARE_FINAL_TYPE (XdpGlobalShortcutsSession, xdp_global_shortcuts_session, XDP, GLOBAL_SHORTCUTS_SESSION, GObject) +struct XdpGlobalShortcut { + // Shortcut ID, Owned by caller + char *name; + // Shortcut description, Owned by caller + char *description; + // Shortcut suggested trigger, nullable, Owned by caller + char *preferred_trigger; +}; + + XDP_PUBLIC void xdp_portal_create_global_shortcuts_session (XdpPortal *portal, GCancellable *cancellable, @@ -49,14 +59,15 @@ XDP_PUBLIC void xdp_global_shortcuts_session_close (XdpGlobalShortcutsSession *session); XDP_PUBLIC -void xdp_global_shortcuts_session_set_pointer_barriers (XdpGlobalShortcutsSession *session, - GList *barriers, +void xdp_global_shortcuts_session_bind_shortcuts(XdpGlobalShortcutsSession *session, + // Contains XdpGlobalShortcut elements + GArray *shortcuts, char *parent_window, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data); XDP_PUBLIC -GList * xdp_global_shortcuts_session_set_pointer_barriers_finish (XdpGlobalShortcutsSession *session, +GArray * xdp_global_shortcuts_session_bind_shortcuts_finish (XdpGlobalShortcutsSession *session, GAsyncResult *result, GError **error); diff --git a/portal-test/gtk3/portal-test-win.c b/portal-test/gtk3/portal-test-win.c index d9f80640..9117b469 100644 --- a/portal-test/gtk3/portal-test-win.c +++ b/portal-test/gtk3/portal-test-win.c @@ -719,6 +719,58 @@ capture_input_release (GtkButton *button, } +static void +globalshortcuts_bind_done (GObject *source, + GAsyncResult *result, + gpointer data) +{ + XdpPortal *portal = XDP_PORTAL (source); + PortalTestWin *win = data; + g_autoptr(GError) error = NULL; + g_autoptr (GString) s = NULL; + XdpGlobalShortcutsSession *session; + + session = xdp_global_shortcuts_session_bind_shortcuts_finish(session, result, &error); + if (session == NULL) + { + g_warning ("Failed to create GlobalShortcuts session: %s", error->message); + return; + } + win->gs_session = session; + + s = g_string_new ("session"); + /* for (GList *elem = g_list_first (zones); elem; elem = g_list_next (elem)) + { + XdpInputCaptureZone *zone = elem->data; + guint w, h; + gint x, y; + + g_object_get (zone, + "width", &w, + "height", &h, + "x", &x, + "y", &y, + NULL); + + g_string_append_printf (s, "%ux%u@%d,%d ", w, h, x, y); + }*/ + gtk_label_set_label (GTK_LABEL (win->inputcapture_label), s->str); +} + +static void +globalshortcuts_bind(XdpGlobalShortcutsSession *session) +{ + struct XdpGlobalShortcut s[2] = {{.description = "Do Foo", .name = "foo", .preferred_trigger = "CTRL+F"}, + {.description = "Do Bar", .name = "bar", .preferred_trigger = NULL}}; + GArray shortcuts = {.data=(char*)s, .len=2}; + xdp_global_shortcuts_session_bind_shortcuts(session, + &shortcuts, + NULL, + NULL, + globalshortcuts_bind_done, + NULL); +} + static void globalshortcuts_session_created (GObject *source, GAsyncResult *result, @@ -727,7 +779,6 @@ globalshortcuts_session_created (GObject *source, XdpPortal *portal = XDP_PORTAL (source); PortalTestWin *win = data; g_autoptr(GError) error = NULL; - GList *zones; g_autoptr (GString) s = NULL; XdpGlobalShortcutsSession *session; @@ -739,7 +790,6 @@ globalshortcuts_session_created (GObject *source, } win->gs_session = session; -// zones = xdp_input_capture_session_get_zones (XDP_INPUT_CAPTURE_SESSION (win->session)); s = g_string_new ("session"); /* for (GList *elem = g_list_first (zones); elem; elem = g_list_next (elem)) { @@ -757,12 +807,13 @@ globalshortcuts_session_created (GObject *source, g_string_append_printf (s, "%ux%u@%d,%d ", w, h, x, y); }*/ gtk_label_set_label (GTK_LABEL (win->inputcapture_label), s->str); + globalshortcuts_bind(win->gs_session); } static void globalshortcuts_session_start (PortalTestWin *win) { - g_clear_object (&win->session); + g_clear_object (&win->gs_session); xdp_portal_create_global_shortcuts_session (win->portal, NULL, @@ -779,14 +830,20 @@ globalshortcuts_session_stop (PortalTestWin *win) gtk_label_set_label (GTK_LABEL (win->globalshortcuts_activations), ""); } + + static void global_shortcuts_request (GtkButton *button, PortalTestWin *win) { if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) + { globalshortcuts_session_start (win); + } else + { globalshortcuts_session_stop (win); + } } static void From ad1c66f2d0aa5727c824da3c191ee98ff809e83e Mon Sep 17 00:00:00 2001 From: dcz Date: Thu, 27 Jun 2024 11:57:00 +0000 Subject: [PATCH 5/9] Bind returns IDs --- libportal/globalshortcuts.c | 52 +++++++++++++++++++++---- libportal/globalshortcuts.h | 7 ++++ portal-test/gtk3/portal-test-win.c | 60 ++++++++--------------------- portal-test/gtk3/portal-test-win.ui | 16 +------- 4 files changed, 70 insertions(+), 65 deletions(-) diff --git a/libportal/globalshortcuts.c b/libportal/globalshortcuts.c index e7071d03..28d3e7ee 100644 --- a/libportal/globalshortcuts.c +++ b/libportal/globalshortcuts.c @@ -414,8 +414,6 @@ bind_shortcuts_done (GDBusConnection *bus, if (response == 0) { - GVariant *zones = NULL; - guint32 zone_set; XdpGlobalShortcutsSession *session = call->session; g_dbus_connection_signal_unsubscribe (call->portal->bus, call->signal_id); @@ -460,12 +458,13 @@ bind_shortcuts_done (GDBusConnection *bus, session, NULL); } + g_task_return_pointer (call->task, ret, g_object_unref); } if (response == 1) - g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "GlobalShortcuts CreateSession() canceled"); + g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "GlobalShortcuts BindShortcuts() canceled"); else if (response == 2) - g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_FAILED, "GlobalShortcuts CreateSession() failed"); + g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_FAILED, "GlobalShortcuts BindShortcuts() failed"); if (response != 0) call_free (call); @@ -868,14 +867,51 @@ xdp_global_shortcuts_session_bind_shortcuts_finish (XdpGlobalShortcutsSession *s { GVariant *r; g_return_val_if_fail (XDP_IS_GLOBAL_SHORTCUTS_SESSION (session), NULL); - g_return_val_if_fail (g_task_is_valid (result, session), NULL); + g_return_val_if_fail (g_task_is_valid (result, session->parent_session->portal), NULL); r = g_task_propagate_pointer (G_TASK (result), error); - - if (session) - return session; + if (r) + { + GVariantIter *items; + if (g_variant_lookup(r, "shortcuts", "a(sa{sv})", &items)) + { + //GVariantIter items; + char *name; + GVariant *item; + struct XdpGlobalShortcutAssigned *shortcuts; + GArray *ret; + guint i = 0; + //g_print("%s", g_variant_print(s, TRUE)); +// g_variant_iter_init(&items, s); + + shortcuts = g_new0(struct XdpGlobalShortcutAssigned, + g_variant_iter_n_children(items)); + + while (g_variant_iter_next(items, "(s@a{sv})", &name, &item)) + { + struct XdpGlobalShortcutAssigned *shortcut = &shortcuts[i]; + shortcut->name = name; + g_variant_lookup(item, "s", "trigger_description", &shortcut->trigger_description); + i++; + } + ret = g_array_new_take(shortcuts, + g_variant_iter_n_children(items), + TRUE, + sizeof(struct XdpGlobalShortcutAssigned)); + + return ret; + } + else + { + g_debug("%s", g_variant_print(r, TRUE)); + g_error("GlobalShortcuts::BindShortcuts() did not return \"shortcuts\" key."); + return NULL; + } + } else + { return NULL; + } } /** diff --git a/libportal/globalshortcuts.h b/libportal/globalshortcuts.h index 327d1011..b13ea54d 100644 --- a/libportal/globalshortcuts.h +++ b/libportal/globalshortcuts.h @@ -40,6 +40,13 @@ struct XdpGlobalShortcut { char *preferred_trigger; }; +struct XdpGlobalShortcutAssigned { + // Shortcut ID, Owned + char *name; + // Human-readable trigger description, Owned + char *trigger_description; +}; + XDP_PUBLIC void xdp_portal_create_global_shortcuts_session (XdpPortal *portal, diff --git a/portal-test/gtk3/portal-test-win.c b/portal-test/gtk3/portal-test-win.c index 9117b469..5e7bd01e 100644 --- a/portal-test/gtk3/portal-test-win.c +++ b/portal-test/gtk3/portal-test-win.c @@ -728,47 +728,38 @@ globalshortcuts_bind_done (GObject *source, PortalTestWin *win = data; g_autoptr(GError) error = NULL; g_autoptr (GString) s = NULL; - XdpGlobalShortcutsSession *session; + XdpGlobalShortcutsSession *session = win->gs_session; + GArray *shortcuts; - session = xdp_global_shortcuts_session_bind_shortcuts_finish(session, result, &error); - if (session == NULL) + shortcuts = xdp_global_shortcuts_session_bind_shortcuts_finish(session, result, &error); + if (shortcuts == NULL) { - g_warning ("Failed to create GlobalShortcuts session: %s", error->message); + g_warning ("Failed to bind GlobalShortcuts: %s", error->message); + gtk_label_set_label (GTK_LABEL (win->globalshortcuts_activations), "failed to bind"); return; } - win->gs_session = session; - s = g_string_new ("session"); - /* for (GList *elem = g_list_first (zones); elem; elem = g_list_next (elem)) + s = g_string_new (""); + for (guint i = 0; i < shortcuts->len; i++) { - XdpInputCaptureZone *zone = elem->data; - guint w, h; - gint x, y; - - g_object_get (zone, - "width", &w, - "height", &h, - "x", &x, - "y", &y, - NULL); - - g_string_append_printf (s, "%ux%u@%d,%d ", w, h, x, y); - }*/ - gtk_label_set_label (GTK_LABEL (win->inputcapture_label), s->str); + struct XdpGlobalShortcutAssigned shortcut = ((struct XdpGlobalShortcutAssigned*)shortcuts->data)[i]; + g_string_append_printf (s, "%s: %s ", shortcut.name, shortcut.trigger_description); + } + gtk_label_set_label (GTK_LABEL (win->globalshortcuts_activations), s->str); } static void -globalshortcuts_bind(XdpGlobalShortcutsSession *session) +globalshortcuts_bind(PortalTestWin *win) { struct XdpGlobalShortcut s[2] = {{.description = "Do Foo", .name = "foo", .preferred_trigger = "CTRL+F"}, {.description = "Do Bar", .name = "bar", .preferred_trigger = NULL}}; GArray shortcuts = {.data=(char*)s, .len=2}; - xdp_global_shortcuts_session_bind_shortcuts(session, + xdp_global_shortcuts_session_bind_shortcuts(win->gs_session, &shortcuts, NULL, NULL, globalshortcuts_bind_done, - NULL); + win); } static void @@ -779,7 +770,6 @@ globalshortcuts_session_created (GObject *source, XdpPortal *portal = XDP_PORTAL (source); PortalTestWin *win = data; g_autoptr(GError) error = NULL; - g_autoptr (GString) s = NULL; XdpGlobalShortcutsSession *session; session = xdp_portal_create_global_shortcuts_session_finish (portal, result, &error); @@ -790,24 +780,8 @@ globalshortcuts_session_created (GObject *source, } win->gs_session = session; - s = g_string_new ("session"); -/* for (GList *elem = g_list_first (zones); elem; elem = g_list_next (elem)) - { - XdpInputCaptureZone *zone = elem->data; - guint w, h; - gint x, y; - - g_object_get (zone, - "width", &w, - "height", &h, - "x", &x, - "y", &y, - NULL); - - g_string_append_printf (s, "%ux%u@%d,%d ", w, h, x, y); - }*/ - gtk_label_set_label (GTK_LABEL (win->inputcapture_label), s->str); - globalshortcuts_bind(win->gs_session); + gtk_label_set_label (GTK_LABEL (win->globalshortcuts_activations), "created"); + globalshortcuts_bind(win); } static void diff --git a/portal-test/gtk3/portal-test-win.ui b/portal-test/gtk3/portal-test-win.ui index cd80c98b..352f1cd0 100644 --- a/portal-test/gtk3/portal-test-win.ui +++ b/portal-test/gtk3/portal-test-win.ui @@ -861,18 +861,6 @@ 22 - - - 1 - 1 - Release - - - - 2 - 22 - - 1 @@ -880,8 +868,8 @@ activations - 1 - 23 + 2 + 22 From ab95c318f5e9c0a97071cfa68993aee9008acfa4 Mon Sep 17 00:00:00 2001 From: dcz Date: Thu, 27 Jun 2024 12:10:19 +0000 Subject: [PATCH 6/9] bind returns triggers but badly --- libportal/globalshortcuts.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libportal/globalshortcuts.c b/libportal/globalshortcuts.c index 28d3e7ee..b1a0e050 100644 --- a/libportal/globalshortcuts.c +++ b/libportal/globalshortcuts.c @@ -889,9 +889,14 @@ xdp_global_shortcuts_session_bind_shortcuts_finish (XdpGlobalShortcutsSession *s while (g_variant_iter_next(items, "(s@a{sv})", &name, &item)) { + GVariant* trigger; struct XdpGlobalShortcutAssigned *shortcut = &shortcuts[i]; shortcut->name = name; - g_variant_lookup(item, "s", "trigger_description", &shortcut->trigger_description); + g_print(g_variant_print(item, TRUE)); + if (trigger = g_variant_lookup_value(item, "trigger_description", "s")) + { + g_variant_get(trigger, "s", &shortcut->trigger_description); + } i++; } ret = g_array_new_take(shortcuts, From e97362f3e55ded80d3ee2ad8c5f01a53feaebbc0 Mon Sep 17 00:00:00 2001 From: dcz Date: Thu, 27 Jun 2024 12:12:44 +0000 Subject: [PATCH 7/9] bind returns triggers well --- libportal/globalshortcuts.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/libportal/globalshortcuts.c b/libportal/globalshortcuts.c index b1a0e050..e50071b7 100644 --- a/libportal/globalshortcuts.c +++ b/libportal/globalshortcuts.c @@ -889,14 +889,10 @@ xdp_global_shortcuts_session_bind_shortcuts_finish (XdpGlobalShortcutsSession *s while (g_variant_iter_next(items, "(s@a{sv})", &name, &item)) { - GVariant* trigger; struct XdpGlobalShortcutAssigned *shortcut = &shortcuts[i]; shortcut->name = name; g_print(g_variant_print(item, TRUE)); - if (trigger = g_variant_lookup_value(item, "trigger_description", "s")) - { - g_variant_get(trigger, "s", &shortcut->trigger_description); - } + g_variant_lookup(item, "trigger_description", "s", &shortcut->trigger_description); i++; } ret = g_array_new_take(shortcuts, From 2767f75d451a56cff2b6a110c9b73490e9569d6e Mon Sep 17 00:00:00 2001 From: dcz Date: Thu, 27 Jun 2024 12:23:51 +0000 Subject: [PATCH 8/9] remove some unused code --- libportal/globalshortcuts.c | 57 +++---------------------------------- 1 file changed, 4 insertions(+), 53 deletions(-) diff --git a/libportal/globalshortcuts.c b/libportal/globalshortcuts.c index e50071b7..1bc04f13 100644 --- a/libportal/globalshortcuts.c +++ b/libportal/globalshortcuts.c @@ -57,12 +57,7 @@ struct _XdpGlobalShortcutsSession { GObject parent_instance; XdpSession *parent_session; /* strong ref */ - - GList *zones; - guint signal_ids[SIGNAL_LAST_SIGNAL]; - guint zone_serial; - guint zone_set; }; G_DEFINE_TYPE (XdpGlobalShortcutsSession, xdp_global_shortcuts_session, G_TYPE_OBJECT) @@ -103,12 +98,10 @@ xdp_global_shortcuts_session_finalize (GObject *object) } g_object_weak_unref (G_OBJECT (parent_session), parent_session_destroy, session); - //session->parent_session->input_capture_session = NULL; + g_clear_pointer (&session->parent_session, g_object_unref); } - g_list_free_full (g_steal_pointer (&session->zones), g_object_unref); - G_OBJECT_CLASS (xdp_global_shortcuts_session_parent_class)->finalize (object); } @@ -178,8 +171,6 @@ static void xdp_global_shortcuts_session_init (XdpGlobalShortcutsSession *session) { session->parent_session = NULL; - session->zones = NULL; - session->zone_set = 0; for (guint i = 0; i < SIGNAL_LAST_SIGNAL; i++) session->signal_ids[i] = 0; } @@ -196,9 +187,6 @@ typedef struct { /* GetZones only */ XdpGlobalShortcutsSession *session; - /* SetPointerBarrier only */ - GList *barriers; - } Call; static void create_session (Call *call); @@ -286,7 +274,7 @@ shortcuts_changed_emit_signal (GObject *source_object, GVariantBuilder options; g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); - g_variant_builder_add (&options, "{sv}", "zone_set", g_variant_new_uint32 (session->zone_set - 1)); +// g_variant_builder_add (&options, "{sv}", "zone_set", g_variant_new_uint32 (session->zone_set - 1)); g_signal_emit (session, signals[SIGNAL_SHORTCUTS_CHANGED], 0, g_variant_new ("a{sv}", &options)); } @@ -713,25 +701,10 @@ list_shortcuts_done (GDBusConnection *bus, Call *call = data; guint32 response; g_autoptr(GVariant) ret = NULL; - GVariant *failed = NULL; GList *failed_list = NULL; g_variant_get (parameters, "(u@a{sv})", &response, &ret); - if (g_variant_lookup (ret, "failed_barriers", "@au", &failed)) - { - const guint *failed_barriers = NULL; - gsize n_elements; - GList *it = call->barriers; - - failed_barriers = g_variant_get_fixed_array (failed, &n_elements, sizeof (guint32)); - - } - - /* all failed barriers have an extra ref in failed_list, so we can unref all barriers - in our original list */ - free_barrier_list (call->barriers); - call->barriers = NULL; g_task_return_pointer (call->task, failed_list, (GDestroyNotify)free_barrier_list); } @@ -740,26 +713,16 @@ static void list_shortcuts (Call *call) { GVariantBuilder options; - GVariantBuilder barriers; - g_autoptr(GVariantType) vtype; - prep_call (call, list_shortcuts_done, &options, NULL); - vtype = g_variant_type_new ("aa{sv}"); - - g_variant_builder_init (&barriers, vtype); - - g_dbus_connection_call (call->portal->bus, PORTAL_BUS_NAME, PORTAL_OBJECT_PATH, "org.freedesktop.portal.GlobalShortcuts", "ListShortcuts", - g_variant_new ("(oa{sv}aa{sv}u)", + g_variant_new ("(oa{sv})", call->session->parent_session->id, - &options, - &barriers, - call->session->zone_set), + &options), NULL, G_DBUS_CALL_FLAGS_NONE, -1, @@ -768,12 +731,6 @@ list_shortcuts (Call *call) call); } -static void -gobject_ref_wrapper (gpointer data, gpointer user_data) -{ - g_object_ref (G_OBJECT (data)); -} - /** * xdp_global_shortcuts_session_bind_shortcuts: * @session: a [class@GlobalShortcutsSession] @@ -923,7 +880,6 @@ xdp_global_shortcuts_session_bind_shortcuts_finish (XdpGlobalShortcutsSession *s */ void xdp_global_shortcuts_session_list_shortcuts (XdpGlobalShortcutsSession *session, - GList *barriers, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data) @@ -932,19 +888,14 @@ xdp_global_shortcuts_session_list_shortcuts (XdpGlobalShortcutsSession * XdpPortal *portal; g_return_if_fail (_xdp_global_shortcuts_session_is_valid (session)); - g_return_if_fail (barriers != NULL); portal = session->parent_session->portal; - /* The list is ours, but we ref each object so we can create the list for the - * returned barriers during _finish*/ - g_list_foreach (barriers, gobject_ref_wrapper, NULL); call = g_new0 (Call, 1); call->portal = g_object_ref (portal); call->session = g_object_ref (session); call->task = g_task_new (session, cancellable, callback, data); - call->barriers = barriers; list_shortcuts (call); } From 6f77348888b224c91c45455f166e1da5c0642e38 Mon Sep 17 00:00:00 2001 From: dcz Date: Thu, 27 Jun 2024 12:31:28 +0000 Subject: [PATCH 9/9] wip: label --- portal-test/gtk3/portal-test-win.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/portal-test/gtk3/portal-test-win.c b/portal-test/gtk3/portal-test-win.c index 5e7bd01e..22f8a362 100644 --- a/portal-test/gtk3/portal-test-win.c +++ b/portal-test/gtk3/portal-test-win.c @@ -719,12 +719,21 @@ capture_input_release (GtkButton *button, } +static void +globalshortcuts_activated (XdpGlobalShortcutsSession *session, + const char *shortcut_id, + guint timestamp, + gpointer user_data) +{ + GtkLabel *label = GTK_LABEL (user_data); + gtk_label_set_text(label, shortcut_id); +} + static void globalshortcuts_bind_done (GObject *source, GAsyncResult *result, gpointer data) { - XdpPortal *portal = XDP_PORTAL (source); PortalTestWin *win = data; g_autoptr(GError) error = NULL; g_autoptr (GString) s = NULL; @@ -746,6 +755,8 @@ globalshortcuts_bind_done (GObject *source, g_string_append_printf (s, "%s: %s ", shortcut.name, shortcut.trigger_description); } gtk_label_set_label (GTK_LABEL (win->globalshortcuts_activations), s->str); + + g_signal_connect (session, "activated", G_CALLBACK (globalshortcuts_activated), win->globalshortcuts_activations); } static void