Fixes and workarounds for event-synthesis/XKB interaction,
authorbillh <billh@e2bd861d-eb25-0410-b326-f6ed22b6b98c>
Mon, 25 Nov 2002 22:54:28 +0000 (22:54 +0000)
committerbillh <billh@e2bd861d-eb25-0410-b326-f6ed22b6b98c>
Mon, 25 Nov 2002 22:54:28 +0000 (22:54 +0000)
and to make modifier notifications reliable at the same time.

git-svn-id: http://svn.gnome.org/svn/at-spi/trunk@374 e2bd861d-eb25-0410-b326-f6ed22b6b98c

ChangeLog
registryd/deviceeventcontroller.c

index d792845..8517072 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,43 @@
+2002-11-25  Bill Haneman  <bill.haneman@sun.com>
+
+        Workarounds and fixes for mouse event/XKB interaction.
+       
+       * registryd/registry.c:
+       (spi_keycodes_contain, spi_dec_init_keycode_list): 
+       New internal methods.
+       (spi_dec_set_unlatch_pending):
+       Split the method that tells XKB to expect an
+       unlatch (and revert it) out of the rest of the
+       event handling code.  This method is called by the
+       two mouse event handlers (the poll and the filter).
+       (spi_dec_update_and_emit, spi_dec_mouse_check): 
+       New methods split from spi_dec_poll_mouse_moved.
+       The second one is now called from inside the filterFn
+       when an XKB delatch event is received, to determine 
+       whether to revert the delatch or not.
+       (spi_poll_mouse_moved): Refactor; also we can now
+       properly handle the case where multiple button states
+       have changed between polling cycles and we didn't get
+       notified due to another client's grab.
+       (global_filter_fn): 
+       Synchronize the server while handling an XKB notification.
+       Ugly, but apparently necessary to avoid nasty races.
+       Check the mouse state on receipt of an XKB latch change,
+       to decide whether to revert the latch or not. (Latches are
+       reverted if there is an immediately preceding mouse button
+       event that a client has consumed, in which case we don't want
+       the user to see the side-effect of the event).
+       (spi_device_event_controller_forward_mouse_event):
+       Check for modifier events and fire before resetting mouse_mask_state,
+       prevents losing modifier events.
+
+       (impl_generate_keyboard_event):
+       If we've just generated a keyboard event for a non-shift key,
+       we don't want to revert the pending XKB latch. 
+       Otherwise, check the modifier state (via a call to 
+       spi_dev_mouse_check) and set the relatch mask to 
+       that state before returning.
+       
 2002-11-24  Bill Haneman  <bill.haneman@sun.com>
 
         * NEWS: 
index df24cde..8849dba 100644 (file)
@@ -26,7 +26,7 @@
 #include <config.h>
 
 #undef  SPI_XKB_DEBUG
-#define  SPI_DEBUG
+#undef  SPI_DEBUG
 #undef  SPI_KEYEVENT_DEBUG
 
 #include <string.h>
@@ -95,17 +95,18 @@ typedef struct {
 } DEControllerKeyListener;
 
 typedef struct {
-       unsigned int last_press_keycode;
-       unsigned int last_release_keycode;
-       struct timeval last_press_time;
-       struct timeval last_release_time;
-       int have_xkb;
-       int xkb_major_extension_opcode;
-       int xkb_base_event_code;
-       int xkb_base_error_code;
-       unsigned int xkb_latch_mask;
-       unsigned int pending_xkb_mod_relatch_mask;
-       XkbDescPtr xkb_desc;
+  unsigned int last_press_keycode;
+  unsigned int last_release_keycode;
+  struct timeval last_press_time;
+  struct timeval last_release_time;
+  int have_xkb;
+  int xkb_major_extension_opcode;
+  int xkb_base_event_code;
+  int xkb_base_error_code;
+  unsigned int xkb_latch_mask;
+  unsigned int pending_xkb_mod_relatch_mask;
+  KeyCode *shift_keycodes;
+  XkbDescPtr xkb_desc;
 } DEControllerPrivateData;
 
 static void     spi_controller_register_with_devices          (SpiDEController           *controller);
