~do-core/do/trunk

209.2.1 by Mathieu Cadet
Added libdo
1
//
2
// This file comes from the Tomboy project.
3
// http://www.gnome.org/projects/tomboy/
4
//
5
/*
6
 * Copyright (C) 2004-2007  Alex Graveley
7
 *
8
 * This library is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Library General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2 of the License, or (at your option) any later version.
12
 *
13
 * This library is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Library General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Library General Public
19
 * License along with this library; if not, write to the
20
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21
 * Boston, MA 02111-1307, USA.
22
 */
23
24
#include <string.h>
25
26
#include <gdk/gdk.h>
27
#include <gdk/gdkwindow.h>
28
#include <gdk/gdkx.h>
29
#include <X11/Xlib.h>
30
31
#include "eggaccelerators.h"
32
#include "keybinder.h"
33
34
/* Uncomment the next line to print a debug trace. */
35
/* #define DEBUG */
36
37
#ifdef DEBUG
38
#  define TRACE(x) x
39
#else
40
#  define TRACE(x) do {} while (FALSE);
41
#endif
42
43
typedef struct _Binding {
44
	GnomeDoBindkeyHandler  handler;
45
	gpointer              user_data;
46
	char                 *keystring;
47
	uint                  keycode;
48
	uint                  modifiers;
49
} Binding;
50
51
static GSList *bindings = NULL;
52
static guint32 last_event_time = 0;
53
static gboolean processing_event = FALSE;
54
55
static guint num_lock_mask, caps_lock_mask, scroll_lock_mask;
56
57
static void
58
lookup_ignorable_modifiers (GdkKeymap *keymap)
59
{
60
	egg_keymap_resolve_virtual_modifiers (keymap, 
61
					      EGG_VIRTUAL_LOCK_MASK,
62
					      &caps_lock_mask);
63
64
	egg_keymap_resolve_virtual_modifiers (keymap, 
65
					      EGG_VIRTUAL_NUM_LOCK_MASK,
66
					      &num_lock_mask);
67
68
	egg_keymap_resolve_virtual_modifiers (keymap, 
69
					      EGG_VIRTUAL_SCROLL_LOCK_MASK,
70
					      &scroll_lock_mask);
71
}
72
73
static void
74
grab_ungrab_with_ignorable_modifiers (GdkWindow *rootwin, 
75
				      Binding   *binding,
76
				      gboolean   grab)
77
{
78
	guint mod_masks [] = {
79
		0, /* modifier only */
80
		num_lock_mask,
81
		caps_lock_mask,
82
		scroll_lock_mask,
83
		num_lock_mask  | caps_lock_mask,
84
		num_lock_mask  | scroll_lock_mask,
85
		caps_lock_mask | scroll_lock_mask,
86
		num_lock_mask  | caps_lock_mask | scroll_lock_mask,
87
	};
88
	int i;
89
90
	for (i = 0; i < G_N_ELEMENTS (mod_masks); i++) {
91
		if (grab) {
92
			XGrabKey (GDK_WINDOW_XDISPLAY (rootwin), 
93
				  binding->keycode, 
94
				  binding->modifiers | mod_masks [i], 
95
				  GDK_WINDOW_XWINDOW (rootwin), 
96
				  False, 
97
				  GrabModeAsync,
98
				  GrabModeAsync);
99
		} else {
100
			XUngrabKey (GDK_WINDOW_XDISPLAY (rootwin),
101
				    binding->keycode,
102
				    binding->modifiers | mod_masks [i], 
103
				    GDK_WINDOW_XWINDOW (rootwin));
104
		}
105
	}
106
}
107
108
static gboolean 
109
do_grab_key (Binding *binding)
110
{
111
	GdkKeymap *keymap = gdk_keymap_get_default ();
112
	GdkWindow *rootwin = gdk_get_default_root_window ();
113
114
	EggVirtualModifierType virtual_mods = 0;
115
	guint keysym = 0;
116
117
	if (keymap == NULL || rootwin == NULL)
118
		return FALSE;
119
120
	if (!egg_accelerator_parse_virtual (binding->keystring, 
121
					    &keysym, 
122
					    &virtual_mods))
123
		return FALSE;
124
125
	TRACE (g_print ("Got accel %d, %d\n", keysym, virtual_mods));
126
127
	binding->keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (rootwin), 
128
					     keysym);
129
	if (binding->keycode == 0)
130
		return FALSE;
131
132
	TRACE (g_print ("Got keycode %d\n", binding->keycode));
133
134
	egg_keymap_resolve_virtual_modifiers (keymap,
135
					      virtual_mods,
136
					      &binding->modifiers);
137
138
	TRACE (g_print ("Got modmask %d\n", binding->modifiers));
139
140
	gdk_error_trap_push ();
141
142
	grab_ungrab_with_ignorable_modifiers (rootwin, 
143
					      binding, 
144
					      TRUE /* grab */);
145
146
	gdk_flush ();
147
148
	if (gdk_error_trap_pop ()) {
149
	   g_warning ("Binding '%s' failed!\n", binding->keystring);
150
	   return FALSE;
151
	}
152
153
	return TRUE;
