summaryrefslogtreecommitdiff
path: root/atspi/atspi-device.c
diff options
context:
space:
mode:
authorMike Gorse <mgorse@suse.com>2021-01-11 13:32:06 -0600
committerMike Gorse <mgorse@suse.com>2021-01-11 13:32:06 -0600
commit7bd1ad706c92a9342e5078238ce4380e544aa967 (patch)
treef09ab765bb97dbcdd0cba047254768da7614eb02 /atspi/atspi-device.c
parentbefced84236ec782d3a41d8cc06ceb3fdb61ae75 (diff)
downloadat-spi2-core-7bd1ad706c92a9342e5078238ce4380e544aa967.tar.gz
Add device API
This is intended to replace the registry-based method for capturing keystrokes. It is needed because gtk 4 no longer sends key notifications in a way that atk-bridge can process them. Unlike the original API, key grabs are separated from key notifications. Clients wishing to consume keystrokes must proactively register a grab for the given key. Currently, there is a backend for X11 and an unfinished legacy back end using the old registry-based method. Hopefully, there will be a mutter/wayland back end in the future, but we need to define a protocol there first.
Diffstat (limited to 'atspi/atspi-device.c')
-rw-r--r--atspi/atspi-device.c400
1 files changed, 400 insertions, 0 deletions
diff --git a/atspi/atspi-device.c b/atspi/atspi-device.c
new file mode 100644
index 00000000..abf6138d
--- /dev/null
+++ b/atspi/atspi-device.c
@@ -0,0 +1,400 @@
+/*
+ * AT-SPI - Assistive Technology Service Provider Interface
+ * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
+ *
+ * Copyright 2020 SUSE LLC.
+ *
+ * This library 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "atspi-private.h"
+#include "atspi-device.h"
+#include "atspi-device-legacy.h"
+#include "atspi-device-x11.h"
+
+typedef struct
+{
+ guint id;
+ guint keycode;
+ guint keysym;
+ guint modifiers;
+ AtspiKeyCallback callback;
+ void *callback_data;
+ GDestroyNotify callback_destroyed;
+} AtspiKeyGrab;
+
+typedef struct _AtspiDevicePrivate AtspiDevicePrivate;
+struct _AtspiDevicePrivate
+{
+ GSList *key_watchers;
+ GSList *keygrabs;
+ guint last_grab_id;
+};
+
+GObjectClass *device_parent_class;
+
+static void
+atspi_device_init (AtspiDevice *device)
+{
+}
+
+G_DEFINE_TYPE_WITH_CODE (AtspiDevice, atspi_device,
+ G_TYPE_OBJECT,
+ G_ADD_PRIVATE (AtspiDevice))
+
+static void
+atspi_device_finalize (GObject *object)
+{
+ AtspiDevice *device = (AtspiDevice *) object;
+ AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
+
+ g_slist_free_full (priv->keygrabs, g_free);
+ priv->keygrabs = NULL;
+
+ device_parent_class->finalize (object);
+}
+
+static void
+atspi_device_real_add_key_grab (AtspiDevice *device, AtspiKeyDefinition *kd)
+{
+}
+
+static void
+atspi_device_real_remove_key_grab (AtspiDevice *device, guint id)
+{
+}
+
+static void
+atspi_device_class_init (AtspiDeviceClass *klass)
+{
+ GObjectClass *object_class = (GObjectClass *) klass;
+
+ device_parent_class = g_type_class_peek_parent (klass);
+ klass->add_key_grab = atspi_device_real_add_key_grab;
+ klass->remove_key_grab = atspi_device_real_remove_key_grab;
+ object_class->finalize = atspi_device_finalize;
+}
+
+/**
+ * atspi_device_new:
+ *
+ * Creates a new #AtspiDevice with a specified callback function.
+ *
+ * Returns: (transfer full): a pointer to a newly-created #AtspiDevice.
+ *
+ **/
+AtspiDevice *
+atspi_device_new ()
+{
+ //if (!g_getenv ("WAYLAND_DISPLAY") && !g_getenv ("ATSPI_USE_LEGACY_DEVICE"))
+#ifdef HAVE_X11
+ if (!g_getenv ("ATSPI_USE_LEGACY_DEVICE"))
+ return ATSPI_DEVICE (atspi_device_x11_new ());
+#endif
+
+ return ATSPI_DEVICE (atspi_device_legacy_new ());
+}
+
+gboolean
+atspi_device_notify_key (AtspiDevice *device, gboolean pressed, int keycode, int keysym, gint state, gchar *text)
+{
+ AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
+ GSList *l;
+ gboolean ret = FALSE;
+
+ for (l = priv->key_watchers; l; l = l->next)
+ {
+ AtspiKeyGrab *grab = l->data;
+ grab->callback (device, pressed, keycode, keysym, state, text, grab->callback_data);
+ }
+
+ for (l = priv->keygrabs; l; l = l->next)
+ {
+ AtspiKeyGrab *grab = l->data;
+ //if (keycode == grab->keycode && (grab->modifiers & state) == grab->modifiers)
+ if (keycode == grab->keycode && grab->modifiers == state)
+ {
+ if (grab->callback)
+ grab->callback (device, pressed, keycode, keysym, state, text, grab->callback_data);
+ ret = TRUE;
+ }
+ }
+
+ return ret;
+}
+
+static gboolean
+is_id_used (AtspiDevice *device, guint id)
+{
+ AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
+ GSList *l;
+
+ for (l = priv->key_watchers; l; l = l->next)
+ {
+ AtspiKeyGrab *grab = l->data;
+ if (grab->id == id)
+ return TRUE;
+ }
+
+ for (l = priv->keygrabs; l; l = l->next)
+ {
+ AtspiKeyGrab *grab = l->data;
+ if (grab->id == id)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static guint
+get_grab_id (AtspiDevice *device)
+{
+ AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
+
+ while (is_id_used (device, priv->last_grab_id)) priv->last_grab_id++;
+ return priv->last_grab_id++;
+}
+
+static gboolean
+grab_has_duplicate (AtspiDevice *device, AtspiKeyGrab *grab)
+{
+ AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
+ GSList *l;
+
+ for (l = priv->keygrabs; l; l = l->next)
+ {
+ AtspiKeyGrab *other_grab = l->data;
+ if (other_grab->id != grab->id && other_grab->keycode == grab->keycode && other_grab->modifiers == grab->modifiers)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ *atspi_device_add_key_grab:
+* @device: the device.
+ * @kd: a #AtspiKeyDefinition specifying the key code to grab.
+ * @callback: (scope notified) (allow-none): the function to call when the
+ * given key is pressed.
+ * @user_data: Data to be passed to @callback.
+ * @callback_destroyed: callback function to be called when @callback is
+ * destroyed.
+ *
+ * Returns: an identifier that can be later used to remove the grab.
+ * Add a key grab for the given key/modifier combination.
+ */
+guint
+atspi_device_add_key_grab (AtspiDevice *device, AtspiKeyDefinition *kd, AtspiKeyCallback callback, void *user_data, GDestroyNotify callback_destroyed)
+{
+ AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
+ AtspiKeyGrab *grab = g_new (AtspiKeyGrab, 1);
+ grab->keycode = kd->keycode;
+ grab->keysym = kd->keysym;
+ grab->modifiers = kd->modifiers;
+ grab->callback = callback;
+ grab->callback_data = user_data;
+ grab->callback_destroyed = callback_destroyed;
+ grab->id = get_grab_id (device);
+ priv->keygrabs = g_slist_append (priv->keygrabs, grab);
+
+ if (!grab_has_duplicate (device, grab))
+ ATSPI_DEVICE_GET_CLASS (device)->add_key_grab (device, kd);
+ return grab->id;
+}
+
+/**
+ * atspi_device_remove_key_grab:
+ * @device: the device.
+ * @id: the identifier of the grab to be removed.
+ *
+ * Removes the key grab specified by @id.
+ */
+void
+atspi_device_remove_key_grab (AtspiDevice *device, guint id)
+{
+ AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
+ GSList *l;
+
+ for (l = priv->keygrabs; l; l = l->next)
+ {
+ AtspiKeyGrab *grab = l->data;
+ if (grab->id == id)
+ {
+ if (!grab_has_duplicate (device, grab))
+ ATSPI_DEVICE_GET_CLASS (device)->remove_key_grab (device, id);
+ priv->keygrabs = g_slist_remove (priv->keygrabs, grab);
+ if (grab->callback_destroyed)
+ (*grab->callback_destroyed) (grab->callback);
+ g_free (grab);
+ return;
+ }
+ }
+}
+
+/**
+ *atspi_device_add_key_watcher:
+* @device: the device.
+ * @callback: (scope notified): the function to call when the given key is
+ * pressed.
+ * @user_data: (closure callback): Data to be passed to @callback.
+ * @callback_destroyed: (destroy callback): callback function to be called
+ * when @callback is destroyed.
+ *
+ * Add a callback that will receive a notification whenever a key is
+ * pressed or released.
+ */
+void
+atspi_device_add_key_watcher (AtspiDevice *device, AtspiKeyCallback callback, void *user_data, GDestroyNotify callback_destroyed)
+{
+ AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
+ AtspiKeyGrab *grab = g_new0 (AtspiKeyGrab, 1);
+ grab->id = get_grab_id (device);
+ grab->callback = callback;
+ grab->callback_data = user_data;
+ grab->callback_destroyed = callback_destroyed;
+ priv->key_watchers = g_slist_append (priv->key_watchers, grab);
+}
+
+AtspiKeyDefinition *
+atspi_device_get_grab_by_id (AtspiDevice *device, guint id)
+{
+ AtspiDevicePrivate *priv = atspi_device_get_instance_private (device);
+ GSList *l;
+
+ for (l = priv->keygrabs; l; l = l->next)
+ {
+ AtspiKeyGrab *grab = l->data;
+ if (grab->id == id)
+ {
+ AtspiKeyDefinition *kd = g_new0 (AtspiKeyDefinition, 1);
+ kd->keycode = grab->keycode;\
+ kd->modifiers = grab->modifiers;
+ return kd;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * atspi_device_map_modifier:
+ * @device: the device.
+ * @keycode: the keycode to map.
+ *
+ * Maps the specified key code to a modifier so that it can be used in
+ * conjunction with other keys to create a key grab. If the given keycode is
+ * already mapped, then this function will return the modifier that is
+ * currently mapped to the keycode, without doing anything else. Otherwise,
+ * it will use the last modifier that AT-SPI used to map a key. If no keys
+ * have yet been mapped using this device, then it will look for a modifier
+ * that is not currently being used. If no unused modifier can be found,
+ * then it will use the first modifier by default.
+ *
+ * Returns: the modifier that is now mapped to this keycode. This return
+ * value can be passed to atspi_device_add_key_grab.
+ */
+guint
+atspi_device_map_modifier (AtspiDevice *device, gint keycode)
+{
+ if (ATSPI_DEVICE_GET_CLASS (device)->map_modifier)
+ return ATSPI_DEVICE_GET_CLASS (device)->map_modifier (device, keycode);
+
+ return 0;
+}
+
+/**
+ * atspi_device_unmap_modifier:
+ * @device: the device.
+ * @keycode: the keycode to unmap.
+ *
+ * Removes a mapped modifier from the given keycode.
+ */
+void
+atspi_device_unmap_modifier (AtspiDevice *device, gint keycode)
+{
+ if (ATSPI_DEVICE_GET_CLASS (device)->unmap_modifier)
+ ATSPI_DEVICE_GET_CLASS (device)->unmap_modifier (device, keycode);
+}
+
+/**
+ * atspi_device_get_modifier:
+ * @device: the device.
+ * @keycode: the keycode to map.
+ *
+ * Gets the modifier for a given keycode, if one exists. Does not creatt a new
+ * mapping. This function should be used when the intention is to query a
+ * locking modifier such as num lock via atspi_device_get_locked_modifiers,
+ * rather than to add key grabs.
+ *
+ * Returns: the modifier that is mapped to this keycode.
+ */
+guint
+atspi_device_get_modifier (AtspiDevice *device, gint keycode)
+{
+ if (ATSPI_DEVICE_GET_CLASS (device)->get_modifier)
+ return ATSPI_DEVICE_GET_CLASS (device)->get_modifier (device, keycode);
+
+ return 0;
+}
+
+/**
+ * atspi_device_get_locked_modifiers:
+ * @device: the device.
+ *
+ * Returns the locked modifiers (ie, num lock, caps lock) associated with this
+ * keyboard.
+ *
+ * Returns: a guint of modifier flags.
+ */
+guint
+atspi_device_get_locked_modifiers (AtspiDevice *device)
+{
+ if (ATSPI_DEVICE_GET_CLASS (device)->get_locked_modifiers)
+ return ATSPI_DEVICE_GET_CLASS (device)->get_locked_modifiers (device);
+
+ return 0;
+}
+
+/**
+ * atspi_device_grab_keyboard:
+ *
+ * Attempts to grab the entire keyboard. This should only be done
+ * temporarily, as it may conflict with other applications that also want to
+ * grab the keyboard.
+ *
+ * Returns: #TRUE if successful, #FALSE otherwise.
+ */
+gboolean
+atspi_device_grab_keyboard (AtspiDevice *device)
+{
+ if (ATSPI_DEVICE_GET_CLASS (device)->grab_keyboard)
+ return ATSPI_DEVICE_GET_CLASS (device)->grab_keyboard (device);
+
+ return FALSE;
+}
+
+/**
+ * atspi_device_ungrab_keyboard:
+ *
+ * Removes a keyboard grab added via a call to atspi_device_add_keyboard.
+ */
+void
+atspi_device_ungrab_keyboard (AtspiDevice *device)
+{
+ if (ATSPI_DEVICE_GET_CLASS (device)->ungrab_keyboard)
+ ATSPI_DEVICE_GET_CLASS (device)->ungrab_keyboard (device);
+}
+