@@ -143,6 +144,51 @@ keycode_for_keysym (long keysym)
   return XKeysymToKeycode (spi_get_display (), (KeySym) keysym);
 }
 
+#define SPI_DEC_MAX_SHIFT_KEYSYMS 15
+static long _shift_keysyms[SPI_DEC_MAX_SHIFT_KEYSYMS] = 
+                                {XK_Shift_L, XK_Shift_R,
+                                XK_Control_L, XK_Control_R,
+                                XK_Caps_Lock, XK_Shift_Lock,
+                                XK_Alt_L, XK_Alt_R,
+                                XK_Meta_L, XK_Meta_R, 
+                                XK_Super_L, XK_Super_R,
+                                XK_Hyper_L, XK_Hyper_R,
+                                0};
+
+static gboolean
+spi_keycodes_contain (KeyCode keycodes[], KeyCode keycode)
+{
+  int i = 0;
+
+  if (keycode)
+    {
+      while (i < SPI_DEC_MAX_SHIFT_KEYSYMS)
+       {
+         if (keycodes[i] == keycode) 
+           return TRUE;
+         ++i;
+       }
+    }
+  return FALSE;
+}
+
+static void
+spi_dec_init_keycode_list (SpiDEController *controller)
+{
+  DEControllerPrivateData *priv = 
+    g_object_get_qdata (G_OBJECT (controller), spi_dec_private_quark);
+  KeyCode keycode;
+  int i;
+  priv->shift_keycodes = g_malloc (sizeof (KeyCode) * 
+                                  SPI_DEC_MAX_SHIFT_KEYSYMS);
+  for (i = 0; _shift_keysyms[i] != 0; ++i)
+    { 
+      keycode = keycode_for_keysym (_shift_keysyms [i]);
+      priv->shift_keycodes [i] = keycode;
+    }
+  priv->shift_keycodes [i] = 0; /* terminate the list */
+}
+
 static DEControllerGrabMask *
 spi_grab_mask_clone (DEControllerGrabMask *grab_mask)
 {
@@ -179,31 +225,164 @@ spi_grab_mask_compare_values (gconstpointer p1, gconstpointer p2)
     }
 }
 
+static void
+spi_dec_set_unlatch_pending (SpiDEController *controller, unsigned mask)
+{
+  DEControllerPrivateData *priv = 
+    g_object_get_qdata (G_OBJECT (controller), spi_dec_private_quark);
+#ifdef SPI_XKB_DEBUG
+  if (priv->xkb_latch_mask) fprintf (stderr, "unlatch pending! %x\n", mask);
+#endif
+  priv->pending_xkb_mod_relatch_mask |= priv->xkb_latch_mask; 
+}
 static gint poll_count = 0;
 
 static gboolean
