Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 2 Mar 2021 11:01:41 +0000 (UTC)
From:      Guido Falsi <madpilot@FreeBSD.org>
To:        ports-committers@freebsd.org, svn-ports-all@freebsd.org, svn-ports-head@freebsd.org
Subject:   svn commit: r566943 - in head/x11/libxfce4menu: . files
Message-ID:  <202103021101.122B1fEY083998@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: madpilot
Date: Tue Mar  2 11:01:40 2021
New Revision: 566943
URL: https://svnweb.freebsd.org/changeset/ports/566943

Log:
  Import patch from upstream to fix key grabbing issues.
  
  This replaces previous homegroown patch.
  
  PR:		244290
  Submitted by:	aryeh.friedman@gmail.com
  Tested by:	Jethro Nederhof <jethro@jethron.id.au>
  Obtained from:	https://gitlab.xfce.org/xfce/libxfce4ui/-/merge_requests/27
  MFH After:	1 week
  MFH:		2021Q1

Added:
  head/x11/libxfce4menu/files/patch-shortcuts-grabber-fix-PR27
     - copied, changed from r566942, head/x11/libxfce4menu/files/patch-libxfce4kbd-private_xfce-shortcuts-grabber.c
Deleted:
  head/x11/libxfce4menu/files/patch-libxfce4kbd-private_xfce-shortcuts-grabber.c
Modified:
  head/x11/libxfce4menu/Makefile

Modified: head/x11/libxfce4menu/Makefile
==============================================================================
--- head/x11/libxfce4menu/Makefile	Tue Mar  2 10:06:39 2021	(r566942)
+++ head/x11/libxfce4menu/Makefile	Tue Mar  2 11:01:40 2021	(r566943)
@@ -3,7 +3,7 @@
 
 PORTNAME=	libxfce4menu
 PORTVERSION=	4.16.0
-PORTREVISION=	1
+PORTREVISION=	2
 CATEGORIES=	x11 xfce
 MASTER_SITES=	XFCE
 DISTNAME=	libxfce4ui-${DISTVERSIONFULL}

Copied and modified: head/x11/libxfce4menu/files/patch-shortcuts-grabber-fix-PR27 (from r566942, head/x11/libxfce4menu/files/patch-libxfce4kbd-private_xfce-shortcuts-grabber.c)
==============================================================================
--- head/x11/libxfce4menu/files/patch-libxfce4kbd-private_xfce-shortcuts-grabber.c	Tue Mar  2 10:06:39 2021	(r566942, copy source)
+++ head/x11/libxfce4menu/files/patch-shortcuts-grabber-fix-PR27	Tue Mar  2 11:01:40 2021	(r566943)
@@ -1,21 +1,1324 @@
---- libxfce4kbd-private/xfce-shortcuts-grabber.c.orig	2020-11-23 10:16:17 UTC
+From 7c1e0e71899d13f75fe4177454656049d3f35d54 Mon Sep 17 00:00:00 2001
+From: Jan Ziak <0xe2.0x9a.0x9b@gmail.com>
+Date: Mon, 4 Jan 2021 17:01:04 +0100
+Subject: [PATCH 1/5] shortcuts-grabber: Record xkb state group (Bug #33)
+
+XkbGroupForCoreState(xevent->xkey.state) returns 0 even after a keyboard
+layout switch. Instead of using the XkbGroupForCoreState() function, this
+patch watches for XkbStateNotify events from which it obtains the xkb state
+group.
+
+Closes: https://gitlab.xfce.org/xfce/libxfce4ui/-/issues/33
+See also: https://gitlab.xfce.org/xfce/libxfce4ui/-/merge_requests/33
+---
+ libxfce4kbd-private/xfce-shortcuts-grabber.c | 38 ++++++++++++++++----
+ 1 file changed, 31 insertions(+), 7 deletions(-)
+
+diff --git a/libxfce4kbd-private/xfce-shortcuts-grabber.c b/libxfce4kbd-private/xfce-shortcuts-grabber.c
+index 60ddfd7..9df45c3 100644
+--- libxfce4kbd-private/xfce-shortcuts-grabber.c
 +++ libxfce4kbd-private/xfce-shortcuts-grabber.c
-@@ -22,6 +22,8 @@
- #include <config.h>
- #endif
+@@ -61,13 +61,14 @@ static void            xfce_shortcuts_grabber_grab             (XfceShortcutsGra
+                                                                 gboolean                   grab);
+ static GdkFilterReturn xfce_shortcuts_grabber_event_filter     (GdkXEvent                 *gdk_xevent,
+                                                                 GdkEvent                  *event,
+-                                                                XfceShortcutsGrabber      *grabber);
++                                                                gpointer                   data);
  
