[Window] Add more hardcoded settings for testing
[tilda-gobject.git] / tomboykeybinder.c
1 #include <string.h>
2
3 #include <gdk/gdk.h>
4 #include <gdk/gdkwindow.h>
5 #include <gdk/gdkx.h>
6 #include <X11/Xlib.h>
7
8 #include "eggaccelerators.h"
9 #include "tomboykeybinder.h"
10
11 /* Uncomment the next line to print a debug trace. */
12 /* #define DEBUG */
13
14 #ifdef DEBUG
15 #  define TRACE(x) x
16 #else
17 #  define TRACE(x) do {} while (FALSE);
18 #endif
19
20 typedef struct _Binding {
21         TomboyBindkeyHandler  handler;
22         gpointer              user_data;
23         char                 *keystring;
24         uint                  keycode;
25         uint                  modifiers;
26 } Binding;
27
28 static GSList *bindings = NULL;
29 static guint32 last_event_time = 0;
30 static gboolean processing_event = FALSE;
31
32 static guint num_lock_mask, caps_lock_mask, scroll_lock_mask;
33
34 static void
35 lookup_ignorable_modifiers (GdkKeymap *keymap)
36 {
37         egg_keymap_resolve_virtual_modifiers (keymap, 
38                                               EGG_VIRTUAL_LOCK_MASK,
39                                               &caps_lock_mask);
40
41         egg_keymap_resolve_virtual_modifiers (keymap, 
42                                               EGG_VIRTUAL_NUM_LOCK_MASK,
43                                               &num_lock_mask);
44
45         egg_keymap_resolve_virtual_modifiers (keymap, 
46                                               EGG_VIRTUAL_SCROLL_LOCK_MASK,
47                                               &scroll_lock_mask);
48 }
49
50 static void
51 grab_ungrab_with_ignorable_modifiers (GdkWindow *rootwin, 
52                                       Binding   *binding,
53                                       gboolean   grab)
54 {
55         guint mod_masks [] = {
56                 0, /* modifier only */
57                 num_lock_mask,
58                 caps_lock_mask,
59                 scroll_lock_mask,
60                 num_lock_mask  | caps_lock_mask,
61                 num_lock_mask  | scroll_lock_mask,
62                 caps_lock_mask | scroll_lock_mask,
63                 num_lock_mask  | caps_lock_mask | scroll_lock_mask,
64         };
65         int i;
66
67         for (i = 0; i < G_N_ELEMENTS (mod_masks); i++) {
68                 if (grab) {
69                         XGrabKey (GDK_WINDOW_XDISPLAY (rootwin), 
70                                   binding->keycode, 
71                                   binding->modifiers | mod_masks [i], 
72                                   GDK_WINDOW_XWINDOW (rootwin), 
73                                   False, 
74                                   GrabModeAsync,
75                                   GrabModeAsync);
76                 } else {
77                         XUngrabKey (GDK_WINDOW_XDISPLAY (rootwin),
78                                     binding->keycode,
79                                     binding->modifiers | mod_masks [i], 
80                                     GDK_WINDOW_XWINDOW (rootwin));
81                 }
82         }
83 }
84
85 static gboolean 
86 do_grab_key (Binding *binding)
87 {
88         GdkKeymap *keymap = gdk_keymap_get_default ();
89         GdkWindow *rootwin = gdk_get_default_root_window ();
90
91         EggVirtualModifierType virtual_mods = 0;
92         guint keysym = 0;
93
94         if (keymap == NULL || rootwin == NULL)
95                 return FALSE;
96
97         if (!egg_accelerator_parse_virtual (binding->keystring, 
98                                             &keysym, 
99                                             &virtual_mods))
100                 return FALSE;
101
102         TRACE (g_print ("Got accel %d, %d\n", keysym, virtual_mods));
103
104         binding->keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (rootwin), 
105                                              keysym);
106         if (binding->keycode == 0)
107                 return FALSE;
108
109         TRACE (g_print ("Got keycode %d\n", binding->keycode));
110
111         egg_keymap_resolve_virtual_modifiers (keymap,
112                                               virtual_mods,
113                                               &binding->modifiers);
114
115         TRACE (g_print ("Got modmask %d\n", binding->modifiers));
116
117         gdk_error_trap_push ();
118
119         grab_ungrab_with_ignorable_modifiers (rootwin, 
120                                               binding, 
121                                               TRUE /* grab */);
122
123         gdk_flush ();
124
125         if (gdk_error_trap_pop ()) {
126            g_warning ("Binding '%s' failed!\n", binding->keystring);
127            return FALSE;
128         }
129
130         return TRUE;
131 }
132
133 static gboolean 
134 do_ungrab_key (Binding *binding)
135 {
136         GdkWindow *rootwin = gdk_get_default_root_window ();
137
138         TRACE (g_print ("Removing grab for '%s'\n", binding->keystring));
139
140         grab_ungrab_with_ignorable_modifiers (rootwin, 
141                                               binding, 
142                                               FALSE /* ungrab */);
143
144         return TRUE;
145 }
146
147 static GdkFilterReturn
148 filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
149 {
150         GdkFilterReturn return_val = GDK_FILTER_CONTINUE;
151         XEvent *xevent = (XEvent *) gdk_xevent;
152         guint event_mods;
153         GSList *iter;
154
155         TRACE (g_print ("Got Event! %d, %d\n", xevent->type, event->type));
156
157         switch (xevent->type) {
158         case KeyPress:
159                 TRACE (g_print ("Got KeyPress! keycode: %d, modifiers: %d\n", 
160                                 xevent->xkey.keycode, 
161                                 xevent->xkey.state));
162
163                 /* 
164                  * Set the last event time for use when showing
165                  * windows to avoid anti-focus-stealing code.
166                  */
167                 processing_event = TRUE;
168                 last_event_time = xevent->xkey.time;
169
170                 event_mods = xevent->xkey.state & ~(num_lock_mask  | 
171                                                     caps_lock_mask | 
172                                                     scroll_lock_mask);
173
174                 for (iter = bindings; iter != NULL; iter = iter->next) {
175                         Binding *binding = (Binding *) iter->data;
176                                                        
177                         if (binding->keycode == xevent->xkey.keycode &&
178                             binding->modifiers == event_mods) {
179
180                                 TRACE (g_print ("Calling handler for '%s'...\n", 
181                                                 binding->keystring));
182
183                                 (binding->handler) (binding->keystring, 
184                                                     binding->user_data);
185                         }
186                 }
187
188                 processing_event = FALSE;
189                 break;
190         case KeyRelease:
191                 TRACE (g_print ("Got KeyRelease! \n"));
192                 break;
193         }
194
195         return return_val;
196 }
197
198 static void 
199 keymap_changed (GdkKeymap *map)
200 {
201         GdkKeymap *keymap = gdk_keymap_get_default ();
202         GSList *iter;
203
204         TRACE (g_print ("Keymap changed! Regrabbing keys..."));
205
206         for (iter = bindings; iter != NULL; iter = iter->next) {
207                 Binding *binding = (Binding *) iter->data;
208                 do_ungrab_key (binding);
209         }
210
211         lookup_ignorable_modifiers (keymap);
212
213         for (iter = bindings; iter != NULL; iter = iter->next) {
214                 Binding *binding = (Binding *) iter->data;
215                 do_grab_key (binding);
216         }
217 }
218
219 void 
220 tomboy_keybinder_init (void)
221 {
222         GdkKeymap *keymap = gdk_keymap_get_default ();
223         GdkWindow *rootwin = gdk_get_default_root_window ();
224
225         lookup_ignorable_modifiers (keymap);
226
227         gdk_window_add_filter (rootwin, 
228                                filter_func, 
229                                NULL);
230
231         g_signal_connect (keymap, 
232                           "keys_changed",
233                           G_CALLBACK (keymap_changed),
234                           NULL);
235 }
236
237 gboolean
238 tomboy_keybinder_bind (const char           *keystring,
239                        TomboyBindkeyHandler  handler,
240                        gpointer              user_data)
241 {
242         Binding *binding;
243         gboolean success;
244
245         binding = g_new0 (Binding, 1);
246         binding->keystring = g_strdup (keystring);
247         binding->handler = handler;
248         binding->user_data = user_data;
249
250         /* Sets the binding's keycode and modifiers */
251         success = do_grab_key (binding);
252
253         if (success) {
254                 bindings = g_slist_prepend (bindings, binding);
255         } else {
256                 g_free (binding->keystring);
257                 g_free (binding);
258         }
259
260         return success;
261 }
262
263 void
264 tomboy_keybinder_unbind (const char           *keystring, 
265                          TomboyBindkeyHandler  handler)
266 {
267         GSList *iter;
268
269         for (iter = bindings; iter != NULL; iter = iter->next) {
270                 Binding *binding = (Binding *) iter->data;
271
272                 if (strcmp (keystring, binding->keystring) != 0 ||
273                     handler != binding->handler) 
274                         continue;
275
276                 do_ungrab_key (binding);
277
278                 bindings = g_slist_remove (bindings, binding);
279
280                 g_free (binding->keystring);
281                 g_free (binding);
282                 break;
283         }
284 }
285
286 /* 
287  * From eggcellrenderkeys.c.
288  */
289 gboolean
290 tomboy_keybinder_is_modifier (guint keycode)
291 {
292         gint i;
293         gint map_size;
294         XModifierKeymap *mod_keymap;
295         gboolean retval = FALSE;
296
297         mod_keymap = XGetModifierMapping (gdk_display);
298
299         map_size = 8 * mod_keymap->max_keypermod;
300
301         i = 0;
302         while (i < map_size) {
303                 if (keycode == mod_keymap->modifiermap[i]) {
304                         retval = TRUE;
305                         break;
306                 }
307                 ++i;
308         }
309
310         XFreeModifiermap (mod_keymap);
311
312         return retval;
313 }
314
315 guint32
316 tomboy_keybinder_get_current_event_time (void)
317 {
318         if (processing_event) 
319                 return last_event_time;
320         else
321                 return GDK_CURRENT_TIME;
322 }