Fix for 128824; improved international key synthesis.
[platform/core/uifw/at-spi2-atk.git] / registryd / deviceeventcontroller.c
index 6edf938..15100ce 100644 (file)
 #include <X11/XKBlib.h>
 #define XK_MISCELLANY
 #include <X11/keysymdef.h>
+
+#ifdef HAVE_XEVIE
+#include <X11/Xproto.h>
+#include <X11/X.h>
+#include <X11/extensions/Xevie.h>
+#endif /* HAVE_XEVIE */
+
 #include <gdk/gdk.h>
 #include <gdk/gdkx.h> /* TODO: hide dependency (wrap in single porting file) */
 #include <gdk/gdkkeysyms.h>
 #include "../libspi/spi-private.h"
 #include "deviceeventcontroller.h"
 
+KeySym ucs2keysym (long ucs);
+
+#define CHECK_RELEASE_DELAY 20
+#define BIT(c, x)       (c[x/8]&(1<<(x%8)))
+static guint check_release_handler = 0;
+static Accessibility_DeviceEvent pressed_event;
+static SpiDEController *saved_controller; 
+static void wait_for_release_event (GdkEvent *event, SpiDEController *controller);
+
 /* Our parent Gtk object type */
 #define PARENT_TYPE BONOBO_TYPE_OBJECT
 
