2003-07-01 Artur Flinta <aflinta@cvs.gnome.org>
[platform/core/uifw/at-spi2-atk.git] / registryd / deviceeventcontroller.c
index bf51240..e7f17c8 100644 (file)
@@ -27,7 +27,7 @@
 
 #undef  SPI_XKB_DEBUG
 #undef  SPI_DEBUG
-#define  SPI_KEYEVENT_DEBUG
+#undef  SPI_KEYEVENT_DEBUG
 
 #include <string.h>
 #include <ctype.h>
@@ -60,7 +60,8 @@ 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;
 
@@ -138,10 +139,60 @@ static gboolean spi_dec_poll_mouse_idle (gpointer data);
 
 /* Private methods */
 
+static unsigned int
+keysym_mod_mask (KeySym keysym, KeyCode keycode)
+{
+       /* we really should use XKB and look directly at the keymap */
+       /* this is very inelegant */
+       Display *display = spi_get_display ();
+       unsigned int mods_rtn = 0;
+       unsigned int retval = 0;
+       KeySym sym_rtn;
+
+       if (XkbLookupKeySym (display, keycode, 0, &mods_rtn, &sym_rtn) &&
+           (sym_rtn == keysym)) {
+               retval = 0;
+       }
+       else if (XkbLookupKeySym (display, keycode, ShiftMask, &mods_rtn, &sym_rtn) &&
+                (sym_rtn == keysym)) {
+               retval = ShiftMask;
+       }
+       else if (XkbLookupKeySym (display, keycode, Mod2Mask, &mods_rtn, &sym_rtn) &&
+                (sym_rtn == keysym)) {
+               retval = Mod2Mask;
+       }
+       else if (XkbLookupKeySym (display, keycode, Mod3Mask, &mods_rtn, &sym_rtn) &&
+                (sym_rtn == keysym)) {
+               retval = Mod3Mask;
+       }
+       else if (XkbLookupKeySym (display, keycode, 
+                                 ShiftMask | Mod2Mask, &mods_rtn, &sym_rtn) &&
+                (sym_rtn == keysym)) {
+               retval = (Mod2Mask | ShiftMask);
+       }
+       else if (XkbLookupKeySym (display, keycode, 
+                                 ShiftMask | Mod3Mask, &mods_rtn, &sym_rtn) &&
+                (sym_rtn == keysym)) {
+               retval = (Mod3Mask | ShiftMask);
+       }
+       else if (XkbLookupKeySym (display, keycode, 
+                                 ShiftMask | Mod4Mask, &mods_rtn, &sym_rtn) &&
+                (sym_rtn == keysym)) {
+               retval = (Mod4Mask | ShiftMask);
+       }
+       else
+               retval = 0xFFFF;
+       return retval;
+}
+
 static KeyCode
-keycode_for_keysym (long keysym)
+keycode_for_keysym (long keysym, unsigned int *modmask)
 {
-  return XKeysymToKeycode (spi_get_display (), (KeySym) keysym);
+       KeyCode keycode = 0;
+       keycode = XKeysymToKeycode (spi_get_display (), (KeySym) keysym);
+       if (modmask) 
+               *modmask = keysym_mod_mask (keysym, keycode);
+       return keycode;
 }
 
 static DEControllerGrabMask *
