1namespace Caribou { 2 /** 3 * Singleton object providing access to the X Window facility. 4 */ 5 public class XAdapter : DisplayAdapter { 6 /* Private properties */ 7 unowned X.Display xdisplay; 8 X.ID xid; 9 Xkb.Desc xkbdesc; 10 Xkl.Engine xkl_engine; 11 uint reserved_keysym; 12 uchar reserved_keycode; 13 uchar modifiers; 14 uchar group; 15 uint[] level_switch_modifiers; 16 17 private class KeyButtonHandler { 18 public unowned KeyButtonCallback cb { get; private set; } 19 public KeyButtonHandler (KeyButtonCallback cb) { 20 this.cb = cb; 21 } 22 } 23 24 Gee.HashMap<uint, KeyButtonHandler> button_funcs; 25 Gee.HashMap<uint, KeyButtonHandler> key_funcs; 26 27 construct { 28 Xkb.State xkb_state; 29 unowned Xkl.State xkl_state; 30 31 Gdk.Window rootwin = Gdk.get_default_root_window(); 32 this.xdisplay = Gdk.X11Display.get_xdisplay (rootwin.get_display ()); 33 xid = Gdk.X11Window.get_xid (rootwin); 34 this.xkbdesc = Xkb.get_keyboard (this.xdisplay, 35 Xkb.GBN_AllComponentsMask, 36 Xkb.UseCoreKbd); 37 this.xkl_engine = Xkl.Engine.get_instance (this.xdisplay); 38 xkl_engine.start_listen (Xkl.EngineListenModes.TRACK_KEYBOARD_STATE); 39 xkl_state = this.xkl_engine.get_current_state (); 40 this.group = (uchar) xkl_state.group; 41 xkl_engine.X_state_changed.connect_after (xkl_state_changed); 42 xkl_engine.X_config_changed.connect_after (xkl_config_changed); 43 44 Xkb.get_state (this.xdisplay, Xkb.UseCoreKbd, out xkb_state); 45 this.modifiers = xkb_state.mods; 46 47 this.reserved_keycode = 0; 48 49 this.level_switch_modifiers = { 50 0, 51 Gdk.ModifierType.SHIFT_MASK 52 }; 53 var lv3_mod = keysym_to_modifier (Gdk.Key.ISO_Level3_Shift); 54 if (lv3_mod != 0) { 55 level_switch_modifiers += lv3_mod; 56 level_switch_modifiers += Gdk.ModifierType.SHIFT_MASK | lv3_mod; 57 } 58 59 button_funcs = new Gee.HashMap<uint, KeyButtonHandler> (); 60 61 key_funcs = new Gee.HashMap<uint, KeyButtonHandler> (); 62 63 Xkb.select_events ( 64 this.xdisplay, Xkb.UseCoreKbd, 65 Xkb.StateNotifyMask | Xkb.AccessXNotifyMask, 66 Xkb.StateNotifyMask | Xkb.AccessXNotifyMask); 67 68 ((Gdk.Window) null).add_filter (x_event_filter); // Did I blow your mind? 69 } 70 71 ~XAdapter () { 72 Xkb.free_keyboard(this.xkbdesc, Xkb.GBN_AllComponentsMask, true); 73 } 74 75 private bool set_slowkeys_enabled (bool enable) { 76 Xkb.get_controls (this.xdisplay, Xkb.AllControlsMask, this.xkbdesc); 77 78 var previous = 79 (this.xkbdesc.ctrls.enabled_ctrls & Xkb.SlowKeysMask) != 0; 80 81 if (enable) 82 this.xkbdesc.ctrls.enabled_ctrls |= Xkb.SlowKeysMask; 83 else 84 this.xkbdesc.ctrls.enabled_ctrls &= ~Xkb.SlowKeysMask; 85 86 Xkb.set_controls (this.xdisplay, 87 Xkb.SlowKeysMask | Xkb.ControlsEnabledMask, 88 this.xkbdesc); 89 90 return previous; 91 } 92 93 private Gdk.FilterReturn x_event_filter (Gdk.XEvent xevent, Gdk.Event event) { 94 // After the following commit, Vala changed the definition 95 // of Gdk.XEvent from struct to class: 96 // http://git.gnome.org/browse/vala/commit/?id=9c52e7b7 97 // 98 // This affects the meaning of address-of (&) operator: 99 // '&xevent' now refers to the address of XEvent*, not 100 // XEvent. 101#if VALA_0_16 102 void* pointer = xevent; 103#else 104 void* pointer = &xevent; 105#endif 106 Xkb.Event* xkbev = (Xkb.Event *) pointer; 107 X.Event* xev = (X.Event *) pointer; 108 109 this.xkl_engine.filter_events(xev); 110 111 if (xev.type == X.EventType.ButtonPress || 112 xev.type == X.EventType.ButtonRelease) { 113 KeyButtonHandler handler = 114 (KeyButtonHandler) button_funcs.get (xev.xbutton.button); 115 if (handler != null) 116 handler.cb (xev.xbutton.button, 117 xev.type == X.EventType.ButtonPress); 118 } else if (xev.type == X.EventType.KeyPress || 119 xev.type == X.EventType.KeyRelease) { 120 121 KeyButtonHandler handler = 122 (KeyButtonHandler) key_funcs.get (xev.xkey.keycode); 123 if (handler != null) 124 handler.cb (xev.xkey.keycode, 125 xev.type == X.EventType.KeyPress); 126 } else if (xkbev.any.xkb_type == Xkb.StateNotify) { 127 Xkb.StateNotifyEvent *sevent = &xkbev.state; 128 if ((sevent.changed & Xkb.ModifierStateMask) != 0) { 129 this.modifiers = (uchar) sevent.mods; 130 } 131 } 132 133 return Gdk.FilterReturn.CONTINUE; 134 } 135 136 private void xkl_state_changed (int type, int group, bool restore) { 137 string group_name; 138 string variant_name; 139 140 this.group = (uchar) group; 141 get_current_group (out group_name, out variant_name); 142 group_changed (this.group, group_name, variant_name); 143 } 144 145 private void xkl_config_changed () { 146 config_changed (); 147 } 148 149 private uchar keysym_to_modifier (uint keyval) { 150 for (int i = xkbdesc.min_key_code; i <= xkbdesc.max_key_code; i++) { 151 unowned Xkb.SymMap symmap = xkbdesc.map.key_sym_map[i]; 152 for (int j = 0; 153 j < symmap.width * (symmap.group_info & 0x0f); 154 j++) 155 if (xkbdesc.map.syms[symmap.offset + j] == keyval) 156 return xkbdesc.map.modmap[i]; 157 } 158 return 0; 159 } 160 161 private uchar get_reserved_keycode () { 162 int i; 163 unowned Xkb.Desc xkbdesc = this.xkbdesc; 164 165 for (i = xkbdesc.max_key_code; i >= xkbdesc.min_key_code; --i) { 166 if (xkbdesc.map.key_sym_map[i].kt_index[0] == Xkb.OneLevelIndex) { 167 if (this.xdisplay.keycode_to_keysym ((uchar) i, 0) != 0) { 168 Gdk.error_trap_push (); 169 this.xdisplay.grab_key (i, 0, 170 Gdk.x11_get_default_root_xwindow (), true, 171 X.GrabMode.Sync, X.GrabMode.Sync); 172 this.xdisplay.flush (); 173 this.xdisplay.ungrab_key ( 174 i, 0, Gdk.x11_get_default_root_xwindow ()); 175 if (Gdk.error_trap_pop () == 0) 176 return (uchar) i; 177 } 178 } 179 } 180 181 return (uchar) this.xdisplay.keysym_to_keycode (0x0023); // XK_numbersign 182 } 183 184 private void replace_keycode (uint keysym) { 185 if (this.reserved_keycode == 0) { 186 this.reserved_keycode = get_reserved_keycode (); 187 this.reserved_keysym = (uint) this.xdisplay.keycode_to_keysym ( 188 this.reserved_keycode, 0); 189 } 190 191 this.xdisplay.flush (); 192 uint offset = this.xkbdesc.map.key_sym_map[this.reserved_keycode].offset; 193 this.xkbdesc.map.syms[offset] = keysym; 194 this.xkbdesc.device_spec = (ushort) Xkb.UseCoreKbd; 195 196 Xkb.MapChanges changes = Xkb.MapChanges (); 197 198 // We don't touch key types here but include the 199 // information in XkbSetMap request to the server, because 200 // some X servers need the information to check the sanity 201 // of the keysyms change. 202 changes.changed = (ushort) (Xkb.KeySymsMask | Xkb.KeyTypesMask); 203 changes.first_key_sym = (char) this.reserved_keycode; 204 changes.num_key_syms = this.xkbdesc.map.key_sym_map[this.reserved_keycode].width; 205 changes.first_type = 0; 206 changes.num_types = this.xkbdesc.map.num_types; 207 Xkb.change_map (this.xdisplay, this.xkbdesc, changes); 208 209 this.xdisplay.flush (); 210 211 if (keysym != this.reserved_keysym) 212 GLib.Timeout.add (500, reset_reserved); 213 } 214 215 private bool reset_reserved () { 216 replace_keycode (this.reserved_keysym); 217 return false; 218 } 219 220 private bool best_keycode_keyval_match (uint keyval, 221 out uchar keycode, 222 out uint modmask) { 223 Gdk.Keymap kmap= Gdk.Keymap.get_default (); 224 Gdk.KeymapKey[] kmk; 225 226 keycode = 0; 227 modmask = 0; 228 229 if (!kmap.get_entries_for_keyval (keyval, out kmk)) 230 return false; 231 232 Gdk.KeymapKey? best_match = null; 233 234 foreach (Gdk.KeymapKey km in kmk) 235 if (km.group == this.group && 236 km.level < this.level_switch_modifiers.length) 237 best_match = km; 238 239 if (best_match == null) 240 return false; 241 242 keycode = (uchar) best_match.keycode; 243 modmask = this.level_switch_modifiers[best_match.level]; 244 245 return true; 246 } 247 248 private uchar keycode_for_keyval (uint keyval, out uint modmask) { 249 uchar keycode = 0; 250 251 if (!best_keycode_keyval_match (keyval, out keycode, out modmask)) { 252 replace_keycode (keyval); 253 keycode = this.reserved_keycode; 254 modmask = 0; 255 } 256 257 return keycode; 258 } 259 260 public override void keyval_press (uint keyval) { 261 uint mask; 262 uchar keycode = keycode_for_keyval (keyval, out mask); 263 264 if (mask != 0) 265 mod_latch (mask); 266 267 var enabled = set_slowkeys_enabled (false); 268 XTest.fake_key_event (this.xdisplay, keycode, true, X.CURRENT_TIME); 269 this.xdisplay.flush (); 270 set_slowkeys_enabled (enabled); 271 } 272 273 public override void keyval_release (uint keyval) { 274 uchar keycode = keycode_for_keyval (keyval, null); 275 276 XTest.fake_key_event (this.xdisplay, keycode, false, X.CURRENT_TIME); 277 this.xdisplay.flush (); 278 } 279 280 public override void mod_lock (uint mask) { 281 Xkb.lock_modifiers (this.xdisplay, Xkb.UseCoreKbd, mask, mask); 282 this.xdisplay.flush (); 283 } 284 285 public override void mod_unlock (uint mask) { 286 Xkb.lock_modifiers (this.xdisplay, Xkb.UseCoreKbd, mask, 0); 287 this.xdisplay.flush(); 288 } 289 290 public override void mod_latch (uint mask) { 291 Xkb.latch_modifiers (this.xdisplay, Xkb.UseCoreKbd, mask, mask); 292 this.xdisplay.flush (); 293 } 294 295 public override void mod_unlatch (uint mask) { 296 Xkb.latch_modifiers (this.xdisplay, Xkb.UseCoreKbd, mask, 0); 297 this.xdisplay.flush (); 298 } 299 300 public override uint get_current_group (out string group_name, 301 out string variant_name) { 302 Xkl.ConfigRec config_rec = new Xkl.ConfigRec (); 303 config_rec.get_from_server (this.xkl_engine); 304 305 if (this.group < config_rec.layouts.length) 306 group_name = config_rec.layouts[this.group]; 307 else 308 group_name = ""; 309 310 if (this.group < config_rec.variants.length) 311 variant_name = config_rec.variants[this.group]; 312 else 313 variant_name = ""; 314 315 if (variant_name == null) 316 variant_name = ""; 317 318 return this.group; 319 } 320 321 public override void get_groups (out string[] group_names, 322 out string[] variant_names) { 323 int i; 324 Xkl.ConfigRec config_rec = new Xkl.ConfigRec (); 325 config_rec.get_from_server (this.xkl_engine); 326 327 for (i=0; i<4; i++) 328 if (config_rec.layouts[i] == null) { 329 i--; 330 break; 331 } 332 333 group_names = new string[i+1]; 334 variant_names = new string[i+1]; 335 336 for (; i>=0; i--) { 337 group_names[i] = config_rec.layouts[i]; 338 if (config_rec.variants[i] != null) 339 variant_names[i] = config_rec.variants[i]; 340 else 341 variant_names[i] = ""; 342 } 343 } 344 345 public override void register_key_func (uint keyval, 346 KeyButtonCallback? func) 347 { 348 uchar keycode; 349 uint modmask; 350 351 if (!best_keycode_keyval_match (keyval, out keycode, out modmask)) { 352 GLib.warning ("No good keycode for %d", (int) keyval); 353 return; 354 } 355 356 if (func != null) { 357 var handler = new KeyButtonHandler (func); 358 key_funcs.set (keycode, handler); 359 xdisplay.grab_key ((int)keycode, 0, xid, 360 true, X.GrabMode.Async, X.GrabMode.Async); 361 362 } else { 363 key_funcs.unset (keycode); 364 xdisplay.ungrab_key ((int)keycode, 0, xid); 365 } 366 } 367 368 public override void register_button_func (uint button, 369 KeyButtonCallback? func) 370 { 371 if (func != null) { 372 var handler = new KeyButtonHandler (func); 373 button_funcs.set (button, handler); 374 xdisplay.grab_button (button, 0, xid, true, 375 X.EventMask.ButtonPressMask | 376 X.EventMask.ButtonReleaseMask, 377 X.GrabMode.Async, X.GrabMode.Async, 0, 0); 378 379 } else { 380 button_funcs.unset (button); 381 xdisplay.ungrab_button (button, 0, xid); 382 } 383 } 384 } 385} 386