[at-spi2-core] Device fixes



commit c6b2af03425bacfef25b08f4730d6f7628743681
Author: Mike Gorse <mgorse suse com>
Date:   Wed Jan 27 16:41:39 2021 -0600

    Device fixes

 atspi/atspi-device-legacy.c | 229 +++++++++++++++++++++++++++++++++++++++++++-
 atspi/atspi-device-x11.c    | 129 ++++++++++++++++++++-----
 atspi/atspi-device.c        |   3 +-
 3 files changed, 331 insertions(+), 30 deletions(-)
---
diff --git a/atspi/atspi-device-legacy.c b/atspi/atspi-device-legacy.c
index fbae5f03..6950bc37 100644
--- a/atspi/atspi-device-legacy.c
+++ b/atspi/atspi-device-legacy.c
@@ -23,10 +23,30 @@
 #include "atspi-private.h"
 #include "atspi-device-legacy.h"
 
+#ifdef HAVE_X11
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/XInput2.h>
+#include <X11/XKBlib.h>
+#endif
+
+typedef struct
+{
+  guint keycode;
+  guint modifier;
+} AtspiLegacyKeyModifier;
+
 typedef struct _AtspiDeviceLegacyPrivate AtspiDeviceLegacyPrivate;
 struct _AtspiDeviceLegacyPrivate
 {
   AtspiDeviceListener *listener;
+#ifdef HAVE_X11
+  Display *display;
+  Window window;
+#endif
+  GSList *modifiers;
+  guint virtual_mods_enabled;
+  gboolean keyboard_grabbed;
 };
 
 GObjectClass *device_legacy_parent_class;
@@ -35,12 +55,203 @@ G_DEFINE_TYPE_WITH_CODE (AtspiDeviceLegacy, atspi_device_legacy,
                          ATSPI_TYPE_DEVICE,
                          G_ADD_PRIVATE (AtspiDeviceLegacy))
 
