diff --git a/include/xkbcommon/xkbcommon.h b/include/xkbcommon/xkbcommon.h index 0527143..dac14ec 100644 --- a/include/xkbcommon/xkbcommon.h +++ b/include/xkbcommon/xkbcommon.h @@ -488,6 +488,22 @@ enum xkb_state_component { (XKB_STATE_DEPRESSED | XKB_STATE_LATCHED | XKB_STATE_LOCKED), }; +/** + * Match flags for xkb_state_mod_indices_are_active and + * xkb_state_mod_names_are_active, specifying how the conditions for a + * successful match. XKB_STATE_MATCH_NON_EXCLUSIVE is bitmaskable with + * the other modes. + */ +enum xkb_state_match { + /** Returns true if any of the modifiers are active. */ + XKB_STATE_MATCH_ANY = (1 << 0), + /** Returns true if all of the modifiers are active. */ + XKB_STATE_MATCH_ALL = (1 << 1), + /** Makes matching non-exclusive, i.e. will not return false if a + * modifier not specified in the arguments is active. */ + XKB_STATE_MATCH_NON_EXCLUSIVE = (1 << 16), +}; + /** * Updates a state object from a set of explicit masks. This entrypoint is * really only for window systems and the like, where a master process @@ -537,12 +553,24 @@ xkb_state_serialise_group(struct xkb_state *state, /** * Returns 1 if the modifier specified by 'name' is active in the manner * specified by 'type', 0 if it is unset, or -1 if the modifier does not - * exist in the current map. + * exist in the map. */ int xkb_state_mod_name_is_active(struct xkb_state *state, const char *name, enum xkb_state_component type); +/** + * Returns 1 if the modifiers specified by the varargs (treated as + * NULL-terminated pointers to strings) are active in the manner + * specified by 'match', 0 otherwise, or -1 if any of the modifiers + * do not exist in the map. + */ +int +xkb_state_mod_names_are_active(struct xkb_state *state, + enum xkb_state_component type, + enum xkb_state_match match, + ...); + /** * Returns 1 if the modifier specified by 'idx' is active in the manner * specified by 'type', 0 if it is unset, or -1 if the modifier does not @@ -552,6 +580,18 @@ int xkb_state_mod_index_is_active(struct xkb_state *state, xkb_mod_index_t idx, enum xkb_state_component type); +/** + * Returns 1 if the modifiers specified by the varargs (treated as + * xkb_mod_index_t, terminated with XKB_MOD_INVALID) are active in the manner + * specified by 'match' and 'type', 0 otherwise, or -1 if the modifier does not + * exist in the current map. + */ +int +xkb_state_mod_indices_are_active(struct xkb_state *state, + enum xkb_state_component type, + enum xkb_state_match match, + ...); + /** * Returns 1 if the group specified by 'name' is active in the manner * specified by 'type', 0 if it is unset, or -1 if the group does not diff --git a/src/state.c b/src/state.c index 8bfe30d..5d731db 100644 --- a/src/state.c +++ b/src/state.c @@ -59,6 +59,7 @@ THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include +#include #include "xkb-priv.h" @@ -683,6 +684,59 @@ xkb_state_mod_index_is_active(struct xkb_state *state, return ret; } +/** + * Helper function for xkb_state_mod_indices_are_active and + * xkb_state_mod_names_are_active. + */ +static int +match_mod_masks(struct xkb_state *state, enum xkb_state_match match, + uint32_t wanted) +{ + uint32_t active = xkb_state_serialise_mods(state, XKB_STATE_EFFECTIVE); + + if (!(match & XKB_STATE_MATCH_NON_EXCLUSIVE) && (active & ~wanted)) + return 0; + + if (match & XKB_STATE_MATCH_ANY) + return !!(active & wanted); + else + return (active & wanted) == wanted; + + return 0; +} + +/** + * Returns 1 if the modifiers are active with the specified type(s), 0 if + * not, or -1 if any of the modifiers are invalid. + */ +_X_EXPORT int +xkb_state_mod_indices_are_active(struct xkb_state *state, + enum xkb_state_component type, + enum xkb_state_match match, + ...) +{ + va_list ap; + xkb_mod_index_t idx = 0; + uint32_t wanted = 0; + int ret = 0; + + va_start(ap, match); + while (1) { + idx = va_arg(ap, xkb_mod_index_t); + if (idx == XKB_MOD_INVALID || idx >= xkb_map_num_mods(state->xkb)) { + ret = -1; + break; + } + wanted |= (1 << idx); + } + va_end(ap); + + if (ret == -1) + return ret; + + return match_mod_masks(state, match, wanted); +} + /** * Returns 1 if the given modifier is active with the specified type(s), 0 if * not, or -1 if the modifier is invalid. @@ -699,6 +753,42 @@ xkb_state_mod_name_is_active(struct xkb_state *state, const char *name, return xkb_state_mod_index_is_active(state, idx, type); } +/** + * Returns 1 if the modifiers are active with the specified type(s), 0 if + * not, or -1 if any of the modifiers are invalid. + */ +_X_EXPORT int +xkb_state_mod_names_are_active(struct xkb_state *state, + enum xkb_state_component type, + enum xkb_state_match match, + ...) +{ + va_list ap; + xkb_mod_index_t idx = 0; + const char *str; + uint32_t wanted = 0; + int ret = 0; + + va_start(ap, match); + while (1) { + str = va_arg(ap, const char *); + if (str == NULL) + break; + idx = xkb_map_mod_get_index(state->xkb, str); + if (idx == XKB_MOD_INVALID) { + ret = -1; + break; + } + wanted |= (1 << idx); + } + va_end(ap); + + if (ret == -1) + return ret; + + return match_mod_masks(state, match, wanted); +} + /** * Returns 1 if the given group is active with the specified type(s), 0 if * not, or -1 if the group is invalid. diff --git a/test/state.c b/test/state.c index c081ba6..d43d25e 100644 --- a/test/state.c +++ b/test/state.c @@ -112,6 +112,20 @@ test_update_key(struct xkb_keymap *xkb) XKB_STATE_DEPRESSED)); assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT, XKB_STATE_DEPRESSED)); + assert(xkb_state_mod_names_are_active(state, XKB_STATE_DEPRESSED, + XKB_STATE_MATCH_ALL, + XKB_MOD_NAME_CTRL, + XKB_MOD_NAME_ALT, + NULL)); + assert(!xkb_state_mod_names_are_active(state, XKB_STATE_DEPRESSED, + XKB_STATE_MATCH_ALL, + XKB_MOD_NAME_ALT, + NULL)); + assert(xkb_state_mod_names_are_active(state, XKB_STATE_DEPRESSED, + (XKB_STATE_MATCH_ANY | + XKB_STATE_MATCH_NON_EXCLUSIVE), + XKB_MOD_NAME_ALT, + NULL)); /* RAlt down */ xkb_state_update_key(state, KEY_LEFTCTRL + EVDEV_OFFSET, XKB_KEY_UP); @@ -121,6 +135,11 @@ test_update_key(struct xkb_keymap *xkb) XKB_STATE_EFFECTIVE)); assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT, XKB_STATE_DEPRESSED)); + assert(xkb_state_mod_names_are_active(state, XKB_STATE_DEPRESSED, + XKB_STATE_MATCH_ANY, + XKB_MOD_NAME_CTRL, + XKB_MOD_NAME_ALT, + NULL)); /* none down */ xkb_state_update_key(state, KEY_RIGHTALT + EVDEV_OFFSET, XKB_KEY_UP);