[at-spi2-core] Add device API



commit 7bd1ad706c92a9342e5078238ce4380e544aa967
Author: Mike Gorse <mgorse suse com>
Date:   Mon Jan 11 13:32:06 2021 -0600

    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.

 atspi/atspi-device-legacy.c |  91 +++++++
 atspi/atspi-device-legacy.h |  59 ++++
 atspi/atspi-device-x11.c    | 635 ++++++++++++++++++++++++++++++++++++++++++++
 atspi/atspi-device-x11.h    |  59 ++++
 atspi/atspi-device.c        | 400 ++++++++++++++++++++++++++++
 atspi/atspi-device.h        | 103 +++++++
 atspi/atspi-registry.c      |   2 +-
 atspi/atspi-types.h         |   9 +-
 atspi/meson.build           |  12 +
 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',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]