2003-04-24 Padraig O'Briain <padraig.obriain@sun.com>
[platform/core/uifw/at-spi2-atk.git] / registryd / deviceeventcontroller.c
index 600c3d4..609deea 100644 (file)
@@ -1,8 +1,8 @@
-/*
- * AT-SPI - Assistive Technology Service Provider Interface
+/* AT-SPI - Assistive Technology Service Provider Interface
+ *
  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
  *
- * Copyright 2001, 2002 Sun Microsystems Inc.,
+ * Copyright 2001, 2003 Sun Microsystems Inc.,
  * Copyright 2001, 2002 Ximian, Inc.
  *
  * This library is free software; you can redistribute it and/or
@@ -32,6 +32,7 @@
 #include <string.h>
 #include <ctype.h>
 #include <stdio.h>
+#include <sys/time.h>
 #include <bonobo/bonobo-exception.h>
 
 #include <X11/Xlib.h>
 /* A pointer to our parent object class */
 static GObjectClass *spi_device_event_controller_parent_class;
 static int spi_error_code = 0;
-static GdkPoint *last_mouse_pos = NULL; 
+static GdkPoint last_mouse_pos_static = {0, 0}; 
+static GdkPoint *last_mouse_pos = &last_mouse_pos_static;
 static unsigned int mouse_mask_state = 0;
 static unsigned int mouse_button_mask =
   Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask;
 static unsigned int key_modifier_mask =
-  Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask | ShiftMask | LockMask | ControlMask;
+  Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask | ShiftMask | LockMask | ControlMask | SPI_KEYMASK_NUMLOCK;
+static unsigned int _numlock_physical_mask = Mod2Mask; /* a guess, will be reset */
 
 static GQuark spi_dec_private_quark = 0;
 
@@ -94,17 +97,17 @@ 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;
+  XkbDescPtr xkb_desc;
 } DEControllerPrivateData;
 
 static void     spi_controller_register_with_devices          (SpiDEController           *controller);
@@ -178,131 +181,262 @@ spi_grab_mask_compare_values (gconstpointer p1, gconstpointer p2)
     }
 }
 
-static gint poll_count = 0;
-
+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", 
+                                    priv->xkb_latch_mask);
+#endif
+  priv->pending_xkb_mod_relatch_mask |= priv->xkb_latch_mask; 
+}
+static void
+spi_dec_clear_unlatch_pending (SpiDEController *controller)
+{
+  DEControllerPrivateData *priv = 
+    g_object_get_qdata (G_OBJECT (controller), spi_dec_private_quark);
+  priv->xkb_latch_mask = 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;
-  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;
-  Display *display = spi_get_display ();
+  gboolean is_consumed = FALSE;
 
-  if (display != NULL)
-         XQueryPointer(display, DefaultRootWindow (display),
-               &root_return, &child_return,
-               &x, &y,
-               &win_x_return, &win_y_return, &mask_return);
-
-  if (mask_return != mouse_mask_state) {
-         if ((mask_return & mouse_button_mask) !=
-             (mouse_mask_state & mouse_button_mask)) {
-                 int button_number = 0;
-                 if (!(mask_return & Button1Mask) &&
-                     (mouse_mask_state & Button1Mask)) {
-                         button_number = 1;
-                 } else if (!(mask_return & Button2Mask) &&
-                            (mouse_mask_state & Button2Mask)) {
-                         button_number = 2;
-                 } else if (!(mask_return & Button3Mask) &&
-                            (mouse_mask_state & Button3Mask)) {
-                         button_number = 3;
-                 } else if (!(mask_return & Button4Mask) &&
-                            (mouse_mask_state & Button1Mask)) {
-                         button_number = 4;
-                 } else if (!(mask_return & Button5Mask) &&
-                            (mouse_mask_state & Button5Mask)) {
-                         button_number = 5;
-                 }
-                 if (button_number) {
+  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 & Button4Mask)) 
+       {
+         mouse_mask_state &= ~Button4Mask;
+         button_number = 4;
+       } 
+      else if ((mask_return & Button4Mask) &&
+              !(mouse_mask_state & Button4Mask)) 
+       {
+         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 Released\n",
-                                  button_number);
+       fprintf (stderr, "Button %d %s\n",
+                button_number, (is_down) ? "Pressed" : "Released");
 #endif
-                         snprintf (event_name, 22, "mouse:button:%dr", button_number);
-                         /* TODO: distinguish between physical and 
-                          * logical buttons 
-                          */
-                         mouse_e.type      = 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 = CORBA_string_dup ("");
-                         mouse_e.is_text   = CORBA_FALSE;
-                         is_consumed = 
-                           spi_controller_notify_mouselisteners (controller, 
-                                                                 &mouse_e, 
-                                                                 &ev);
-                         CORBA_free (mouse_e.event_string);
-
-                         e.type = event_name;
-                         e.source = BONOBO_OBJREF (registry->desktop);
-                         e.detail1 = last_mouse_pos->x;
-                         e.detail2 = last_mouse_pos->y;
-                         CORBA_exception_init (&ev);
-                         if (!is_consumed)
-                           Accessibility_Registry_notifyEvent (BONOBO_OBJREF (registry),
-                                                               &e,
-                                                               &ev);  
-                 }
+       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);  
          }