154
}
155
156
static gboolean 
157
do_ungrab_key (Binding *binding)
158
{
159
	GdkWindow *rootwin = gdk_get_default_root_window ();
160
161
	TRACE (g_print ("Removing grab for '%s'\n", binding->keystring));
162
163
	grab_ungrab_with_ignorable_modifiers (rootwin, 
164
					      binding, 
165
					      FALSE /* ungrab */);
166
167
	return TRUE;
168
}
169
170
static GdkFilterReturn
171
filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
172
{
173
	GdkFilterReturn return_val = GDK_FILTER_CONTINUE;
174
	XEvent *xevent = (XEvent *) gdk_xevent;
175
	guint event_mods;
176
	GSList *iter;
177
178
	TRACE (g_print ("Got Event! %d, %d\n", xevent->type, event->type));
179
180
	switch (xevent->type) {
181
	case KeyPress:
182
		TRACE (g_print ("Got KeyPress! keycode: %d, modifiers: %d\n", 
183
				xevent->xkey.keycode, 
184
				xevent->xkey.state));
185
186
		/* 
187
		 * Set the last event time for use when showing
188
		 * windows to avoid anti-focus-stealing code.
189
		 */
190
		processing_event = TRUE;
191
		last_event_time = xevent->xkey.time;
192
193
		event_mods = xevent->xkey.state & ~(num_lock_mask  | 
194
						    caps_lock_mask | 
195
						    scroll_lock_mask);
196
197
		for (iter = bindings; iter != NULL; iter = iter->next) {
198
			Binding *binding = (Binding *) iter->data;
199
						       
200
			if (binding->keycode == xevent->xkey.keycode &&
201
			    binding->modifiers == event_mods) {
202
203
				TRACE (g_print ("Calling handler for '%s'...\n", 
204
						binding->keystring));
205
206
				(binding->handler) (binding->keystring, 
207
						    binding->user_data);
208
			}
209
		}
210
211
		processing_event = FALSE;
212
		break;
213
	case KeyRelease:
214
		TRACE (g_print ("Got KeyRelease! \n"));
215
		break;
216
	}
217
218
	return return_val;
219
}
220
221
static void 
222
keymap_changed (GdkKeymap *map)
223
{
224
	GdkKeymap *keymap = gdk_keymap_get_default ();
225
	GSList *iter;
226
227
	TRACE (g_print ("Keymap changed! Regrabbing keys..."));
228
229
	for (iter = bindings; iter != NULL; iter = iter->next) {
230
		Binding *binding = (Binding *) iter->data;
231
		do_ungrab_key (binding);
232
	}
233
234
	lookup_ignorable_modifiers (keymap);
235
236
	for (iter = bindings; iter != NULL; iter = iter->next) {
237
		Binding *binding = (Binding *) iter->data;
238
		do_grab_key (binding);
239
	}
240
}
241
242
void 
243
gnomedo_keybinder_init (void)
244
{
245
	GdkKeymap *keymap = gdk_keymap_get_default ();
246
	GdkWindow *rootwin = gdk_get_default_root_window ();
247
248
	lookup_ignorable_modifiers (keymap);
249
250
	gdk_window_add_filter (rootwin, 
251
			       filter_func, 
252
			       NULL);
253
254
	g_signal_connect (keymap, 
255
			  "keys_changed",
256
			  G_CALLBACK (keymap_changed),
257
			  NULL);
258
}
259
260
void 
261
gnomedo_keybinder_bind (const char           *keystring,
262
		       GnomeDoBindkeyHandler  handler,
263
		       gpointer              user_data)
264
{
265
	Binding *binding;
266
	gboolean success;
267
268
	binding = g_new0 (Binding, 1);
269
	binding->keystring = g_strdup (keystring);
270
	binding->handler = handler;
271
	binding->user_data = user_data;
272
273
	/* Sets the binding's keycode and modifiers */
274
	success = do_grab_key (binding);
275
276
	if (success) {
277
		bindings = g_slist_prepend (bindings, binding);
278
	} else {
279
		g_free (binding->keystring);
280
		g_free (binding);
281
	}
282
}
283
284
void
285
gnomedo_keybinder_unbind (const char           *keystring, 
286
			 GnomeDoBindkeyHandler  handler)
287
{
288
	GSList *iter;
289
290
	for (iter = bindings; iter != NULL; iter = iter->next) {
291
		Binding *binding = (Binding *) iter->data;
292
293
		if (strcmp (keystring, binding->keystring) != 0 ||
294
		    handler != binding->handler) 
295
			continue;
296
297
		do_ungrab_key (binding);
298
299
		bindings = g_slist_remove (bindings, binding);
300
301
		g_free (binding->keystring);
302
		g_free (binding);
303
		break;
304
	}
305
}
306
307
/* 
308
 * From eggcellrenderkeys.c.
309
 */
310
gboolean
311
gnomedo_keybinder_is_modifier (guint keycode)
312
{
313
	gint i;
314
	gint map_size;
315
	XModifierKeymap *mod_keymap;
316
	gboolean retval = FALSE;
317
318
	mod_keymap = XGetModifierMapping (gdk_display);
319
320
	map_size = 8 * mod_keymap->max_keypermod;
321
322
	i = 0;
323
	while (i < map_size) {
324
		if (keycode == mod_keymap->modifiermap[i]) {
325
			retval = TRUE;
326
			break;
327
		}
328
		++i;
329
	}
330
331
	XFreeModifiermap (mod_keymap);
332
333
	return retval;
334
}
335
336
guint32
337
gnomedo_keybinder_get_current_event_time (void)
338
{
339
	if (processing_event) 
340
		return last_event_time;
341
	else
342
		return GDK_CURRENT_TIME;
343
}