Hack Japan [yen bar] & [backslash underbar] keys.
authorHuang Peng <shawn.p.huang@gmail.com>
Sat, 6 Sep 2008 02:04:23 +0000 (10:04 +0800)
committerHuang Peng <shawn.p.huang@gmail.com>
Sat, 6 Sep 2008 02:04:23 +0000 (10:04 +0800)
client/x11/gdk-private.c
configure.ac
lib/gtk2/Makefile.am
lib/gtk2/ibusimclient.c

index e3d5522aad4ed151fe3f3b9ecd10b6ed4fee05ac..410870817a3ffb4dc77b777cc23fef3b06715908 100644 (file)
 #include <gdk/gdkx.h>
 #include <gdk/gdkkeysyms.h>
 
+#ifdef HAVE_X11_XKBLIB_H
+#  define HAVE_XKB
+#  include <X11/XKBlib.h>
+#endif
+
 void
 translate_key_event (GdkDisplay *display,
                     GdkEvent   *event,
@@ -35,8 +40,11 @@ translate_key_event (GdkDisplay *display,
   event->key.time = xevent->xkey.time;
 
   event->key.state = (GdkModifierType) xevent->xkey.state;
-  //event->key.group = _gdk_x11_get_group_for_state (display, xevent->xkey.state);
+#ifdef HAVE_XKB
+  event->key.group = XkbGroupForCoreState (xevent->xkey.state);
+#else
   event->key.group = 0;
+#endif
   event->key.hardware_keycode = xevent->xkey.keycode;
 
   event->key.keyval = GDK_VoidSymbol;
@@ -49,7 +57,7 @@ translate_key_event (GdkDisplay *display,
                                       NULL, NULL, NULL);
 
   //_gdk_keymap_add_virtual_modifiers (keymap, &event->key.state);
-  event->key.is_modifier = 
+  event->key.is_modifier =
        (event->key.state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) != 0;
 //  event->key.is_modifier = _gdk_keymap_key_is_modifier (keymap, event->key.hardware_keycode);
 
@@ -57,7 +65,7 @@ translate_key_event (GdkDisplay *display,
    * depend on it.
    */
   event->key.string = NULL;
-  
+
   if (event->key.keyval != GDK_VoidSymbol)
     c = gdk_keyval_to_unicode (event->key.keyval);
 
@@ -82,10 +90,10 @@ translate_key_event (GdkDisplay *display,
          else if (c == '8') c = '\177';
          else if (c == '/') c = '_' & 0x1F;
        }
-      
+
       len = g_unichar_to_utf8 (c, buf);
       buf[len] = '\0';
-      
+
       event->key.string = g_locale_from_utf8 (buf, len,
                                              NULL, &bytes_written,
                                              NULL);
@@ -109,7 +117,7 @@ translate_key_event (GdkDisplay *display,
       event->key.length = 0;
       event->key.string = g_strdup ("");
     }
-  
+
  out:
   return;
 }
index 4ac10a7526505429d729fa46b0310d61e1e09a9b..d064f352dad189a6fec6ce9fe98301aaf495bf2f 100644 (file)
@@ -107,6 +107,8 @@ PKG_CHECK_MODULES(X11, [
     x11
 ])
 
+AC_CHECK_HEADERS([X11/XKBlib.h])
+
 # check env
 AC_PATH_PROG(ENV, env)
 AC_SUBST(ENV)
index bd255c9e28943a1010398cf50735d58c5632c981..dc46812bf98d733e60db4fdcb8cc5689ebe190ab 100644 (file)
@@ -36,11 +36,13 @@ libibus_gtk_la_SOURCES = \
        ibusimclient.c \
        $(NULL)
 libibus_gtk_la_CFLAGS = \
+       @X11_CFLAGS@ \
        @GDK2_CFLAGS@ \
        @DBUS_CFLAGS@ \
        -DG_LOG_DOMAIN=\"IBUS\" \
        $(NULL)
 libibus_gtk_la_LDFLAGS = \
+       @X11_LIBS@ \
        @GDK2_LIBS@ \
        @DBUS_LIBS@ \
        $(NULL)
index 553c2363f24e8b8b6f62dc0bf12565fa1cff244e..7d871fb43e95ee88aa8b3850aa717d5825197d5c 100644 (file)
 #include <dbus/dbus-glib.h>
 #include <dbus/dbus-glib-lowlevel.h>
 
+#ifdef HAVE_X11_XKBLIB_H
+#  define HAVE_XKB
+#  include <X11/XKBlib.h>
+#endif
+
 #ifdef HAVE_SYS_INOTIFY_H
 #define HAVE_INOTIFY
 #  include <sys/inotify.h>
@@ -71,6 +76,13 @@ struct _IBusIMClientPrivate {
     guint           inotify_source;
 #endif
 
+    GdkKeymap      *keymap;
+    gulong          keymap_handler_id;
+    /* hack japanese [yen bar] & [backslash underbar] key */
+
+    gulong          japan_groups;
+    GArray         *japan_yen_bar_keys;
+
     DBusConnection *ibus;
 
 };
@@ -86,6 +98,11 @@ static void     ibus_im_client_class_init   (IBusIMClientClass  *klass);
 static void     ibus_im_client_init         (IBusIMClient       *client);
 static void     ibus_im_client_finalize     (GObject            *obj);
 
+static void     _keymap_find_japan_group    (IBusIMClient       *client);
+static void     _keymap_find_yen_bar_keys   (IBusIMClient       *client);
+static void     _keymap_keys_changed_cb     (GdkKeymap          *keymap,
+                                             IBusIMClient       *client);
+
 static void     _ibus_im_client_ibus_open   (IBusIMClient       *client);
 static void     _ibus_im_client_ibus_close  (IBusIMClient       *client);
 
@@ -531,6 +548,21 @@ ibus_im_client_init (IBusIMClient *obj)
         return;
     }
 #endif
+
+    if ((priv->keymap = gdk_keymap_get_default ()) != NULL) {
+        g_object_ref (priv->keymap);
+        _keymap_find_japan_group (client);
+        _keymap_find_yen_bar_keys (client);
+
+        int i;
+        for (i = 0; i < client->priv->japan_yen_bar_keys->len; i++) {
+            GdkKeymapKey *key = &g_array_index (client->priv->japan_yen_bar_keys, GdkKeymapKey, i);
+        }
+
+        priv->keymap_handler_id =
+            g_signal_connect (priv->keymap, "keys-changed",
+                G_CALLBACK (_keymap_keys_changed_cb), client);
+    }
 #if 0
     /* get dbus proxy */
     priv->dbus = dbus_g_proxy_new_for_name (priv->ibus,
@@ -572,6 +604,11 @@ ibus_im_client_finalize (GObject *obj)
     IBusIMClient *client = IBUS_IM_CLIENT (obj);
     IBusIMClientPrivate *priv = client->priv;
 
+    if (priv->keymap) {
+        g_signal_handler_disconnect (priv->keymap, priv->keymap_handler_id);
+        g_object_unref (priv->keymap);
+    }
+
 #ifdef HAVE_INOTIFY
     g_source_remove (priv->inotify_source);
     g_io_channel_unref (priv->inotify_channel);
@@ -646,6 +683,22 @@ ibus_im_client_filter_keypress (IBusIMClient *client, const gchar *ic, GdkEventK
         return FALSE;
     }
 
+    /* hack for [yen bar] and [backslash underscore] keys in Japan keyboard */
+    guint keyval = event->keyval;
+    if (event->keyval == GDK_backslash &&
+        priv->japan_yen_bar_keys != NULL &&
+        (1L << event->group) & priv->japan_groups)
+    {
+        int i;
+        for (i = 0; i < priv->japan_yen_bar_keys->len; i++) {
+            GdkKeymapKey *key = &g_array_index (priv->japan_yen_bar_keys, GdkKeymapKey, i);
+            if (event->hardware_keycode == key->keycode && event->group == key->group) {
+                keyval = GDK_yen;
+                break;
+            }
+        }
+    }
+
     /* Call IBus ProcessKeyEvent method */
     if (!block) {
         if (!_ibus_call_with_reply (priv->ibus,
@@ -654,7 +707,7 @@ ibus_im_client_filter_keypress (IBusIMClient *client, const gchar *ic, GdkEventK
                 _key_press_call_data_new (client, ic, (GdkEvent *)event),
                 (DBusFreeFunction)_key_press_call_data_free,
                 DBUS_TYPE_STRING, &ic,
-                DBUS_TYPE_UINT32, &event->keyval,
+                DBUS_TYPE_UINT32, &keyval,
                 DBUS_TYPE_BOOLEAN, &is_press,
                 DBUS_TYPE_UINT32, &state,
                 DBUS_TYPE_INVALID))
@@ -667,7 +720,7 @@ ibus_im_client_filter_keypress (IBusIMClient *client, const gchar *ic, GdkEventK
         if (!_ibus_call_with_reply_and_block (priv->ibus,
                 "ProcessKeyEvent",
                 DBUS_TYPE_STRING, &ic,
-                DBUS_TYPE_UINT32, &event->keyval,
+                DBUS_TYPE_UINT32, &keyval,
                 DBUS_TYPE_BOOLEAN, &is_press,
                 DBUS_TYPE_UINT32, &state,
                 DBUS_TYPE_INVALID,
@@ -785,7 +838,144 @@ ibus_im_client_get_connected (IBusIMClient *client)
     return dbus_connection_get_is_connected (priv->ibus);
 }
 
+static void
+_keymap_find_japan_group (IBusIMClient *client)
+{
+
+#ifdef HAVE_XKB
+    /* If have XKB, we will find Japan groups by groups' names */
+    gboolean retval = FALSE;
+    Status status;
+    XkbDescPtr desc;
+
+    IBusIMClientPrivate *priv = client->priv;
+
+    priv->japan_groups = 0L;
+
+    desc = XkbGetMap (GDK_DISPLAY (), 0, XkbUseCoreKbd);
+    if (desc == NULL) {
+        g_warning ("Can not allocate XkbDescRec!");
+        return;
+    }
+
+    retval =
+        Success == (status = XkbGetControls (GDK_DISPLAY (),
+                                XkbSlowKeysMask,
+                                desc)) &&
+        Success == (status = XkbGetNames (GDK_DISPLAY (),
+                                XkbGroupNamesMask | XkbIndicatorNamesMask,
+                                desc)) &&
+        Success == (status = XkbGetIndicatorMap (GDK_DISPLAY (),
+                                XkbAllIndicatorsMask,
+                                desc));
+    if (retval) {
+        Atom *pa = desc->names->groups;
+        int i;
+        for (i = 0; i < desc->ctrls->num_groups; pa++, i++) {
+            gchar *group_name = NULL;
+            if (*pa == None)
+                continue;
+            group_name = XGetAtomName (GDK_DISPLAY (), *pa);
+            if (g_strcmp0(group_name, "Japan") == 0) {
+                priv->japan_groups |= (1L << i);
+            }
+        }
+    }
+    else {
+        g_warning ("Can not get groups' names from Xkb");
+    }
+
+    XkbFreeKeyboard(desc, XkbAllComponentsMask, True);
+#else
+    /* if not have XKB, we assume only Japan group has key [backslash, underbar] */
+    priv->japan_groups = 0L;
+    gint backslash_num, underbar_num;
+    GdkKeymapKey *backslash_keys, *underbar_keys;
+    gboolean retval;
+
+    retval = gdk_keymap_get_entries_for_keyval (priv->keymap, GDK_backslash, &backslash_keys, &backslash_num);
+    if (!retval) {
+        g_warning ("Can not get keycode for backslash key!");
+        return;
+    }
+
+    retval = gdk_keymap_get_entries_for_keyval (priv->keymap, GDK_underbar, &underbar_keys, &underbar_num);
+    if (!retval) {
+        g_warning ("Can not get keycode for underbar key!");
+        g_free (backslash_keys);
+        return;
+    }
+
+
+    int i, j;
+    for (i = 0; i < backslash_num; i++) {
+        for (j = 0; j < underbar_num; j++) {
+            if (backslash_keys[i].keycode != underbar_keys[j].keycode ||
+                backslash_keys[i].group != underbar_keys[j].group)
+                continue;
+            priv->japan_groups |= (1L << backslash_keys[i].group);
+        }
+    }
+    g_free (backslash_keys);
+    g_free (bar_keys);
+#endif
+}
+
+
+static void
+_keymap_find_yen_bar_keys (IBusIMClient *client)
+{
+    IBusIMClientPrivate *priv = client->priv;
+    gint backslash_num, bar_num;
+    GdkKeymapKey *backslash_keys, *bar_keys;
+    gboolean retval;
+
+    if (priv->japan_yen_bar_keys == NULL) {
+        priv->japan_yen_bar_keys = g_array_new (TRUE, TRUE, sizeof(GdkKeymapKey));
+    }
+    else if (priv->japan_yen_bar_keys->len > 0) {
+        g_array_remove_range (priv->japan_yen_bar_keys, 0, priv->japan_yen_bar_keys->len);
+    }
+
+    if (priv->japan_groups == 0)
+        return;
+
+    retval = gdk_keymap_get_entries_for_keyval (priv->keymap, GDK_backslash, &backslash_keys, &backslash_num);
+    if (!retval) {
+        g_warning ("Can not get keycode for backslash key!");
+        return;
+    }
+
+    retval = gdk_keymap_get_entries_for_keyval (priv->keymap, GDK_bar, &bar_keys, &bar_num);
+    if (!retval) {
+        g_warning ("Can not get keycode for bar key!");
+        g_free (backslash_keys);
+        return;
+    }
+
+    int i, j;
+    for (i = 0; i < backslash_num; i++) {
+        for (j = 0; j < bar_num; j++) {
+            if (backslash_keys[i].keycode != bar_keys[j].keycode ||
+                backslash_keys[i].group != bar_keys[j].group)
+                continue;
+
+            if (0 == (priv->japan_groups & (1L << backslash_keys[i].group)))
+                continue;
+            g_array_append_val (priv->japan_yen_bar_keys, backslash_keys[i]);
+        }
+    }
 
+    g_free (backslash_keys);
+    g_free (bar_keys);
+}
+
+static void
+_keymap_keys_changed_cb (GdkKeymap *keymap, IBusIMClient *client)
+{
+    _keymap_find_japan_group (client);
+    _keymap_find_yen_bar_keys (client);
+}
 
 static void
 _ibus_signal_commit_string_handler (DBusConnection *connection, DBusMessage *message, IBusIMClient *client)