+
+static guint
+find_virtual_mapping (AtspiDeviceLegacy *legacy_device, gint keycode)
+{
+  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
+  GSList *l;
+
+  for (l = priv->modifiers; l; l = l->next)
+  {
+    AtspiLegacyKeyModifier *entry = l->data;
+    if (entry->keycode == keycode)
+      return entry->modifier;
+  }
+
+  return 0;
+}
+
+static void
+set_virtual_modifier (AtspiDeviceLegacy *legacy_device, gint keycode, gboolean enabled)
+{
+  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
+  guint modifier = find_virtual_mapping (legacy_device, keycode);
+
+  if (enabled)
+    priv->virtual_mods_enabled |= modifier;
+  else
+    priv->virtual_mods_enabled &= ~modifier;
+}
+
+
 gboolean
 key_cb (const AtspiDeviceEvent *event, void *user_data)
 {
-  AtspiDeviceLegacy *device = ATSPI_DEVICE_LEGACY (user_data);
+  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (user_data);
+  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
+  gboolean ret = priv->keyboard_grabbed;
+
+  set_virtual_modifier (legacy_device, event->hw_code,
+                        event->type == (AtspiEventType)ATSPI_KEY_PRESS);
+  ret |= atspi_device_notify_key (ATSPI_DEVICE (legacy_device),
+                                  event->type == (AtspiEventType)ATSPI_KEY_PRESS,
+                                  event->hw_code, event->id,
+                                  event->modifiers | priv->virtual_mods_enabled,
+                                  event->event_string);
+
+  return ret;
+}
+
+static guint
+atspi_device_legacy_get_locked_modifiers (AtspiDevice *device)
+{
+#ifdef HAVE_X11
+  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
+  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
+  XkbStateRec state_rec;
+
+  memset (&state_rec, 0, sizeof (state_rec));
+  XkbGetState (priv->display, XkbUseCoreKbd, &state_rec);
+  return state_rec.locked_mods;
+#else
+  return 0;
+#endif
+}
+
+static gboolean
+check_virtual_modifier (AtspiDeviceLegacy *legacy_device, guint modifier)
+{
+  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
+  GSList *l;
 
-   return atspi_device_notify_key (ATSPI_DEVICE (device), event->type == (AtspiEventType)ATSPI_KEY_PRESS, 
event->hw_code, event->id, event->modifiers, event->event_string);
+  for (l = priv->modifiers; l; l = l->next)
+  {
+    AtspiLegacyKeyModifier *entry = l->data;
+    if (entry->modifier == modifier)
+      return TRUE;
+  }
+
+  return FALSE;
+}
+
+static guint
+get_unused_virtual_modifier (AtspiDeviceLegacy *legacy_device)
+{
+  guint ret = 0x1000;
+
+  while (ret < 0x10000)
+  {
+    if (!check_virtual_modifier (legacy_device, ret))
+      return ret;
+    ret <<= 1;
+  }
+
+  return 0;
+}
+
+static guint
+atspi_device_legacy_map_modifier (AtspiDevice *device, gint keycode)
+{
+  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
+  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
+  guint ret;
+  AtspiLegacyKeyModifier *entry;
+#ifdef HAVE_X11
+  XkbDescPtr desc;
+
+  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;
+#endif
+
+  ret = find_virtual_mapping (legacy_device, keycode);
+  if (ret)
+    return ret;
+
+  ret = get_unused_virtual_modifier (legacy_device);
+
+  entry = g_new (AtspiLegacyKeyModifier, 1);
+  entry->keycode = keycode;
+  entry->modifier = ret;
+  priv->modifiers = g_slist_append (priv->modifiers, entry);
+
+  return ret;
+}
+
+static void
+atspi_device_legacy_unmap_modifier (AtspiDevice *device, gint keycode)
+{
+  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
+  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
+  GSList *l;
+
+  for (l = priv->modifiers; l; l = l->next)
+  {
+    AtspiLegacyKeyModifier *entry = l->data;
+    if (entry->keycode == keycode)
+    {
+      g_free (entry);
+      priv->modifiers = g_slist_remove (priv->modifiers, entry);
+      return;
+    }
+  }
+}
+
+static guint
+atspi_device_legacy_get_modifier (AtspiDevice *device, gint keycode)
+{
+  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
+#ifdef HAVE_X11
+  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_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;
+#endif
+
+  return find_virtual_mapping (legacy_device, keycode);
+}
+
+static gboolean
+atspi_device_legacy_grab_keyboard (AtspiDevice *device)
+{
+  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
+  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
+
+  priv->keyboard_grabbed = TRUE;
+  return TRUE;
+}
+
+static void
+atspi_device_legacy_ungrab_keyboard (AtspiDevice *device)
+{
+  AtspiDeviceLegacy *legacy_device = ATSPI_DEVICE_LEGACY (device);
+  AtspiDeviceLegacyPrivate *priv = atspi_device_legacy_get_instance_private (legacy_device);
+
+  priv->keyboard_grabbed = FALSE;
 }
 
 static void
@@ -52,6 +263,13 @@ atspi_device_legacy_init (AtspiDeviceLegacy *device)
   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);
+
+#ifdef HAVE_X11
+  priv->display=XOpenDisplay("");
+  if (priv->display)
+    priv->window = DefaultRootWindow(priv->display);
+#endif
+
 }
 
 static void