@@ -116,7 +132,7 @@ static gboolean spi_controller_update_key_grabs               (SpiDEController
 static gboolean spi_controller_register_device_listener       (SpiDEController           *controller,
                                                               DEControllerListener      *l,
                                                               CORBA_Environment         *ev);
-static void     spi_device_event_controller_forward_key_event (SpiDEController           *controller,
+static gboolean spi_device_event_controller_forward_key_event (SpiDEController           *controller,
                                                               const XEvent              *event);
 static void     spi_deregister_controller_device_listener (SpiDEController            *controller,
                                                           DEControllerListener *listener,
@@ -339,7 +355,7 @@ spi_dec_button_update_and_emit (SpiDEController *controller,
 #endif
        snprintf (event_name, 22, "mouse:button:%d%c", button_number,
                  (is_down) ? 'p' : 'r');
-       /* TODO: distinguish between physical and 
+       /* TODO: FIXME distinguish between physical and 
         * logical buttons 
         */
        mouse_e.type      = (is_down) ? 
@@ -536,10 +552,14 @@ spi_dec_init_mouse_listener (SpiRegistry *registry)
   if (display)
     {
       if (XGrabButton (display, AnyButton, AnyModifier,
-                  gdk_x11_get_default_root_xwindow (),
-                  True, ButtonPressMask | ButtonReleaseMask,
-                  GrabModeSync, GrabModeAsync, None, None) != Success)
+                      gdk_x11_get_default_root_xwindow (),
+                      True, ButtonPressMask | ButtonReleaseMask,
+                      GrabModeSync, GrabModeAsync, None, None) != Success) {
+#ifdef SPI_DEBUG
        fprintf (stderr, "WARNING: could not grab mouse buttons!\n");
+#endif
+       ;
+      }
       XSync (display, False);
 #ifdef SPI_DEBUG
       fprintf (stderr, "mouse buttons grabbed\n");
@@ -557,8 +577,6 @@ spi_dec_init_mouse_listener (SpiRegistry *registry)
 static Accessibility_ControllerEventMask
 spi_dec_translate_mask (Accessibility_ControllerEventMask mask)
 {
-  DEControllerPrivateData *priv;
-
   if (mask == SPI_KEYMASK_NUMLOCK) {
     mask = _numlock_physical_mask;
   }
@@ -767,7 +785,10 @@ spi_controller_register_global_keygrabs (SpiDEController         *controller,
                                         DEControllerKeyListener *key_listener)
 {
   handle_keygrab (controller, key_listener, _register_keygrab);
-  return spi_controller_update_key_grabs (controller, NULL);
+  if (controller->xevie_display == NULL)
+    return spi_controller_update_key_grabs (controller, NULL);
+  else
+    return TRUE;
 }
 
 static void
@@ -775,7 +796,8 @@ spi_controller_deregister_global_keygrabs (SpiDEController         *controller,
                                           DEControllerKeyListener *key_listener)
 {
   handle_keygrab (controller, key_listener, _deregister_keygrab);
-  spi_controller_update_key_grabs (controller, NULL);
+  if (controller->xevie_display == NULL)
+    spi_controller_update_key_grabs (controller, NULL);
 }
 
 static gboolean
@@ -817,6 +839,7 @@ spi_controller_notify_mouselisteners (SpiDEController                 *controlle
   GSList  *notify = NULL, *l2;
   GList  **listeners = &controller->mouse_listeners;
   gboolean is_consumed;
+  gboolean found = FALSE;
 
   if (!listeners)
     {
@@ -836,12 +859,13 @@ spi_controller_notify_mouselisteners (SpiDEController                 *controlle
               /* we clone (don't dup) the listener, to avoid refcount inc. */
               notify = g_slist_prepend (notify,
                                         spi_listener_clone (listener, ev));
+               found = TRUE;
             }
          }
     }
 
 #ifdef SPI_KEYEVENT_DEBUG
-  if (!notify)
+  if (!found)
     {
       g_print ("no match for event\n");
     }
@@ -929,7 +953,7 @@ spi_device_event_controller_forward_mouse_event (SpiDEController *controller,
   snprintf (event_name, 22, "mouse:button:%d%c", button,
            (xevent->type == ButtonPress) ? 'p' : 'r');
 
-  /* TODO: distinguish between physical and logical buttons */
+  /* TODO: FIXME distinguish between physical and logical buttons */
   mouse_e.type      = (xevent->type == ButtonPress) ? 
                       Accessibility_BUTTON_PRESSED_EVENT :
                       Accessibility_BUTTON_RELEASED_EVENT;
@@ -988,7 +1012,20 @@ global_filter_fn (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
 
   if (xevent->type == KeyPress || xevent->type == KeyRelease)
     {
-      spi_device_event_controller_forward_key_event (controller, xevent);
+      if (controller->xevie_display == NULL)
+        {
+          gboolean is_consumed =
+            spi_device_event_controller_forward_key_event (controller, xevent);
+
+          if (is_consumed)
+            XAllowEvents (spi_get_display (), AsyncKeyboard, CurrentTime);
+          else
+            {
+              wait_for_release_event (event, controller);
+              XAllowEvents (spi_get_display (), ReplayKeyboard, CurrentTime);
+            }
+        }
+
       return GDK_FILTER_CONTINUE;
     }
   if (xevent->type == ButtonPress || xevent->type == ButtonRelease)
@@ -1140,10 +1177,13 @@ spi_key_set_contains_key (Accessibility_KeySet            *key_set,
   for (i = 0; i < len; ++i)
     {
 #ifdef SPI_KEYEVENT_DEBUG          
-      g_print ("key_set[%d] = %d; key_event %d, code %d, string %s\n",
-               i, (int) key_set->_buffer[i].keycode,
-              (int) key_event->id, (int) key_event->hw_code,
-              key_event->event_string); 
+      g_print ("key_set[%d] event = %d, code = %d; key_event %d, code %d, string %s\n",
+                i,
+                (int)key_set->_buffer[i].keysym,
+                (int) key_set->_buffer[i].keycode,
+                (int) key_event->id,
+                (int) key_event->hw_code,
+                key_event->event_string); 
 #endif
       if (key_set->_buffer[i].keysym == (CORBA_long) key_event->id)
         {
@@ -1159,7 +1199,7 @@ spi_key_set_contains_key (Accessibility_KeySet            *key_set,
           return TRUE;
        }
     }
-  
+
   return FALSE;
 }
 
@@ -1419,17 +1459,29 @@ 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; 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)");
+  {
+    char *pressed_str  = "pressed";
+    char *released_str = "released";
+    char *state_ptr;
+
+    if (key_event.type == Accessibility_KEY_PRESSED_EVENT)
+      state_ptr = pressed_str;
+    else
+      state_ptr = released_str;
+    fprintf (stderr,
+            "Key %lu %s (%c), modifiers %d; string=%s [%x] %s\n",
+            (unsigned long) keysym,
+            state_ptr,
+            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",
+  fprintf (stderr, "%s%c\n",
      (x_key_event->state & Mod1Mask)?"Alt-":"",
      ((x_key_event->state & ShiftMask)^(x_key_event->state & LockMask))?
      g_ascii_toupper (keysym) : g_ascii_tolower (keysym));
@@ -1549,6 +1601,16 @@ spi_device_event_controller_object_finalize (GObject *object)
   /* disconnect any special listeners, get rid of outstanding keygrabs */
   XUngrabKey (spi_get_display (), AnyKey, AnyModifier, DefaultRootWindow (spi_get_display ()));
 
+#ifdef HAVE_XEVIE
+  if (controller->xevie_display != NULL)
+    {
+      XevieEnd(controller->xevie_display);
+#ifdef SPI_KEYEVENT_DEBUG
+      printf("XevieEnd(dpy) finished \n");
+#endif
+    }
+#endif
+
   private = g_object_get_data (G_OBJECT (controller), "spi-dec-private");
   if (private->xkb_desc)
          XkbFreeKeyboard (private->xkb_desc, 0, True);
@@ -1893,47 +1955,10 @@ dec_unlock_modifiers (SpiDEController *controller, unsigned modifiers)
                          modifiers, 0);
 }
 
+static KeySym
 dec_keysym_for_unichar (SpiDEController *controller, gunichar unichar)
 {
-       /* TODO: table lookups within a range, for various code pages! */
-       KeySym keysym = NoSymbol;
-
-       if (unichar >= 0x20 && unichar < 0x7f) { /* Basic Latin/ASCII */
-               keysym = (KeySym) unichar;
-       }
-       else if (unichar >= 0xa0 && unichar <= 0xff) { /* Latin 1 extensions */
-               keysym = (KeySym) unichar;
-       }
-       else if (unichar >= 0x100 && unichar <= 0x233) { /* unfortunately the mapping gets nasty for Latin-2 and 3... help! */
-               keysym = NoSymbol;
-       }
-       else if (unichar >= 0x7c1 && unichar <= 0x3a1) { /* let's try Greek anyway... */
-               keysym = (KeySym) (0x391 + (unichar - 0x7c1));
-       }
-       else if (unichar >= 0x3a3 && unichar <= 0x3a9) { /* let's try Greek anyway... */
-               keysym = (KeySym) (0x7d2 + (unichar - 0x3a3));
-       }
-       else if (unichar >= 0x3b1 && unichar <= 0x3c1) { /* let's try Greek anyway... */
-               keysym = (KeySym) (0x7e1 + (unichar - 0x3b1));
-       }
-       else if (unichar == 0x3c2) {
-               keysym = (KeySym) 0x7f3; /* XK_Greek_finalsmallsigma; */
-       }
-       else if (unichar >= 0x3c3 && unichar <= 0x3c9) { /* let's try Greek anyway... */
-               keysym = (KeySym) (0x7f2 + (unichar - 0x3c2));
-       }       
-       else if (unichar >= 0x5d0 && unichar <= 0x5ea) { /* Hebrew basics */
-               /* probably broken :-) */
-               keysym = (KeySym) (0xce0 + (unichar - 0x5d0));
-       }       
-       else if (unichar >= 0x30a1 && unichar <= 0x30ab) { /* partial katakana support */
-               /* TODO: complete! */
-               keysym = (KeySym) (0x4b1 + (unichar - 0x30a1)/2);
-       }
-       else if (unichar >= 0x20a0 && unichar <= 0x20ac) { /* currency */
-               keysym = (KeySym) unichar; /* how convenient ;-) */
-       }
-       return keysym;
+       return ucs2keysym ((long) unichar);
 }
 
 static gboolean
@@ -1950,6 +1975,7 @@ dec_synth_keysym (SpiDEController *controller, KeySym keysym)
        modifiers = dec_get_modifier_state (controller);
        /* side-effect; we may unset mousebutton modifiers here! */
 
+       lock_mods = 0;
        if (synth_mods != modifiers) {
                lock_mods = synth_mods & ~modifiers;
                dec_lock_modifiers (controller, lock_mods);
@@ -2051,7 +2077,6 @@ impl_generate_keyboard_event (PortableServer_Servant           servant,
    * and fall back to XSendEvent() if XTest is not available.
    */
   
-  /* TODO: implement keystring mode also */
   gdk_error_trap_push ();
   key_synth_code = keycode;
 
@@ -2218,13 +2243,74 @@ spi_device_event_controller_class_init (SpiDEControllerClass *klass)
          spi_dec_private_quark = g_quark_from_static_string ("spi-dec-private");
 }
 
+#ifdef HAVE_XEVIE
+Bool isEvent(dpy,event,arg)
+     Display *dpy;
+     XEvent *event;
+     char *arg;
+{
+   return TRUE;
+}
+
+gboolean
+handle_io (GIOChannel *source,
+           GIOCondition condition,
+           gpointer data) 
+{
+  SpiDEController *controller = (SpiDEController *) data;
+  gboolean is_consumed = FALSE;
+  XEvent ev;
+
+  while (XCheckIfEvent(controller->xevie_display, &ev, isEvent, NULL))
+    {
+      if (ev.type == KeyPress || ev.type == KeyRelease)
+        is_consumed = spi_device_event_controller_forward_key_event (controller, &ev);
+
+      if (! is_consumed)
+        XevieSendEvent(controller->xevie_display, &ev, XEVIE_UNMODIFIED);
+    }
+
+  return TRUE;
+}
+#endif /* HAVE_XEVIE */
+
 static void
 spi_device_event_controller_init (SpiDEController *device_event_controller)
 {
+#ifdef HAVE_XEVIE
+  GIOChannel *ioc;
+  int fd;
+#endif /* HAVE_XEVIE */
+
   DEControllerPrivateData *private;    
   device_event_controller->key_listeners   = NULL;
   device_event_controller->mouse_listeners = NULL;
   device_event_controller->keygrabs_list   = NULL;
+  device_event_controller->xevie_display   = NULL;
+
+#ifdef HAVE_XEVIE
+  device_event_controller->xevie_display = XOpenDisplay(NULL);
+
+  if (XevieStart(device_event_controller->xevie_display) == TRUE)
+    {
+#ifdef SPI_KEYEVENT_DEBUG
+      fprintf (stderr, "XevieStart() success \n");
+#endif
+      XevieSelectInput(device_event_controller->xevie_display, KeyPressMask | KeyReleaseMask);
+
+      fd = ConnectionNumber(device_event_controller->xevie_display);
+      ioc = g_io_channel_unix_new (fd);
+      g_io_add_watch (ioc, G_IO_IN | G_IO_HUP, handle_io, device_event_controller);
+      g_io_channel_unref (ioc);
+    }
+  else
+    {
+      device_event_controller->xevie_display = NULL;
+#ifdef SPI_KEYEVENT_DEBUG
+      fprintf (stderr, "XevieStart() failed, only one client is allowed to do event int exception\n");
+#endif
+    }
+#endif /* HAVE_XEVIE */
 
   private = g_new0 (DEControllerPrivateData, 1);
   gettimeofday (&private->last_press_time, NULL);
@@ -2235,11 +2321,10 @@ spi_device_event_controller_init (SpiDEController *device_event_controller)
   spi_controller_register_with_devices (device_event_controller);
 }
 
-static void
+static gboolean
 spi_device_event_controller_forward_key_event (SpiDEController *controller,
                                               const XEvent    *event)
 {
-  gboolean is_consumed = FALSE;
   CORBA_Environment ev;
   Accessibility_DeviceEvent key_event;
 
@@ -2249,20 +2334,11 @@ spi_device_event_controller_forward_key_event (SpiDEController *controller,
 
   key_event = spi_keystroke_from_x_key_event ((XKeyEvent *) event);
 
-  spi_controller_update_key_grabs (controller, &key_event);
+  if (controller->xevie_display == NULL)
+    spi_controller_update_key_grabs (controller, &key_event);
 
   /* relay to listeners, and decide whether to consume it or not */
-  is_consumed = spi_controller_notify_keylisteners (
-         controller, &key_event, CORBA_TRUE, &ev);
-
-  if (is_consumed)
-    {
-      XAllowEvents (spi_get_display (), AsyncKeyboard, CurrentTime);
-    }
-  else
-    {
-      XAllowEvents (spi_get_display (), ReplayKeyboard, CurrentTime);
-    }
+  return spi_controller_notify_keylisteners (controller, &key_event, CORBA_TRUE, &ev);
 }
 
 SpiDEController *
@@ -2279,6 +2355,46 @@ spi_device_event_controller_new (SpiRegistry *registry)
   return retval;
 }
 
+static gboolean
+is_key_released (KeyCode code)
+{
+  char keys[32];
+  int down;
+  int i;
+
+  XQueryKeymap (spi_get_display (), keys);
+  down = BIT (keys, code);
+  return (down == 0);
+}
+
+static gboolean
+check_release (gpointer data)
+{
+  gboolean released;
+  Accessibility_DeviceEvent *event = (Accessibility_DeviceEvent *)data;
+  KeyCode code = event->hw_code;
+  CORBA_Environment ev;
+
+  released = is_key_released (code);
+
+  if (released)
+    {
+      check_release_handler = 0;
+      event->type = Accessibility_KEY_RELEASED_EVENT;
+      ev._major = CORBA_NO_EXCEPTION;
+      spi_controller_notify_keylisteners (saved_controller, event, CORBA_TRUE, &ev);
+    }
+  return (released == 0);
+}
+
+static void wait_for_release_event (GdkEvent        *event,
+                                    SpiDEController *controller)
+{
+  pressed_event = spi_keystroke_from_x_key_event ((XKeyEvent *) event);
+  saved_controller = controller;
+  check_release_handler = g_timeout_add (CHECK_RELEASE_DELAY, check_release, &pressed_event);
+}
+
 BONOBO_TYPE_FUNC_FULL (SpiDEController,
                       Accessibility_DeviceEventController,
                       PARENT_TYPE,