~ubuntu-branches/ubuntu/oneiric/guake/oneiric

« back to all changes in this revision

Viewing changes to src/globalhotkeys/keybinder.c

  • Committer: Bazaar Package Importer
  • Author(s): Sylvestre Ledru
  • Date: 2008-08-11 10:43:03 UTC
  • Revision ID: james.westby@ubuntu.com-20080811104303-0ezz5fon32d6pcqj
Tags: upstream-0.3.1
ImportĀ upstreamĀ versionĀ 0.3.1

Show diffs side-by-side

added added

removed removed

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