@@ -69,9 +287,16 @@ static void
 atspi_device_legacy_class_init (AtspiDeviceLegacyClass *klass)
 {
   GObjectClass *object_class = (GObjectClass *) klass;
+  AtspiDeviceClass *device_class = ATSPI_DEVICE_CLASS (klass);
 
   device_legacy_parent_class = g_type_class_peek_parent (klass);
   object_class->finalize = atspi_device_legacy_finalize;
+  device_class->map_modifier = atspi_device_legacy_map_modifier;
+  device_class->unmap_modifier = atspi_device_legacy_unmap_modifier;
+  device_class->get_modifier = atspi_device_legacy_get_modifier;
+  device_class->get_locked_modifiers = atspi_device_legacy_get_locked_modifiers;
+  device_class->grab_keyboard = atspi_device_legacy_grab_keyboard;
+  device_class->ungrab_keyboard = atspi_device_legacy_ungrab_keyboard;
 }
 
 /**
diff --git a/atspi/atspi-device-x11.c b/atspi/atspi-device-x11.c
index adc600be..e8f21995 100644
--- a/atspi/atspi-device-x11.c
+++ b/atspi/atspi-device-x11.c
@@ -29,7 +29,7 @@
 #include <X11/XKBlib.h>
 
 
-#define ATSPI_VIRTUAL_MODIFIER_MASK 0xffff0000
+#define ATSPI_VIRTUAL_MODIFIER_MASK 0x0000f000
 
 typedef struct _AtspiDeviceX11Private AtspiDeviceX11Private;
 struct _AtspiDeviceX11Private
@@ -39,9 +39,11 @@ struct _AtspiDeviceX11Private
   GSource *source;
   int xi_opcode;
   int device_id;
+  int device_id_alt;
   GSList *modifiers;
   GSList *key_grabs;
   guint virtual_mods_enabled;
+  gboolean keyboard_grabbed;
 };
 
 GObjectClass *device_x11_parent_class;
@@ -149,6 +151,11 @@ static gboolean
 grab_should_be_enabled (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab)
 {
   AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+
+  /* If the whole keyboard is grabbed, then all keys are grabbed elsewhere */
+  if (priv->keyboard_grabbed)
+    return FALSE;
+
   guint virtual_mods_used = grab->kd->modifiers & ATSPI_VIRTUAL_MODIFIER_MASK;
   return ((priv->virtual_mods_enabled & virtual_mods_used) == virtual_mods_used);
 }
@@ -169,16 +176,14 @@ grab_has_active_duplicate (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab)
 }
 
 static void
-enable_key_grab (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab)
+grab_key (AtspiDeviceX11 *x11_device, int keycode, int modmask)
 {
   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.modifiers = modmask;
   xi_modifiers.status = 0;
 
                        eventmask.deviceid = XIAllDevices;
@@ -188,17 +193,38 @@ enable_key_grab (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab)
                        XISetMask (mask, XI_KeyPress);
                        XISetMask (mask, XI_KeyRelease);
 
+  XIGrabKeycode (priv->display, XIAllMasterDevices, keycode, priv->window, XIGrabModeSync, XIGrabModeAsync, 
False, &eventmask, 1, &xi_modifiers);
+}
+
+static void
+enable_key_grab (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab)
+{
+  AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+
+  g_return_if_fail (priv->display != NULL);
+
   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_key (x11_device, grab->kd->keycode, grab->kd->modifiers & ~ATSPI_VIRTUAL_MODIFIER_MASK);
   grab->enabled = TRUE;
 }
 
 static void
-disable_key_grab (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab)
+ungrab_key (AtspiDeviceX11 *x11_device, int keycode, int modmask)
 {
   AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
   XIGrabModifiers xi_modifiers;
 
+  xi_modifiers.modifiers = modmask;
+  xi_modifiers.status = 0;
+
+  XIUngrabKeycode (priv->display, XIAllMasterDevices, keycode, priv->window, sizeof(xi_modifiers), 
&xi_modifiers);
+}
+
+static void
+disable_key_grab (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab)
+{
+  AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+
   g_return_if_fail (priv->display != NULL);
 
   if (!grab->enabled)
@@ -209,10 +235,24 @@ disable_key_grab (AtspiDeviceX11 *x11_device, AtspiX11KeyGrab *grab)
   if (grab_has_active_duplicate (x11_device, grab))
     return;
 
-  xi_modifiers.modifiers = grab->kd->modifiers & ~ATSPI_VIRTUAL_MODIFIER_MASK;
-  xi_modifiers.status = 0;
+  ungrab_key (x11_device, grab->kd->keycode, grab->kd->modifiers & ~ATSPI_VIRTUAL_MODIFIER_MASK);
+}
+
+static void
+refresh_key_grabs (AtspiDeviceX11 *x11_device)
+{
+  AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+  GSList *l;
 
-  XIUngrabKeycode (priv->display, XIAllMasterDevices, grab->kd->keycode, priv->window, sizeof(xi_modifiers), 
&xi_modifiers);
+  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 void
@@ -220,7 +260,6 @@ 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;
@@ -238,15 +277,7 @@ set_virtual_modifier (AtspiDeviceX11 *x11_device, gint keycode, gboolean enabled
     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);
-  }
+  refresh_key_grabs (x11_device);
 }
 
 static gboolean
@@ -270,7 +301,7 @@ do_event_dispatch (gpointer user_data)
     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);
