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