-spi_dec_poll_mouse_moved (gpointer data)
+spi_dec_button_update_and_emit (SpiDEController *controller, 
+                               guint mask_return)
 {
-  SpiRegistry *registry = SPI_REGISTRY (data);
-  SpiDEController *controller = registry->de_controller;
   CORBA_Environment ev;
   Accessibility_Event e;
   Accessibility_DeviceEvent mouse_e;
-  Window root_return, child_return;
+  gchar event_name[24];
+  gboolean is_consumed = FALSE;
+
+  if ((mask_return & mouse_button_mask) !=
+      (mouse_mask_state & mouse_button_mask)) 
+    {
+      int button_number = 0;
+      gboolean is_down = False;
+      
+      if (!(mask_return & Button1Mask) &&
+         (mouse_mask_state & Button1Mask)) 
+       {
+         mouse_mask_state &= ~Button1Mask;
+         button_number = 1;
+       } 
+      else if ((mask_return & Button1Mask) &&
+              !(mouse_mask_state & Button1Mask)) 
+       {
+         mouse_mask_state |= Button1Mask;
+         button_number = 1;
+         is_down = True;
+       } 
+      else if (!(mask_return & Button2Mask) &&
+              (mouse_mask_state & Button2Mask)) 
+       {
+         mouse_mask_state &= ~Button2Mask;
+         button_number = 2;
+       } 
+      else if ((mask_return & Button2Mask) &&
+              !(mouse_mask_state & Button2Mask)) 
+       {
+         mouse_mask_state |= Button2Mask;
+         button_number = 2;
+         is_down = True;
+       } 
+      else if (!(mask_return & Button3Mask) &&
+              (mouse_mask_state & Button3Mask)) 
+       {
+         mouse_mask_state &= ~Button3Mask;
+         button_number = 3;
+       } 
+      else if ((mask_return & Button3Mask) &&
+              !(mouse_mask_state & Button3Mask)) 
+       {
+         mouse_mask_state |= Button3Mask;
+         button_number = 3;
+         is_down = True;
+       } 
+      else if (!(mask_return & Button4Mask) &&
+              (mouse_mask_state & Button1Mask)) 
+       {
+         mouse_mask_state &= ~Button4Mask;
+         button_number = 4;
+       } 
+      else if ((mask_return & Button4Mask) &&
+              !(mouse_mask_state & Button1Mask)) 
+       {
+         mouse_mask_state |= Button4Mask;
+         button_number = 4;
+         is_down = True;
+       } 
+      else if (!(mask_return & Button5Mask) &&
+              (mouse_mask_state & Button5Mask)) 
+       {
+         mouse_mask_state &= ~Button5Mask;
+         button_number = 5;
+       }
+      else if ((mask_return & Button5Mask) &&
+              !(mouse_mask_state & Button5Mask)) 
+       {
+         mouse_mask_state |= Button5Mask;
+         button_number = 5;
+         is_down = True;
+       }
+      if (button_number) {
+#ifdef SPI_DEBUG                 
+       fprintf (stderr, "Button %d %s\n",
+                button_number, (is_down) ? "Pressed" : "Released");
+#endif
+       snprintf (event_name, 22, "mouse:button:%d%c", button_number,
+                 (is_down) ? 'p' : 'r');
+       /* TODO: distinguish between physical and 
+        * logical buttons 
+        */
+       mouse_e.type      = (is_down) ? 
+         Accessibility_BUTTON_PRESSED_EVENT :
+         Accessibility_BUTTON_RELEASED_EVENT;
+       mouse_e.id        = button_number;
+       mouse_e.hw_code   = button_number;
+       mouse_e.modifiers = (CORBA_unsigned_short) mouse_mask_state; 
+       mouse_e.timestamp = 0;
+       mouse_e.event_string = "";
+       mouse_e.is_text   = CORBA_FALSE;
+       is_consumed = 
+         spi_controller_notify_mouselisteners (controller, 
+                                               &mouse_e, 
+                                               &ev);
+       e.type = event_name;
+       e.source = BONOBO_OBJREF (controller->registry->desktop);
+       e.detail1 = last_mouse_pos->x;
+       e.detail2 = last_mouse_pos->y;
+       spi_init_any_nil (&e.any_data);
+       CORBA_exception_init (&ev);
+       if (!is_consumed)
+         {
+           Accessibility_Registry_notifyEvent (BONOBO_OBJREF (controller->registry),
+                                               &e,
+                                               &ev);  
+         }
+       else
+         spi_dec_set_unlatch_pending (controller, mask_return);
+      }
+      return TRUE;
+    }
+  else
+    {
+      return FALSE;
+    }
+}
+
+
+static guint
+spi_dec_mouse_check (SpiDEController *controller, 
+                    int *x, int *y, gboolean *moved)
+{
+  Accessibility_Event e;
+  CORBA_Environment ev;
   int win_x_return,win_y_return;
-  int x, y;
   int poll_count_modulus = 10;
   unsigned int mask_return;
-  gchar event_name[24];
-  gboolean is_consumed;
+  Window root_return, child_return;
   Display *display = spi_get_display ();
 
   if (display != NULL)
     XQueryPointer(display, DefaultRootWindow (display),
                  &root_return, &child_return,
-                 &x, &y,
+                 x, y,
                  &win_x_return, &win_y_return, &mask_return);
-  
   /* 
    * Since many clients grab the pointer, and X goes an automatic
    * pointer grab on mouse-down, we often must detect mouse button events
@@ -213,158 +392,85 @@ spi_dec_poll_mouse_moved (gpointer data)
    */
   if (mask_return != mouse_mask_state) 
     {
-      while ((mask_return & mouse_button_mask) !=
-            (mouse_mask_state & mouse_button_mask)) 
-       {
-         int button_number = 0;
-         gboolean is_down = False;
+      while (spi_dec_button_update_and_emit (controller, mask_return));
+    }
 
-         if (!(mask_return & Button1Mask) &&
-             (mouse_mask_state & Button1Mask)) 
-           {
-             mouse_mask_state &= ~Button1Mask;
-             button_number = 1;
-           } 
-         else if ((mask_return & Button1Mask) &&
-             !(mouse_mask_state & Button1Mask)) 
-           {
-             mouse_mask_state |= Button1Mask;
-             button_number = 1;
-             is_down = True;
-           } 
-         else if (!(mask_return & Button2Mask) &&
-                      (mouse_mask_state & Button2Mask)) 
-           {
-             mouse_mask_state &= ~Button2Mask;
-             button_number = 2;
-           } 
-         else if ((mask_return & Button2Mask) &&
-                      !(mouse_mask_state & Button2Mask)) 
-           {
-             mouse_mask_state |= Button2Mask;
-             button_number = 2;
-             is_down = True;
-           } 
-         else if (!(mask_return & Button3Mask) &&
-                  (mouse_mask_state & Button3Mask)) 
-           {
-             mouse_mask_state &= ~Button3Mask;
-             button_number = 3;
-           } 
-         else if ((mask_return & Button3Mask) &&
-                  !(mouse_mask_state & Button3Mask)) 
-           {
-             mouse_mask_state |= Button3Mask;
-             button_number = 3;
-             is_down = True;
-           } 
-         else if (!(mask_return & Button4Mask) &&
-                  (mouse_mask_state & Button1Mask)) 
-           {
-             mouse_mask_state &= ~Button4Mask;
-             button_number = 4;
-           } 
-         else if ((mask_return & Button4Mask) &&
-                  !(mouse_mask_state & Button1Mask)) 
-           {
-             mouse_mask_state |= Button4Mask;
-             button_number = 4;
-             is_down = True;
-           } 
-         else if (!(mask_return & Button5Mask) &&
-                  (mouse_mask_state & Button5Mask)) 
-           {
-             mouse_mask_state &= ~Button5Mask;
-             button_number = 5;
-           }
-         else if ((mask_return & Button5Mask) &&
-                  !(mouse_mask_state & Button5Mask)) 
-           {
-             mouse_mask_state |= Button5Mask;
-             button_number = 5;
-             is_down = True;
-           }
-         if (button_number) {
-#ifdef SPI_DEBUG                 
-           fprintf (stderr, "Button %d %s\n",
-                    button_number, (is_down) ? "Pressed" : "Released");
-#endif
-           snprintf (event_name, 22, "mouse:button:%d%c", button_number,
-                     (is_down) ? 'p' : 'r');
-           /* TODO: distinguish between physical and 
-            * logical buttons 
-            */
-           mouse_e.type      = (is_down) ? 
-             Accessibility_BUTTON_PRESSED_EVENT :
-             Accessibility_BUTTON_RELEASED_EVENT;
-           mouse_e.id        = button_number;
-           mouse_e.hw_code   = button_number;
-           mouse_e.modifiers = (CORBA_unsigned_short) mouse_mask_state; 
-           mouse_e.timestamp = 0;
-           mouse_e.event_string = "";
-           mouse_e.is_text   = CORBA_FALSE;
-           is_consumed = 
-             spi_controller_notify_mouselisteners (controller, 
-                                                   &mouse_e, 
-                                                   &ev);
-           e.type = event_name;
-           e.source = BONOBO_OBJREF (registry->desktop);
-           e.detail1 = last_mouse_pos->x;
-           e.detail2 = last_mouse_pos->y;
-           spi_init_any_nil (&e.any_data);
-           CORBA_exception_init (&ev);
-           if (!is_consumed)
-             Accessibility_Registry_notifyEvent (BONOBO_OBJREF (registry),
-                                                 &e,
-                                                 &ev);  
-         }
-       }
-      
-      if ((mask_return & key_modifier_mask) !=
-      (mouse_mask_state & key_modifier_mask)) {
-#ifdef SPI_DEBUG
-       fprintf (stderr, "MODIFIER CHANGE EVENT!\n");
-#endif
-       e.type = "keyboard:modifiers";  
-       e.source = BONOBO_OBJREF (registry->desktop);
-       e.detail1 = mouse_mask_state;
-       e.detail2 = mask_return;
-       spi_init_any_nil (&e.any_data);
-       CORBA_exception_init (&ev);
-       Accessibility_Registry_notifyEvent (BONOBO_OBJREF (registry),
-                                           &e,
-                                           &ev);
-      }
-      mouse_mask_state = mask_return;
-    }  
-  
   if (poll_count++ == poll_count_modulus) {
     poll_count = 0;
     e.type = "mouse:abs";  
-    e.source = BONOBO_OBJREF (registry->desktop);
-    e.detail1 = x;
-    e.detail2 = y;
-    spi_init_any_nil (&e.any_data);
-    CORBA_exception_init (&ev);
-    Accessibility_Registry_notifyEvent (BONOBO_OBJREF (registry),
-                                       &e,
-                                       &ev);
-  }
-  if (x != last_mouse_pos->x || y != last_mouse_pos->y) {
-    e.type = "mouse:rel";  
-    e.source = BONOBO_OBJREF (registry->desktop);
-    e.detail1 = x - last_mouse_pos->x;
-    e.detail2 = y - last_mouse_pos->y;
+    e.source = BONOBO_OBJREF (controller->registry->desktop);
+    e.detail1 = *x;
+    e.detail2 = *y;
     spi_init_any_nil (&e.any_data);
     CORBA_exception_init (&ev);
-    last_mouse_pos->x = x;
-    last_mouse_pos->y = y;
-    Accessibility_Registry_notifyEvent (BONOBO_OBJREF (registry),
+    Accessibility_Registry_notifyEvent (BONOBO_OBJREF (controller->registry),
                                        &e,
                                        &ev);
-    return TRUE;
   }
-  return FALSE;
+  if (*x != last_mouse_pos->x || *y != last_mouse_pos->y) 
+    {
+      e.type = "mouse:rel";  
+      e.source = BONOBO_OBJREF (controller->registry->desktop);
+      e.detail1 = *x - last_mouse_pos->x;
+      e.detail2 = *y - last_mouse_pos->y;
+      spi_init_any_nil (&e.any_data);
+      CORBA_exception_init (&ev);
+      last_mouse_pos->x = *x;
+      last_mouse_pos->y = *y;
+      Accessibility_Registry_notifyEvent (BONOBO_OBJREF (controller->registry),
+                                         &e,
+                                         &ev);
+      *moved = True;
+    }
+  else
+    {
+      *moved = False;
+    }
+
+  return mask_return;
+}
+
+static void
+spi_dec_emit_modifier_event (SpiDEController *controller, guint prev_mask, 
+                            guint current_mask)
+{
+  Accessibility_Event e;
+  CORBA_Environment ev;
+
+#ifdef SPI_XKB_DEBUG
+  fprintf (stderr, "MODIFIER CHANGE EVENT! %x to %x\n", 
+          prev_mask, current_mask);
+#endif
+  e.type = "keyboard:modifiers";  
+  e.source = BONOBO_OBJREF (controller->registry->desktop);
+  e.detail1 = prev_mask & key_modifier_mask;
+  e.detail2 = current_mask & key_modifier_mask;
+  spi_init_any_nil (&e.any_data);
+  CORBA_exception_init (&ev);
+  Accessibility_Registry_notifyEvent (BONOBO_OBJREF (controller->registry),
+                                     &e,
+                                     &ev);
+}
+
+static gboolean
+spi_dec_poll_mouse_moved (gpointer data)
+{
+  SpiRegistry *registry = SPI_REGISTRY (data);
+  SpiDEController *controller = registry->de_controller;
+  int x, y;  
+  gboolean moved;
+  guint mask_return;
+
+  mask_return = spi_dec_mouse_check (controller, &x, &y, &moved);
+  
+  if ((mask_return & key_modifier_mask) !=
+      (mouse_mask_state & key_modifier_mask)) 
+    {
+      spi_dec_emit_modifier_event (controller, mouse_mask_state, mask_return);
+      mouse_mask_state = mask_return;
+    }
+
+  return moved;
 }
 
 static gboolean
