From 32b8efe4e0b209f8065f8d6bd31fa68e4f5beb9f Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Tue, 4 Jul 2023 09:34:03 +0200 Subject: [PATCH] state: support querying whether virtual modifiers are active 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_active - xkb_state_mod_indices_are_active - xkb_state_mod_name_is_active - xkb_state_mod_names_are_active Note that it may overmatch if some modifier mappings overlap. For example, the default “us” PC layout maps “Alt” and “Meta” to the real modifier “Mod1”; thus “Mod1”, “Alt” and “Meta” modifiers will return the same result with these functions. --- include/xkbcommon/xkbcommon-names.h | 9 ++-- src/keymap.h | 5 ++ src/state.c | 25 ++++++++-- test/state.c | 77 +++++++++++++++++++++++++---- 4 files changed, 100 insertions(+), 16 deletions(-) diff --git a/include/xkbcommon/xkbcommon-names.h b/include/xkbcommon/xkbcommon-names.h index ecb551ff1..7b253558d 100644 --- a/include/xkbcommon/xkbcommon-names.h +++ b/include/xkbcommon/xkbcommon-names.h @@ -34,9 +34,12 @@ #define XKB_MOD_NAME_SHIFT "Shift" #define XKB_MOD_NAME_CAPS "Lock" #define XKB_MOD_NAME_CTRL "Control" -#define XKB_MOD_NAME_ALT "Mod1" -#define XKB_MOD_NAME_NUM "Mod2" -#define XKB_MOD_NAME_LOGO "Mod4" +#define XKB_MOD_NAME_ALT "Alt" +#define XKB_MOD_NAME_META "Meta" +#define XKB_MOD_NAME_LOGO "Super" +#define XKB_MOD_NAME_SUPER "Super" +#define XKB_MOD_NAME_HYPER "Hyper" +#define XKB_MOD_NAME_NUM "NumLock" #define XKB_LED_NAME_CAPS "Caps Lock" #define XKB_LED_NAME_NUM "Num Lock" diff --git a/src/keymap.h b/src/keymap.h index f7ea5bdf1..5f8570023 100644 --- a/src/keymap.h +++ b/src/keymap.h @@ -479,6 +479,11 @@ XkbWrapGroupIntoRange(int32_t group, enum xkb_range_exceed_type out_of_range_group_action, xkb_layout_index_t out_of_range_group_number); +static inline bool +is_real_mod(struct xkb_keymap *keymap, xkb_mod_index_t mod) { + return keymap->mods.mods[mod].type & MOD_REAL; +} + xkb_mod_mask_t mod_mask_get_effective(struct xkb_keymap *keymap, xkb_mod_mask_t mods); diff --git a/src/state.c b/src/state.c index b269e6d6e..0bda0be43 100644 --- a/src/state.c +++ b/src/state.c @@ -1138,7 +1138,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); + } } /** @@ -1162,6 +1168,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. @@ -1186,7 +1205,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); @@ -1237,7 +1256,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 08832f9e6..cd85cb274 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, "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, + "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, "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, + "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, + "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), + "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, "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, + "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, + "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, "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, "Alt"); assert(alt != XKB_MOD_INVALID); + meta = xkb_keymap_mod_get_index(keymap, "Meta"); + assert(alt != XKB_MOD_INVALID); mod1 = xkb_keymap_mod_get_index(keymap, "Mod1"); assert(mod1 != XKB_MOD_INVALID); mod2 = xkb_keymap_mod_get_index(keymap, "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, "Mod1"); + assert(mod1 != XKB_MOD_INVALID); mod5 = xkb_keymap_mod_get_index(keymap, "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);