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