-+#include <sys/param.h>
+ 
+ 
+ struct _XfceShortcutsGrabberPrivate
+ {
+   GHashTable *keys;
++  gint        xkbEventType, xkbStateGroup;
+ };
+ 
+ typedef enum
+@@ -141,20 +142,26 @@ static void
+ xfce_shortcuts_grabber_constructed (GObject *object)
+ {
+   GdkDisplay *display;
++  Display    *xdisplay;
+   GdkKeymap  *keymap;
+ 
+   XfceShortcutsGrabber *grabber = XFCE_SHORTCUTS_GRABBER (object);
+ 
+   display = gdk_display_get_default ();
++  xdisplay = GDK_DISPLAY_XDISPLAY (display);
+   keymap = gdk_keymap_get_for_display (display);
+   g_signal_connect (keymap, "keys-changed", G_CALLBACK (xfce_shortcuts_grabber_keys_changed),
+                     grabber);
+ 
++  if (G_UNLIKELY (!XkbQueryExtension (xdisplay, 0, &grabber->priv->xkbEventType, 0, 0, 0)))
++    grabber->priv->xkbEventType = -1;
++  grabber->priv->xkbStateGroup = -1;
 +
- #include <glib.h>
- #include <glib-object.h>
+   /* Flush events before adding the event filter */
+-  XAllowEvents (GDK_DISPLAY_XDISPLAY (display), AsyncBoth, CurrentTime);
++  XAllowEvents (xdisplay, AsyncBoth, CurrentTime);
  
-@@ -180,6 +182,9 @@ xfce_shortcuts_grabber_keys_changed (GdkKeymap        
+   /* Add event filter */
+-  gdk_window_add_filter (NULL, (GdkFilterFunc) xfce_shortcuts_grabber_event_filter, grabber);
++  gdk_window_add_filter (NULL, xfce_shortcuts_grabber_event_filter, grabber);
+ }
  
+ 
+@@ -417,10 +424,11 @@ find_event_key (const gchar                *shortcut,
+ 
+ 
+ static GdkFilterReturn
+-xfce_shortcuts_grabber_event_filter (GdkXEvent            *gdk_xevent,
+-                                     GdkEvent             *event,
+-                                     XfceShortcutsGrabber *grabber)
++xfce_shortcuts_grabber_event_filter (GdkXEvent *gdk_xevent,
++                                     GdkEvent  *event,
++                                     gpointer   data)
+ {
++  XfceShortcutsGrabber       *const grabber = data;
+   struct EventKeyFindContext  context;
+   GdkKeymap                  *keymap;
+   GdkModifierType             consumed, modifiers;
+@@ -434,6 +442,22 @@ xfce_shortcuts_grabber_event_filter (GdkXEvent            *gdk_xevent,
+ 
+   xevent = (XEvent *) gdk_xevent;
+ 
++  if (xevent->type == grabber->priv->xkbEventType)
++    {
++      const XkbEvent *e = (const XkbEvent*) xevent;
++      TRACE ("xkb event: any.xkb_type=%d", e->any.xkb_type);
++      if (e->any.xkb_type == XkbStateNotify)
++        {
++          TRACE ("xkb event: any.xkb_type=XkbStateNotify, state.group=%d", e->state.group);
++          if (grabber->priv->xkbStateGroup != e->state.group)
++            {
++              grabber->priv->xkbStateGroup = e->state.group;
++              xfce_shortcuts_grabber_ungrab_all (grabber);
++              xfce_shortcuts_grabber_grab_all (grabber);
++            }
++        }
++    }
++
+   if (xevent->type != KeyPress)
+     return GDK_FILTER_CONTINUE;
+ 
+@@ -450,7 +474,7 @@ xfce_shortcuts_grabber_event_filter (GdkXEvent            *gdk_xevent,
+ 
+   gdk_keymap_translate_keyboard_state (keymap, xevent->xkey.keycode,
+                                        modifiers,
+-                                       XkbGroupForCoreState (xevent->xkey.state),
++                                       grabber->priv->xkbStateGroup,
+                                        &keyval, NULL, NULL, &consumed);
+ 
+   /* We want Alt + Print to be Alt + Print not SysReq. See bug #7897 */
+-- 
+GitLab
+
+
+From 5d34aac693e160de3d22d1700b89664dcac12394 Mon Sep 17 00:00:00 2001
+From: Jan Ziak <0xe2.0x9a.0x9b@gmail.com>
+Date: Fri, 26 Feb 2021 05:27:16 +0100
+Subject: [PATCH 2/5] shortcuts: Fix a memory leak
+
+---
+ libxfce4kbd-private/xfce-shortcuts-provider.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/libxfce4kbd-private/xfce-shortcuts-provider.c b/libxfce4kbd-private/xfce-shortcuts-provider.c
+index b7f7a47..83ab6c0 100644
+--- libxfce4kbd-private/xfce-shortcuts-provider.c
++++ libxfce4kbd-private/xfce-shortcuts-provider.c
+@@ -711,6 +711,7 @@ void
+ xfce_shortcuts_free (GList *shortcuts)
+ {
+   g_list_foreach (shortcuts, (GFunc) (void (*)(void)) xfce_shortcut_free, NULL);
++  g_list_free (shortcuts);
+ }
+ 
+ 
+-- 
+GitLab
+
+
+From c18f068ab2bd69647af6519e389d76728c1f924e Mon Sep 17 00:00:00 2001
+From: Jan Ziak <0xe2.0x9a.0x9b@gmail.com>
+Date: Fri, 26 Feb 2021 05:57:42 +0100
+Subject: [PATCH 3/5] shortcuts-grabber: Stop search when the first match is
+ found
+
+---
+ libxfce4kbd-private/xfce-shortcuts-grabber.c | 14 ++++++--------
+ 1 file changed, 6 insertions(+), 8 deletions(-)
+
+diff --git a/libxfce4kbd-private/xfce-shortcuts-grabber.c b/libxfce4kbd-private/xfce-shortcuts-grabber.c
+index 9df45c3..61f8ef8 100644
+--- libxfce4kbd-private/xfce-shortcuts-grabber.c
++++ libxfce4kbd-private/xfce-shortcuts-grabber.c
+@@ -391,10 +391,9 @@ xfce_shortcuts_grabber_grab (XfceShortcutsGrabber *grabber,
+ 
+ struct EventKeyFindContext
+ {
+-  XfceShortcutsGrabber *grabber;
+-  GdkModifierType       modifiers;
+-  guint                 keyval;
+-  const gchar          *result;
++  GdkModifierType modifiers;
++  guint           keyval;
++  const gchar    *result;
+ };
+ 
+ 
+@@ -461,7 +460,6 @@ xfce_shortcuts_grabber_event_filter (GdkXEvent *gdk_xevent,
+   if (xevent->type != KeyPress)
+     return GDK_FILTER_CONTINUE;
+ 
+-  context.grabber = grabber;
+   context.result = NULL;
+   timestamp = xevent->xkey.time;
+ 
+@@ -520,9 +518,9 @@ xfce_shortcuts_grabber_event_filter (GdkXEvent *gdk_xevent,
+   TRACE ("Looking for %s", raw_shortcut_name);
+   g_free (raw_shortcut_name);
+ 
+-  g_hash_table_foreach (grabber->priv->keys,
+-                        (GHFunc) (void (*)(void)) find_event_key,
+-                        &context);
++  g_hash_table_find (grabber->priv->keys,
++                     (GHRFunc) (void (*)(void)) find_event_key,
++                     &context);
+ 
+   if (G_LIKELY (context.result != NULL))
+     /* We had a positive match */
+-- 
+GitLab
+
+
+From 51deff8231b94f040060f663bcdb1c65d090884e Mon Sep 17 00:00:00 2001
+From: Jan Ziak <0xe2.0x9a.0x9b@gmail.com>
+Date: Fri, 26 Feb 2021 06:52:04 +0100
+Subject: [PATCH 4/5] shortcuts-grabber: Redesign shortcut regrabbing (Bug #33)
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch hopes to fix shortcut grabbing issues related to keyboard
+layouts while maintaining high performance.
+
+The implementation uses a new hash-table for tracking the keycodes grabbed
+from the X server. The grabbed X11/Xorg keys are reference counted:
+X11 XGrabKey() is called once per a keycode+modifiers combination and
+X11 XUngrabKey() is called when the reference count of the combination
+drops to zero. It is common for the reference counts to, for example,
+reach the value of 4 if the user is using 4 keyboard layouts, in which
+case the new implementation will use a single XGrabKey() call compared
+to 4 such calls in previous implementations.
+
+The grab_all() function has been removed and has been replaced by an
+optimized regrab_all() function which is more efficient than the sequence
+ungrab_all()+grab_all().
+
+Tested keyboard layouts:
+ - English
+ - English (Colemak)
+ - French (BÉPO)
+ - Slovak (QWERTY)
+
+Test environments:
+ - Arch Linux (FR-BÉPO)
+ - FreeBSD (basic testing)
+ - Gentoo Linux (EN, EN-Colemak, SK-QWERTY)
+ - Xubuntu (basic testing)
+
+Closes: https://gitlab.xfce.org/xfce/libxfce4ui/-/issues/33
+---
+ libxfce4kbd-private/xfce-shortcuts-grabber.c | 598 ++++++++++++++-----
+ 1 file changed, 449 insertions(+), 149 deletions(-)
+
+diff --git a/libxfce4kbd-private/xfce-shortcuts-grabber.c b/libxfce4kbd-private/xfce-shortcuts-grabber.c
+index 61f8ef8..1de5929 100644
+--- libxfce4kbd-private/xfce-shortcuts-grabber.c
++++ libxfce4kbd-private/xfce-shortcuts-grabber.c
+@@ -54,11 +54,13 @@ static void            xfce_shortcuts_grabber_constructed      (GObject
+ static void            xfce_shortcuts_grabber_finalize         (GObject                   *object);
+ static void            xfce_shortcuts_grabber_keys_changed     (GdkKeymap                 *keymap,
+                                                                 XfceShortcutsGrabber      *grabber);
+-static void            xfce_shortcuts_grabber_grab_all         (XfceShortcutsGrabber      *grabber);
++static void            xfce_shortcuts_grabber_regrab_all       (XfceShortcutsGrabber      *grabber);
+ static void            xfce_shortcuts_grabber_ungrab_all       (XfceShortcutsGrabber      *grabber);
+ static void            xfce_shortcuts_grabber_grab             (XfceShortcutsGrabber      *grabber,
++                                                                XfceKey                   *key);
++static void            xfce_shortcuts_grabber_ungrab           (XfceShortcutsGrabber      *grabber,
+                                                                 XfceKey                   *key,
+-                                                                gboolean                   grab);
++                                                                gboolean                   trace);
+ static GdkFilterReturn xfce_shortcuts_grabber_event_filter     (GdkXEvent                 *gdk_xevent,
+                                                                 GdkEvent                  *event,
+                                                                 gpointer                   data);
+@@ -67,25 +69,37 @@ static GdkFilterReturn xfce_shortcuts_grabber_event_filter     (GdkXEvent
+ 
+ struct _XfceShortcutsGrabberPrivate
+ {
++  /* Maps a shortcut string to a pointer to XfceKey */
+   GHashTable *keys;
+-  gint        xkbEventType, xkbStateGroup;
+-};
+ 
+-typedef enum
+-{
+-  UNDEFINED_GRAB_STATE = 0, /* Initial value after g_new0(XfceKey) */
+-  NOT_GRABBED,
+-  GRABBED,
+-} XfceKeyGrabState;
++  /* Maps an XfceXGrab to a reference count.
++   * The reference count tracks the number of shortcuts that grab the XfceXGrab. */
++  GHashTable *grabbed_keycodes;
++
++  gint xkbEventType, xkbStateGroup;
++};
+ 
+ struct _XfceKey
+ {
+   guint            keyval;
+-  guint            modifiers;
+-  GArray          *keycodes;
+-  XfceKeyGrabState grab_state;
++  GdkModifierType  modifiers;
++
++  /* Information about how the key has been grabbed */
++  guint            n_keys;  /* Equals 0 if the key isn't grabbed */
++  GdkKeymapKey    *keys;
++  GdkModifierType  non_virtual_modifiers;
++  guint            numlock_modifier;
+ };
+ 
++typedef struct
++{
++  guint            keycode;
++  GdkModifierType  non_virtual_modifiers;
++  guint            numlock_modifier;
++} XfceXGrab;
++
++typedef guint XfceXGrabRefcount;
++
+ 
+ 
+ G_DEFINE_TYPE (XfceShortcutsGrabber, xfce_shortcuts_grabber, G_TYPE_OBJECT)
+@@ -117,6 +131,45 @@ xfce_shortcuts_grabber_class_init (XfceShortcutsGrabberClass *klass)
+ 
+ 
+ 
++static void
++free_key (gpointer data)
++{
++  XfceKey *key = data;
++  g_free (key->keys);
++  g_free (key);
++}
++
++static gboolean
++xgrab_equal (gconstpointer data1, gconstpointer data2)
++{
++  const XfceXGrab *a = data1;
++  const XfceXGrab *b = data2;
++
++  if (a == b)
++    return TRUE;
++
++  return a->keycode == b->keycode &&
++         a->non_virtual_modifiers == b->non_virtual_modifiers &&
++         a->numlock_modifier == b->numlock_modifier;
++}
++
++static void
++xgrab_free (gpointer data)
++{
++  XfceXGrab *g = data;
++  g_free (g);
++}
++
++static guint
++xgrab_hash (gconstpointer data)
++{
++  const XfceXGrab *g = data;
++  return g->keycode ^ g->non_virtual_modifiers ^ g->numlock_modifier;
++}
++
++
++
++
+ static void
+ xfce_shortcuts_grabber_init (XfceShortcutsGrabber *grabber)
+ {
+@@ -124,7 +177,8 @@ xfce_shortcuts_grabber_init (XfceShortcutsGrabber *grabber)
+   GdkKeymap       *keymap;
+ 
+   grabber->priv = XFCE_SHORTCUTS_GRABBER_GET_PRIVATE (grabber);
+-  grabber->priv->keys = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
++  grabber->priv->keys = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_key);
++  grabber->priv->grabbed_keycodes = g_hash_table_new_full (xgrab_hash, xgrab_equal, xgrab_free, g_free);
+ 
+   /* Workaround: Make sure modmap is up to date
+    * There is possibly a bug in GTK+ where virtual modifiers are not
+@@ -173,6 +227,7 @@ xfce_shortcuts_grabber_finalize (GObject *object)
+ 
+   xfce_shortcuts_grabber_ungrab_all (grabber);
+   g_hash_table_unref (grabber->priv->keys);
++  g_hash_table_unref (grabber->priv->grabbed_keycodes);
+ 
+   (*G_OBJECT_CLASS (xfce_shortcuts_grabber_parent_class)->finalize) (object);
+ }
+@@ -187,29 +242,73 @@ xfce_shortcuts_grabber_keys_changed (GdkKeymap            *keymap,
+ 
    TRACE ("Keys changed, regrabbing");
  
-+#ifdef __FreeBSD__
-+  xfce_shortcuts_grabber_ungrab_all (grabber);
+-  xfce_shortcuts_grabber_grab_all (grabber);
++  xfce_shortcuts_grabber_regrab_all (grabber);
+ }
+ 
+ 
+ 
+ static gboolean
+-grab_key (const gchar          *shortcut,
+-          XfceKey              *key,
+-          XfceShortcutsGrabber *grabber)
++xfce_shortcuts_grabber_xgrab (XfceXGrab g, gboolean grab)
+ {
+-  xfce_shortcuts_grabber_grab (grabber, key, TRUE);
+-  return FALSE;
+-}
++  GdkDisplay *display = gdk_display_get_default ();
++  Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
++  Window root_window;
++  guint k;
++  gboolean success = TRUE;
++
++  /* Ignorable modifiers */
++  const guint mod_masks [] = {
++    0,
++    GDK_MOD2_MASK,
++    g.numlock_modifier | GDK_MOD2_MASK,
++    GDK_LOCK_MASK,
++    g.numlock_modifier | GDK_LOCK_MASK,
++    GDK_MOD5_MASK,
++    g.numlock_modifier | GDK_MOD5_MASK,
++    GDK_MOD2_MASK | GDK_LOCK_MASK,
++    g.numlock_modifier | GDK_MOD2_MASK | GDK_LOCK_MASK,
++    GDK_MOD2_MASK | GDK_MOD5_MASK,
++    g.numlock_modifier | GDK_MOD2_MASK | GDK_MOD5_MASK,
++    GDK_LOCK_MASK | GDK_MOD5_MASK,
++    g.numlock_modifier | GDK_LOCK_MASK | GDK_MOD5_MASK,
++    GDK_MOD2_MASK | GDK_LOCK_MASK | GDK_MOD5_MASK,
++    g.numlock_modifier | GDK_MOD2_MASK | GDK_LOCK_MASK | GDK_MOD5_MASK,
++  };
++
++  /* Retrieve the root window of the screen */
++  root_window = GDK_WINDOW_XID (gdk_screen_get_root_window (gdk_display_get_default_screen (display)));
++
++  TRACE ("%s keycode %u, non_virtual_modifiers 0x%x",
++         grab ? "Grabbing" : "Ungrabbing",
++         g.keycode, g.non_virtual_modifiers);
+ 
++  gdk_x11_display_error_trap_push (display);
+ 
++  for (k = 0; k < G_N_ELEMENTS (mod_masks); k++)
++    {
++      /* Take ignorable modifiers into account when grabbing/ungrabbing */
++      if (grab)
++        XGrabKey (xdisplay,
++                  g.keycode,
++                  g.non_virtual_modifiers | mod_masks [k],
++                  root_window,
++                  False, GrabModeAsync, GrabModeAsync);
++      else
++        XUngrabKey (xdisplay,
++                    g.keycode,
++                    g.non_virtual_modifiers | mod_masks [k],
++                    root_window);
++    }
+ 
+-static void
+-xfce_shortcuts_grabber_grab_all (XfceShortcutsGrabber *grabber)
+-{
+-  g_return_if_fail (XFCE_IS_SHORTCUTS_GRABBER (grabber));
+-  g_hash_table_foreach (grabber->priv->keys,
+-                        (GHFunc) (void (*)(void)) grab_key,
+-                        grabber);
++  gdk_display_flush (display);
++  if (gdk_x11_display_error_trap_pop (display))
++    {
++      g_warning ("Failed to %s keycode %u",
++                 grab ? "grab" : "ungrab", g.keycode);
++      success = FALSE;
++    }
++
++  return success;
+ }
+ 
+ 
+@@ -219,7 +318,7 @@ ungrab_key (const gchar          *shortcut,
+             XfceKey              *key,
+             XfceShortcutsGrabber *grabber)
+ {
+-  xfce_shortcuts_grabber_grab (grabber, key, FALSE);
++  xfce_shortcuts_grabber_ungrab (grabber, key, TRUE);
+   return FALSE;
+ }
+ 
+@@ -236,155 +335,358 @@ xfce_shortcuts_grabber_ungrab_all (XfceShortcutsGrabber *grabber)
+ 
+ 
+ 
++static gboolean
++get_entries_for_keyval (GdkKeymap     *keymap,
++                        guint          keyval,
++                        GdkKeymapKey **keys,
++                        gint          *n_keys)
++{
++  /* Get all keys generating keyval */
++  if (!gdk_keymap_get_entries_for_keyval (keymap, keyval, keys, n_keys))
++    {
++      TRACE ("Got no keys for keyval");
++      return FALSE;
++    }
++
++  if (G_UNLIKELY (*n_keys <= 0))
++    {
++      g_free (*keys);
++      return FALSE;
++    }
++
++  return TRUE;
++}
++
++
++
++static gboolean
++map_virtual_modifiers (GdkKeymap       *keymap,
++                       GdkModifierType  virtual_modifiers,
++                       GdkModifierType *non_virtual_modifiers)
++{
++  /* Map virtual modifiers to non-virtual modifiers */
++  GdkModifierType non_virtual = virtual_modifiers;
++  if (!gdk_keymap_map_virtual_modifiers (keymap, &non_virtual))
++    return FALSE;
++
++  if (non_virtual == virtual_modifiers &&
++      (GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK) & non_virtual)
++    {
++      TRACE ("Failed to map virtual modifiers");
++      return FALSE;
++    }
++
++  *non_virtual_modifiers = non_virtual;
++  return TRUE;
++}
++
++
++
++
+ static void
+-xfce_shortcuts_grabber_grab (XfceShortcutsGrabber *grabber,
+-                             XfceKey              *key,
+-                             gboolean              grab)
++xfce_shortcuts_grabber_regrab_all (XfceShortcutsGrabber *grabber)
+ {
+-  GdkModifierType  numlock_modifier;
+-  GdkKeymapKey    *keys;
+-  GdkDisplay      *display;
+-  GdkKeymap       *keymap;
+-  gchar           *shortcut_name;
+-  guint            modifiers;
+-  guint            k;
+-  gint             i;
+-  gint             j;
+-  gint             n_keys;
+-  gint             screens;
++  GdkDisplay     *display;
++  Display        *xdisplay;
++  GdkKeymap      *keymap;
++  guint           numlock_modifier;
++  GHashTable     *grabbed_keycodes;
++  GHashTableIter  iter;
++  gpointer        hash_value;
++  guint           n_already_grabbed = 0;
++  guint           n_regrab = 0;
++  XfceKey       **regrab; /* list of keys to re-grab */
++  guint           i;
+ 
+   g_return_if_fail (XFCE_IS_SHORTCUTS_GRABBER (grabber));
+-  g_return_if_fail (key != NULL);
+-
+-  if (key->grab_state == (grab ? GRABBED : NOT_GRABBED)) {
+-    TRACE (grab ? "Key already grabbed" : "Key already ungrabbed");
+-    return;
+-  }
+-  key->grab_state = UNDEFINED_GRAB_STATE;
+ 
+   display = gdk_display_get_default ();
+-  screens = 1;
++  xdisplay = GDK_DISPLAY_XDISPLAY (display);
+   keymap = gdk_keymap_get_for_display (display);
++  numlock_modifier = XkbKeysymToModifiers (xdisplay, GDK_KEY_Num_Lock);
++  grabbed_keycodes = grabber->priv->grabbed_keycodes;
++
++  regrab = g_malloc (g_hash_table_size (grabber->priv->keys) * sizeof (*regrab));
++
++  /* Phase 1: Ungrab all keys that need to be re-grabbed
++   *          and collect them into the 'regrab' list */
++  g_hash_table_iter_init (&iter, grabber->priv->keys);
++  while (g_hash_table_iter_next (&iter, NULL, &hash_value))
++  {
++    XfceKey         *const key = hash_value;
++    GdkKeymapKey    *keys;
++    GdkModifierType  non_virtual_modifiers;
++    gint             n_keys;
++    gboolean         already_grabbed;
++
++    if (!map_virtual_modifiers (keymap, key->modifiers, &non_virtual_modifiers))
++      continue;
++    if (!get_entries_for_keyval (keymap, key->keyval, &keys, &n_keys))
++      continue;
++
++    already_grabbed = TRUE;
++    if (key->n_keys == (guint) n_keys &&
++        key->non_virtual_modifiers == non_virtual_modifiers &&
++        key->numlock_modifier == numlock_modifier)
++      {
++        gint j;
++        for (j = 0; j < n_keys; j++)
++          if (memcmp (&key->keys[j], &keys[j], sizeof(*keys)) != 0)
++            {
++              already_grabbed = FALSE;
++              break;
++            }
++      }
++    else
++      already_grabbed = FALSE;
++
++    if (already_grabbed)
++      {
++        n_already_grabbed++;
++        g_free (keys);
++      }
++    else
++      {
++        /* Undo current X11 grabs of the key */
++        xfce_shortcuts_grabber_ungrab (grabber, key, FALSE);
++
++        /* Set key->keys to the keycodes that need to be grabbed in phase 2 */
++        if (G_UNLIKELY (key->keys))
++        {
++          g_free (key->keys);
++          key->keys = NULL;
++        }
++        key->n_keys = n_keys;
++        if (n_keys != 0)
++          key->keys = keys;
++        else
++          g_free (keys);
++        key->non_virtual_modifiers = non_virtual_modifiers;
++        key->numlock_modifier = numlock_modifier;
++
++        /* Remember to grab the key in phase 2 */
++        regrab[n_regrab++] = key;
++      }
++  }
+ 
+-  /* Map virtual modifiers to non-virtual modifiers */
+-  modifiers = key->modifiers;
+-  gdk_keymap_map_virtual_modifiers (keymap, &modifiers);
++  TRACE ("n_already_grabbed=%u, n_regrab=%u", n_already_grabbed, n_regrab);
++
++  /* Phase 2: Grab all keys that have been stored in the 'regrab' list */
++  for (i = 0; i < n_regrab; i++)
++  {
++    XfceKey *const key = regrab[i];
++    guint    j;
++
++#ifdef DEBUG_TRACE
++    gchar *shortcut_name = gtk_accelerator_name (key->keyval, key->non_virtual_modifiers);
++    TRACE (key->n_keys==0 ? "Grabbing %s" : "Regrabbing %s", shortcut_name);
++    TRACE ("  key->keyval: %d", key->keyval);
++    TRACE ("  key->modifiers: 0x%x", key->modifiers);
++    TRACE ("  key->non_virtual_modifiers: 0x%x", key->non_virtual_modifiers);
++    TRACE ("  key->n_keys: %d", key->n_keys);
++    g_free (shortcut_name);
++    shortcut_name = NULL;
 +#endif
-   xfce_shortcuts_grabber_grab_all (grabber);
+ 
+-  /* Debugging information */
+-  shortcut_name = gtk_accelerator_name (key->keyval, modifiers);
++    /* Grab all hardware keys generating keyval */
++    for (j = 0; j < key->n_keys;)
++      {
++        XfceXGrab g;
++        gpointer  refcount;
++
++        g.keycode = key->keys[j].keycode;
++        g.non_virtual_modifiers = key->non_virtual_modifiers;
++        g.numlock_modifier = key->numlock_modifier;
++        if (!g_hash_table_lookup_extended (grabbed_keycodes, &g, NULL, &refcount))
++          {
++            if (xfce_shortcuts_grabber_xgrab (g, TRUE))
++              {
++                XfceXGrab *g1 = g_new (XfceXGrab, 1);
++                XfceXGrabRefcount *refcount1 = g_new (XfceXGrabRefcount, 1);
++                *g1 = g;
++                *refcount1 = 1;
++                g_hash_table_insert (grabbed_keycodes, g1, refcount1);
++                j++;
++              }
++            else
++              /* Failed to grab key->keys[j], remove it from key->keys */
++              key->keys[j] = key->keys[--key->n_keys];
++          }
++        else
++          {
++            // 'g' has already been grabbed, increment its refcount only
++            XfceXGrabRefcount *refcount1 = refcount;
++            (*refcount1)++;
++            TRACE ("keycode %u, non_virtual_modifiers 0x%x: ++refcount = %u",
++                   g.keycode, g.non_virtual_modifiers, *refcount1);
++            j++;
++          }
++      }
++
++    if (key->n_keys == 0 && key->keys != NULL)
++      {
++        g_free (key->keys);
++        key->keys = NULL;
++      }
++  }
+ 
+-  TRACE (grab ? "Grabbing %s" : "Ungrabbing %s", shortcut_name);
+-  TRACE ("Keyval: %d", key->keyval);
+-  TRACE ("Modifiers: 0x%x", modifiers);
++  g_free (regrab);
++}
+ 
++
++
++static void
++xfce_shortcuts_grabber_grab (XfceShortcutsGrabber *grabber, XfceKey *key)
++{
++  GdkDisplay      *display;
++  Display         *xdisplay;
++  GdkKeymap       *keymap;
++  guint            numlock_modifier;
++  GHashTable      *grabbed_keycodes;
++  GdkKeymapKey    *keys;
++  GdkModifierType  non_virtual_modifiers;
++  gint             i, n_keys;
++#ifdef DEBUG_TRACE
++  gchar           *shortcut_name;
++#endif
++
++  display = gdk_display_get_default ();
++  xdisplay = GDK_DISPLAY_XDISPLAY (display);
++  keymap = gdk_keymap_get_for_display (display);
++  numlock_modifier = XkbKeysymToModifiers (xdisplay, GDK_KEY_Num_Lock);
++  grabbed_keycodes = grabber->priv->grabbed_keycodes;
++
++  if (!map_virtual_modifiers (keymap, key->modifiers, &non_virtual_modifiers))
++    return;
++  if (!get_entries_for_keyval (keymap, key->keyval, &keys, &n_keys))
++    return;
++
++#ifdef DEBUG_TRACE
++  shortcut_name = gtk_accelerator_name (key->keyval, non_virtual_modifiers);
++  TRACE (key->n_keys==0 ? "Grabbing %s" : "Regrabbing %s", shortcut_name);
++  TRACE ("  key->keyval: %d", key->keyval);
++  TRACE ("  key->modifiers: 0x%x", key->modifiers);
++  TRACE ("  non_virtual_modifiers: 0x%x", non_virtual_modifiers);
++  TRACE ("  n_keys: %d", n_keys);
+   g_free (shortcut_name);
++  shortcut_name = NULL;
++#endif
+ 
+-  if (modifiers == key->modifiers &&
+-      (GDK_SUPER_MASK | GDK_HYPER_MASK | GDK_META_MASK) & modifiers)
++  /* Undo old grabs (just in case there are some old grabs) */
++  if (G_UNLIKELY (key->n_keys != 0))
+     {
+-      TRACE ("Failed to map virtual modifiers");
+-      return;
++      g_warning ("keyval %u already grabbed", key->keyval);
++      xfce_shortcuts_grabber_ungrab (grabber, key, TRUE);
+     }
+ 
+-  /* Get all keys generating keyval */
+-  if (!gdk_keymap_get_entries_for_keyval (keymap,key->keyval,
+-                                          &keys, &n_keys))
++  /* Grab all hardware keys generating keyval */
++  for (i = 0; i < n_keys;)
+     {
+-      TRACE ("Got no keys for keyval");
+-      return;
++      XfceXGrab g;
++      gpointer  refcount;
++
++      g.keycode = keys[i].keycode;
++      g.non_virtual_modifiers = non_virtual_modifiers;
++      g.numlock_modifier = numlock_modifier;
++      if (!g_hash_table_lookup_extended (grabbed_keycodes, &g, NULL, &refcount))
++        {
++          if (xfce_shortcuts_grabber_xgrab (g, TRUE))
++            {
++              XfceXGrab *g1 = g_new (XfceXGrab, 1);
++              XfceXGrabRefcount *refcount1 = g_new (XfceXGrabRefcount, 1);
++              *g1 = g;
++              *refcount1 = 1;
++              g_hash_table_insert (grabbed_keycodes, g1, refcount1);
++              TRACE ("[group %d] keycode %u, non_virtual_modifiers 0x%x: refcount := %u",
++                     keys[i].group, g.keycode, g.non_virtual_modifiers, *refcount1);
++              i++;
++            }
++          else
++            /* Failed to grab keys[i], remove it from keys */
++            keys[i] = keys[--n_keys];
++        }
++      else
++        {
++          // 'g' has already been grabbed, increment its refcount only
++          XfceXGrabRefcount *refcount1 = refcount;
++          (*refcount1)++;
++          TRACE ("[group %d] keycode %u, non_virtual_modifiers 0x%x: ++refcount = %u",
++                 keys[i].group, g.keycode, g.non_virtual_modifiers, *refcount1);
++          i++;
++        }
+     }
+ 
+-  if (n_keys == 0)
+-    {
+-      g_free (keys);
++  /* Set key->keys to the list of keys that been succesfully grabbed */
++  g_free (key->keys);
++  key->keys = NULL;
++  key->n_keys = n_keys;
++  if (n_keys != 0)
++    key->keys = keys;
++  else
++    g_free (keys);
++  key->non_virtual_modifiers = non_virtual_modifiers;
++  key->numlock_modifier = numlock_modifier;
++}
+ 
+-      TRACE ("Got 0 keys for keyval");
+-      return;
+-    }
++static void
++xfce_shortcuts_grabber_ungrab (XfceShortcutsGrabber *grabber, XfceKey *key,
++                               gboolean trace)
++{
++  GHashTable *grabbed_keycodes;
++  guint       i;
+ 
+-  numlock_modifier =
+-    XkbKeysymToModifiers (GDK_DISPLAY_XDISPLAY (display), GDK_KEY_Num_Lock);
++  grabbed_keycodes = grabber->priv->grabbed_keycodes;
+ 
+-  key->grab_state = (grab ? GRABBED : NOT_GRABBED);
+-  for (i = 0; i < n_keys; i ++)
++  if (trace)
+     {
+-      /* Grab all hardware keys generating keyval */
++      gchar *shortcut_name = gtk_accelerator_name (key->keyval, key->non_virtual_modifiers);
++      TRACE ("Ungrabbing %s", shortcut_name);
++      TRACE ("  key->keyval: %d", key->keyval);
++      TRACE ("  key->modifiers: 0x%x", key->modifiers);
++      TRACE ("  key->non_virtual_modifiers: 0x%x", key->non_virtual_modifiers);
++      TRACE ("  key->n_keys: %u", key->n_keys);
++      g_free (shortcut_name);
++    }
+ 
+-      TRACE ("Keycode: %d", keys[i].keycode);
++  for (i = 0; i < key->n_keys; i++)
++    {
++      XfceXGrab g;
++      gpointer  refcount;
+ 
+-      for (j = 0; j < screens; j++)
++      g.keycode = key->keys[i].keycode;
++      g.non_virtual_modifiers = key->non_virtual_modifiers;
++      g.numlock_modifier = key->numlock_modifier;
++      if (G_LIKELY (g_hash_table_lookup_extended (grabbed_keycodes, &g, NULL, &refcount)))
+         {
+-          /* Do the grab on all screens */
+-          Window root_window;
+-
+-          /* Ignorable modifiers */
+-          guint mod_masks [] = {
+-            0,
+-            GDK_MOD2_MASK,
+-            numlock_modifier | GDK_MOD2_MASK,
+-            GDK_LOCK_MASK,
+-            numlock_modifier | GDK_LOCK_MASK,
+-            GDK_MOD5_MASK,
+-            numlock_modifier | GDK_MOD5_MASK,
+-            GDK_MOD2_MASK | GDK_LOCK_MASK,
+-            numlock_modifier | GDK_MOD2_MASK | GDK_LOCK_MASK,
+-            GDK_MOD2_MASK | GDK_MOD5_MASK,
+-            numlock_modifier | GDK_MOD2_MASK | GDK_MOD5_MASK,
+-            GDK_LOCK_MASK | GDK_MOD5_MASK,
+-            numlock_modifier | GDK_LOCK_MASK | GDK_MOD5_MASK,
+-            GDK_MOD2_MASK | GDK_LOCK_MASK | GDK_MOD5_MASK,
+-            numlock_modifier | GDK_MOD2_MASK | GDK_LOCK_MASK | GDK_MOD5_MASK,
+-          };
+-
+-          /* Retrieve the root window of the screen */
+-          root_window = GDK_WINDOW_XID (gdk_screen_get_root_window (gdk_display_get_default_screen (display)));
+-          gdk_x11_display_error_trap_push (display);
+-
+-          for (k = 0; k < G_N_ELEMENTS (mod_masks); k++)
++          XfceXGrabRefcount *refcount1 = refcount;
++          if (G_LIKELY (*refcount1 != 0))
+             {
+-              /* Take ignorable modifiers into account when grabbing */
+-              if (grab)
+-                XGrabKey (GDK_DISPLAY_XDISPLAY (display),
+-                          keys[i].keycode,
+-                          modifiers | mod_masks [k],
+-                          root_window,
+-                          False,
+-                          GrabModeAsync,
+-                          GrabModeAsync);
+-              else
++              (*refcount1)--;
++              if (trace)
++                TRACE ("[group %d] keycode %u, non_virtual_modifiers 0x%x: --refcount = %u",
++                       key->keys[i].group, g.keycode, g.non_virtual_modifiers, *refcount1);
++              if(*refcount1 == 0)
+                 {
+-                  if (i >= (gint) key->keycodes->len)
+-                    break;
+-                  XUngrabKey (GDK_DISPLAY_XDISPLAY (display),
+-                              g_array_index (key->keycodes, guint, i),
+-                              modifiers | mod_masks [k],
+-                              root_window);
++                  xfce_shortcuts_grabber_xgrab (g, FALSE);
++                  g_hash_table_remove (grabbed_keycodes, &g);
+                 }
+             }
+-
+-          gdk_display_flush (display);
+-
+-          if (gdk_x11_display_error_trap_pop (display))
++          else
+             {
+-              TRACE (grab ? "Failed to grab" : "Failed to ungrab");
+-              key->grab_state = UNDEFINED_GRAB_STATE;
++              g_warning ("corrupted refcount");
+             }
+         }
+-      /* Remember the old keycode, as we need it to ungrab. */
+-      if (grab)
+-        g_array_append_val (key->keycodes, keys[i].keycode);
+       else
+-        g_array_index (key->keycodes, guint, i) = UINT_MAX;
+-    }
+-
+-  /* Cleanup elements containing UINT_MAX from the key->keycodes array */
+-  for (i = key->keycodes->len - 1; i >= 0; i --)
+-    {
+-      if (g_array_index (key->keycodes, guint, i) == UINT_MAX)
+-        g_array_remove_index_fast (key->keycodes, i);
++        {
++          g_warning ("corrupted hashtable");
++        }
+     }
+ 
+-  g_free (keys);
++  g_free (key->keys);
++  key->keys = NULL;
++  key->n_keys = 0;
++  key->non_virtual_modifiers = 0;
++  key->numlock_modifier = 0;
  }
  
+ 
+@@ -451,8 +753,7 @@ xfce_shortcuts_grabber_event_filter (GdkXEvent *gdk_xevent,
+           if (grabber->priv->xkbStateGroup != e->state.group)
+             {
+               grabber->priv->xkbStateGroup = e->state.group;
+-              xfce_shortcuts_grabber_ungrab_all (grabber);
+-              xfce_shortcuts_grabber_grab_all (grabber);
++              xfce_shortcuts_grabber_regrab_all (grabber);
+             }
+         }
+     }
+@@ -553,19 +854,18 @@ xfce_shortcuts_grabber_add (XfceShortcutsGrabber *grabber,
+   g_return_if_fail (shortcut != NULL);
+ 
+   key = g_new0 (XfceKey, 1);
+-  key->keycodes = g_array_new (FALSE, TRUE, sizeof (guint));
+ 
+   gtk_accelerator_parse (shortcut, &key->keyval, &key->modifiers);
++  TRACE ("parse %s -> keyval=0x%x, modifiers=0x%x", shortcut, key->keyval, key->modifiers);
+ 
+   if (G_LIKELY (key->keyval != 0))
+     {
+-      xfce_shortcuts_grabber_grab (grabber, key, TRUE);
++      xfce_shortcuts_grabber_grab (grabber, key);
+       g_hash_table_insert (grabber->priv->keys, g_strdup (shortcut), key);
+     }
+   else
+     {
+-      g_array_free (key->keycodes, TRUE);
+-      g_free (key);
++      free_key (key);
+     }
+ }
+ 
+@@ -584,7 +884,7 @@ xfce_shortcuts_grabber_remove (XfceShortcutsGrabber *grabber,
+ 
+   if (G_LIKELY (key != NULL))
+     {
+-      xfce_shortcuts_grabber_grab (grabber, key, FALSE);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202103021101.122B1fEY083998>