~cairo-dock-team/ubuntu/oneiric/cairo-dock/2.3.0-3

« back to all changes in this revision

Viewing changes to src/gldit/cairo-dock-keybinder.c

  • Committer: Bazaar Package Importer
  • Author(s): Matthieu Baerts (matttbe)
  • Date: 2010-08-09 23:26:12 UTC
  • mto: This revision was merged to the branch mainline in revision 13.
  • Revision ID: james.westby@ubuntu.com-20100809232612-pocdxliaxjdetm37
Tags: upstream-2.2.0~0beta4
ImportĀ upstreamĀ versionĀ 2.2.0~0beta4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
** cairo-dock-keybinder.c
 
3
** Login : <ctaf42@localhost.localdomain>
 
4
** Started on  Thu Jan 31 03:57:17 2008 Cedric GESTES
 
5
** $Id$
 
6
**
 
7
** Author(s)
 
8
**  - Cedric GESTES <ctaf42@gmail.com>
 
9
**  - Havoc Pennington
 
10
**  - Tim Janik
 
11
**
 
12
** Copyright (C) 2008 Cedric GESTES
 
13
** This program is free software; you can redistribute it and/or modify
 
14
** it under the terms of the GNU General Public License as published by
 
15
** the Free Software Foundation; either version 3 of the License, or
 
16
** (at your option) any later version.
 
17
**
 
18
** This program is distributed in the hope that it will be useful,
 
19
** but WITHOUT ANY WARRANTY; without even the implied warranty of
 
20
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
21
** GNU General Public License for more details.
 
22
**
 
23
** You should have received a copy of the GNU General Public License
 
24
** along with this program; if not, write to the Free Software
 
25
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
26
*
 
27
* imported from tomboy_keybinder.c
 
28
*/
 
29
 
 
30
#include <string.h>
 
31
#include <sys/types.h>
 
32
#include <gdk/gdk.h>
 
33
#include <gdk/gdkwindow.h>
 
34
#include <gdk/gdkx.h>
 
35
#include <X11/Xlib.h>
 
36
#include "../config.h"
 
37
#ifdef HAVE_XEXTEND
 
38
#include <X11/extensions/XTest.h>
 
39
#endif
 
40
 
 
41
#include "eggaccelerators.h"
 
42
#include "cairo-dock-log.h"
 
43
#include "cairo-dock-X-utilities.h"
 
44
#include "cairo-dock-keybinder.h"
 
45
 
 
46
typedef unsigned int uint;
 
47
typedef struct _Binding {
 
48
        CDBindkeyHandler        handler;
 
49
        gpointer              user_data;
 
50
        char                 *keystring;
 
51
        uint                  keycode;
 
52
        uint                  modifiers;
 
53
} Binding;
 
54
 
 
55
static GSList *bindings = NULL;
 
56
static guint32 last_event_time = 0;
 
57
static gboolean processing_event = FALSE;
 
58
 
 
59
static guint num_lock_mask=0, caps_lock_mask=0, scroll_lock_mask=0;
 
60
 
 
61
static void
 
62
lookup_ignorable_modifiers (GdkKeymap *keymap)
 
63
{
 
64
        egg_keymap_resolve_virtual_modifiers (keymap,
 
65
                                              EGG_VIRTUAL_LOCK_MASK,
 
66
                                              &caps_lock_mask);
 
67
 
 
68
        egg_keymap_resolve_virtual_modifiers (keymap,
 
69
                                              EGG_VIRTUAL_NUM_LOCK_MASK,
 
70
                                              &num_lock_mask);
 
71
 
 
72
        egg_keymap_resolve_virtual_modifiers (keymap,
 
73
                                              EGG_VIRTUAL_SCROLL_LOCK_MASK,
 
74
                                              &scroll_lock_mask);
 
75
}
 
76
 
 
77
static void
 
78
grab_ungrab_with_ignorable_modifiers (GdkWindow *rootwin,
 
79
                                      Binding   *binding,
 
80
                                      gboolean   grab)
 
