diff options
author | Eitan Isaacson <eitan@monotonous.org> | 2011-04-26 15:28:40 -0700 |
---|---|---|
committer | Eitan Isaacson <eitan@monotonous.org> | 2011-05-02 10:18:49 -0700 |
commit | f72b4505ac6a46e03b106ea0b294e4fb2b67f535 (patch) | |
tree | 834bc5a3534b08112867f6ec50b03b47b139cc0b /libcaribou/xadapter.vala | |
parent | b319f06ecb7ba4d9bdb4e998cfdacf639edaa1aa (diff) | |
download | caribou-f72b4505ac6a46e03b106ea0b294e4fb2b67f535.tar.gz |
libcaribou: Ported XKB helper to Vala.
Diffstat (limited to 'libcaribou/xadapter.vala')
-rw-r--r-- | libcaribou/xadapter.vala | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/libcaribou/xadapter.vala b/libcaribou/xadapter.vala new file mode 100644 index 0000000..d240f08 --- /dev/null +++ b/libcaribou/xadapter.vala @@ -0,0 +1,213 @@ +using Xkl; +using Gdk; +using Xkb; +using XTest; +using X; + +namespace Caribou { + public class XAdapter : Object { + + /* Signals */ + public signal void modifiers_changed (uint modifiers); + public signal void group_changed (uint gid, string group, string variant); + + /* Private properties */ + private static XAdapter instance; + X.Display xdisplay; + Xkb.Desc xkbdesc; + Xkl.Engine xkl_engine; + uint reserved_keysym; + uchar reserved_keycode; + uchar modifiers; + uchar group; + + construct { + Xkb.State state; + + this.xdisplay = new X.Display (); + this.xkbdesc = Xkb.get_keyboard (this.xdisplay, + Xkb.GBN_AllComponentsMask, + Xkb.UseCoreKbd); + this.xkl_engine = Xkl.Engine.get_instance (this.xdisplay); + + Xkb.get_state (this.xdisplay, Xkb.UseCoreKbd, out state); + + this.group = state.group; + this.modifiers = state.mods; + + this.reserved_keycode = 0; + + Xkb.select_events ( + this.xdisplay, Xkb.UseCoreKbd, + Xkb.StateNotifyMask | Xkb.AccessXNotifyMask, + Xkb.StateNotifyMask | Xkb.AccessXNotifyMask); + + ((Gdk.Window) null).add_filter (x_event_filter); // Did I blow your mind? + } + + ~XAdapter () { + Xkb.free_keyboard(this.xkbdesc, Xkb.GBN_AllComponentsMask, true); + } + + public static XAdapter get_default() { + if (instance == null) + instance = new XAdapter (); + return instance; + } + + private Gdk.FilterReturn x_event_filter (Gdk.XEvent xevent, Gdk.Event event) { + void* pointer = &xevent; + Xkb.Event *xev = (Xkb.Event *) pointer; + + if (xev.any.xkb_type == Xkb.StateNotify) { + Xkb.StateNotifyEvent *sevent = &xev.state; + if ((sevent.changed & Xkb.GroupStateMask) != 0) { + Xkl.ConfigRec config_rec = new Xkl.ConfigRec (); + config_rec.get_from_server (this.xkl_engine); + group_changed (sevent.group, config_rec.layouts[sevent.group], + config_rec.variants[sevent.group]); + this.group = (uchar) sevent.group; + } else if ((sevent.changed & Xkb.ModifierStateMask) != 0) { + this.modifiers = (uchar) sevent.mods; + } + } + + return Gdk.FilterReturn.CONTINUE; + } + + private uchar get_reserved_keycode () { + uchar i; + unowned Xkb.Desc xkbdesc = this.xkbdesc; + + for (i = xkbdesc.max_key_code; i >= xkbdesc.min_key_code; --i) { + if (xkbdesc.map.key_sym_map[i].kt_index[0] == Xkb.OneLevelIndex) { + if (X.keycode_to_keysym (this.xdisplay, i, 0) != 0) { + Gdk.error_trap_push (); + this.xdisplay.grab_key (i, 0, + Gdk.x11_get_default_root_xwindow (), true, + X.GrabMode.Sync, X.GrabMode.Sync); + this.xdisplay.flush (); + this.xdisplay.ungrab_key ( + i, 0, Gdk.x11_get_default_root_xwindow ()); + if (Gdk.error_trap_pop () == 0) + return i; + } + } + } + + return (uchar) this.xdisplay.keysym_to_keycode (0x0023); // XK_numbersign + } + + private void replace_keycode (uint keysym) { + if (this.reserved_keycode == 0) { + this.reserved_keycode = get_reserved_keycode (); + this.reserved_keysym = X.keycode_to_keysym (this.xdisplay, + this.reserved_keycode, 0); + } + + this.xdisplay.flush (); + uint offset = this.xkbdesc.map.key_sym_map[this.reserved_keycode].offset; + + this.xkbdesc.map.syms[offset] = keysym; + + Xkb.set_map (this.xdisplay, Xkb.AllMapComponentsMask, this.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. + **/ + + this.xdisplay.flush (); + + if (keysym != this.reserved_keysym) + GLib.Timeout.add (500, reset_reserved); + } + + private bool reset_reserved () { + replace_keycode (this.reserved_keysym); + return false; + } + + private uchar keycode_for_keyval (uint keyval, out uint modmask) { + Gdk.Keymap kmap= Gdk.Keymap.get_default (); + Gdk.KeymapKey[] kmk; + uchar keycode = 0; + + if (kmap.get_entries_for_keyval (keyval, out kmk)) { + Gdk.KeymapKey best_match = kmk[0]; + foreach (KeymapKey km in kmk) + if (km.group == this.group) + best_match = km; + + keycode = (uchar) best_match.keycode; + modmask = (best_match.level == 1) ? Gdk.ModifierType.SHIFT_MASK : 0; + } else { + replace_keycode (keyval); + keycode = this.reserved_keycode; + } + + return keycode; + } + + public void keyval_press (uint keyval) { + uint mask; + uchar keycode = keycode_for_keyval (keyval, out mask); + + if (mask != 0) + mod_latch (mask); + + XTest.fake_key_event (this.xdisplay, keycode, true, X.CURRENT_TIME); + this.xdisplay.flush (); + } + + public void keyval_release (uint keyval) { + uchar keycode = keycode_for_keyval (keyval, null); + + XTest.fake_key_event (this.xdisplay, keycode, false, X.CURRENT_TIME); + this.xdisplay.flush (); + } + + public void mod_latch (uint mask) { + Xkb.latch_modifiers (this.xdisplay, Xkb.UseCoreKbd, mask, mask); + this.xdisplay.flush (); + } + + public void mod_unlatch (uint mask) { + Xkb.latch_modifiers (this.xdisplay, Xkb.UseCoreKbd, mask, 0); + this.xdisplay.flush (); + } + + public uint get_current_group (out string group_name, + out string variant_name) { + Xkl.ConfigRec config_rec = new Xkl.ConfigRec (); + config_rec.get_from_server (this.xkl_engine); + group_name = config_rec.layouts[this.group]; + variant_name = config_rec.variants[this.group]; + + return this.group; + } + + public void get_groups (out string[] group_names, + out string[] variant_names) { + int i; + Xkl.ConfigRec config_rec = new Xkl.ConfigRec (); + config_rec.get_from_server (this.xkl_engine); + + for (i=0; i<4; i++) + if (config_rec.layouts[i] == null) + break; + + group_names = new string[i]; + variant_names = new string[i]; + + for (; i>=0; i--) { + group_names[i] = config_rec.layouts[i]; + variant_names[i] = config_rec.variants[i]; + } + } + + public void me () { + stdout.printf("%p\n", this.xkl_engine); + } + } +} |