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>