diff --git a/changes/api/+query-virtual-modifiers-state.bugfix.md b/changes/api/+query-virtual-modifiers-state.bugfix.md new file mode 100644 index 00000000..f2b72fc1 --- /dev/null +++ b/changes/api/+query-virtual-modifiers-state.bugfix.md @@ -0,0 +1,9 @@ +The following functions now allow to query also *virtual* modifiers, so they work +with *any* modifiers (real *and* virtual): +- `xkb_state_mod_index_is_active` +- `xkb_state_mod_indices_are_active` +- `xkb_state_mod_name_is_active` +- `xkb_state_mod_names_are_active` + +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 219d197b..60067f58 100644 --- a/include/xkbcommon/xkbcommon.h +++ b/include/xkbcommon/xkbcommon.h @@ -1706,10 +1706,18 @@ xkb_state_serialize_layout(struct xkb_state *state, /** * Test whether a modifier is active in a given keyboard state by name. * + * @warning For [virtual modifiers], this function may *overmatch* in case + * there are virtual modifiers with overlapping mappings to real modifiers. + * * @returns 1 if the modifier is active, 0 if it is not. If the modifier * name does not exist in the keymap, returns -1. * * @memberof xkb_state + * + * @since 0.1.0: Works only with *real* modifiers + * @since 1.8.0: Works also with *virtual* modifiers + * + * [virtual modifiers]: @ref virtual-modifier-def */ int xkb_state_mod_name_is_active(struct xkb_state *state, const char *name, @@ -1719,6 +1727,9 @@ xkb_state_mod_name_is_active(struct xkb_state *state, const char *name, * Test whether a set of modifiers are active in a given keyboard state by * name. * + * @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 type The component of the state against which to match the * given modifiers. @@ -1731,6 +1742,11 @@ xkb_state_mod_name_is_active(struct xkb_state *state, const char *name, * the modifier names do not exist in the keymap, returns -1. * * @memberof xkb_state + * + * @since 0.1.0: Works only with *real* modifiers + * @since 1.8.0: Works also with *virtual* modifiers + * + * [virtual modifiers]: @ref virtual-modifier-def */ int xkb_state_mod_names_are_active(struct xkb_state *state, @@ -1741,10 +1757,18 @@ xkb_state_mod_names_are_active(struct xkb_state *state, /** * Test whether a modifier is active in a given keyboard state by index. * + * @warning For [virtual modifiers], this function may *overmatch* in case + * there are virtual modifiers with overlapping mappings to real modifiers. + * * @returns 1 if the modifier is active, 0 if it is not. If the modifier * index is invalid in the keymap, returns -1. * * @memberof xkb_state + * + * @since 0.1.0: Works only with *real* modifiers + * @since 1.8.0: Works also with *virtual* modifiers + * + * [virtual modifiers]: @ref virtual-modifier-def */ int xkb_state_mod_index_is_active(struct xkb_state *state, xkb_mod_index_t idx, @@ -1754,6 +1778,9 @@ xkb_state_mod_index_is_active(struct xkb_state *state, xkb_mod_index_t idx, * Test whether a set of modifiers are active in a given keyboard state by * index. * + * @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 type The component of the state against which to match the * given modifiers. @@ -1766,6 +1793,11 @@ xkb_state_mod_index_is_active(struct xkb_state *state, xkb_mod_index_t idx, * the modifier indices are invalid in the keymap, returns -1. * * @memberof xkb_state + * + * @since 0.1.0: Works only with *real* modifiers + * @since 1.8.0: Works also with *virtual* modifiers + * + * [virtual modifiers]: @ref virtual-modifier-def */ int xkb_state_mod_indices_are_active(struct xkb_state *state, diff --git a/src/state.c b/src/state.c index 287f0eef..5001a5bf 100644 --- a/src/state.c +++ b/src/state.c @@ -1282,6 +1282,11 @@ mod_mask_get_effective(struct xkb_keymap *keymap, xkb_mod_mask_t mods) return mask; } +static inline bool +is_real_mod(struct xkb_keymap *keymap, xkb_mod_index_t mod) { + return keymap->mods.mods[mod].type & MOD_REAL; +} + /** * Returns 1 if the given modifier is active with the specified type(s), 0 if * not, or -1 if the modifier is invalid. @@ -1294,7 +1299,13 @@ xkb_state_mod_index_is_active(struct xkb_state *state, if (idx >= xkb_keymap_num_mods(state->keymap)) return -1; - return !!(xkb_state_serialize_mods(state, type) & (1u << idx)); + if (is_real_mod(state->keymap, idx)) { + return !!(xkb_state_serialize_mods(state, type) & (1u << idx)); + } else { + /* WARNING: this may overmatch */ + xkb_mod_mask_t mapping = state->keymap->mods.mods[idx].mapping; + return !!((xkb_state_serialize_mods(state, type) & mapping) == mapping); + } } /** @@ -1318,6 +1329,19 @@ match_mod_masks(struct xkb_state *state, return (active & wanted) == wanted; } +/* + * Get the mapping of a modifier. + * We cannot use `mods.mods[idx].mapping` directly, because it is + * not set for real modifiers. + */ +static inline xkb_mod_mask_t +get_mod_mapping(struct xkb_keymap *keymap, xkb_mod_index_t idx) +{ + return is_real_mod(keymap, idx) + ? (1u << idx) + : keymap->mods.mods[idx].mapping; +} + /** * Returns 1 if the modifiers are active with the specified type(s), 0 if * not, or -1 if any of the modifiers are invalid. @@ -1342,7 +1366,7 @@ xkb_state_mod_indices_are_active(struct xkb_state *state, ret = -1; break; } - wanted |= (1u << idx); + wanted |= get_mod_mapping(state->keymap, idx); } va_end(ap); @@ -1393,7 +1417,7 @@ xkb_state_mod_names_are_active(struct xkb_state *state, ret = -1; break; } - wanted |= (1u << idx); + wanted |= get_mod_mapping(state->keymap, idx); } va_end(ap); diff --git a/test/state.c b/test/state.c index c44f28ad..eb8fb62a 100644 --- a/test/state.c +++ b/test/state.c @@ -126,32 +126,68 @@ test_update_key(struct xkb_keymap *keymap) print_state(state); assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_DEPRESSED) > 0); + assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_MOD1, + XKB_STATE_MODS_DEPRESSED) > 0); assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT, XKB_STATE_MODS_DEPRESSED) > 0); + assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_META, + XKB_STATE_MODS_DEPRESSED) > 0); assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED, XKB_STATE_MATCH_ALL, XKB_MOD_NAME_CTRL, + XKB_MOD_NAME_MOD1, XKB_MOD_NAME_ALT, + XKB_MOD_NAME_META, NULL) > 0); assert(xkb_state_mod_indices_are_active(state, XKB_STATE_MODS_DEPRESSED, XKB_STATE_MATCH_ALL, xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CTRL), + xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_MOD1), xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_ALT), + xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_META), XKB_MOD_INVALID) > 0); + assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED, + XKB_STATE_MATCH_ALL, + XKB_MOD_NAME_MOD1, + NULL) == 0); assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED, XKB_STATE_MATCH_ALL, XKB_MOD_NAME_ALT, NULL) == 0); + assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED, + XKB_STATE_MATCH_ALL, + XKB_MOD_NAME_META, + NULL) == 0); + assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED, + XKB_STATE_MATCH_ALL | + XKB_STATE_MATCH_NON_EXCLUSIVE, + XKB_MOD_NAME_MOD1, + NULL) > 0); assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED, XKB_STATE_MATCH_ALL | XKB_STATE_MATCH_NON_EXCLUSIVE, XKB_MOD_NAME_ALT, NULL) > 0); + assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED, + XKB_STATE_MATCH_ALL | + XKB_STATE_MATCH_NON_EXCLUSIVE, + XKB_MOD_NAME_META, + NULL) > 0); + assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED, + (XKB_STATE_MATCH_ANY | + XKB_STATE_MATCH_NON_EXCLUSIVE), + XKB_MOD_NAME_MOD1, + NULL) > 0); assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED, (XKB_STATE_MATCH_ANY | XKB_STATE_MATCH_NON_EXCLUSIVE), XKB_MOD_NAME_ALT, NULL) > 0); + assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED, + (XKB_STATE_MATCH_ANY | + XKB_STATE_MATCH_NON_EXCLUSIVE), + XKB_MOD_NAME_META, + NULL) > 0); /* RAlt down */ xkb_state_update_key(state, KEY_LEFTCTRL + EVDEV_OFFSET, XKB_KEY_UP); @@ -159,23 +195,35 @@ test_update_key(struct xkb_keymap *keymap) print_state(state); assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) == 0); + assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_MOD1, + XKB_STATE_MODS_DEPRESSED) > 0); assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT, XKB_STATE_MODS_DEPRESSED) > 0); + assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_META, + XKB_STATE_MODS_DEPRESSED) > 0); assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED, XKB_STATE_MATCH_ANY, XKB_MOD_NAME_CTRL, + XKB_MOD_NAME_MOD1, XKB_MOD_NAME_ALT, + XKB_MOD_NAME_META, NULL) > 0); assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_LATCHED, XKB_STATE_MATCH_ANY, XKB_MOD_NAME_CTRL, + XKB_MOD_NAME_MOD1, XKB_MOD_NAME_ALT, + XKB_MOD_NAME_META, NULL) == 0); /* none down */ xkb_state_update_key(state, KEY_RIGHTALT + EVDEV_OFFSET, XKB_KEY_UP); + assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_MOD1, + XKB_STATE_MODS_EFFECTIVE) == 0); assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) == 0); + assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_META, + XKB_STATE_MODS_EFFECTIVE) == 0); /* Caps locked */ xkb_state_update_key(state, KEY_CAPSLOCK + EVDEV_OFFSET, XKB_KEY_DOWN); @@ -201,6 +249,8 @@ test_update_key(struct xkb_keymap *keymap) XKB_STATE_MODS_LOCKED) > 0); assert(xkb_state_mod_name_is_active(state, "Mod2", XKB_STATE_MODS_LOCKED) > 0); + assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_NUM, + XKB_STATE_MODS_LOCKED) > 0); num_syms = xkb_state_key_get_syms(state, KEY_KP1 + EVDEV_OFFSET, &syms); assert(num_syms == 1 && syms[0] == XKB_KEY_KP_1); assert(xkb_state_led_name_is_active(state, XKB_LED_NAME_NUM) > 0); @@ -302,7 +352,7 @@ static void test_update_mask_mods(struct xkb_keymap *keymap) { struct xkb_state *state = xkb_state_new(keymap); - xkb_mod_index_t caps, shift, num, alt, mod1, mod2; + xkb_mod_index_t caps, shift, num, alt, meta, mod1, mod2; enum xkb_state_component changed; assert(state); @@ -315,6 +365,8 @@ test_update_mask_mods(struct xkb_keymap *keymap) assert(num != XKB_MOD_INVALID); alt = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_ALT); assert(alt != XKB_MOD_INVALID); + meta = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_META); + assert(meta != XKB_MOD_INVALID); mod1 = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_MOD1); assert(mod1 != XKB_MOD_INVALID); mod2 = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_MOD2); @@ -346,6 +398,11 @@ test_update_mask_mods(struct xkb_keymap *keymap) assert(xkb_state_serialize_mods(state, XKB_STATE_MODS_EFFECTIVE) == ((1u << alt) | (1u << mod1))); + changed = xkb_state_update_mask(state, (1 << meta), 0, 0, 0, 0, 0); + assert(changed == (XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_EFFECTIVE)); + assert(xkb_state_serialize_mods(state, XKB_STATE_MODS_EFFECTIVE) == + ((1u << meta) | (1u << mod1))); + changed = xkb_state_update_mask(state, 0, 0, (1 << num), 0, 0, 0); assert(changed == (XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LOCKED | XKB_STATE_MODS_EFFECTIVE | XKB_STATE_LEDS)); @@ -381,20 +438,20 @@ static void test_consume(struct xkb_keymap *keymap) { struct xkb_state *state; - xkb_mod_index_t alt, shift, caps, ctrl, mod5; + xkb_mod_index_t shift, caps, ctrl, mod1, mod5; xkb_mod_mask_t mask; state = xkb_state_new(keymap); assert(state); - alt = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_ALT); - assert(alt != XKB_MOD_INVALID); shift = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_SHIFT); assert(shift != XKB_MOD_INVALID); caps = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CAPS); assert(caps != XKB_MOD_INVALID); ctrl = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CTRL); assert(ctrl != XKB_MOD_INVALID); + mod1 = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_MOD1); + assert(mod1 != XKB_MOD_INVALID); mod5 = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_MOD5); assert(mod5 != XKB_MOD_INVALID); @@ -407,10 +464,10 @@ test_consume(struct xkb_keymap *keymap) print_state(state); mask = xkb_state_serialize_mods(state, XKB_STATE_MODS_EFFECTIVE); - assert(mask == ((1U << alt) | (1U << shift))); + assert(mask == ((1U << mod1) | (1U << shift))); mask = xkb_state_mod_mask_remove_consumed(state, KEY_EQUAL + EVDEV_OFFSET, mask); - assert(mask == (1U << alt)); + assert(mask == (1U << mod1)); /* Test get_consumed_mods() */ mask = xkb_state_key_get_consumed_mods(state, KEY_EQUAL + EVDEV_OFFSET); @@ -450,16 +507,16 @@ test_consume(struct xkb_keymap *keymap) assert(state); mask = xkb_state_key_get_consumed_mods(state, KEY_F1 + EVDEV_OFFSET); - assert(mask == ((1U << shift) | (1U << alt) | (1U << ctrl) | (1U << mod5))); + assert(mask == ((1U << shift) | (1U << mod1) | (1U << ctrl) | (1U << mod5))); /* Shift is preserved. */ xkb_state_update_key(state, KEY_LEFTSHIFT + EVDEV_OFFSET, XKB_KEY_DOWN); mask = xkb_state_key_get_consumed_mods(state, KEY_F1 + EVDEV_OFFSET); - assert(mask == ((1U << alt) | (1U << ctrl) | (1U << mod5))); + assert(mask == ((1U << mod1) | (1U << ctrl) | (1U << mod5))); xkb_state_update_key(state, KEY_LEFTSHIFT + EVDEV_OFFSET, XKB_KEY_UP); mask = xkb_state_key_get_consumed_mods(state, KEY_F1 + EVDEV_OFFSET); - assert(mask == ((1U << shift) | (1U << alt) | (1U << ctrl) | (1U << mod5))); + assert(mask == ((1U << shift) | (1U << mod1) | (1U << ctrl) | (1U << mod5))); xkb_state_unref(state); @@ -479,7 +536,7 @@ test_consume(struct xkb_keymap *keymap) 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))); + assert(mask == ((1U << mod1) | (1U << ctrl))); xkb_state_unref(state);