81
{
 
82
        guint mod_masks [] = {
 
83
                0, /* modifier only */
 
84
                num_lock_mask,
 
85
                caps_lock_mask,
 
86
                scroll_lock_mask,
 
87
                num_lock_mask  | caps_lock_mask,
 
88
                num_lock_mask  | scroll_lock_mask,
 
89
                caps_lock_mask | scroll_lock_mask,
 
90
                num_lock_mask  | caps_lock_mask | scroll_lock_mask,
 
91
        };
 
92
        
 
93
        guint i;
 
94
        for (i = 0; i < G_N_ELEMENTS (mod_masks); i++) {
 
95
                if (grab) {
 
96
                        XGrabKey (GDK_WINDOW_XDISPLAY (rootwin),
 
97
                                  binding->keycode,
 
98
                                  binding->modifiers | mod_masks [i],
 
99
                                  GDK_WINDOW_XWINDOW (rootwin),
 
100
                                  False,
 
101
                                  GrabModeAsync,
 
102
                                  GrabModeAsync);
 
103
                } else {
 
104
                        XUngrabKey (GDK_WINDOW_XDISPLAY (rootwin),
 
105
                                    binding->keycode,
 
106
                                    binding->modifiers | mod_masks [i],
 
107
                                    GDK_WINDOW_XWINDOW (rootwin));
 
108
                }
 
109
        }
 
110
}
 
111
 
 
112
static gboolean
 
113
do_grab_key (Binding *binding)
 
114
{
 
115
        GdkKeymap *keymap = gdk_keymap_get_default ();
 
116
        GdkWindow *rootwin = gdk_get_default_root_window ();
 
117
 
 
118
        EggVirtualModifierType virtual_mods = 0;
 
119
        guint keysym = 0;
 
120
 
 
121
        if (keymap == NULL || rootwin == NULL)
 
122
                return FALSE;
 
123
 
 
124
        if (!egg_accelerator_parse_virtual (binding->keystring,
 
125
                                            &keysym,
 
126
                                            &virtual_mods))
 
127
                return FALSE;
 
128
 
 
129
        cd_debug ("Got accel %d, %d", keysym, virtual_mods);
 
130
 
 
131
        binding->keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (rootwin),
 
132
                                             keysym);
 
133
        if (binding->keycode == 0)
 
134
                return FALSE;
 
135
 
 
136
        cd_debug ("Got keycode %d", binding->keycode);
 
137
 
 
138
        egg_keymap_resolve_virtual_modifiers (keymap,
 
139
                                              virtual_mods,
 
140
                                              &binding->modifiers);
 
141
 
 
142
        cd_debug ("Got modmask %d", binding->modifiers);
 
143
 
 
144
        gdk_error_trap_push ();
 
145
 
 
146
        grab_ungrab_with_ignorable_modifiers (rootwin,
 
147
                                              binding,
 
148
                                              TRUE /* grab */);
 
149
 
 
150
        gdk_flush ();
 
151
 
 
152
        if (gdk_error_trap_pop ()) {
 
153
           g_warning ("Binding '%s' failed!", binding->keystring);
 
154
           return FALSE;
 
155
        }
 
156
 
 
157
        return TRUE;
 
158
}
 
159
 
 
160
static gboolean
 
161
do_ungrab_key (Binding *binding)
 
162
{
 
163
        GdkWindow *rootwin = gdk_get_default_root_window ();
 
164
 
 
165
        cd_debug ("Removing grab for '%s'", binding->keystring);
 
166
 
 
167
        grab_ungrab_with_ignorable_modifiers (rootwin,
 
168
                                              binding,
 
169
                                              FALSE /* ungrab */);
 
170
 
 
171
        return TRUE;
 
172
}
 
173
 
 
174
static GdkFilterReturn
 
175
filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
 
