state: add GTK consumed modifiers mode 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>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
diff --git a/src/state.c b/src/state.c
index 6611d33..039115a 100644
--- a/src/state.c
+++ b/src/state.c
@@ -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,
diff --git a/test/interactive-evdev.c b/test/interactive-evdev.c
index 0ba3eab..4f12e67 100644
--- a/test/interactive-evdev.c
+++ b/test/interactive-evdev.c
@@ -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);
}
}
diff --git a/test/state.c b/test/state.c
index d774496..1f2c75d 100644
--- a/test/state.c
+++ b/test/state.c
@@ -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
diff --git a/xkbcommon/xkbcommon.h b/xkbcommon/xkbcommon.h
index 30c0508..4902dc4 100644
--- a/xkbcommon/xkbcommon.h
+++ b/xkbcommon/xkbcommon.h
@@ -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
};
/**