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