176
{
 
177
        GdkFilterReturn return_val = GDK_FILTER_CONTINUE;
 
178
        XEvent *xevent = (XEvent *) gdk_xevent;
 
179
        guint event_mods;
 
180
        GSList *iter;
 
181
 
 
182
        cd_debug ("Got Event! %d, %d", xevent->type, event->type);
 
183
 
 
184
        switch (xevent->type) {
 
185
        case KeyPress:
 
186
                cd_debug ("Got KeyPress! keycode: %d, modifiers: %d",
 
187
                                xevent->xkey.keycode,
 
188
                                xevent->xkey.state);
 
189
 
 
190
                /*
 
191
                 * Set the last event time for use when showing
 
192
                 * windows to avoid anti-focus-stealing code.
 
193
                 */
 
194
                processing_event = TRUE;
 
195
                last_event_time = xevent->xkey.time;
 
196
 
 
197
                event_mods = xevent->xkey.state & ~(num_lock_mask  |
 
198
                                                    caps_lock_mask |
 
199
                                                    scroll_lock_mask);
 
200
 
 
201
                for (iter = bindings; iter != NULL; iter = iter->next) {
 
202
                        Binding *binding = (Binding *) iter->data;
 
203
 
 
204
                        if (binding->keycode == xevent->xkey.keycode &&
 
205
                            binding->modifiers == event_mods) {
 
206
 
 
207
                                cd_debug ("Calling handler for '%s'...",
 
208
                                                binding->keystring);
 
209
 
 
210
                                (binding->handler) (binding->keystring,
 
211
                                                    binding->user_data);
 
212
                        }
 
213
                }
 
214
 
 
215
                processing_event = FALSE;
 
216
                break;
 
217
        case KeyRelease:
 
218
                cd_debug ("Got KeyRelease! ");
 
219
                break;
 
220
        }
 
221
 
 
222
        return return_val;
 
223
}
 
224
 
 
225
static void
 
226
keymap_changed (GdkKeymap *map)
 
227
{
 
228
        GdkKeymap *keymap = gdk_keymap_get_default ();
 
229
        GSList *iter;
 
230
 
 
231
        cd_debug ("Keymap changed! Regrabbing keys...");
 
232
 
 
233
        for (iter = bindings; iter != NULL; iter = iter->next) {
 
234
                Binding *binding = (Binding *) iter->data;
 
235
                do_ungrab_key (binding);
 
236
        }
 
237
 
 
238
        lookup_ignorable_modifiers (keymap);
 
239
 
 
240
        for (iter = bindings; iter != NULL; iter = iter->next) {
 
241
                Binding *binding = (Binding *) iter->data;
 
242
                do_grab_key (binding);
 
243
        }
 
244
}
 
245
 
 
246
void
 
247
cd_keybinder_init (void)
 
248
{
 
249
        GdkKeymap *keymap = gdk_keymap_get_default ();
 
250
        GdkWindow *rootwin = gdk_get_default_root_window ();
 
251
 
 
252
        lookup_ignorable_modifiers (keymap);
 
253
 
 
254
        gdk_window_add_filter (rootwin,
 
255
                filter_func,
 
256
                NULL);
 
257
 
 
258
        g_signal_connect (keymap,
 
259
                "keys_changed",
 
260
                G_CALLBACK (keymap_changed),
 
261
                NULL);
 
262
}
 
263
 
 
264
void
 
265
cd_keybinder_stop (void)
 
266
{
 
267
        GdkKeymap *keymap = gdk_keymap_get_default ();
 
268
        GdkWindow *rootwin = gdk_get_default_root_window ();
 
269
 
 
270
        num_lock_mask=0, caps_lock_mask=0, scroll_lock_mask=0;
 
271
 
 
272
        g_signal_handlers_disconnect_by_func (keymap,
 
273
                G_CALLBACK (keymap_changed),
 
274
                NULL);
 
275
        
 
276
        gdk_window_remove_filter (rootwin,
 
277
                filter_func,
 
278
                NULL);
 
279
}
 
280
 
 
281
 
 
282
gboolean
 
283
cd_keybinder_bind (const char           *keystring,
 
284
                       CDBindkeyHandler  handler,
 
285
                       gpointer              user_data)
 
286
{
 
287
        Binding *binding;
 
288
        gboolean success;
 
289
 
 
290
        cd_debug ("%s (%s)", __func__, keystring);
 
291
        if (!keystring)
 
292
                return FALSE;
 
293
        binding = g_new0 (Binding, 1);
 
294
        binding->keystring = g_strdup (keystring);
 
295
        binding->handler = handler;
 
296
        binding->user_data = user_data;
 
297
 
 
298
        /* Sets the binding's keycode and modifiers */
 
299
        success = do_grab_key (binding);
 
300
 
 
301
        if (success) {
 
302
                bindings = g_slist_prepend (bindings, binding);
 
303
        } else {
 
304
                cd_warning ("Couldn't bind %s\n This shortkey is probably already used by another applet or another application", keystring);
 
305
                g_free (binding->keystring);
 
306
                g_free (binding);
 
307
        }
 
308
 
 
309
        return success;
 
310
}
 
