1 /* vi:set expandtab sw=2 sts=2: */
2 /*
3 * Copyright (c) 2008 Jannis Pohlmann <jannis@xfce.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <glib.h>
26 #include <glib-object.h>
27
28 #include <X11/Xlib.h>
29 #include <X11/XKBlib.h>
30
31 #include <gdk/gdk.h>
32 #include <gdk/gdkx.h>
33 #include <gdk/gdkkeysyms.h>
34
35 #include <gtk/gtk.h>
36
37 #include <libxfce4util/libxfce4util.h>
38
39 #include <libxfce4kbd-private/xfce-shortcuts-grabber.h>
40 #include <libxfce4kbd-private/xfce-shortcuts-marshal.h>
41
42
43
44 #define XFCE_SHORTCUTS_GRABBER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), XFCE_TYPE_SHORTCUTS_GRABBER, XfceShortcutsGrabberPrivate))
45 #define MODIFIERS_ERROR ((GdkModifierType)(-1))
46 #define MODIFIERS_NONE 0
47
48
49 typedef struct _XfceKey XfceKey;
50
51
52
53 static void xfce_shortcuts_grabber_constructed (GObject *object);
54 static void xfce_shortcuts_grabber_finalize (GObject *object);
55 static void xfce_shortcuts_grabber_keys_changed (GdkKeymap *keymap,
56 XfceShortcutsGrabber *grabber);
57 static void xfce_shortcuts_grabber_regrab_all (XfceShortcutsGrabber *grabber);
58 static void xfce_shortcuts_grabber_ungrab_all (XfceShortcutsGrabber *grabber);
59 static void xfce_shortcuts_grabber_grab (XfceShortcutsGrabber *grabber,
60 XfceKey *key);
61 static void xfce_shortcuts_grabber_ungrab (XfceShortcutsGrabber *grabber,
62 XfceKey *key);
63 static GdkFilterReturn xfce_shortcuts_grabber_event_filter (GdkXEvent *gdk_xevent,
64 GdkEvent *event,
65 gpointer data);
66
67
68
69 struct _XfceShortcutsGrabberPrivate
70 {
71 /* Maps a shortcut string to a pointer to XfceKey */
72 GHashTable *keys;
73
74 /* Maps an XfceXGrab to a reference count.
75 * The reference count tracks the number of shortcuts that grab the XfceXGrab. */
76 GHashTable *grabbed_keycodes;
77
78 gint xkbEventType, xkbStateGroup;
79 };
80
81 struct _XfceKey
82 {
83 guint keyval;
84 GdkModifierType modifiers;
85
86 /* Information about how the key has been grabbed */
87 guint n_keys; /* Equals 0 if the key isn't grabbed */
88 GdkKeymapKey *keys;
89 GdkModifierType non_virtual_modifiers;
90 guint numlock_modifier;
91 };
92
93 typedef struct
94 {
95 guint keycode;
96 GdkModifierType non_virtual_modifiers;
97 guint numlock_modifier;
98 } XfceXGrab;
99
100 typedef guint XfceXGrabRefcount;
101
102
103
G_DEFINE_TYPE(XfceShortcutsGrabber,xfce_shortcuts_grabber,G_TYPE_OBJECT)104 G_DEFINE_TYPE (XfceShortcutsGrabber, xfce_shortcuts_grabber, G_TYPE_OBJECT)
105
106
107
108 static void
109 xfce_shortcuts_grabber_class_init (XfceShortcutsGrabberClass *klass)
110 {
111 GObjectClass *gobject_class;
112
113 g_type_class_add_private (klass, sizeof (XfceShortcutsGrabberPrivate));
114
115 gobject_class = G_OBJECT_CLASS (klass);
116 gobject_class->constructed = xfce_shortcuts_grabber_constructed;
117 gobject_class->finalize = xfce_shortcuts_grabber_finalize;
118
119 g_signal_new ("shortcut-activated",
120 XFCE_TYPE_SHORTCUTS_GRABBER,
121 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
122 0,
123 NULL,
124 NULL,
125 _xfce_shortcuts_marshal_VOID__STRING_INT,
126 G_TYPE_NONE,
127 2,
128 G_TYPE_STRING, G_TYPE_INT);
129 }
130
131
132
133 static void
free_key(gpointer data)134 free_key (gpointer data)
135 {
136 XfceKey *key = data;
137 g_free (key->keys);
138 g_free (key);
139 }
140
141 static gboolean
xgrab_equal(gconstpointer data1,gconstpointer data2)142 xgrab_equal (gconstpointer data1, gconstpointer data2)
143 {
144 const XfceXGrab *a = data1;
145 const XfceXGrab *b = data2;
146
147 if (a == b)
148 return TRUE;
149
150 return a->keycode == b->keycode &&
151 a->non_virtual_modifiers == b->non_virtual_modifiers &&
152 a->numlock_modifier == b->numlock_modifier;
153 }
154
155 static void
xgrab_free(gpointer data)156 xgrab_free (gpointer data)
157 {
158 XfceXGrab *g = data;
159 g_free (g);
160 }
161
162 static guint
xgrab_hash(gconstpointer data)163 xgrab_hash (gconstpointer data)
164 {
165 const XfceXGrab *g = data;
166 return g->keycode ^ g->non_virtual_modifiers ^ g->numlock_modifier;
167 }
168
169
170
171
172 static void
xfce_shortcuts_grabber_init(XfceShortcutsGrabber * grabber)173 xfce_shortcuts_grabber_init (XfceShortcutsGrabber *grabber)
174 {
175 GdkDisplay *display;
176 GdkKeymap *keymap;
177
178 grabber->priv = XFCE_SHORTCUTS_GRABBER_GET_PRIVATE (grabber);
179 grabber->priv->keys = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_key);
180 grabber->priv->grabbed_keycodes = g_hash_table_new_full (xgrab_hash, xgrab_equal, xgrab_free, g_free);
181
182 /* Workaround: Make sure modmap is up to date
183 * There is possibly a bug in GTK+ where virtual modifiers are not
184 * mapped because the modmap is not updated. The following function
185 * updates it.
186 */
187 display = gdk_display_get_default ();
188 keymap = gdk_keymap_get_for_display (display);
189 (void) gdk_keymap_have_bidi_layouts (keymap);
190 }
191
192
193
194 static void
xfce_shortcuts_grabber_constructed(GObject * object)195 xfce_shortcuts_grabber_constructed (GObject *object)
196 {
197 GdkDisplay *display;
198 Display *xdisplay;
199 GdkKeymap *keymap;
200
201 XfceShortcutsGrabber *grabber = XFCE_SHORTCUTS_GRABBER (object);
202
203 display = gdk_display_get_default ();
204 xdisplay = GDK_DISPLAY_XDISPLAY (display);
205 keymap = gdk_keymap_get_for_display (display);
206 g_signal_connect (keymap, "keys-changed", G_CALLBACK (xfce_shortcuts_grabber_keys_changed),
207 grabber);
208
209 if (G_UNLIKELY (!XkbQueryExtension (xdisplay, 0, &grabber->priv->xkbEventType, 0, 0, 0)))
210 grabber->priv->xkbEventType = -1;
211 grabber->priv->xkbStateGroup = -1;
212
213 /* Flush events before adding the event filter */
214 XAllowEvents (xdisplay, AsyncBoth, CurrentTime);
215
216 /* Add event filter */
217 gdk_window_add_filter (NULL, xfce_shortcuts_grabber_event_filter, grabber);
218 }
219
220
221
222 static void
xfce_shortcuts_grabber_finalize(GObject * object)223 xfce_shortcuts_grabber_finalize (GObject *object)
224 {
225 XfceShortcutsGrabber *grabber = XFCE_SHORTCUTS_GRABBER (object);
226
227 xfce_shortcuts_grabber_ungrab_all (grabber);
228 g_hash_table_unref (grabber->priv->keys);
229 g_hash_table_unref (grabber->priv->grabbed_keycodes);
230
231 (*G_OBJECT_CLASS (xfce_shortcuts_grabber_parent_class)->finalize) (object);
232 }
233
234
235
236 static void
xfce_shortcuts_grabber_keys_changed(GdkKeymap * keymap,XfceShortcutsGrabber * grabber)237 xfce_shortcuts_grabber_keys_changed (GdkKeymap *keymap,
238 XfceShortcutsGrabber *grabber)
239 {
240 g_return_if_fail (XFCE_IS_SHORTCUTS_GRABBER (grabber));
241
242 TRACE ("Keys changed, regrabbing");
243
244 xfce_shortcuts_grabber_regrab_all (grabber);
245 }
246
247
248
249 static gboolean
xfce_shortcuts_grabber_xgrab(XfceXGrab g,gboolean grab)250 xfce_shortcuts_grabber_xgrab (XfceXGrab g, gboolean grab)
251 {
252 GdkDisplay *display = gdk_display_get_default ();
253 Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
254 Window root_window;
255 guint k;
256 gboolean success = TRUE;
257
258 /* Ignorable modifiers */
259 const guint mod_masks [] = {
260 0,
261 GDK_MOD2_MASK,
262 g.numlock_modifier | GDK_MOD2_MASK,
263 GDK_LOCK_MASK,
264 g.numlock_modifier | GDK_LOCK_MASK,
265 GDK_MOD5_MASK,
266 g.numlock_modifier | GDK_MOD5_MASK,
267 GDK_MOD2_MASK | GDK_LOCK_MASK,
268 g.numlock_modifier | GDK_MOD2_MASK | GDK_LOCK_MASK,
269 GDK_MOD2_MASK | GDK_MOD5_MASK,
270 g.numlock_modifier | GDK_MOD2_MASK | GDK_MOD5_MASK,
271 GDK_LOCK_MASK | GDK_MOD5_MASK,
272 g.numlock_modifier | GDK_LOCK_MASK | GDK_MOD5_MASK,
273 GDK_MOD2_MASK | GDK_LOCK_MASK | GDK_MOD5_MASK,
274 g.numlock_modifier | GDK_MOD2_MASK | GDK_LOCK_MASK | GDK_MOD5_MASK,
275 };
276
277 /* Retrieve the root window of the screen */
278 root_window = GDK_WINDOW_XID (gdk_screen_get_root_window (gdk_display_get_default_screen (display)));
279
280 TRACE ("%s keycode %u, non_virtual_modifiers 0x%x",
281 grab ? "Grabbing" : "Ungrabbing",
282 g.keycode, g.non_virtual_modifiers);
283
284 gdk_x11_display_error_trap_push (display);
285
286 for (k = 0; k < G_N_ELEMENTS (mod_masks); k++)
287 {
288 /* Take ignorable modifiers into account when grabbing/ungrabbing */
289 if (grab)
290 XGrabKey (xdisplay,
291 g.keycode,
292 g.non_virtual_modifiers | mod_masks [k],
293 root_window,
294 False, GrabModeAsync, GrabModeAsync);
295 else
296 XUngrabKey (xdisplay,
297 g.keycode,
298 g.non_virtual_modifiers | mod_masks [k],
299 root_window);
300 }
301
302 gdk_display_flush (display);
303 if (gdk_x11_display_error_trap_pop (display))
304 {
305 g_warning ("Failed to %s keycode %u",
306 grab ? "grab" : "ungrab", g.keycode);
307 success = FALSE;
308 }
309
310 return success;
311 }
312
313
314
315 static gboolean
ungrab_key(const gchar * shortcut,XfceKey * key,XfceShortcutsGrabber * grabber)316 ungrab_key (const gchar *shortcut,
317 XfceKey *key,
318 XfceShortcutsGrabber *grabber)
319 {
320 xfce_shortcuts_grabber_ungrab (grabber, key);
321 return FALSE;
322 }
323
324
325
326 static void
xfce_shortcuts_grabber_ungrab_all(XfceShortcutsGrabber * grabber)327 xfce_shortcuts_grabber_ungrab_all (XfceShortcutsGrabber *grabber)
328 {
329 g_return_if_fail (XFCE_IS_SHORTCUTS_GRABBER (grabber));
330 g_hash_table_foreach (grabber->priv->keys,
331 (GHFunc) (void (*)(void)) ungrab_key,
332 grabber);
333 }
334
335
336
337 static gboolean
get_entries_for_keyval(GdkKeymap * keymap,gint group,guint keyval,GdkKeymapKey ** keys,guint * n_keys)338 get_entries_for_keyval (GdkKeymap *keymap,
339 gint group,
340 guint keyval,
341 GdkKeymapKey **keys,
342 guint *n_keys)
343 {
344 GdkKeymapKey *keys1;
345 gint n_keys1;
346
347 *keys = NULL;
348 *n_keys = 0;
349
350 /* Get all keys generating keyval */
351 if (!gdk_keymap_get_entries_for_keyval (keymap, keyval, &keys1, &n_keys1))
352 {
353 TRACE ("Got no keys for keyval");
354 return FALSE;
355 }
356
357 if (G_UNLIKELY (n_keys1 <= 0))
358 {
359 g_free (keys1);
360 return FALSE;
361 }
362
363 /* Filter keys by group */
364 {
365 gboolean group0_only;
366 gint i, n_matches;
367
368 /* For keys such as F12:
369 * keys1[i].group is always 0 (even if n_keys1 >= 2)
370 * and thus n_matches will be zero if group != 0 */
371
372 group0_only = TRUE;
373 n_matches = 0;
374 for (i = 0; i < n_keys1; i++)
375 {
376 group0_only &= (keys1[i].group == 0) ? TRUE : FALSE;
377 if (keys1[i].group == group)
378 n_matches++;
379 }
380
381 if (!group0_only || n_matches != 0)
382 {
383 /* Remove keys that do not match the group*/
384 for (i = 0; i < n_keys1;)
385 if (keys1[i].group == group)
386 i++;
387 else
388 keys1[i] = keys1[--n_keys1];
389 }
390 }
391
392 if (G_UNLIKELY (n_keys1 == 0))
393 {
394 g_free (keys1);
395 keys1 = NULL;
396 }
397
398 *keys = keys1;
399 *n_keys = n_keys1;
400 return TRUE;
401 }
402
403
404
405 static gboolean
map_virtual_modifiers(GdkKeymap * keymap,GdkModifierType virtual_modifiers,GdkModifierType * non_virtual_modifiers)406 map_virtual_modifiers (GdkKeymap *keymap,
407 GdkModifierType virtual_modifiers,
408 GdkModifierType *non_virtual_modifiers)
409 {
410 /* Map virtual modifiers to non-virtual modifiers */
411 GdkModifierType non_virtual = virtual_modifiers;
412 if (!gdk_keymap_map_virtual_modifiers (keymap, &non_virtual))
413 return FALSE;
414
415 if (non_virtual == virtual_modifiers &&
416 (GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK) & non_virtual)
417 {
418 TRACE ("Failed to map virtual modifiers");
419 return FALSE;
420 }
421
422 *non_virtual_modifiers = non_virtual;
423 return TRUE;
424 }
425
426
427
428
429 static void
xfce_shortcuts_grabber_regrab_all(XfceShortcutsGrabber * grabber)430 xfce_shortcuts_grabber_regrab_all (XfceShortcutsGrabber *grabber)
431 {
432 GdkDisplay *display;
433 Display *xdisplay;
434 GdkKeymap *keymap;
435 guint numlock_modifier;
436 GHashTable *grabbed_keycodes;
437 GHashTableIter iter;
438 gpointer hash_value;
439 guint n_already_grabbed = 0;
440 guint n_regrab = 0;
441 XfceKey **regrab; /* list of keys to re-grab */
442 guint i;
443 gint group;
444
445 g_return_if_fail (XFCE_IS_SHORTCUTS_GRABBER (grabber));
446
447 display = gdk_display_get_default ();
448 xdisplay = GDK_DISPLAY_XDISPLAY (display);
449 keymap = gdk_keymap_get_for_display (display);
450 numlock_modifier = XkbKeysymToModifiers (xdisplay, GDK_KEY_Num_Lock);
451 grabbed_keycodes = grabber->priv->grabbed_keycodes;
452 group = grabber->priv->xkbStateGroup;
453 if (G_UNLIKELY (group == -1))
454 group = 0;
455
456 regrab = g_malloc (g_hash_table_size (grabber->priv->keys) * sizeof (*regrab));
457
458 /* Phase 1: Ungrab all keys that need to be re-grabbed
459 * and collect them into the 'regrab' list */
460 g_hash_table_iter_init (&iter, grabber->priv->keys);
461 while (g_hash_table_iter_next (&iter, NULL, &hash_value))
462 {
463 XfceKey *const key = hash_value;
464 GdkKeymapKey *keys;
465 GdkModifierType non_virtual_modifiers;
466 guint n_keys;
467 gboolean already_grabbed;
468
469 if (!map_virtual_modifiers (keymap, key->modifiers, &non_virtual_modifiers))
470 continue;
471 if (!get_entries_for_keyval (keymap, group, key->keyval, &keys, &n_keys))
472 continue;
473
474 already_grabbed = TRUE;
475 if (key->n_keys == n_keys &&
476 key->non_virtual_modifiers == non_virtual_modifiers &&
477 key->numlock_modifier == numlock_modifier)
478 {
479 guint j;
480 for (j = 0; j < n_keys; j++)
481 if (memcmp (&key->keys[j], &keys[j], sizeof(*keys)) != 0)
482 {
483 already_grabbed = FALSE;
484 break;
485 }
486 }
487 else
488 already_grabbed = FALSE;
489
490 if (already_grabbed)
491 {
492 n_already_grabbed++;
493 g_free (keys);
494 }
495 else
496 {
497 /* Undo current X11 grabs of the key */
498 if (key->n_keys != 0)
499 xfce_shortcuts_grabber_ungrab (grabber, key);
500
501 /* Set key->keys to the keycodes that need to be grabbed in phase 2 */
502 if (G_UNLIKELY (key->keys))
503 {
504 g_free (key->keys);
505 key->keys = NULL;
506 }
507 key->n_keys = n_keys;
508 if (n_keys != 0)
509 key->keys = keys;
510 else
511 g_free (keys);
512 key->non_virtual_modifiers = non_virtual_modifiers;
513 key->numlock_modifier = numlock_modifier;
514
515 /* Remember to grab the key in phase 2 */
516 if (n_keys != 0)
517 regrab[n_regrab++] = key;
518 }
519 }
520
521 TRACE ("n_already_grabbed=%u, n_regrab=%u", n_already_grabbed, n_regrab);
522
523 /* Phase 2: Grab all keys that have been stored in the 'regrab' list */
524 for (i = 0; i < n_regrab; i++)
525 {
526 XfceKey *const key = regrab[i];
527 guint j;
528
529 #ifdef DEBUG_TRACE
530 {
531 gchar *shortcut_name = gtk_accelerator_name (key->keyval, key->non_virtual_modifiers);
532 TRACE (key->n_keys==0 ? "Grabbing %s" : "Regrabbing %s", shortcut_name);
533 TRACE (" key->keyval: %d", key->keyval);
534 TRACE (" key->modifiers: 0x%x", key->modifiers);
535 TRACE (" key->non_virtual_modifiers: 0x%x", key->non_virtual_modifiers);
536 TRACE (" key->n_keys: %u", key->n_keys);
537 g_free (shortcut_name);
538 }
539 #endif
540
541 /* Grab all hardware keys generating keyval */
542 for (j = 0; j < key->n_keys;)
543 {
544 XfceXGrab g;
545 gpointer refcount;
546
547 g.keycode = key->keys[j].keycode;
548 g.non_virtual_modifiers = key->non_virtual_modifiers;
549 g.numlock_modifier = key->numlock_modifier;
550 if (!g_hash_table_lookup_extended (grabbed_keycodes, &g, NULL, &refcount))
551 {
552 if (xfce_shortcuts_grabber_xgrab (g, TRUE))
553 {
554 XfceXGrab *g1 = g_new (XfceXGrab, 1);
555 XfceXGrabRefcount *refcount1 = g_new (XfceXGrabRefcount, 1);
556 *g1 = g;
557 *refcount1 = 1;
558 g_hash_table_insert (grabbed_keycodes, g1, refcount1);
559 j++;
560 }
561 else
562 /* Failed to grab key->keys[j], remove it from key->keys */
563 key->keys[j] = key->keys[--key->n_keys];
564 }
565 else
566 {
567 // 'g' has already been grabbed, increment its refcount only
568 XfceXGrabRefcount *refcount1 = refcount;
569 (*refcount1)++;
570 TRACE ("keycode %u, non_virtual_modifiers 0x%x: ++refcount = %u",
571 g.keycode, g.non_virtual_modifiers, *refcount1);
572 j++;
573 }
574 }
575
576 if (key->n_keys == 0 && key->keys != NULL)
577 {
578 g_free (key->keys);
579 key->keys = NULL;
580 }
581 }
582
583 g_free (regrab);
584 }
585
586
587
588 static void
xfce_shortcuts_grabber_grab(XfceShortcutsGrabber * grabber,XfceKey * key)589 xfce_shortcuts_grabber_grab (XfceShortcutsGrabber *grabber, XfceKey *key)
590 {
591 GdkDisplay *display;
592 Display *xdisplay;
593 GdkKeymap *keymap;
594 guint numlock_modifier;
595 GHashTable *grabbed_keycodes;
596 GdkKeymapKey *keys;
597 GdkModifierType non_virtual_modifiers;
598 guint i, n_keys;
599 gint group;
600
601 display = gdk_display_get_default ();
602 xdisplay = GDK_DISPLAY_XDISPLAY (display);
603 keymap = gdk_keymap_get_for_display (display);
604 numlock_modifier = XkbKeysymToModifiers (xdisplay, GDK_KEY_Num_Lock);
605 grabbed_keycodes = grabber->priv->grabbed_keycodes;
606 group = grabber->priv->xkbStateGroup;
607 if (G_UNLIKELY (group == -1))
608 group = 0;
609
610 if (!map_virtual_modifiers (keymap, key->modifiers, &non_virtual_modifiers))
611 return;
612 if (!get_entries_for_keyval (keymap, group, key->keyval, &keys, &n_keys))
613 return;
614
615 #ifdef DEBUG_TRACE
616 {
617 char *shortcut_name = gtk_accelerator_name (key->keyval, non_virtual_modifiers);
618 TRACE (key->n_keys==0 ? "Grabbing %s" : "Regrabbing %s", shortcut_name);
619 TRACE (" key->keyval: %d", key->keyval);
620 TRACE (" key->modifiers: 0x%x", key->modifiers);
621 TRACE (" non_virtual_modifiers: 0x%x", non_virtual_modifiers);
622 TRACE (" n_keys: %u", n_keys);
623 g_free (shortcut_name);
624 }
625 #endif
626
627 /* Undo old grabs (just in case there are some old grabs) */
628 if (G_UNLIKELY (key->n_keys != 0))
629 {
630 g_warning ("keyval %u already grabbed", key->keyval);
631 xfce_shortcuts_grabber_ungrab (grabber, key);
632 }
633
634 /* Grab all hardware keys generating keyval */
635 for (i = 0; i < n_keys;)
636 {
637 XfceXGrab g;
638 gpointer refcount;
639
640 g.keycode = keys[i].keycode;
641 g.non_virtual_modifiers = non_virtual_modifiers;
642 g.numlock_modifier = numlock_modifier;
643 if (!g_hash_table_lookup_extended (grabbed_keycodes, &g, NULL, &refcount))
644 {
645 if (xfce_shortcuts_grabber_xgrab (g, TRUE))
646 {
647 XfceXGrab *g1 = g_new (XfceXGrab, 1);
648 XfceXGrabRefcount *refcount1 = g_new (XfceXGrabRefcount, 1);
649 *g1 = g;
650 *refcount1 = 1;
651 g_hash_table_insert (grabbed_keycodes, g1, refcount1);
652 TRACE ("group %d, keycode %u, non_virtual_modifiers 0x%x: refcount := %u",
653 keys[i].group, g.keycode, g.non_virtual_modifiers, *refcount1);
654 i++;
655 }
656 else
657 /* Failed to grab keys[i], remove it from keys */
658 keys[i] = keys[--n_keys];
659 }
660 else
661 {
662 // 'g' has already been grabbed, increment its refcount only
663 XfceXGrabRefcount *refcount1 = refcount;
664 (*refcount1)++;
665 TRACE ("group %d, keycode %u, non_virtual_modifiers 0x%x: ++refcount = %u",
666 keys[i].group, g.keycode, g.non_virtual_modifiers, *refcount1);
667 i++;
668 }
669 }
670
671 /* Set key->keys to the list of keys that been succesfully grabbed */
672 g_free (key->keys);
673 key->keys = NULL;
674 key->n_keys = n_keys;
675 if (n_keys != 0)
676 key->keys = keys;
677 else
678 g_free (keys);
679 key->non_virtual_modifiers = non_virtual_modifiers;
680 key->numlock_modifier = numlock_modifier;
681 }
682
683 static void
xfce_shortcuts_grabber_ungrab(XfceShortcutsGrabber * grabber,XfceKey * key)684 xfce_shortcuts_grabber_ungrab (XfceShortcutsGrabber *grabber, XfceKey *key)
685 {
686 GHashTable *grabbed_keycodes;
687 guint i;
688
689 grabbed_keycodes = grabber->priv->grabbed_keycodes;
690
691 #ifdef DEBUG_TRACE
692 {
693 gchar *shortcut_name = gtk_accelerator_name (key->keyval, key->non_virtual_modifiers);
694 TRACE ("Ungrabbing %s", shortcut_name);
695 TRACE (" key->keyval: %d", key->keyval);
696 TRACE (" key->modifiers: 0x%x", key->modifiers);
697 TRACE (" key->non_virtual_modifiers: 0x%x", key->non_virtual_modifiers);
698 TRACE (" key->n_keys: %u", key->n_keys);
699 g_free (shortcut_name);
700 }
701 #endif
702
703 for (i = 0; i < key->n_keys; i++)
704 {
705 XfceXGrab g;
706 gpointer refcount;
707
708 g.keycode = key->keys[i].keycode;
709 g.non_virtual_modifiers = key->non_virtual_modifiers;
710 g.numlock_modifier = key->numlock_modifier;
711 if (G_LIKELY (g_hash_table_lookup_extended (grabbed_keycodes, &g, NULL, &refcount)))
712 {
713 XfceXGrabRefcount *refcount1 = refcount;
714 if (G_LIKELY (*refcount1 != 0))
715 {
716 (*refcount1)--;
717 TRACE ("group %d, keycode %u, non_virtual_modifiers 0x%x: --refcount = %u",
718 key->keys[i].group, g.keycode, g.non_virtual_modifiers, *refcount1);
719 if(*refcount1 == 0)
720 {
721 xfce_shortcuts_grabber_xgrab (g, FALSE);
722 g_hash_table_remove (grabbed_keycodes, &g);
723 }
724 }
725 else
726 {
727 g_warning ("corrupted refcount");
728 }
729 }
730 else
731 {
732 g_warning ("corrupted hashtable");
733 }
734 }
735
736 g_free (key->keys);
737 key->keys = NULL;
738 key->n_keys = 0;
739 key->non_virtual_modifiers = 0;
740 key->numlock_modifier = 0;
741 }
742
743
744
745 struct EventKeyFindContext
746 {
747 GdkModifierType modifiers;
748 guint keyval;
749 const gchar *result;
750 };
751
752
753
754 static gboolean
find_event_key(const gchar * shortcut,XfceKey * key,struct EventKeyFindContext * context)755 find_event_key (const gchar *shortcut,
756 XfceKey *key,
757 struct EventKeyFindContext *context)
758 {
759 g_return_val_if_fail (context != NULL, FALSE);
760
761 TRACE ("Comparing to %s", shortcut);
762
763 if ((key->modifiers & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK | GDK_SUPER_MASK))
764 == (context->modifiers)
765 && (key->keyval == context->keyval))
766 {
767 context->result = shortcut;
768
769 TRACE ("Positive match for %s", context->result);
770 return TRUE;
771 }
772
773 return FALSE;
774 }
775
776
777
778 static GdkFilterReturn
xfce_shortcuts_grabber_event_filter(GdkXEvent * gdk_xevent,GdkEvent * event,gpointer data)779 xfce_shortcuts_grabber_event_filter (GdkXEvent *gdk_xevent,
780 GdkEvent *event,
781 gpointer data)
782 {
783 XfceShortcutsGrabber *const grabber = data;
784 struct EventKeyFindContext context;
785 GdkKeymap *keymap;
786 GdkModifierType consumed, modifiers;
787 GdkDisplay *display;
788 XEvent *xevent;
789 guint keyval, mod_mask;
790 gchar *raw_shortcut_name;
791 gint timestamp;
792
793 g_return_val_if_fail (XFCE_IS_SHORTCUTS_GRABBER (grabber), GDK_FILTER_CONTINUE);
794
795 xevent = (XEvent *) gdk_xevent;
796
797 if (xevent->type == grabber->priv->xkbEventType)
798 {
799 const XkbEvent *e = (const XkbEvent*) xevent;
800 if (e->any.xkb_type == XkbStateNotify)
801 {
802 if (grabber->priv->xkbStateGroup != e->state.group)
803 {
804 TRACE ("xkb event: any.xkb_type=XkbStateNotify, state.group=%d", e->state.group);
805 grabber->priv->xkbStateGroup = e->state.group;
806 xfce_shortcuts_grabber_regrab_all (grabber);
807 }
808 }
809 }
810
811 if (xevent->type != KeyPress)
812 return GDK_FILTER_CONTINUE;
813
814 context.result = NULL;
815 timestamp = xevent->xkey.time;
816
817 /* Get the keyboard state */
818 display = gdk_display_get_default ();
819 gdk_x11_display_error_trap_push (display);
820 keymap = gdk_keymap_get_for_display (display);
821 mod_mask = gtk_accelerator_get_default_mod_mask ();
822 modifiers = xevent->xkey.state;
823
824 gdk_keymap_translate_keyboard_state (keymap, xevent->xkey.keycode,
825 modifiers,
826 grabber->priv->xkbStateGroup,
827 &keyval, NULL, NULL, &consumed);
828
829 /* We want Alt + Print to be Alt + Print not SysReq. See bug #7897 */
830 if (keyval == GDK_KEY_Sys_Req && (modifiers & GDK_MOD1_MASK) != 0)
831 {
832 consumed = 0;
833 keyval = GDK_KEY_Print;
834 }
835
836 /* Get the modifiers */
837
838 /* If Shift was used when translating the keyboard state, we remove it
839 * from the consumed bit because gtk_accelerator_{name,parse} fail to
840 * handle this correctly. This allows us to have shortcuts with Shift
841 * as a modifier key (see bug #8744). */
842 if ((modifiers & GDK_SHIFT_MASK) && (consumed & GDK_SHIFT_MASK))
843 consumed &= ~GDK_SHIFT_MASK;
844
845 /*
846 * !!! FIX ME !!!
847 * Turn MOD4 into SUPER key press events. Although it is not clear if
848 * this is a proper solution, it fixes bug #10373 which some people
849 * experience without breaking functionality for other users.
850 */
851 if (modifiers & GDK_MOD4_MASK)
852 {
853 modifiers &= ~GDK_MOD4_MASK;
854 modifiers |= GDK_SUPER_MASK;
855 consumed &= ~GDK_MOD4_MASK;
856 consumed &= ~GDK_SUPER_MASK;
857 }
858
859 modifiers &= ~consumed;
860 modifiers &= mod_mask;
861
862 /* Use the keyval and modifiers values of gtk_accelerator_parse. We
863 * will compare them with values we also get from this function and as
864 * it has its own logic, it's easier and safer to do so.
865 * See bug #8744 for a "live" example. */
866 raw_shortcut_name = gtk_accelerator_name (keyval, modifiers);
867 gtk_accelerator_parse (raw_shortcut_name, &context.keyval, &context.modifiers);
868
869 TRACE ("Looking for %s", raw_shortcut_name);
870 g_free (raw_shortcut_name);
871
872 g_hash_table_find (grabber->priv->keys,
873 (GHRFunc) (void (*)(void)) find_event_key,
874 &context);
875
876 if (G_LIKELY (context.result != NULL))
877 /* We had a positive match */
878 g_signal_emit_by_name (grabber, "shortcut-activated",
879 context.result, timestamp);
880
881 gdk_display_flush (display);
882 gdk_x11_display_error_trap_pop_ignored (display);
883
884 return GDK_FILTER_CONTINUE;
885 }
886
887
888
889 XfceShortcutsGrabber *
xfce_shortcuts_grabber_new(void)890 xfce_shortcuts_grabber_new (void)
891 {
892 return g_object_new (XFCE_TYPE_SHORTCUTS_GRABBER, NULL);
893 }
894
895
896
897 void
xfce_shortcuts_grabber_add(XfceShortcutsGrabber * grabber,const gchar * shortcut)898 xfce_shortcuts_grabber_add (XfceShortcutsGrabber *grabber,
899 const gchar *shortcut)
900 {
901 XfceKey *key;
902
903 g_return_if_fail (XFCE_IS_SHORTCUTS_GRABBER (grabber));
904 g_return_if_fail (shortcut != NULL);
905
906 key = g_new0 (XfceKey, 1);
907
908 gtk_accelerator_parse (shortcut, &key->keyval, &key->modifiers);
909 TRACE ("parse %s -> keyval=0x%x, modifiers=0x%x", shortcut, key->keyval, key->modifiers);
910
911 if (G_LIKELY (key->keyval != 0))
912 {
913 xfce_shortcuts_grabber_grab (grabber, key);
914 g_hash_table_insert (grabber->priv->keys, g_strdup (shortcut), key);
915 }
916 else
917 {
918 free_key (key);
919 }
920 }
921
922
923
924 void
xfce_shortcuts_grabber_remove(XfceShortcutsGrabber * grabber,const gchar * shortcut)925 xfce_shortcuts_grabber_remove (XfceShortcutsGrabber *grabber,
926 const gchar *shortcut)
927 {
928 XfceKey *key;
929
930 g_return_if_fail (XFCE_IS_SHORTCUTS_GRABBER (grabber));
931 g_return_if_fail (shortcut != NULL);
932
933 key = g_hash_table_lookup (grabber->priv->keys, shortcut);
934
935 if (G_LIKELY (key != NULL))
936 {
937 xfce_shortcuts_grabber_ungrab (grabber, key);
938 g_hash_table_remove (grabber->priv->keys, shortcut);
939 }
940 }
941