@@ -749,7 +855,6 @@ spi_device_event_controller_forward_mouse_event (SpiDEController *controller,
   gchar event_name[24];
   gboolean is_consumed = FALSE;
   gboolean xkb_mod_unlatch_occurred;
-  DEControllerPrivateData *priv;
   XButtonEvent *xbutton_event = (XButtonEvent *) xevent;
 
   int button = xbutton_event->button;
@@ -774,6 +879,7 @@ spi_device_event_controller_forward_mouse_event (SpiDEController *controller,
            mouse_button_state |= Button5Mask;
            break;
     }
+
   last_mouse_pos->x = ((XButtonEvent *) xevent)->x_root;
   last_mouse_pos->y = ((XButtonEvent *) xevent)->y_root;
 
@@ -797,8 +903,12 @@ spi_device_event_controller_forward_mouse_event (SpiDEController *controller,
   mouse_e.event_string = "";
   mouse_e.is_text   = CORBA_FALSE;
   if ((mouse_button_state & mouse_button_mask) != 
-      (mouse_mask_state & mouse_button_mask)) 
+       (mouse_mask_state & mouse_button_mask))
     { 
+      if ((mouse_mask_state & key_modifier_mask) !=
+         (mouse_button_state & key_modifier_mask))
+       spi_dec_emit_modifier_event (controller, 
+                                    mouse_mask_state, mouse_button_state);
       mouse_mask_state = mouse_button_state;
       is_consumed = 
        spi_controller_notify_mouselisteners (controller, &mouse_e, &ev);
@@ -813,17 +923,15 @@ spi_device_event_controller_forward_mouse_event (SpiDEController *controller,
                                          &e,
                                          &ev);
     }
+
   xkb_mod_unlatch_occurred = (xevent->type == ButtonPress ||
                              xevent->type == ButtonRelease);
-      
+  
   /* if client wants to consume this event, and XKB latch state was
    *   unset by this button event, we reset it
    */
-  if (is_consumed && xkb_mod_unlatch_occurred)
-    {
-      priv = g_object_get_qdata (G_OBJECT (controller), spi_dec_private_quark);            
-      priv->pending_xkb_mod_relatch_mask |= priv->xkb_latch_mask; 
-    }
+  if (is_consumed && (xkb_mod_unlatch_occurred))
+    spi_dec_set_unlatch_pending (controller, mouse_mask_state);
   
   XAllowEvents (spi_get_display (),
                (is_consumed) ? SyncPointer : ReplayPointer,
@@ -853,12 +961,24 @@ global_filter_fn (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
   if (xevent->type == priv->xkb_base_event_code)
     {
       XkbAnyEvent * xkb_ev = (XkbAnyEvent *) xevent;
+      /* ugly but probably necessary...*/
+      XSynchronize (spi_get_display (), TRUE);
 
       if (xkb_ev->xkb_type == XkbStateNotify)
         {
          XkbStateNotifyEvent *xkb_snev =
                  (XkbStateNotifyEvent *) xkb_ev;
-         priv->xkb_latch_mask = xkb_snev->latched_mods;
+         /* check the mouse, to catch mouse events grabbed by
+          * another client; in case we should revert this XKB delatch 
+          */
+         if (!priv->pending_xkb_mod_relatch_mask)
+           {
+             int x, y;
+             gboolean moved;
+             spi_dec_mouse_check (controller, &x, &y, &moved);
+           }
+         /* we check again, since the previous call may have 
+            changed this flag */
          if (priv->pending_xkb_mod_relatch_mask)
            {
              unsigned int feedback_mask;
@@ -896,9 +1016,14 @@ global_filter_fn (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
 #endif
              priv->pending_xkb_mod_relatch_mask = 0;
            }
+         else
+           {
+             priv->xkb_latch_mask = xkb_snev->latched_mods;
+           }
        }
         else
               DBG (2, g_warning ("XKB event %d\n", xkb_ev->xkb_type));
+      XSynchronize (spi_get_display (), FALSE);
     }
   
   return GDK_FILTER_CONTINUE;
@@ -1716,6 +1841,7 @@ impl_generate_keyboard_event (PortableServer_Servant           servant,
 {
   SpiDEController *controller =
        SPI_DEVICE_EVENT_CONTROLLER (bonobo_object (servant));
+  DEControllerPrivateData *priv;
   long key_synth_code;
   unsigned int slow_keys_delay;
   unsigned int press_time;
@@ -1734,7 +1860,8 @@ impl_generate_keyboard_event (PortableServer_Servant           servant,
   
   /* TODO: implement keystring mode also */
   gdk_error_trap_push ();
-  
+  key_synth_code = keycode;
+
   switch (synth_type)
     {
       case Accessibility_KEY_PRESS:
@@ -1761,6 +1888,26 @@ impl_generate_keyboard_event (PortableServer_Servant           servant,
     {
       DBG (-1, g_warning ("Error emitting keystroke"));
     }
+
+  /* now we must determine whether this keystroke is expected 
+   * to delatch XKB. This is a bit of a hack :-( */
+  priv =  g_object_get_qdata (G_OBJECT (controller), spi_dec_private_quark);
+  if (!priv->shift_keycodes) 
+    spi_dec_init_keycode_list (controller);
+  if (!spi_keycodes_contain (priv->shift_keycodes, key_synth_code)) 
+    {
+      priv->pending_xkb_mod_relatch_mask = 0;
+      priv->xkb_latch_mask = 0;
+      fprintf (stderr, "resetting the relatch masks.\n");
+    }
+  else
+    {
+      int x, y;
+      gboolean moved;
+      priv->pending_xkb_mod_relatch_mask = 
+       spi_dec_mouse_check (controller, &x, &y, &moved);
+      fprintf (stderr, "preparing to relatch %x\n");
+    }
 }
 
 /* Accessibility::DEController::generateMouseEvent */
@@ -1898,7 +2045,7 @@ spi_device_event_controller_init (SpiDEController *device_event_controller)
   g_object_set_qdata (G_OBJECT (device_event_controller),
                      spi_dec_private_quark,
                      private);
-  
+  private->shift_keycodes = NULL;
   spi_controller_register_with_devices (device_event_controller);
 }