+      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)
@@ -286,11 +317,18 @@ do_event_dispatch (gpointer user_data)
           XLookupString((XKeyEvent *)&keyevent, text, sizeof (text), &keysym, &status);
           if (text[0] < ' ')
             text[0] = '\0';
+          /* The deviceid can change. Would be nice to find a better way of
+             handling this */
+          if (priv->device_id && priv->device_id_alt && xiDevEv->deviceid != priv->device_id && 
xiDevEv->deviceid != priv->device_id_alt)
+            priv->device_id = priv->device_id_alt = 0;
+          else if (priv->device_id && !priv->device_id_alt && xiDevEv->deviceid != priv->device_id)
+            priv->device_id_alt = xiDevEv->deviceid;
           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);
+          /* otherwise it's probably a duplicate event from a key grab */
           XFreeEventData (priv->display, &xevent.xcookie);
           break;
         }
@@ -371,9 +409,9 @@ check_virtual_modifier (AtspiDeviceX11 *x11_device, guint modifier)
 static guint
 get_unused_virtual_modifier (AtspiDeviceX11 *x11_device)
 {
-  guint ret = 0x10000;
+  guint ret = 0x1000;
 
-  while (ret)
+  while (ret < 0x10000)
   {
     if (!check_virtual_modifier (x11_device, ret))
       return ret;
@@ -578,16 +616,41 @@ atspi_device_x11_get_locked_modifiers (AtspiDevice *device)
   return state_rec.locked_mods;
 }
 
+static void
+get_keycode_range (AtspiDeviceX11 *x11_device, int *min, int *max)
+{
+  AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+  XkbDescPtr desc;
+
+  desc = XkbGetMap (priv->display, XkbModifierMapMask, XkbUseCoreKbd);
+  *min = desc->min_key_code;
+  *max = desc->max_key_code;
+  XkbFreeKeyboard (desc, XkbModifierMapMask, TRUE);
+}
+
 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;
+  int min, max;
+  gint i;
 
   g_return_val_if_fail (priv->display != NULL, FALSE);
-  result = XGrabKeyboard (priv->display, priv->window, TRUE, GrabModeAsync, GrabModeSync, CurrentTime);
-  return (result == 0);
+#if 0
+  /* THis seems like the right thing to do, but it fails for me */
+  return (XGrabKeyboard (priv->display, priv->window, TRUE, GrabModeAsync, GrabModeSync, CurrentTime)) == 0;
+#else
+  if (priv->keyboard_grabbed)
+    return TRUE;
+  priv->keyboard_grabbed = TRUE;
+  refresh_key_grabs (x11_device);
+
+  get_keycode_range (x11_device, &min, &max);
+  for (i = min; i < max; i++)
+    grab_key (x11_device, i, 0);
+  return TRUE;
+#endif
 }
 
 static void
@@ -595,9 +658,23 @@ atspi_device_x11_ungrab_keyboard (AtspiDevice *device)
 {
   AtspiDeviceX11 *x11_device = ATSPI_DEVICE_X11 (device);
   AtspiDeviceX11Private *priv = atspi_device_x11_get_instance_private (x11_device);
+  int min, max;
+  gint i;
 
   g_return_if_fail (priv->display != NULL);
+#if 0
   XUngrabKeyboard (priv->display, CurrentTime);
+#else
+  if (!priv->keyboard_grabbed)
+    return;
+  priv->keyboard_grabbed = FALSE;
+
+  get_keycode_range (x11_device, &min, &max);
+  for (i = min; i < max; i++)
+    ungrab_key (x11_device, i, 0);
+
+  refresh_key_grabs (x11_device);
+#endif
 }
 
 static void
diff --git a/atspi/atspi-device.c b/atspi/atspi-device.c
index abf6138d..5f62dc3c 100644
--- a/atspi/atspi-device.c
+++ b/atspi/atspi-device.c
@@ -99,9 +99,8 @@ atspi_device_class_init (AtspiDeviceClass *klass)
 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"))
+  if (!g_getenv ("WAYLAND_DISPLAY") && !g_getenv ("ATSPI_USE_LEGACY_DEVICE"))
     return ATSPI_DEVICE (atspi_device_x11_new ());
 #endif
 


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