-         if ((mask_return & key_modifier_mask) !=
-             (mouse_mask_state & key_modifier_mask)) {
-#ifdef SPI_DEBUG
-                 fprintf (stderr, "MODIFIER CHANGE EVENT!\n");
+       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;
+  unsigned int mask_return;
+  Window root_return, child_return;
+  Display *display = spi_get_display ();
+
+  if (display != NULL)
+    XQueryPointer(display, DefaultRootWindow (display),
+                 &root_return, &child_return,
+                 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
+   * by polling rather than via a button grab. 
+   * The while loop (rather than if) is used since it's possible that 
+   * multiple buttons have changed state since we last checked.
+   */
+  if (mask_return != mouse_mask_state) 
+    {
+      while (spi_dec_button_update_and_emit (controller, mask_return));
+    }
+
+  if (*x != last_mouse_pos->x || *y != last_mouse_pos->y) 
+    {
+      e.type = "mouse:abs";  
+      e.source = BONOBO_OBJREF (controller->registry->desktop);
+      e.detail1 = *x;
+      e.detail2 = *y;
+      spi_init_any_nil (&e.any_data);
+      CORBA_exception_init (&ev);
+      Accessibility_Registry_notifyEvent (BONOBO_OBJREF (controller->registry),
+                                         &e,
+                                         &ev);
+      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 (registry->desktop);
-                 e.detail1 = mouse_mask_state;
-                 e.detail2 = mask_return;
-                 CORBA_exception_init (&ev);
-                 Accessibility_Registry_notifyEvent (BONOBO_OBJREF (registry),
-                                                     &e,
-                                                     &ev);
-         }
-         mouse_mask_state = mask_return;
-  }
-  if (last_mouse_pos == NULL) {
-         last_mouse_pos = g_new0 (GdkPoint, 1);
-         last_mouse_pos->x = 0;
-         last_mouse_pos->y = 0;
-  }
-  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;
-         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;
-         CORBA_exception_init (&ev);
-         last_mouse_pos->x = x;
-         last_mouse_pos->y = y;
-         Accessibility_Registry_notifyEvent (BONOBO_OBJREF (registry),
-                                             &e,
-                                             &ev);
-         return TRUE;
-  }
-  return FALSE;
+
+  /* set bits for the virtual modifiers like NUMLOCK */
+  if (prev_mask & _numlock_physical_mask) 
+    prev_mask |= SPI_KEYMASK_NUMLOCK;
+  if (current_mask & _numlock_physical_mask) 
+    current_mask |= SPI_KEYMASK_NUMLOCK;
+
+  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
@@ -329,19 +463,19 @@ spi_dec_poll_mouse_moving (gpointer data)
     }
 }
 
+#ifdef WE_NEED_UGRAB_MOUSE
 static int
 spi_dec_ungrab_mouse (gpointer data)
 {
        Display *display = spi_get_display ();
-       fprintf (stderr, "mouse ungrab : display = %p\n", display);
        if (display)
          {
            XUngrabButton (spi_get_display (), AnyButton, AnyModifier,
                           XDefaultRootWindow (spi_get_display ()));
-           fprintf (stderr, "mouse grab released\n");
          }
        return FALSE;
 }
+#endif
 
 static void
 spi_dec_init_mouse_listener (SpiRegistry *registry)
