summaryrefslogtreecommitdiff
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
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.
-rw-r--r--atspi/atspi-device-legacy.c91
-rw-r--r--atspi/atspi-device-legacy.h59
-rw-r--r--atspi/atspi-device-x11.c635
-rw-r--r--atspi/atspi-device-x11.h59
-rw-r--r--atspi/atspi-device.c400
-rw-r--r--atspi/atspi-device.h103
-rw-r--r--atspi/atspi-registry.c2
-rw-r--r--atspi/atspi-types.h9
-rw-r--r--atspi/meson.build12
9 files changed, 1368 insertions, 2 deletions
diff --git a/atspi/atspi-device-legacy.c b/atspi/atspi-device-legacy.c
new file mode 100644
index 00000000..fbae5f03
--- /dev/null
+++ b/atspi/atspi-device-legacy.c
@@ -0,0 +1,91 @@
+/*
+ * 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-legacy.h"
+
+typedef struct _AtspiDeviceLegacyPrivate AtspiDeviceLegacyPrivate;
+struct _AtspiDeviceLegacyPrivate
+{
+ AtspiDeviceListener *listener;
+};
+
+GObjectClass *device_legacy_parent_class;
+
+G_DEFINE_TYPE_WITH_CODE (AtspiDeviceLegacy, atspi_device_legacy,
+ ATSPI_TYPE_DEVICE,
+ G_ADD_PRIVATE (AtspiDeviceLegacy))
+
+gboolean
+key_cb (const AtspiDeviceEvent *event, void *user_data)
+{
+ AtspiDeviceLegacy *device = ATSPI_DEVICE_LEGACY (user_data);
+
+ return atspi_device_notify_key (ATSPI_DEVICE (device), event->type == (AtspiEventType)ATSPI_KEY_PRESS, event->hw_code, event->id, event->modifiers, event->event_string);
+}
+
+static void
+atspi_device_legacy_init (AtspiDeviceLegacy *device)
+{
+ AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (device);
+ gint i;
+
+ priv->listener = atspi_device_listener_new (key_cb, device, NULL);
+ for (i = 0; i < 256; i++)
+ atspi_register_keystroke_listener (priv->listener, NULL, i, 3, ATSPI_KEYLISTENER_SYNCHRONOUS | ATSPI_KEYLISTENER_CANCONSUME, NULL);
+}
+
+static void
+atspi_device_legacy_finalize (GObject *object)
+{
+ AtspiDeviceLegacy *device = ATSPI_DEVICE_LEGACY (object);
+ AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (device);
+
+ g_clear_object (&priv->listener);
+ device_legacy_parent_class->finalize (object);
+}
+
+
+static void
+atspi_device_legacy_class_init (AtspiDeviceLegacyClass *klass)
+{
+ GObjectClass *object_class = (GObjectClass *) klass;
+
+ device_legacy_parent_class = g_type_class_peek_parent (klass);
+ object_class->finalize = atspi_device_legacy_finalize;
+}
+
+/**
+ * atspi_device_legacy_new:
+ *
+ * Creates a new #AtspiDeviceLegacy.
+ *
+ * Returns: (transfer full): a pointer to a newly-created #AtspiDeviceLegacy.
+ *
+ **/
+AtspiDeviceLegacy *
+atspi_device_legacy_new ()
+{
+ AtspiDeviceLegacy *device = g_object_new (atspi_device_legacy_get_type (), NULL);
+
+ return device;
+}
diff --git a/atspi/atspi-device-legacy.h b/atspi/atspi-device-legacy.h
new file mode 100644
index 00000000..50f777e2
--- /dev/null
+++ b/atspi/atspi-device-legacy.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#ifndef _ATSPI_DEVICE_LEGACY_H_
+#define _ATSPI_DEVICE_LEGACY_H_
+
+#include "glib-object.h"
+
+#include "atspi-types.h"
+#include "atspi-device.h"
+
+G_BEGIN_DECLS
+
+#define ATSPI_TYPE_DEVICE_LEGACY (atspi_device_legacy_get_type ())
+#define ATSPI_DEVICE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ATSPI_TYPE_DEVICE_LEGACY, AtspiDeviceLegacy))
+#define ATSPI_DEVICE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ATSPI_TYPE_DEVICE_LEGACY, AtspiDeviceLegacyClass))
+#define ATSPI_IS_DEVICE_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ATSPI_TYPE_DEVICE_LEGACY))
+#define ATSPI_IS_DEVICE_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ATSPI_TYPE_DEVICE_LEGACY))
+#define ATSPI_DEVICE_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ATSPI_TYPE_DEVICE_LEGACY, AtspiDeviceLegacyClass))
+
+typedef struct _AtspiDeviceLegacy AtspiDeviceLegacy;
+struct _AtspiDeviceLegacy
+{
+ AtspiDevice parent;
+};
+
+typedef struct _AtspiDeviceLegacyClass AtspiDeviceLegacyClass;
+struct _AtspiDeviceLegacyClass
+{
+ AtspiDeviceClass parent_class;
+};
+
+GType atspi_device_legacy_get_type (void);
+
+AtspiDeviceLegacy *atspi_device_legacy_new ();
+
+G_END_DECLS
+
+#endif /* _ATSPI_DEVICE_LEGACY_H_ */
diff --git a/atspi/atspi-device-x11.c b/atspi/atspi-device-x11.c
new file mode 100644
index 00000000..adc600be
--- /dev/null
+++ b/atspi/atspi-device-x11.c
@@ -0,0 +1,635 @@
+/*
+ * 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-x11.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/XInput2.h>
+#include <X11/XKBlib.h>
+
+
+#define ATSPI_VIRTUAL_MODIFIER_MASK 0xffff0000
+
+typedef struct _AtspiDeviceX11Private AtspiDeviceX11Private;
+struct _AtspiDeviceX11Private
+{
+ Display *display;
+ Window window;
+ GSource *source;
+ int xi_opcode;
+ int device_id;
+ GSList *modifiers;
+ GSList *key_grabs;
+ guint virtual_mods_enabled;
+};
+
+GObjectClass *device_x11_parent_class;
+
+typedef struct _DisplaySource
+{
+ GSource source;
+
+ Display *display;
+ GPollFD event_poll_fd;
+} DisplaySource;
+
+typedef struct
+{
+ guint keycode;
+ guint modifier;
+} AtspiX11KeyModifier;
+
+typedef struct
+{
+ AtspiKeyDefinition *kd;
+ gboolean enabled;
+} AtspiX11KeyGrab;
+
+static gboolean
+event_prepare (GSource *source, gint *timeout)
+{
+ Display *display = ((DisplaySource *)source)->display;
+ gboolean retval;
+
+ *timeout = -1;
+ retval = XPending (display);
+
+ return retval;
+}
+
+static gboolean
+event_check (GSource *source)
+{
+ DisplaySource *display_source = (DisplaySource*)source;
+ gboolean retval;
+
+ if (display_source->event_poll_fd.revents & G_IO_IN)
+ retval = XPending (display_source->display);
+ else
+ retval = FALSE;
+
+ return retval;
+}
+
+static void
+xi2keyevent (XIDeviceEvent *xievent, XEvent *xkeyevent)
+{
+ memset (xkeyevent, 0, sizeof (*xkeyevent));
+
+ switch (xievent->evtype)
+ {
+ case XI_KeyPress:
+ xkeyevent->type = KeyPress;
+ break;
+ case XI_KeyRelease:
+ xkeyevent->type = KeyRelease;
+ break;
+ default:
+ break;
+ }
+ xkeyevent->xkey.serial = xievent->serial;
+ xkeyevent->xkey.send_event = xievent->send_event;
+ xkeyevent->xkey.display = xievent->display;
+ xkeyevent->xkey.window = xievent->event;
+ xkeyevent->xkey.root = xievent->root;
+ xkeyevent->xkey.subwindow = xievent->child;
+ xkeyevent->xkey.time = xievent->time;
+ xkeyevent->xkey.x = xievent->event_x;
+ xkeyevent->xkey.y = xievent->event_y;
+ xkeyevent->xkey.x_root = xievent->root_x;
+ xkeyevent->xkey.y_root = xievent->root_y;
+ xkeyevent->xkey.state = xievent->mods.effective;
+ xkeyevent->xkey.keycode = xievent->detail;
+ xkeyevent->xkey.same_screen = 1;
+}
+
+G_DEFINE_TYPE_WITH_CODE (AtspiDeviceX11, atspi_device_x11,
+ ATSPI_TYPE_DEVICE,
+ G_ADD_PRIVATE (AtspiDeviceX11))
+
+
+static guint
+find_virtual_mapping (AtspiDeviceX11 *x11_device, gint keycode)
+{
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+ GSList *l;
+
+ for (l = priv->modifiers; l; l = l->next)
+ {
+ AtspiX11KeyModifier *entry = l->data;
+ if (entry->keycode == keycode)
+ return entry->modifier;
+ }
+
+ return 0;
+}
+
+static gboolean
+grab_should_be_enabled (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab)
+{
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+ guint virtual_mods_used = grab->kd->modifiers & ATSPI_VIRTUAL_MODIFIER_MASK;
+ return ((priv->virtual_mods_enabled & virtual_mods_used) == virtual_mods_used);
+}
+
+static gboolean
+grab_has_active_duplicate (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab)
+{
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+ GSList *l;
+
+ for (l = priv->key_grabs; l; l = l->next)
+ {
+ AtspiX11KeyGrab *other = l->data;
+ if (other != grab && other->enabled && other->kd->keycode == grab->kd->keycode && (other->kd->modifiers & ~ATSPI_VIRTUAL_MODIFIER_MASK) == (grab->kd->modifiers & ~ATSPI_VIRTUAL_MODIFIER_MASK))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+enable_key_grab (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab)
+{
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+ XIGrabModifiers xi_modifiers;
+ XIEventMask eventmask;
+ unsigned char mask[XIMaskLen (XI_LASTEVENT)] = { 0 };
+
+ g_return_if_fail (priv->display != NULL);
+
+ xi_modifiers.modifiers = grab->kd->modifiers & ~ATSPI_VIRTUAL_MODIFIER_MASK;
+ xi_modifiers.status = 0;
+
+ eventmask.deviceid = XIAllDevices;
+ eventmask.mask_len = sizeof(mask);
+ eventmask.mask = mask;
+
+ XISetMask (mask, XI_KeyPress);
+ XISetMask (mask, XI_KeyRelease);
+
+ if (!grab_has_active_duplicate (x11_device, grab))
+ XIGrabKeycode (priv->display, XIAllMasterDevices, grab->kd->keycode, priv->window, XIGrabModeSync, XIGrabModeAsync, False, &eventmask, 1, &xi_modifiers);
+ grab->enabled = TRUE;
+}
+
+static void
+disable_key_grab (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab)
+{
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+ XIGrabModifiers xi_modifiers;
+
+ g_return_if_fail (priv->display != NULL);
+
+ if (!grab->enabled)
+ return;
+
+ grab->enabled = FALSE;
+
+ if (grab_has_active_duplicate (x11_device, grab))
+ return;
+
+ xi_modifiers.modifiers = grab->kd->modifiers & ~ATSPI_VIRTUAL_MODIFIER_MASK;
+ xi_modifiers.status = 0;
+
+ XIUngrabKeycode (priv->display, XIAllMasterDevices, grab->kd->keycode, priv->window, sizeof(xi_modifiers), &xi_modifiers);
+}
+
+static void
+set_virtual_modifier (AtspiDeviceX11 *x11_device, gint keycode, gboolean enabled)
+{
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+ guint modifier = find_virtual_mapping (x11_device, keycode);
+ GSList *l;
+
+ if (!modifier)
+ return;
+
+ if (enabled)
+ {
+ if (priv->virtual_mods_enabled & modifier)
+ return;
+ priv->virtual_mods_enabled |= modifier;
+ }
+ else
+ {
+ if (!(priv->virtual_mods_enabled & modifier))
+ return;
+ priv->virtual_mods_enabled &= ~modifier;
+ }
+
+ for (l = priv->key_grabs; l; l = l->next)
+ {
+ AtspiX11KeyGrab *grab = l->data;
+ gboolean new_enabled = grab_should_be_enabled (x11_device, grab);
+ if (new_enabled && !grab->enabled)
+ enable_key_grab (x11_device, grab);
+ else if (grab->enabled && !new_enabled)
+ disable_key_grab (x11_device, grab);
+ }
+}
+
+static gboolean
+do_event_dispatch (gpointer user_data)
+{
+ AtspiDeviceX11 *device = user_data;
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (device);
+ Display *display = priv->display;
+ XEvent xevent;
+ char text[10];
+ KeySym keysym;
+ XComposeStatus status;
+
+ while (XPending (display))
+ {
+ XNextEvent (display, &xevent);
+ XEvent keyevent;
+
+ switch (xevent.type)
+ {
+ case KeyPress:
+ case KeyRelease:
+ XLookupString(&xevent.xkey, text, sizeof (text), &keysym, &status);
+ atspi_device_notify_key (ATSPI_DEVICE (device), (xevent.type == KeyPress), xevent.xkey.keycode, keysym, xevent.xkey.state & priv->virtual_mods_enabled, text);
+ break;
+ case GenericEvent:
+ if (xevent.xcookie.extension == priv->xi_opcode)
+ {
+ XGetEventData(priv->display, &xevent.xcookie);
+ XIRawEvent *xiRawEv = (XIRawEvent *) xevent.xcookie.data;
+ XIDeviceEvent *xiDevEv = (XIDeviceEvent *) xevent.xcookie.data;
+ switch (xevent.xcookie.evtype)
+ {
+ case XI_KeyPress:
+ case XI_KeyRelease:
+ xi2keyevent (xiDevEv, &keyevent);
+ XLookupString((XKeyEvent *)&keyevent, text, sizeof (text), &keysym, &status);
+ if (text[0] < ' ')
+ text[0] = '\0';
+ if (!priv->device_id)
+ priv->device_id = xiDevEv->deviceid;
+ set_virtual_modifier (device, xiRawEv->detail, xevent.xcookie.evtype == XI_KeyPress);
+ if (xiDevEv->deviceid == priv->device_id)
+ atspi_device_notify_key (ATSPI_DEVICE (device), (xevent.xcookie.evtype == XI_KeyPress), xiRawEv->detail, keysym, keyevent.xkey.state, text);
+ XFreeEventData (priv->display, &xevent.xcookie);
+ break;
+ }
+ }
+ default:
+ if (XFilterEvent (&xevent, None))
+ continue;
+ }
+ }
+ return TRUE;
+}
+
+static gboolean
+event_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
+{
+ if (callback)
+ callback (user_data);
+ return G_SOURCE_CONTINUE;
+}
+
+static GSourceFuncs event_funcs = {
+ event_prepare,
+ event_check,
+ event_dispatch,
+ NULL
+};
+
+static GSource *
+display_source_new (Display *display)
+{
+ GSource *source = g_source_new (&event_funcs, sizeof (DisplaySource));
+ DisplaySource *display_source = (DisplaySource *) source;
+ g_source_set_name (source, "[at-spi2-core] display_source_funcs");
+
+ display_source->display = display;
+
+ return source;
+}
+
+static void
+create_event_source (AtspiDeviceX11 *device)
+{
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (device);
+ DisplaySource *display_source;
+
+ int connection_number = ConnectionNumber (priv->display);
+
+ priv->source = display_source_new (priv->display);
+ display_source = (DisplaySource *)priv->source;
+
+ g_source_set_priority (priv->source, G_PRIORITY_DEFAULT);
+
+ display_source->event_poll_fd.fd = connection_number;
+ display_source->event_poll_fd.events = G_IO_IN;
+
+ g_source_add_poll (priv->source, &display_source->event_poll_fd);
+ g_source_set_can_recurse (priv->source, TRUE);
+ g_source_set_callback (priv->source, do_event_dispatch, device, NULL);
+ g_source_attach (priv->source, NULL);
+}
+
+static gboolean
+check_virtual_modifier (AtspiDeviceX11 *x11_device, guint modifier)
+{
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+ GSList *l;
+
+ for (l = priv->modifiers; l; l = l->next)
+ {
+ AtspiX11KeyModifier *entry = l->data;
+ if (entry->modifier == modifier)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static guint
+get_unused_virtual_modifier (AtspiDeviceX11 *x11_device)
+{
+ guint ret = 0x10000;
+
+ while (ret)
+ {
+ if (!check_virtual_modifier (x11_device, ret))
+ return ret;
+ ret <<= 1;
+ }
+
+ return 0;
+}
+
+static guint
+atspi_device_x11_map_modifier (AtspiDevice *device, gint keycode)
+{
+ AtspiDeviceX11 *x11_device = ATSPI_DEVICE_X11 (device);
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+ XkbDescPtr desc;
+ guint ret;
+ AtspiX11KeyModifier *entry;
+
+ desc = XkbGetMap (priv->display, XkbModifierMapMask, XkbUseCoreKbd);
+
+ if (keycode < desc->min_key_code || keycode >= desc->max_key_code)
+ {
+ XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE);
+ g_warning ("Passed invalid keycode %d", keycode);
+ return 0;
+ }
+
+ ret = desc->map->modmap[keycode];
+ XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE);
+ if (ret & (ShiftMask | ControlMask))
+ return ret;
+
+ ret = find_virtual_mapping (x11_device, keycode);
+ if (ret)
+ return ret;
+
+ ret = get_unused_virtual_modifier (x11_device);
+
+ entry = g_new (AtspiX11KeyModifier, 1);
+ entry->keycode = keycode;
+ entry->modifier = ret;
+ priv->modifiers = g_slist_append (priv->modifiers, entry);
+
+ return ret;
+}
+
+static void
+atspi_device_x11_unmap_modifier (AtspiDevice *device, gint keycode)
+{
+ AtspiDeviceX11 *x11_device = ATSPI_DEVICE_X11 (device);
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+ GSList *l;
+
+ for (l = priv->modifiers; l; l = l->next)
+ {
+ AtspiX11KeyModifier *entry = l->data;
+ if (entry->keycode == keycode)
+ {
+ g_free (entry);
+ priv->modifiers = g_slist_remove (priv->modifiers, entry);
+ return;
+ }
+ }
+}
+
+static guint
+atspi_device_x11_get_modifier (AtspiDevice *device, gint keycode)
+{
+ AtspiDeviceX11 *x11_device = ATSPI_DEVICE_X11 (device);
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+ XkbDescPtr desc;
+ guint ret;
+
+ desc = XkbGetMap (priv->display, XkbModifierMapMask, XkbUseCoreKbd);
+
+ if (keycode < desc->min_key_code || keycode >= desc->max_key_code)
+ {
+ XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE);
+ g_warning ("Passed invalid keycode %d", keycode);
+ return 0;
+ }
+
+ ret = desc->map->modmap[keycode];
+ XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE);
+ if (ret)
+ return ret;
+
+ return find_virtual_mapping (x11_device, keycode);
+}
+
+static void
+atspi_device_x11_init (AtspiDeviceX11 *device)
+{
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (device);
+ int first_event, first_error;
+
+ priv->display=XOpenDisplay("");
+ g_return_if_fail (priv->display != NULL);
+ priv->window = DefaultRootWindow(priv->display);
+
+ if (XQueryExtension(priv->display, "XInputExtension", &priv->xi_opcode, &first_event, &first_error))
+ {
+ int major = 2;
+ int minor = 1;
+ if (XIQueryVersion(priv->display, &major, &minor) != BadRequest)
+ {
+ XIEventMask eventmask;
+ unsigned char mask[XIMaskLen (XI_LASTEVENT)] = { 0 };
+
+ eventmask.deviceid = XIAllDevices;
+ eventmask.mask_len = sizeof(mask);
+ eventmask.mask = mask;
+
+ XISetMask (mask, XI_KeyPress);
+ XISetMask (mask, XI_KeyRelease);
+ XISetMask (mask, XI_ButtonPress);
+ XISetMask (mask, XI_ButtonRelease);
+ XISetMask (mask, XI_Motion);
+ XISelectEvents (priv->display, priv->window, &eventmask, 1);
+ create_event_source (device);
+ }
+ }
+}
+
+static void
+atspi_device_x11_finalize (GObject *object)
+{
+ AtspiDeviceX11 *device = ATSPI_DEVICE_X11 (object);
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (device);
+ GSList *l;
+
+ for (l = priv->key_grabs; l; l = l->next)
+ {
+ AtspiX11KeyGrab *grab = l->data;
+ disable_key_grab (device, grab);
+ g_boxed_free (ATSPI_TYPE_KEY_DEFINITION, grab->kd);
+ g_free (grab);
+ }
+ g_slist_free (priv->key_grabs);
+ priv->key_grabs = NULL;
+
+ g_slist_free_full (priv->modifiers, g_free);
+ priv->modifiers = NULL;
+
+ if (priv->source)
+ {
+ g_source_destroy ((GSource *) priv->source);
+ g_source_unref ((GSource *) priv->source);
+ priv->source = NULL;
+ }
+
+ device_x11_parent_class->finalize (object);
+}
+
+
+static void
+atspi_device_x11_add_key_grab (AtspiDevice *device, AtspiKeyDefinition *kd)
+{
+ AtspiDeviceX11 *x11_device = ATSPI_DEVICE_X11 (device);
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+ AtspiX11KeyGrab *grab;
+
+ grab = g_new (AtspiX11KeyGrab, 1);
+ grab->kd = g_boxed_copy (ATSPI_TYPE_KEY_DEFINITION, kd);
+ grab->enabled = FALSE;
+ priv->key_grabs = g_slist_append (priv->key_grabs, grab);
+ if (grab_should_be_enabled (x11_device, grab))
+ enable_key_grab (x11_device, grab);
+}
+
+static void
+atspi_device_x11_remove_key_grab (AtspiDevice *device, guint id)
+{
+ AtspiDeviceX11 *x11_device = ATSPI_DEVICE_X11 (device);
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+ AtspiKeyDefinition *kd;
+ GSList *l;
+
+ kd = atspi_device_get_grab_by_id (device, id);
+
+ for (l = priv->key_grabs; l; l = g_slist_next (l))
+ {
+ AtspiX11KeyGrab *other = l->data;
+ if (other->kd->keycode == kd->keycode && other->kd->modifiers == kd->modifiers)
+ {
+ disable_key_grab (x11_device, other);
+ priv->key_grabs = g_slist_remove (priv->key_grabs, other);
+ return;
+ }
+ }
+}
+
+static guint
+atspi_device_x11_get_locked_modifiers (AtspiDevice *device)
+{
+ AtspiDeviceX11 *x11_device = ATSPI_DEVICE_X11 (device);
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+ XkbStateRec state_rec;
+
+ memset (&state_rec, 0, sizeof (state_rec));
+ XkbGetState (priv->display, XkbUseCoreKbd, &state_rec);
+ return state_rec.locked_mods;
+}
+
+static gboolean
+atspi_device_x11_grab_keyboard (AtspiDevice *device)
+{
+ AtspiDeviceX11 *x11_device = ATSPI_DEVICE_X11 (device);
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+ int result;
+
+ g_return_val_if_fail (priv->display != NULL, FALSE);
+ result = XGrabKeyboard (priv->display, priv->window, TRUE, GrabModeAsync, GrabModeSync, CurrentTime);
+ return (result == 0);
+}
+
+static void
+atspi_device_x11_ungrab_keyboard (AtspiDevice *device)
+{
+ AtspiDeviceX11 *x11_device = ATSPI_DEVICE_X11 (device);
+ AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+
+ g_return_if_fail (priv->display != NULL);
+ XUngrabKeyboard (priv->display, CurrentTime);
+}
+
+static void
+atspi_device_x11_class_init (AtspiDeviceX11Class *klass)
+{
+ AtspiDeviceClass *device_class = ATSPI_DEVICE_CLASS (klass);
+ GObjectClass *object_class = (GObjectClass *) klass;
+
+ device_x11_parent_class = g_type_class_peek_parent (klass);
+ device_class->add_key_grab = atspi_device_x11_add_key_grab;
+ device_class->map_modifier = atspi_device_x11_map_modifier;
+ device_class->unmap_modifier = atspi_device_x11_unmap_modifier;
+ device_class->get_modifier = atspi_device_x11_get_modifier;
+ device_class->remove_key_grab = atspi_device_x11_remove_key_grab;
+ device_class->get_locked_modifiers = atspi_device_x11_get_locked_modifiers;
+ device_class->grab_keyboard = atspi_device_x11_grab_keyboard;
+ device_class->ungrab_keyboard = atspi_device_x11_ungrab_keyboard;
+ object_class->finalize = atspi_device_x11_finalize;
+}
+
+/**
+ * atspi_device_x11_new:
+ *
+ * Creates a new #AtspiDeviceX11.
+ *
+ * Returns: (transfer full): a pointer to a newly-created #AtspiDeviceX11.
+ *
+ **/
+AtspiDeviceX11 *
+atspi_device_x11_new ()
+{
+ AtspiDeviceX11 *device = g_object_new (atspi_device_x11_get_type (), NULL);
+
+ return device;
+}
diff --git a/atspi/atspi-device-x11.h b/atspi/atspi-device-x11.h
new file mode 100644
index 00000000..3a91edca
--- /dev/null
+++ b/atspi/atspi-device-x11.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#ifndef _ATSPI_DEVICE_X11_H_
+#define _ATSPI_DEVICE_X11_H_
+
+#include "glib-object.h"
+
+#include "atspi-types.h"
+#include "atspi-device.h"
+
+G_BEGIN_DECLS
+
+#define ATSPI_TYPE_DEVICE_X11 (atspi_device_x11_get_type ())
+#define ATSPI_DEVICE_X11(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ATSPI_TYPE_DEVICE_X11, AtspiDeviceX11))
+#define ATSPI_DEVICE_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ATSPI_TYPE_DEVICE_X11, AtspiDeviceX11Class))
+#define ATSPI_IS_DEVICE_X11(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ATSPI_TYPE_DEVICE_X11))
+#define ATSPI_IS_DEVICE_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ATSPI_TYPE_DEVICE_X11))
+#define ATSPI_DEVICE_X11_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ATSPI_TYPE_DEVICE_X11, AtspiDeviceX11Class))
+
+typedef struct _AtspiDeviceX11 AtspiDeviceX11;
+struct _AtspiDeviceX11
+{
+ AtspiDevice parent;
+};
+
+typedef struct _AtspiDeviceX11Class AtspiDeviceX11Class;
+struct _AtspiDeviceX11Class
+{
+ AtspiDeviceClass parent_class;
+};
+
+GType atspi_device_x11_get_type (void);
+
+AtspiDeviceX11 *atspi_device_x11_new ();
+
+G_END_DECLS
+
+#endif /* _ATSPI_DEVICE_X11_H_ */
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);
+}
+
diff --git a/atspi/atspi-device.h b/atspi/atspi-device.h
new file mode 100644
index 00000000..a246842a
--- /dev/null
+++ b/atspi/atspi-device.h
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+#ifndef _ATSPI_DEVICE_H_
+#define _ATSPI_DEVICE_H_
+
+#include "glib-object.h"
+
+#include "atspi-types.h"
+
+G_BEGIN_DECLS
+
+#define ATSPI_TYPE_DEVICE (atspi_device_get_type ())
+#define ATSPI_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ATSPI_TYPE_DEVICE, AtspiDevice))
+#define ATSPI_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ATSPI_TYPE_DEVICE, AtspiDeviceClass))
+#define ATSPI_IS_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ATSPI_TYPE_DEVICE))
+#define ATSPI_IS_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ATSPI_TYPE_DEVICE))
+#define ATSPI_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ATSPI_TYPE_DEVICE, AtspiDeviceClass))
+
+typedef struct _AtspiDevice AtspiDevice;
+struct _AtspiDevice
+{
+ GObject parent;
+};
+
+typedef struct _AtspiDeviceClass AtspiDeviceClass;
+struct _AtspiDeviceClass
+{
+ GObjectClass parent_class;
+
+ void (*add_key_grab) (AtspiDevice *device, AtspiKeyDefinition *kd);
+ void (*remove_key_grab) (AtspiDevice *device, guint id);
+ guint (*map_modifier) (AtspiDevice *device, gint keycode);
+ void (*unmap_modifier) (AtspiDevice *device, gint keycode);
+ guint (*get_modifier) (AtspiDevice *device, gint keycode);
+ gboolean (*grab_keyboard) (AtspiDevice *device);
+ void (*ungrab_keyboard) (AtspiDevice *device);
+ guint (*get_locked_modifiers) (AtspiDevice *device);
+};
+
+GType atspi_device_get_type (void);
+
+/**
+ * AtspiKeyCallback:
+ * @device: the device.
+ * @pressed: TRUE if the key is being pressed, FALSE if being released.
+ * @keycode: the hardware code for the key.
+ * @keysym: the keysym for the key.
+ * @modifiers: a bitflag indicating which key modifiers are active.
+ * @keystring: the text corresponding to the keypress.
+ * @user_data: (closure): user-supplied data
+ *
+ * A callback that will be invoked when a key is pressed.
+ */
+typedef void (*AtspiKeyCallback) (AtspiDevice *device, gboolean pressed, guint keycode, guint keysym, guint modifiers, const gchar *keystring, void *user_data);
+
+AtspiDevice *atspi_device_new ();
+
+gboolean atspi_device_notify_key (AtspiDevice *device, gboolean pressed, int keycode, int keysym, gint state, gchar *text);
+
+guint atspi_device_add_key_grab (AtspiDevice *device, AtspiKeyDefinition *kd, AtspiKeyCallback callback, void *user_data, GDestroyNotify callback_destroyed);
+
+void atspi_device_remove_key_grab (AtspiDevice *device, guint id);
+
+void atspi_device_add_key_watcher (AtspiDevice *device, AtspiKeyCallback callback, void *user_data, GDestroyNotify callback_destroyed);
+
+AtspiKeyDefinition *atspi_device_get_grab_by_id (AtspiDevice *device, guint id);
+
+guint atspi_device_map_modifier (AtspiDevice *device, gint keycode);
+
+void atspi_device_unmap_modifier (AtspiDevice *device, gint keycode);
+
+guint atspi_device_get_modifier (AtspiDevice *device, gint keycode);
+
+guint atspi_device_get_locked_modifiers (AtspiDevice *device);
+
+gboolean atspi_device_grab_keyboard (AtspiDevice *device);
+
+void atspi_device_ungrab_keyboard (AtspiDevice *device);
+
+G_END_DECLS
+
+#endif /* _ATSPI_DEVICE_H_ */
diff --git a/atspi/atspi-registry.c b/atspi/atspi-registry.c
index ca2a6c3e..dea5878e 100644
--- a/atspi/atspi-registry.c
+++ b/atspi/atspi-registry.c
@@ -556,7 +556,7 @@ atspi_key_definition_copy (AtspiKeyDefinition *src)
dst->keysym = src->keysym;
if (src->keystring)
dst->keystring = g_strdup (src->keystring);
- dst->unused = src->unused;
+ dst->modifiers = src->modifiers;
return dst;
}
diff --git a/atspi/atspi-types.h b/atspi/atspi-types.h
index 2631baf7..ab43ca8c 100644
--- a/atspi/atspi-types.h
+++ b/atspi/atspi-types.h
@@ -78,9 +78,16 @@ struct _AtspiKeyDefinition
gint keycode;
gint keysym;
gchar *keystring;
- gint unused;
+ guint modifiers;
};
+/**
+ * ATSPI_TYPE_KEY_DEFINITION:
+ *
+ * The #GType for a boxed type holding a #AtspiKeyDefinition.
+ */
+#define ATSPI_TYPE_KEY_DEFINITION (atspi_key_definition_get_type ())
+
typedef struct _AtspiEvent AtspiEvent;
struct _AtspiEvent
{
diff --git a/atspi/meson.build b/atspi/meson.build
index 5797f73a..a6f12384 100644
--- a/atspi/meson.build
+++ b/atspi/meson.build
@@ -4,6 +4,8 @@ atspi_sources = [
'atspi-application.c',
'atspi-collection.c',
'atspi-component.c',
+ 'atspi-device.c',
+ 'atspi-device-legacy.c',
'atspi-device-listener.c',
'atspi-document.c',
'atspi-editabletext.c',
@@ -26,6 +28,13 @@ atspi_sources = [
'atspi-value.c',
]
+x11_option = get_option('x11')
+if x11_option != 'no'
+ if x11_dep.found()
+ atspi_sources += ['atspi-device-x11.c']
+ endif
+endif
+
atspi_headers = [
'atspi.h',
'atspi-accessible.h',
@@ -34,7 +43,10 @@ atspi_headers = [
'atspi-collection.h',
'atspi-component.h',
'atspi-constants.h',
+ 'atspi-device.h',
+ 'atspi-device-legacy.h',
'atspi-device-listener.h',
+ 'atspi-device-x11.h',
'atspi-document.h',
'atspi-editabletext.h',
'atspi-event-listener.h',