summaryrefslogtreecommitdiff
path: root/libcaribou
diff options
context:
space:
mode:
authorEitan Isaacson <eitan@monotonous.org>2011-04-22 17:32:36 -0700
committerEitan Isaacson <eitan@monotonous.org>2011-05-02 10:18:49 -0700
commitfde58831fdef2a0d89a884822e0b065e54a62996 (patch)
treee294e9949a68ca05c36e2b0ebbefbd0e9fca740e /libcaribou
parentc4c811f2860faafe0b2aee6e9cc53dfcfb33bef2 (diff)
downloadcaribou-fde58831fdef2a0d89a884822e0b065e54a62996.tar.gz
libcaribou: Make any keysym work with weird keyboard remapping hack.
Diffstat (limited to 'libcaribou')
-rw-r--r--libcaribou/caribou-virtual-keyboard.c99
1 files changed, 93 insertions, 6 deletions
diff --git a/libcaribou/caribou-virtual-keyboard.c b/libcaribou/caribou-virtual-keyboard.c
index 0f32bf2..5b6d28c 100644
--- a/libcaribou/caribou-virtual-keyboard.c
+++ b/libcaribou/caribou-virtual-keyboard.c
@@ -16,6 +16,8 @@
struct _CaribouVirtualKeyboardPrivate {
XkbDescPtr xkbdesc;
XklEngine *xkl_engine;
+ KeyCode reserved_keycode;
+ KeySym reserved_keysym;
gchar modifiers;
gchar group;
};
@@ -40,6 +42,77 @@ dispose (GObject *object)
G_OBJECT_CLASS (caribou_virtual_keyboard_parent_class)->dispose (object);
}
+/* A hack stolen from AT-SPI registry */
+static guint64
+_get_reserved_keycode (CaribouVirtualKeyboard *self)
+{
+ guint64 i;
+ XkbDescPtr xkbdesc = self->priv->xkbdesc;
+
+ for (i = xkbdesc->max_key_code; i >= xkbdesc->min_key_code; --i)
+ {
+ if (xkbdesc->map->key_sym_map[i].kt_index[0] == XkbOneLevelIndex)
+ {
+ if (XKeycodeToKeysym (XDISPLAY, i, 0) != 0)
+ {
+ /* don't use this one if there's a grab client! */
+ gdk_error_trap_push ();
+ XGrabKey (XDISPLAY, i, 0,
+ gdk_x11_get_default_root_xwindow (),
+ TRUE,
+ GrabModeSync, GrabModeSync);
+ XSync (XDISPLAY, TRUE);
+ XUngrabKey (XDISPLAY, i, 0,
+ gdk_x11_get_default_root_xwindow ());
+ if (!gdk_error_trap_pop ())
+ return i;
+ }
+ }
+ }
+
+ return XKeysymToKeycode (XDISPLAY, XK_numbersign);
+}
+
+static void _replace_keycode (CaribouVirtualKeyboard *self, KeySym keysym);
+
+static gboolean
+_reset_reserved (gpointer user_data)
+{
+ CaribouVirtualKeyboard *self = CARIBOU_VIRTUAL_KEYBOARD (user_data);
+
+ _replace_keycode (self, self->priv->reserved_keysym);
+
+ return FALSE;
+}
+
+static void
+_replace_keycode (CaribouVirtualKeyboard *self, KeySym keysym)
+{
+ CaribouVirtualKeyboardPrivate *priv = self->priv;
+ gint offset;
+ g_return_if_fail (priv->xkbdesc != NULL && priv->xkbdesc->map != NULL);
+
+ XFlush (XDISPLAY);
+ XSync (XDISPLAY, False);
+
+ offset = priv->xkbdesc->map->key_sym_map[priv->reserved_keycode].offset;
+
+ priv->xkbdesc->map->syms[offset] = keysym;
+
+ XkbSetMap (XDISPLAY, XkbAllMapComponentsMask, priv->xkbdesc);
+ /**
+ * FIXME: the use of XkbChangeMap, and the reuse of the priv->xkb_desc structure,
+ * would be far preferable.
+ * HOWEVER it does not seem to work using XFree 4.3.
+ **/
+ /* XkbChangeMap (XDISPLAY, priv->xkb_desc, priv->changes); */
+ XFlush (XDISPLAY);
+ XSync (XDISPLAY, False);
+
+ if (keysym != priv->reserved_keysym)
+ g_timeout_add (500, _reset_reserved, self);
+}
+
static GdkFilterReturn
_filter_x_evt (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
{
@@ -91,6 +164,9 @@ caribou_virtual_keyboard_init (CaribouVirtualKeyboard *self)
gdk_window_add_filter (NULL, (GdkFilterFunc) _filter_x_evt, self);
+ self->priv->reserved_keycode = _get_reserved_keycode (self);
+ self->priv->reserved_keysym = XKeycodeToKeysym (XDISPLAY,
+ self->priv->reserved_keycode, 0);
}
static void
@@ -152,20 +228,31 @@ caribou_virtual_keyboard_new ()
}
static KeyCode
-keycode_for_keyval (guint keyval,
+keycode_for_keyval (CaribouVirtualKeyboard *self,
+ guint keyval,
guint *modmask)
{
GdkKeymap *km = gdk_keymap_get_default ();
GdkKeymapKey *kmk;
gint len;
- KeyCode keycode;
+ KeyCode keycode = 38; // Initializing to something legal
g_return_val_if_fail (modmask != NULL, 0);
if (gdk_keymap_get_entries_for_keyval (km, keyval, &kmk, &len)) {
- keycode = kmk[0].keycode;
- *modmask = (kmk[0].level == 1) ? GDK_SHIFT_MASK : 0;
+ gint i;
+ GdkKeymapKey best_match = kmk[0];
+
+ for (i=0; i<len; i++)
+ if (kmk[i].group == self->priv->group)
+ best_match = kmk[i];
+
+ keycode = best_match.keycode;
+ *modmask = (best_match.level == 1) ? GDK_SHIFT_MASK : 0;
g_free (kmk);
+ } else {
+ _replace_keycode (self, keyval);
+ keycode = self->priv->reserved_keycode;
}
return keycode;
@@ -211,7 +298,7 @@ caribou_virtual_keyboard_keyval_press (CaribouVirtualKeyboard *self,
guint keyval)
{
guint mask;
- KeyCode keycode = keycode_for_keyval (keyval, &mask);
+ KeyCode keycode = keycode_for_keyval (self, keyval, &mask);
if (mask != 0)
caribou_virtual_keyboard_mod_latch (self, mask);
@@ -231,7 +318,7 @@ caribou_virtual_keyboard_keyval_release (CaribouVirtualKeyboard *self,
guint keyval)
{
guint mask;
- KeyCode keycode = keycode_for_keyval (keyval, &mask);
+ KeyCode keycode = keycode_for_keyval (self, keyval, &mask);
XTestFakeKeyEvent(XDISPLAY, keycode, FALSE, CurrentTime);