state: add GTK consumed modifiers mode
authorRan Benita <ran234@gmail.com>
Sat, 27 Feb 2016 20:31:16 +0000 (22:31 +0200)
committerRan Benita <ran234@gmail.com>
Mon, 31 Oct 2016 10:52:28 +0000 (12:52 +0200)
This is more or less what is implemented here:
https://git.gnome.org/browse/gtk+/tree/gdk/x11/gdkkeys-x11.c?h=3.19.10#n1131

The implementation here is more technically correct but should provide
the same results.

Try it out with ./test/interactive-evdev -g (modifiers prefixed with "-"
are consumed).

https://bugzilla.gnome.org/show_bug.cgi?id=754110
https://github.com/xkbcommon/libxkbcommon/issues/17

Signed-off-by: Ran Benita <ran234@gmail.com>
src/state.c
test/interactive-evdev.c
test/state.c
xkbcommon/xkbcommon.h

index 6611d33..039115a 100644 (file)
@@ -1340,6 +1340,30 @@ key_get_consumed(struct xkb_state *state, const struct xkb_key *key,
     case XKB_CONSUMED_MODE_XKB:
         consumed = type->mods.mask;
         break;
+
+    case XKB_CONSUMED_MODE_GTK: {
+        const struct xkb_key_type_entry *no_mods_entry;
+        xkb_level_index_t no_mods_leveli;
+        const struct xkb_level *no_mods_level, *level;
+
+        no_mods_entry = get_entry_for_mods(type, 0);
+        no_mods_leveli = no_mods_entry ? no_mods_entry->level : 0;
+        no_mods_level = &key->groups[group].levels[no_mods_leveli];
+
+        for (unsigned i = 0; i < type->num_entries; i++) {
+            const struct xkb_key_type_entry *entry = &type->entries[i];
+            if (!entry_is_active(entry))
+                continue;
+
+            level = &key->groups[group].levels[entry->level];
+            if (XkbLevelsSameSyms(level, no_mods_level))
+                continue;
+
+            if (entry == matching_entry || popcount(entry->mods.mask) == 1)
+                consumed |= entry->mods.mask & ~entry->preserve.mask;
+        }
+        break;
+    }
     }
 
     return consumed & ~preserve;