311
 
 
312
void
 
313
cd_keybinder_unbind (const char           *keystring,
 
314
                         CDBindkeyHandler  handler)
 
315
{
 
316
        cd_debug ("%s (%s)", __func__, keystring);
 
317
        GSList *iter;
 
318
 
 
319
        if (!keystring)
 
320
          return;
 
321
        for (iter = bindings; iter != NULL; iter = iter->next) {
 
322
                Binding *binding = (Binding *) iter->data;
 
323
 
 
324
                if (strcmp (keystring, binding->keystring) != 0 ||
 
325
                    handler != binding->handler)
 
326
                        continue;
 
327
 
 
328
                do_ungrab_key (binding);
 
329
 
 
330
                bindings = g_slist_remove (bindings, binding);
 
331
                
 
332
                cd_debug (" --- remove key binding '%s'\n", binding->keystring);
 
333
                g_free (binding->keystring);
 
334
                g_free (binding);
 
335
                break;
 
336
        }
 
337
}
 
338
 
 
339
/*
 
340
 * From eggcellrenderkeys.c.
 
341
 */
 
342
gboolean
 
343
cd_keybinder_is_modifier (guint keycode)
 
344
{
 
345
        gint i;
 
346
        gint map_size;
 
347
        XModifierKeymap *mod_keymap;
 
348
        gboolean retval = FALSE;
 
349
 
 
350
        mod_keymap = XGetModifierMapping (gdk_display);
 
351
 
 
352
        map_size = 8 * mod_keymap->max_keypermod;
 
353
 
 
354
        i = 0;
 
355
        while (i < map_size) {
 
356
                if (keycode == mod_keymap->modifiermap[i]) {
 
357
                        retval = TRUE;
 
358
                        break;
 
359
                }
 
360
                ++i;
 
361
        }
 
362
 
 
363
        XFreeModifiermap (mod_keymap);
 
364
 
 
365
        return retval;
 
366
}
 
367
 
 
368
guint32
 
369
cd_keybinder_get_current_event_time (void)
 
370
{
 
371
        if (processing_event)
 
372
                return last_event_time;
 
373
        else
 
374
                return GDK_CURRENT_TIME;
 
375
}
 
376
 
 
377
 
 
378
gboolean cairo_dock_simulate_key_sequence (gchar *cKeyString)  // the idea was taken from xdo.
 
379
{
 
380
#ifdef HAVE_XEXTEND
 
381
        g_return_val_if_fail (cairo_dock_xtest_is_available (), FALSE);
 
382
        g_return_val_if_fail (cKeyString != NULL, FALSE);
 
383
        cd_message ("%s (%s)", __func__, cKeyString);
 
384
        
 
385
        int iNbKeys = 0;
 
386
        int *pKeySyms = egg_keystring_to_keysyms (cKeyString, &iNbKeys);
 
387
 
 
388
        int i;
 
389
        int keycode;
 
390
        Display *dpy = cairo_dock_get_Xdisplay ();
 
391
        for (i = 0; i < iNbKeys; i ++)
 
392
        {
 
393
                keycode = XKeysymToKeycode (dpy, pKeySyms[i]);
 
394
                XTestFakeKeyEvent (dpy, keycode, TRUE, CurrentTime);  // TRUE <=> presse.
 
395
        }
 
396
        
 
397
        for (i = iNbKeys-1; i >=0; i --)
 
398
        {
 
399
                keycode = XKeysymToKeycode (dpy, pKeySyms[i]);
 
400
                XTestFakeKeyEvent (dpy, keycode, FALSE, CurrentTime);  // TRUE <=> presse.
 
401
        }
 
402
        
 
403
        XFlush (dpy);
 
404
        
 
405
        return TRUE;
 
406
#else
 
407
        cd_warning ("The dock was not compiled with the support of XTest.");
 
408
        return FALSE;
 
409
#endif
 
410
}
 
411
 
 
412