@@ -351,10 +485,11 @@ spi_dec_init_mouse_listener (SpiRegistry *registry)
 
   if (display)
     {
-      XGrabButton (display, AnyButton, AnyModifier,
+      if (XGrabButton (display, AnyButton, AnyModifier,
                   gdk_x11_get_default_root_xwindow (),
                   True, ButtonPressMask | ButtonReleaseMask,
-                  GrabModeSync, GrabModeAsync, None, None);
+                  GrabModeSync, GrabModeAsync, None, None) != Success)
+       fprintf (stderr, "WARNING: could not grab mouse buttons!\n");
       XSync (display, False);
 #ifdef SPI_DEBUG
       fprintf (stderr, "mouse buttons grabbed\n");
@@ -362,6 +497,24 @@ spi_dec_init_mouse_listener (SpiRegistry *registry)
     }
 }
 
+/**
+ * Eventually we can use this to make the marshalling of mask types
+ * more sane, but for now we just use this to detect 
+ * the use of 'virtual' masks such as Mumlock and convert them to
+ * system-specific mask values (i.e. ModMask).
+ * 
+ **/
+static Accessibility_ControllerEventMask
+spi_dec_translate_mask (Accessibility_ControllerEventMask mask)
+{
+  DEControllerPrivateData *priv;
+
+  if (mask == SPI_KEYMASK_NUMLOCK) {
+    mask = _numlock_physical_mask;
+  }
+  return mask;
+}
+
 static DEControllerKeyListener *
 spi_dec_key_listener_new (CORBA_Object                            l,
                          const Accessibility_KeySet             *keys,
@@ -374,7 +527,7 @@ spi_dec_key_listener_new (CORBA_Object                            l,
   key_listener->listener.object = bonobo_object_dup_ref (l, ev);
   key_listener->listener.type = SPI_DEVICE_TYPE_KBD;
   key_listener->keys = ORBit_copy_value (keys, TC_Accessibility_KeySet);
-  key_listener->mask = mask;
+  key_listener->mask = spi_dec_translate_mask (mask);
   key_listener->listener.typeseq = ORBit_copy_value (typeseq, TC_Accessibility_EventTypeSeq);
   if (mode)
     key_listener->mode = ORBit_copy_value (mode, TC_Accessibility_EventListenerMode);
@@ -511,7 +664,7 @@ _deregister_keygrab (SpiDEController      *controller,
     }
   else
     {
-      g_warning ("De-registering non-existant grab");
+      DBG (1, g_warning ("De-registering non-existant grab"));
     }
 }
 
@@ -599,7 +752,7 @@ spi_controller_register_device_listener (SpiDEController      *controller,
       controller->mouse_listeners = g_list_prepend (controller->mouse_listeners, listener);
       break;
   default:
-      fprintf (stderr, "WARNING: listener registration for unknown device type.\n");
+      DBG (1, g_warning ("listener registration for unknown device type.\n"));
       break;
   }
   return FALSE; 
@@ -650,15 +803,12 @@ spi_controller_notify_mouselisteners (SpiDEController                 *controlle
       DEControllerListener *listener = l2->data;           
       Accessibility_DeviceEventListener ls = listener->object;
 
-      fprintf (stderr, "notifying mouse listener\n");
       CORBA_exception_init (ev);
       is_consumed = Accessibility_DeviceEventListener_notifyEvent (ls, event, ev);
-      fprintf (stderr, "%sconsumed\n", is_consumed ? "" : "not ");
-
       if (BONOBO_EX (ev))
         {
           is_consumed = FALSE;
-         fprintf (stderr, "error notifying listener, removing it\n");
+         DBG (2, g_warning ("error notifying listener, removing it\n"));
          spi_deregister_controller_device_listener (controller, listener,
                                                     ev);
           CORBA_exception_free (ev);
@@ -692,7 +842,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;
@@ -717,6 +866,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;
 
@@ -737,20 +887,29 @@ spi_device_event_controller_forward_mouse_event (SpiDEController *controller,
   mouse_e.hw_code   = button;
   mouse_e.modifiers = (CORBA_unsigned_short) xbutton_event->state;
   mouse_e.timestamp = (CORBA_unsigned_long) xbutton_event->time;
-  mouse_e.event_string = CORBA_string_dup ("");
+  mouse_e.event_string = "";
   mouse_e.is_text   = CORBA_FALSE;
-  is_consumed = spi_controller_notify_mouselisteners (controller, &mouse_e, &ev);
-  CORBA_free (mouse_e.event_string);
-
-  e.type = CORBA_string_dup (event_name);
-  e.source = BONOBO_OBJREF (controller->registry->desktop);
-  e.detail1 = last_mouse_pos->x;
-  e.detail2 = last_mouse_pos->y;
-  CORBA_exception_init (&ev);
-  
-  Accessibility_Registry_notifyEvent (BONOBO_OBJREF (controller->registry),
-                                     &e,
-                                     &ev);
+  if ((mouse_button_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);
+      e.type = CORBA_string_dup (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);
+      
+      Accessibility_Registry_notifyEvent (BONOBO_OBJREF (controller->registry),
+                                         &e,
+                                         &ev);
+    }
 
   xkb_mod_unlatch_occurred = (xevent->type == ButtonPress ||
                              xevent->type == ButtonRelease);
@@ -759,10 +918,7 @@ spi_device_event_controller_forward_mouse_event (SpiDEController *controller,
    *   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; 
-    }
+    spi_dec_set_unlatch_pending (controller, mouse_mask_state);
   
   XAllowEvents (spi_get_display (),
                (is_consumed) ? SyncPointer : ReplayPointer,
@@ -792,17 +948,31 @@ 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;
+#ifdef SPI_XKB_DEBUG
              fprintf (stderr, "relatching %x\n",
                       priv->pending_xkb_mod_relatch_mask);
+#endif
              /* temporarily turn off the latch bell, if it's on */
              XkbGetControls (display,
                              XkbAccessXFeedbackMask,
@@ -816,6 +986,7 @@ global_filter_fn (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
                              &= ~(XkbAX_StickyKeysFBMask);
                XkbChangeControls (display, priv->xkb_desc, &changes);
              }
+             /* TODO: account for lock as well as latch */
              XkbLatchModifiers (display,
                                 XkbUseCoreKbd,
                                 priv->pending_xkb_mod_relatch_mask,
@@ -827,13 +998,20 @@ global_filter_fn (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
                priv->xkb_desc->ctrls->ax_options = feedback_mask;
                XkbChangeControls (display, priv->xkb_desc, &changes);
              }
+#ifdef SPI_XKB_DEBUG
              fprintf (stderr, "relatched %x\n",
                       priv->pending_xkb_mod_relatch_mask);
+#endif
              priv->pending_xkb_mod_relatch_mask = 0;
            }
+         else
+           {
+             priv->xkb_latch_mask = xkb_snev->latched_mods;
+           }
        }
         else
-               fprintf (stderr, "XKB event %d\n", xkb_ev->xkb_type);
+              DBG (2, g_warning ("XKB event %d\n", xkb_ev->xkb_type));
+      XSynchronize (spi_get_display (), FALSE);
     }
   
   return GDK_FILTER_CONTINUE;
@@ -859,8 +1037,8 @@ spi_controller_register_with_devices (SpiDEController *controller)
 {
   DEControllerPrivateData *priv = (DEControllerPrivateData *) 
          g_object_get_qdata (G_OBJECT (controller), spi_dec_private_quark);     
-
-  priv->xkb_desc = XkbGetMap (spi_get_display (), 0, XkbUseCoreKbd);
+  /* FIXME: should check for extension first! */
+  XTestGrabControl (spi_get_display (), True);
 
   /* calls to device-specific implementations and routines go here */
   /* register with: keyboard hardware code handler */
@@ -872,10 +1050,14 @@ spi_controller_register_with_devices (SpiDEController *controller)
                                      &priv->xkb_base_error_code, NULL, NULL);
   if (priv->have_xkb)
     {
+      priv->xkb_desc = XkbGetMap (spi_get_display (), 0, XkbUseCoreKbd);
       XkbSelectEvents (spi_get_display (),
                       XkbUseCoreKbd,
                       XkbStateNotifyMask, XkbStateNotifyMask);     
+      _numlock_physical_mask = XkbKeysymToModifiers (spi_get_display (), 
+                                                    XK_Num_Lock);
     }  
+
   gdk_window_add_filter (NULL, global_filter_fn, controller);
 
   gdk_window_set_events (gdk_get_default_root_window (),
@@ -972,7 +1154,7 @@ spi_key_event_matches_listener (const Accessibility_DeviceEvent *key_event,
                                DEControllerKeyListener         *listener,
                                CORBA_boolean                    is_system_global)
 {
-  if ((key_event->modifiers == (CORBA_unsigned_short) (listener->mask & 0xFFFF)) &&
+  if ((key_event->modifiers == (CORBA_unsigned_short) (listener->mask & 0xFF)) &&
        spi_key_set_contains_key (listener->keys, key_event) &&
        spi_eventtype_seq_contains_event (listener->listener.typeseq, key_event) && 
       (is_system_global == listener->mode->global))
@@ -1073,18 +1255,19 @@ spi_keystroke_from_x_key_event (XKeyEvent *x_key_event)
   Accessibility_DeviceEvent key_event;
   KeySym keysym;
   const int cbuf_bytes = 20;
-  char cbuf [cbuf_bytes];
-  
-  keysym = XLookupKeysym (x_key_event, 0);
+  char cbuf [cbuf_bytes+1];
+  int nbytes;
+
+  nbytes = XLookupString (x_key_event, cbuf, cbuf_bytes, &keysym, NULL);  
   key_event.id = (CORBA_long)(keysym);
   key_event.hw_code = (CORBA_short) x_key_event->keycode;
   if (((XEvent *) x_key_event)->type == KeyPress)
     {
-      key_event.type = Accessibility_KEY_PRESSED;
+      key_event.type = Accessibility_KEY_PRESSED_EVENT;
     }
   else
     {
-      key_event.type = Accessibility_KEY_RELEASED;
+      key_event.type = Accessibility_KEY_RELEASED_EVENT;
     } 
   key_event.modifiers = (CORBA_unsigned_short)(x_key_event->state);
   key_event.is_text = CORBA_FALSE;
@@ -1094,9 +1277,6 @@ spi_keystroke_from_x_key_event (XKeyEvent *x_key_event)
         key_event.event_string = CORBA_string_dup ("space");
         break;
       case XK_Tab:
-#ifdef SPI_KEYEVENT_DEBUG
-       fprintf(stderr, "Tab\n");
-#endif
         key_event.event_string = CORBA_string_dup ("Tab");
        break;
       case XK_BackSpace:
@@ -1169,12 +1349,16 @@ spi_keystroke_from_x_key_event (XKeyEvent *x_key_event)
         key_event.event_string = CORBA_string_dup ("Right");
        break;
       default:
-        if (XLookupString (x_key_event, cbuf, cbuf_bytes, &keysym, NULL) > 0)
+        if (nbytes > 0)
           {
+           gunichar c;
+           cbuf[nbytes] = '\0'; /* OK since length is cbuf_bytes+1 */
             key_event.event_string = CORBA_string_dup (cbuf);
-           if (isgraph (keysym))
+           c = g_utf8_get_char_validated (cbuf, nbytes);
+           if ((c > 0) && g_unichar_isprint (c))
              {
-               key_event.is_text = CORBA_TRUE; /* FIXME: incorrect for some composed chars? */
+               key_event.is_text = CORBA_TRUE; 
+               /* incorrect for some composed chars? */
              }
           }
         else
@@ -1186,10 +1370,13 @@ spi_keystroke_from_x_key_event (XKeyEvent *x_key_event)
   key_event.timestamp = (CORBA_unsigned_long) x_key_event->time;
 #ifdef SPI_KEYEVENT_DEBUG
   fprintf (stderr,
-     "Key %lu pressed (%c), modifiers %d\n",
-     (unsigned long) keysym,
-     keysym ? (int) keysym : '*',
-     (int) x_key_event->state);
+          "Key %lu pressed (%c), modifiers %d; string=%s [%x] %s\n",
+          (unsigned long) keysym,
+          keysym ? (int) keysym : '*',
+          (int) x_key_event->state,
+          key_event.event_string,
+          key_event.event_string[0],
+          (key_event.is_text == CORBA_TRUE) ? "(text)" : "(not text)");
 #endif
 #ifdef SPI_DEBUG
   fprintf (stderr, "%s%c",
@@ -1227,7 +1414,6 @@ spi_controller_update_key_grabs (SpiDEController           *controller,
       next = l->next;
 
       re_issue_grab = recv &&
-/*           (recv->type == Accessibility_KEY_RELEASED) && - (?) */
              (recv->modifiers & grab_mask->mod_mask) &&
              (grab_mask->key_val == keycode_for_keysym (recv->id));
 
@@ -1412,8 +1598,13 @@ spi_deregister_controller_device_listener (SpiDEController            *controlle
                                           DEControllerListener *listener,
                                           CORBA_Environment          *ev)
 {
+  RemoveListenerClosure  ctx;
+
+  ctx.ev = ev;
+  ctx.listener = listener;
+
   spi_re_entrant_list_foreach (&controller->mouse_listeners,
-                              remove_listener_cb, listener);
+                              remove_listener_cb, &ctx);
 }
 
 static void
@@ -1582,7 +1773,6 @@ dec_synth_keycode_press (SpiDEController *controller,
 #endif
                }
        }
-       fprintf (stderr, "press %d\n", (int) keycode);
         XTestFakeKeyEvent (spi_get_display (), keycode, True, time);
        priv->last_press_keycode = keycode;
        XSync (spi_get_display (), False);
@@ -1626,7 +1816,6 @@ dec_synth_keycode_release (SpiDEController *controller,
 #endif
                }
        }
-       fprintf (stderr, "release %d\n", (int) keycode);
         XTestFakeKeyEvent (spi_get_display (), keycode, False, time);
        priv->last_release_keycode = keycode;
        XSync (spi_get_display (), False);
@@ -1648,9 +1837,7 @@ impl_generate_keyboard_event (PortableServer_Servant           servant,
   SpiDEController *controller =
        SPI_DEVICE_EVENT_CONTROLLER (bonobo_object (servant));
   long key_synth_code;
-  unsigned int slow_keys_delay;
-  unsigned int press_time;
-  unsigned int release_time;
+  KeySym keysym;
 
 #ifdef SPI_DEBUG
        fprintf (stderr, "synthesizing keystroke %ld, type %d\n",
@@ -1665,7 +1852,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:
@@ -1690,7 +1878,17 @@ impl_generate_keyboard_event (PortableServer_Servant           servant,
     }
   if (gdk_error_trap_pop ())
     {
-      g_warning ("Error emitting keystroke");
+      DBG (-1, g_warning ("Error emitting keystroke"));
+    }
+  if (synth_type == Accessibility_KEY_SYM) {
+    keysym = keycode;
+  }
+  else {
+    keysym = XkbKeycodeToKeysym (spi_get_display (), keycode, 0, 0);
+  }
+  if (XkbKeysymToModifiers (spi_get_display (), keysym) == 0) 
+    {
+      spi_dec_clear_unlatch_pending (controller);
     }
 }
 
@@ -1702,14 +1900,13 @@ impl_generate_mouse_event (PortableServer_Servant servant,
                           const CORBA_char      *eventName,
                           CORBA_Environment     *ev)
 {
-  int button;
+  int button = 0;
   gboolean error = FALSE;
   Display *display = spi_get_display ();
 #ifdef SPI_DEBUG
   fprintf (stderr, "generating mouse %s event at %ld, %ld\n",
           eventName, (long int) x, (long int) y);
 #endif
-  g_message ("mouse event synthesis\n");
   switch (eventName[0])
     {
       case 'b':
@@ -1725,6 +1922,12 @@ impl_generate_mouse_event (PortableServer_Servant servant,
          case '3':
                  button = 3;
                  break;
+         case '4':
+                 button = 4;
+                 break;
+         case '5':
+                 button = 5;
+                 break;
          default:
                  error = TRUE;
          }
@@ -1817,20 +2020,12 @@ spi_device_event_controller_init (SpiDEController *device_event_controller)
   device_event_controller->mouse_listeners = NULL;
   device_event_controller->keygrabs_list   = NULL;
 
-  /*
-   * TODO: fixme, this module makes the foolish assumptions that
-   * registryd uses the same display as the apps, and that the
-   * DISPLAY environment variable is set.
-   */
-  gdk_init (NULL, NULL);
-  
   private = g_new0 (DEControllerPrivateData, 1);
   gettimeofday (&private->last_press_time, NULL);
   gettimeofday (&private->last_release_time, NULL);
   g_object_set_qdata (G_OBJECT (device_event_controller),
                      spi_dec_private_quark,
                      private);
-  
   spi_controller_register_with_devices (device_event_controller);
 }
 
@@ -1869,7 +2064,6 @@ spi_device_event_controller_new (SpiRegistry *registry)
 {
   SpiDEController *retval = g_object_new (
     SPI_DEVICE_EVENT_CONTROLLER_TYPE, NULL);
-  DEControllerPrivateData *private;
   
   retval->registry = SPI_REGISTRY (bonobo_object_ref (
          BONOBO_OBJECT (registry)));
@@ -1882,4 +2076,4 @@ spi_device_event_controller_new (SpiRegistry *registry)
 BONOBO_TYPE_FUNC_FULL (SpiDEController,
                       Accessibility_DeviceEventController,
                       PARENT_TYPE,
-                      spi_device_event_controller);
+                      spi_device_event_controller)