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