From 5fbc0ec7e82c9e16e319acb8755a3fb446ef1463 Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Mon, 14 Oct 2024 16:05:52 +0200 Subject: [PATCH] state: Support querying whether virtual mods are consumed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously it was not possible to query the status of virtual modifiers with the following functions: - `xkb_state_mod_index_is_consumed` - `xkb_state_mod_index_is_consumed2` Note that it may *overmatch* if some modifier mappings overlap. For example, the default “us” PC layout maps both “Alt” and “Meta” to the real modifier “Mod1”; thus “Mod1”, “Alt” and “Meta” modifiers will return the same result with these functions. --- .../+query-virtual-modifiers-state.bugfix.md | 2 + include/xkbcommon/xkbcommon.h | 22 ++++- src/state.c | 9 +- test/state.c | 94 ++++++++++++++++++- 4 files changed, 120 insertions(+), 7 deletions(-) diff --git a/changes/api/+query-virtual-modifiers-state.bugfix.md b/changes/api/+query-virtual-modifiers-state.bugfix.md index f2b72fc1..4ec59db0 100644 --- a/changes/api/+query-virtual-modifiers-state.bugfix.md +++ b/changes/api/+query-virtual-modifiers-state.bugfix.md @@ -4,6 +4,8 @@ with *any* modifiers (real *and* virtual): - `xkb_state_mod_indices_are_active` - `xkb_state_mod_name_is_active` - `xkb_state_mod_names_are_active` +- `xkb_state_mod_index_is_consumed` +- `xkb_state_mod_index_is_consumed2` Warning: they may overmatch in case there are overlappings virtual-to-real modifiers mappings. diff --git a/include/xkbcommon/xkbcommon.h b/include/xkbcommon/xkbcommon.h index 097dcbe7..e6a6e8ae 100644 --- a/include/xkbcommon/xkbcommon.h +++ b/include/xkbcommon/xkbcommon.h @@ -1918,10 +1918,12 @@ enum xkb_consumed_mode { * @param key The keycode of the key. * @param mode The consumed modifiers mode to use; see enum description. * - * @returns a mask of the consumed modifiers. + * @returns a mask of the consumed [real modifiers] modifiers. * * @memberof xkb_state * @since 0.7.0 + * + * [real modifiers]: @ref real-modifier-def */ xkb_mod_mask_t xkb_state_key_get_consumed_mods2(struct xkb_state *state, xkb_keycode_t key, @@ -1940,6 +1942,9 @@ xkb_state_key_get_consumed_mods(struct xkb_state *state, xkb_keycode_t key); * Test whether a modifier is consumed by keyboard state translation for * a key. * + * @warning For [virtual modifiers], this function may *overmatch* in case + * there are virtual modifiers with overlapping mappings to [real modifiers]. + * * @param state The keyboard state. * @param key The keycode of the key. * @param idx The index of the modifier to check. @@ -1951,7 +1956,11 @@ xkb_state_key_get_consumed_mods(struct xkb_state *state, xkb_keycode_t key); * @sa xkb_state_mod_mask_remove_consumed() * @sa xkb_state_key_get_consumed_mods() * @memberof xkb_state - * @since 0.7.0 + * @since 0.7.0: Works only with *real* modifiers + * @since 1.8.0: Works also with *virtual* modifiers + * + * [virtual modifiers]: @ref virtual-modifier-def + * [real modifiers]: @ref real-modifier-def */ int xkb_state_mod_index_is_consumed2(struct xkb_state *state, @@ -1962,8 +1971,15 @@ xkb_state_mod_index_is_consumed2(struct xkb_state *state, /** * Same as xkb_state_mod_index_is_consumed2() with mode XKB_CONSUMED_MOD_XKB. * + * @warning For [virtual modifiers], this function may *overmatch* in case + * there are virtual modifiers with overlapping mappings to [real modifiers]. + * * @memberof xkb_state - * @since 0.4.1 + * @since 0.4.1: Works only with *real* modifiers + * @since 1.8.0: Works also with *virtual* modifiers + * + * [virtual modifiers]: @ref virtual-modifier-def + * [real modifiers]: @ref real-modifier-def */ int xkb_state_mod_index_is_consumed(struct xkb_state *state, xkb_keycode_t key, diff --git a/src/state.c b/src/state.c index 651b788b..6d1883eb 100644 --- a/src/state.c +++ b/src/state.c @@ -1547,10 +1547,15 @@ xkb_state_mod_index_is_consumed2(struct xkb_state *state, xkb_keycode_t kc, { const struct xkb_key *key = XkbKey(state->keymap, kc); - if (!key || idx >= xkb_keymap_num_mods(state->keymap)) + if (unlikely(!key || idx >= xkb_keymap_num_mods(state->keymap))) return -1; - return !!((1u << idx) & key_get_consumed(state, key, mode)); + xkb_mod_mask_t mapping = mod_mapping(&state->keymap->mods.mods[idx], idx); + if (!mapping) { + /* Modifier not mapped */ + return 0; + } + return !!((mapping & key_get_consumed(state, key, mode)) == mapping); } XKB_EXPORT int diff --git a/test/state.c b/test/state.c index 3a698d8e..b5041fe6 100644 --- a/test/state.c +++ b/test/state.c @@ -526,6 +526,8 @@ test_consume(struct xkb_keymap *keymap) xkb_mod_index_t ctrl = _xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CTRL); xkb_mod_index_t mod1 = _xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_MOD1); xkb_mod_index_t mod5 = _xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_MOD5); + xkb_mod_index_t alt = _xkb_keymap_mod_get_index(keymap, XKB_VMOD_NAME_ALT); + xkb_mod_index_t meta = _xkb_keymap_mod_get_index(keymap, XKB_VMOD_NAME_META); struct xkb_state *state = xkb_state_new(keymap); assert(state); @@ -550,6 +552,11 @@ test_consume(struct xkb_keymap *keymap) mask = xkb_state_key_get_consumed_mods(state, KEY_ESC + EVDEV_OFFSET); assert(mask == 0); + assert(xkb_state_mod_index_is_consumed(state, KEY_EQUAL + EVDEV_OFFSET, shift) > 0); + assert(xkb_state_mod_index_is_consumed(state, KEY_EQUAL + EVDEV_OFFSET, mod1) == 0); + assert(xkb_state_mod_index_is_consumed(state, KEY_EQUAL + EVDEV_OFFSET, alt) == 0); + assert(xkb_state_mod_index_is_consumed(state, KEY_EQUAL + EVDEV_OFFSET, meta) == 0); + xkb_state_unref(state); /* Test is_consumed() - simple ALPHABETIC type. */ @@ -611,6 +618,11 @@ test_consume(struct xkb_keymap *keymap) mask = xkb_state_key_get_consumed_mods2(state, KEY_F1 + EVDEV_OFFSET, XKB_CONSUMED_MODE_GTK); assert(mask == ((1U << mod1) | (1U << ctrl))); + assert(xkb_state_mod_index_is_consumed(state, KEY_F1 + EVDEV_OFFSET, shift) > 0); + assert(xkb_state_mod_index_is_consumed(state, KEY_F1 + EVDEV_OFFSET, ctrl) > 0); + assert(xkb_state_mod_index_is_consumed(state, KEY_F1 + EVDEV_OFFSET, mod1) > 0); + assert(xkb_state_mod_index_is_consumed(state, KEY_F1 + EVDEV_OFFSET, alt) > 0); + assert(xkb_state_mod_index_is_consumed(state, KEY_F1 + EVDEV_OFFSET, meta) > 0); xkb_state_unref(state); @@ -638,18 +650,28 @@ test_overlapping_mods(struct xkb_context *context) /* Super and Hyper are overlapping (full overlap) */ keymap = test_compile_rules(context, "evdev", NULL, "us", NULL, - "overlapping_modifiers:super_hyper"); + "overlapping_modifiers:super_hyper," + "grp:win_space_toggle"); assert(keymap); + xkb_mod_index_t shiftIdx = _xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_SHIFT); + xkb_mod_index_t capsIdx = _xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CAPS); + xkb_mod_index_t ctrlIdx = _xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CTRL); xkb_mod_index_t mod1Idx = _xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_MOD1); xkb_mod_index_t mod3Idx = _xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_MOD3); xkb_mod_index_t mod4Idx = _xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_MOD4); + xkb_mod_index_t mod5Idx = _xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_MOD5); xkb_mod_index_t altIdx = _xkb_keymap_mod_get_index(keymap, XKB_VMOD_NAME_ALT); xkb_mod_index_t metaIdx = _xkb_keymap_mod_get_index(keymap, XKB_VMOD_NAME_META); xkb_mod_index_t superIdx = _xkb_keymap_mod_get_index(keymap, XKB_VMOD_NAME_SUPER); xkb_mod_index_t hyperIdx = _xkb_keymap_mod_get_index(keymap, XKB_VMOD_NAME_HYPER); + /* Note: not mapped */ + xkb_mod_index_t scrollIdx = _xkb_keymap_mod_get_index(keymap, XKB_VMOD_NAME_SCROLL); + xkb_mod_mask_t shift = (1u << shiftIdx); + xkb_mod_mask_t ctrl = (1u << ctrlIdx); xkb_mod_mask_t mod1 = (1u << mod1Idx); xkb_mod_mask_t mod3 = (1u << mod3Idx); xkb_mod_mask_t mod4 = (1u << mod4Idx); + xkb_mod_mask_t mod5 = (1u << mod5Idx); xkb_mod_mask_t alt = (1u << altIdx); xkb_mod_mask_t meta = (1u << metaIdx); xkb_mod_mask_t super = (1u << superIdx); @@ -682,13 +704,48 @@ test_overlapping_mods(struct xkb_context *context) XKB_STATE_MATCH_ALL, mod3Idx, mod4Idx, superIdx, hyperIdx, XKB_MOD_INVALID) > 0); + assert(xkb_state_key_get_consumed_mods2(state, KEY_F1 + EVDEV_OFFSET, XKB_CONSUMED_MODE_XKB) == + (shift | ctrl | mod1 | mod5)); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, shiftIdx, XKB_CONSUMED_MODE_XKB) > 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, capsIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, ctrlIdx, XKB_CONSUMED_MODE_XKB) > 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, mod1Idx, XKB_CONSUMED_MODE_XKB) > 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, mod5Idx, XKB_CONSUMED_MODE_XKB) > 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, altIdx, XKB_CONSUMED_MODE_XKB) > 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, metaIdx, XKB_CONSUMED_MODE_XKB) > 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, superIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, hyperIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, scrollIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_key_get_consumed_mods2(state, KEY_SPACE + EVDEV_OFFSET, XKB_CONSUMED_MODE_XKB) == mod4); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, shiftIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, capsIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, ctrlIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, mod1Idx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, mod5Idx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, altIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, metaIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, superIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, hyperIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, scrollIdx, XKB_CONSUMED_MODE_XKB) == 0); + xkb_state_update_mask(state, mod4, 0, 0, 0, 0, 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, shiftIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, capsIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, ctrlIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, mod1Idx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, mod5Idx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, altIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, metaIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, superIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, hyperIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, scrollIdx, XKB_CONSUMED_MODE_XKB) == 0); xkb_state_unref(state); xkb_keymap_unref(keymap); /* Super and Hyper are overlapping (full overlap). * Alt overlaps with Meta (incomplete overlap) */ keymap = test_compile_rules(context, "evdev", NULL, "us", NULL, - "overlapping_modifiers:meta"); + "overlapping_modifiers:meta," + "grp:win_space_toggle"); assert(keymap); altIdx = _xkb_keymap_mod_get_index(keymap, XKB_VMOD_NAME_ALT); metaIdx = _xkb_keymap_mod_get_index(keymap, XKB_VMOD_NAME_META); @@ -731,6 +788,28 @@ test_overlapping_mods(struct xkb_context *context) mod1Idx, mod3Idx, mod4Idx, altIdx, metaIdx, superIdx, hyperIdx, XKB_MOD_INVALID) > 0); + assert(xkb_state_key_get_consumed_mods2(state, KEY_F1 + EVDEV_OFFSET, XKB_CONSUMED_MODE_XKB) == + (shift | ctrl | mod1 | mod5)); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, shiftIdx, XKB_CONSUMED_MODE_XKB) > 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, capsIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, ctrlIdx, XKB_CONSUMED_MODE_XKB) > 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, mod1Idx, XKB_CONSUMED_MODE_XKB) > 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, mod5Idx, XKB_CONSUMED_MODE_XKB) > 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, altIdx, XKB_CONSUMED_MODE_XKB) > 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, metaIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, superIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, hyperIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_key_get_consumed_mods2(state, KEY_SPACE + EVDEV_OFFSET, XKB_CONSUMED_MODE_XKB) == + mod4); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, shiftIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, capsIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, ctrlIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, mod1Idx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, mod5Idx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, altIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, metaIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, superIdx, XKB_CONSUMED_MODE_XKB) > 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_SPACE + EVDEV_OFFSET, hyperIdx, XKB_CONSUMED_MODE_XKB) > 0); xkb_state_update_mask(state, mod1, 0, 0, 0, 0, 0); assert(xkb_state_mod_indices_are_active(state, XKB_STATE_MODS_EFFECTIVE, XKB_STATE_MATCH_ANY, @@ -799,6 +878,17 @@ test_overlapping_mods(struct xkb_context *context) mod1Idx, mod3Idx, mod4Idx, altIdx, metaIdx, superIdx, hyperIdx, XKB_MOD_INVALID) > 0); + assert(xkb_state_key_get_consumed_mods2(state, KEY_F1 + EVDEV_OFFSET, XKB_CONSUMED_MODE_XKB) == + (shift | ctrl | mod1 | mod5)); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, shiftIdx, XKB_CONSUMED_MODE_XKB) > 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, capsIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, ctrlIdx, XKB_CONSUMED_MODE_XKB) > 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, mod1Idx, XKB_CONSUMED_MODE_XKB) > 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, mod5Idx, XKB_CONSUMED_MODE_XKB) > 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, altIdx, XKB_CONSUMED_MODE_XKB) > 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, metaIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, superIdx, XKB_CONSUMED_MODE_XKB) == 0); + assert(xkb_state_mod_index_is_consumed2(state, KEY_F1 + EVDEV_OFFSET, hyperIdx, XKB_CONSUMED_MODE_XKB) == 0); xkb_state_update_mask(state, mod1 | mod3, 0, 0, 0, 0, 0); assert(xkb_state_mod_indices_are_active(state, XKB_STATE_MODS_EFFECTIVE, XKB_STATE_MATCH_ANY,