@@ -1386,6 +1410,7 @@ xkb_state_key_get_consumed_mods2(struct xkb_state *state, xkb_keycode_t kc,
 
     switch (mode) {
     case XKB_CONSUMED_MODE_XKB:
+    case XKB_CONSUMED_MODE_GTK:
         break;
     default:
         log_err_func(state->keymap->ctx,
index 0ba3eab..4f12e67 100644 (file)
@@ -48,6 +48,7 @@ static bool terminate;
 static int evdev_offset = 8;
 static bool report_state_changes;
 static bool with_compose;
+static enum xkb_consumed_mode consumed_mode = XKB_CONSUMED_MODE_XKB;
 
 #define NLONGS(n) (((n) + LONG_BIT - 1) / LONG_BIT)
 
@@ -261,7 +262,7 @@ process_event(struct keyboard *kbd, uint16_t type, uint16_t code, int32_t value)
 
     if (value != KEY_STATE_RELEASE)
         test_print_keycode_state(kbd->state, kbd->compose_state, keycode,
-                                 XKB_CONSUMED_MODE_XKB);
+                                 consumed_mode);
 
     if (with_compose) {
         status = xkb_compose_state_get_status(kbd->compose_state);
@@ -382,7 +383,7 @@ main(int argc, char *argv[])
 
     setlocale(LC_ALL, "");
 
-    while ((opt = getopt(argc, argv, "r:m:l:v:o:k:n:cd")) != -1) {
+    while ((opt = getopt(argc, argv, "r:m:l:v:o:k:n:cdg")) != -1) {
         switch (opt) {
         case 'r':
             rules = optarg;
@@ -416,6 +417,9 @@ main(int argc, char *argv[])
         case 'd':
             with_compose = true;
             break;
+        case 'g':
+            consumed_mode = XKB_CONSUMED_MODE_GTK;
+            break;
         case '?':
             fprintf(stderr, "   Usage: %s [-r <rules>] [-m <model>] "
                     "[-l <layout>] [-v <variant>] [-o <options>]\n",
@@ -424,7 +428,8 @@ main(int argc, char *argv[])
                     argv[0]);
             fprintf(stderr, "For both: -n <evdev keycode offset>\n"
                             "          -c (to report changes to the state)\n"
-                            "          -d (to enable compose)\n");
+                            "          -d (to enable compose)\n"
+                            "          -g (to use GTK consumed mode)\n");
             exit(2);
         }
     }
index d774496..1f2c75d 100644 (file)
@@ -460,6 +460,41 @@ test_consume(struct xkb_keymap *keymap)
     assert(mask == ((1U << shift) | (1U << alt) | (1U << ctrl) | (1U << mod5)));
 
     xkb_state_unref(state);
+
+    /* Test XKB_CONSUMED_MODE_GTK, CTRL+ALT */
+    state = xkb_state_new(keymap);
+    assert(state);
+
+    mask = xkb_state_key_get_consumed_mods2(state, KEY_F1 + EVDEV_OFFSET,
+                                            XKB_CONSUMED_MODE_GTK);
+    assert(mask == 0);
+
+    xkb_state_update_key(state, KEY_LEFTCTRL + EVDEV_OFFSET, XKB_KEY_DOWN);
+    mask = xkb_state_key_get_consumed_mods2(state, KEY_F1 + EVDEV_OFFSET,
+                                            XKB_CONSUMED_MODE_GTK);
+    assert(mask == 0);
+
+    xkb_state_update_key(state, KEY_LEFTALT + EVDEV_OFFSET, XKB_KEY_DOWN);
+    mask = xkb_state_key_get_consumed_mods2(state, KEY_F1 + EVDEV_OFFSET,
+                                            XKB_CONSUMED_MODE_GTK);
+    assert(mask == ((1U << alt) | (1U << ctrl)));
+
+    xkb_state_unref(state);
+
+    /* Test XKB_CONSUMED_MODE_GTK, Simple Shift */
+    state = xkb_state_new(keymap);
+    assert(state);
+
+    mask = xkb_state_key_get_consumed_mods2(state, KEY_A + EVDEV_OFFSET,
+                                            XKB_CONSUMED_MODE_GTK);
+    assert(mask == ((1U << shift) | (1U << caps)));
+
+    xkb_state_update_key(state, KEY_LEFTALT + EVDEV_OFFSET, XKB_KEY_DOWN);
+    mask = xkb_state_key_get_consumed_mods2(state, KEY_A + EVDEV_OFFSET,
+                                            XKB_CONSUMED_MODE_GTK);
+    assert(mask == ((1U << shift) | (1U << caps)));
+
+    xkb_state_unref(state);
 }
 
 static void
index 30c0508..4902dc4 100644 (file)
@@ -1687,7 +1687,22 @@ enum xkb_consumed_mode {
      * even though they are not active, since if they *were* active they would
      * have affected key translation.
      */
-    XKB_CONSUMED_MODE_XKB
+    XKB_CONSUMED_MODE_XKB,
+    /**
+     * This is the mode used by the GTK+ toolkit.
+     *
+     * The mode consists of the following two heuristics:
+     *
+     * - The active set of modifiers, excluding modifiers which do not affect
+     *   the key (as described above), are considered consumed, if they result
+     *   in different keysyms being produced than when no modifiers are active.
+     *
+     * - Additionally, a single modifier is considered consumed if, were it the
+     *   only active modifier affecting the key (as described above), it would
+     *   result in different keysyms being produced than when no modifiers are
+     *   active.
+     */
+    XKB_CONSUMED_MODE_GTK
 };
 
 /**