After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 725588 - keybindings: Keep keybindings in an hash table instead of an array
keybindings: Keep keybindings in an hash table instead of an array
Status: RESOLVED FIXED
Product: mutter
Classification: Core
Component: general
unspecified
Other All
: Normal normal
: ---
Assigned To: mutter-maint
mutter-maint
Depends on:
Blocks:
 
 
Reported: 2014-03-03 15:43 UTC by Rui Matos
Modified: 2014-03-04 14:44 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
keybindings: Make MetaKeyPref, MetaKeyCombo and MetaKeyHandler private (4.74 KB, patch)
2014-03-03 15:43 UTC, Rui Matos
committed Details | Review
keybindings: Rename MetaKeyPref.bindings to MetaKeyPref.combos (4.25 KB, patch)
2014-03-03 15:43 UTC, Rui Matos
committed Details | Review
keybindings: Keep keybindings in an hash table instead of an array (24.01 KB, patch)
2014-03-03 15:44 UTC, Rui Matos
reviewed Details | Review
keybinding: Use display_get_keybinding() instead of looping explicitly (7.12 KB, patch)
2014-03-03 18:42 UTC, Rui Matos
committed Details | Review
keybindings: Keep keybindings in an hash table instead of an array (19.64 KB, patch)
2014-03-03 18:43 UTC, Rui Matos
committed Details | Review