@@ -288,7 +339,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) ? 
@@ -399,6 +450,13 @@ spi_dec_emit_modifier_event (SpiDEController *controller, guint prev_mask,
   fprintf (stderr, "MODIFIER CHANGE EVENT! %x to %x\n", 
           prev_mask, current_mask);
 #endif
+
+  /* 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;
@@ -478,10 +536,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");
@@ -489,6 +551,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,
@@ -501,7 +581,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);
@@ -853,7 +933,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;
@@ -1028,6 +1108,8 @@ spi_controller_register_with_devices (SpiDEController *controller)
       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);
@@ -1126,7 +1208,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))
@@ -1235,11 +1317,11 @@ spi_keystroke_from_x_key_event (XKeyEvent *x_key_event)
   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;
@@ -1386,9 +1468,8 @@ 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));
+             (grab_mask->key_val == keycode_for_keysym (recv->id, NULL));
 
 #ifdef SPI_DEBUG
       fprintf (stderr, "mask=%lx %lx (%c%c) %s\n",
@@ -1796,6 +1877,157 @@ dec_synth_keycode_release (SpiDEController *controller,
        return TRUE;
 }
 
+static unsigned
+dec_get_modifier_state (SpiDEController *controller)
+{
+       return mouse_mask_state;
+}
+
+static gboolean
+dec_lock_modifiers (SpiDEController *controller, unsigned modifiers)
+{
+       return XkbLockModifiers (spi_get_display (), XkbUseCoreKbd, 
+                         modifiers, modifiers);
+}
+
+static gboolean
+dec_unlock_modifiers (SpiDEController *controller, unsigned modifiers)
+{
+       return XkbLockModifiers (spi_get_display (), XkbUseCoreKbd, 
+                         modifiers, 0);
+}
+
+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;
+}
+
+static gboolean
+dec_synth_keysym (SpiDEController *controller, KeySym keysym)
+{
+       KeyCode key_synth_code;
+       unsigned int modifiers, synth_mods, lock_mods;
+
+       key_synth_code = keycode_for_keysym (keysym, &synth_mods);
+
+       if ((key_synth_code == 0) || (synth_mods == 0xFF)) return FALSE;
+
+       /* TODO: set the modifiers accordingly! */
+       modifiers = dec_get_modifier_state (controller);
+       /* side-effect; we may unset mousebutton modifiers here! */
+
+       if (synth_mods != modifiers) {
+               lock_mods = synth_mods & ~modifiers;
+               dec_lock_modifiers (controller, lock_mods);
+       }
+       dec_synth_keycode_press (controller, key_synth_code);
+       dec_synth_keycode_release (controller, key_synth_code);
+       if (synth_mods != modifiers) 
+               dec_unlock_modifiers (controller, lock_mods);
+       return TRUE;
+}
+
+
+static gboolean
+dec_synth_keystring (SpiDEController *controller, const CORBA_char *keystring)
+{
+       /* probably we need to create and inject an XIM handler eventually. */
+       /* for now, try to match the string to existing 
+        * keycode+modifier states. 
+         */
+       KeySym *keysyms;
+       gint maxlen = 0;
+       gunichar unichar = 0;
+       gint i = 0;
+       gboolean retval = TRUE;
+       const gchar *c;
+
+       maxlen = strlen (keystring);
+       keysyms = g_new0 (KeySym, maxlen);
+       if (!(keystring && *keystring && g_utf8_validate (keystring, -1, &c))) { 
+               retval = FALSE;
+       } 
+       else {
+#ifdef SPI_DEBUG
+               fprintf (stderr, "[keystring synthesis attempted on %s]\n", keystring);
+#endif
+               while (keystring && (unichar = g_utf8_get_char (keystring))) {
+                       KeySym keysym;
+                       char bytes[6];
+                       gint mbytes;
+                       
+                       mbytes = g_unichar_to_utf8 (unichar, bytes);
+                       bytes[mbytes] = '\0';
+#ifdef SPI_DEBUG
+                       fprintf (stderr, "[unichar %s]", bytes);
+#endif
+                       keysym = dec_keysym_for_unichar (controller, unichar);
+                       if (keysym == NoSymbol) {
+#ifdef SPI_DEBUG
+                               fprintf (stderr, "no keysym for %s", bytes);
+#endif
+                               retval = FALSE;
+                               break;
+                       }
+                       keysyms[i++] = keysym;
+                       keystring = g_utf8_next_char (keystring); 
+               }
+               keysyms[i++] = 0;
+               for (i = 0; keysyms[i]; ++i) {
+                       if (!dec_synth_keysym (controller, keysyms[i])) {
+#ifdef SPI_DEBUG
+                               fprintf (stderr, "could not synthesize %c\n",
+                                        (int) keysyms[i]);
+#endif
+                               retval = FALSE;
+                               break;
+                       }
+               }
+       }
+       g_free (keysyms);
+
+       return retval;
+}
+
+
 /*
  * CORBA Accessibility::DEController::registerKeystrokeListener
  *     method implementation
@@ -1823,7 +2055,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;
 
@@ -1841,12 +2072,17 @@ impl_generate_keyboard_event (PortableServer_Servant           servant,
 #ifdef SPI_XKB_DEBUG         
              fprintf (stderr, "KeySym synthesis\n");
 #endif
-             key_synth_code = keycode_for_keysym (keycode);
-             dec_synth_keycode_press (controller, key_synth_code);
-             dec_synth_keycode_release (controller, key_synth_code);
+             /* 
+              * note: we are using long for 'keycode'
+              * in our arg list; it can contain either
+              * a keycode or a keysym.
+              */
+             dec_synth_keysym (controller, (KeySym) keycode);
              break;
       case Accessibility_KEY_STRING:
-             fprintf (stderr, "Not yet implemented\n");
+             if (!dec_synth_keystring (controller, keystring))
+                     fprintf (stderr, "Keystring synthesis failure, string=%s\n",
+                              keystring);
              break;
     }
   if (gdk_error_trap_pop ())
@@ -2049,4 +2285,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)