Description Rui Matos 2014-03-03 15:43:48 UTC
This applies to both master and wayland (with a minor change in
process_event() so I'm only attaching the master version here).

It paves the way for proper keybinding support as a wayland compositor
(stop relying on the X keymap, etc.) which will come a bit later.
Comment 1 Rui Matos 2014-03-03 15:43:50 UTC
Created attachment 270814 [details] [review]
keybindings: Make MetaKeyPref, MetaKeyCombo and MetaKeyHandler private

There's no need for these to be public and keeping them all together
in the same header makes it easier when reading the code.
Comment 2 Rui Matos 2014-03-03 15:43:56 UTC
Created attachment 270815 [details] [review]
keybindings: Rename MetaKeyPref.bindings to MetaKeyPref.combos

Let's call it what it really is, otherwise it's just confusing since
MetaKeyBinding also exists.
Comment 3 Rui Matos 2014-03-03 15:44:02 UTC
Created attachment 270816 [details] [review]
keybindings: Keep keybindings in an hash table instead of an array

This allows us to look for a match with an O(1) search instead of O(n)
which is nice, particularly when running as a wayland compositor in
which case we have to do this search for every key press event (as
opposed to only when our passive grab triggers in the X compositor
case).

We actually need two hash tables. On one we keep all the keybindings
themselves which allows us to add external grabs without constantly
re-allocating the array we were using previously.

The other hash table is an index of the keybindings in the first table
by their keycodes and mask which is how we actually match the key
press events. This second table thus needs to be rebuilt when the
keymap changes since keycodes have to be resolved then but since we're
only keeping pointers to the first table it's a fast operation.
Comment 4 Jasper St. Pierre (not reading bugmail) 2014-03-03 15:51:24 UTC
Review of attachment 270815 [details] [review]:

OK.
Comment 5 Jasper St. Pierre (not reading bugmail) 2014-03-03 15:51:59 UTC
Review of attachment 270814 [details] [review]:

Yeah.
Comment 6 Jasper St. Pierre (not reading bugmail) 2014-03-03 16:12:38 UTC
Review of attachment 270816 [details] [review]:

::: src/core/keybindings.c
@@ +165,3 @@
+{
+  guint32 key = keycode & 0xffff;
+  return (key << 16) | (mask & 0xffff);

We take a 32-bit keycode and 32-bit mask, and then truncate them to 16-bits. Is it a guarantee that keycodes and masks will only ever be 16-bits? Can we make key_binding_key take uint16_t's instead?

@@ +1101,3 @@
 }
 
+struct ChangeKeygrabData

Please use typedef struct { ... } ChangeKeygrabData;

@@ +1114,3 @@
+                        gpointer data)
+{
+  struct ChangeKeygrabData *d = data;

Can you change this to data = user_data;

@@ +1329,3 @@
 
+  g_hash_table_add (display->key_bindings, binding);
+  foreach_binding_index (binding, binding, display);

If you want to use the foreach function outside of a function, please extract the common functionality. I thought this would actually iterate over all the bindings and rebuild the index.

So, we'd have:

    static void
    index_binding (MetaDisplay    *display,
                   MetaKeyBinding *binding)
    {
      /* ... */
    }

    static void
    foreach_binding_index (gpointer key,
                           gpointer value,
                           gpointer data)
    {
      MetaDisplay *display = META_DISPLAY (data);
      MetaKeyBinding *binding = (MetaKeyBinding *) value;
      index_binding (display, binding);
    }

(In a related note, I don't like the "foreach_" naming convention, because they imply an iteration. Perhaps index_binding_foreach is a bit better?)

@@ +1648,1 @@
                MetaScreen           *screen,

I wonder if the bindings / n_bindings cleanup makes sense as a prereq patch as well, considering it already has the MetaDisplay.

@@ +1657,3 @@
     return FALSE;
 
+  binding = display_get_keybinding (display,

I wonder if all the display_get_keybinding cleanups would make sense split out. Would certainly make the patch easier to read for me.

@@ +1696,2 @@
   meta_topic (META_DEBUG_KEYBINDINGS,
               "No handler found for this event in this binding table\n");

Heh, staring at it now, if we filter out a compositor keybinding, we'll log this.

We should probably remove this log, since it will probably drive us up the wall in a Wayland world where this is run on every event.

Also, the "not_found" label name doesn't really make sense in the compositor-filtered case. I can't really think of another one, though.

Perhaps break it out into a separate if statement and add a comment?

    /* If the compositor filtered out the keybindings, that
     * means they don't want the binding to trigger, so we do
     * the same thing as if the binding didn't exist. */
    if (meta_compositor_filter_keybinding (...))
      goto out;

I'll do that as a follow-up patch, since it was already like this to begin with.
Comment 7 Rui Matos 2014-03-03 18:42:28 UTC
Created attachment 270834 [details] [review]
keybinding: Use display_get_keybinding() instead of looping explicitly
Comment 8 Rui Matos 2014-03-03 18:43:03 UTC
Created attachment 270835 [details] [review]
keybindings: Keep keybindings in an hash table instead of an array
Comment 9 Rui Matos 2014-03-03 18:48:15 UTC
(In reply to comment #6)
> We take a 32-bit keycode and 32-bit mask, and then truncate them to 16-bits. Is
> it a guarantee that keycodes and masks will only ever be 16-bits? Can we make
> key_binding_key take uint16_t's instead?

I added a comment that hopefully explains the rationale. Also, I'd prefer to keep the arguments there as guint32 so that the whole concatenation is explicit and kept in a single place.

Note that Clutter events use a 16 bit value for keycodes and an enum for the modifiers (which is likely 32 bits since the biggest value in the enum is 1 << 30). XIDeviceEvent keeps both the keycode and the modifiers mask in 32 bit values.

> I wonder if the bindings / n_bindings cleanup makes sense as a prereq patch as
> well, considering it already has the MetaDisplay.

This one would just create pointless churn on its own, see rebuild_binding_table() for why.

> @@ +1657,3 @@
>      return FALSE;
> 
> +  binding = display_get_keybinding (display,
> 
> I wonder if all the display_get_keybinding cleanups would make sense split out.
> Would certainly make the patch easier to read for me.

Yeah, this makes sense. I've split it out now.
 
> @@ +1696,2 @@
>    meta_topic (META_DEBUG_KEYBINDINGS,
>                "No handler found for this event in this binding table\n");
> 
> Heh, staring at it now, if we filter out a compositor keybinding, we'll log
> this.
> 
> We should probably remove this log, since it will probably drive us up the wall
> in a Wayland world where this is run on every event.
> 
> Also, the "not_found" label name doesn't really make sense in the
> compositor-filtered case. I can't really think of another one, though.
> 
> Perhaps break it out into a separate if statement and add a comment?
> 
>     /* If the compositor filtered out the keybindings, that
>      * means they don't want the binding to trigger, so we do
>      * the same thing as if the binding didn't exist. */
>     if (meta_compositor_filter_keybinding (...))
>       goto out;
> 
> I'll do that as a follow-up patch, since it was already like this to begin
> with.

That makes sense. Thanks
Comment 10 Jasper St. Pierre (not reading bugmail) 2014-03-03 23:05:43 UTC
Review of attachment 270834 [details] [review]:

A slightly expanded commit message here would be nice:

    Instead of looping over an array of keybindings to find the
    correct binding, just use display_get_keybinding().

    In the next commit, we'll change the array to be a hash map,
    so this helps the patch be cleaner.
Comment 11 Jasper St. Pierre (not reading bugmail) 2014-03-03 23:06:11 UTC
Review of attachment 270835 [details] [review]:

Looks good.
Comment 12 Rui Matos 2014-03-04 14:44:05 UTC
Ammended the commit msg and pushed, thanks

Attachment 270814 [details] pushed as 91389c8 - keybindings: Make MetaKeyPref, MetaKeyCombo and MetaKeyHandler private
Attachment 270815 [details] pushed as 2b3fc74 - keybindings: Rename MetaKeyPref.bindings to MetaKeyPref.combos
Attachment 270834 [details] pushed as 266ac00 - keybindings: Use display_get_keybinding() instead of looping explicitly
Attachment 270835 [details] pushed as 7e8833a - keybindings: Keep keybindings in an hash table instead of an array