diff options
-rw-r--r-- | include/xkbcommon/xkbcommon.h | 117 | ||||
-rw-r--r-- | src/map.c | 139 | ||||
-rw-r--r-- | src/state.c | 70 | ||||
-rw-r--r-- | test/state.c | 75 | ||||
-rwxr-xr-x | test/state.sh | 13 |
5 files changed, 400 insertions, 14 deletions
diff --git a/include/xkbcommon/xkbcommon.h b/include/xkbcommon/xkbcommon.h index 9507d15..0c9202e 100644 --- a/include/xkbcommon/xkbcommon.h +++ b/include/xkbcommon/xkbcommon.h @@ -62,6 +62,12 @@ THE USE OR PERFORMANCE OF THIS SOFTWARE. typedef uint32_t xkb_keycode_t; typedef uint32_t xkb_keysym_t; +typedef uint32_t xkb_mod_index_t; +typedef uint32_t xkb_group_index_t; + +#define XKB_MOD_INVALID (0xffffffff) +#define XKB_GROUP_INVALID (0xffffffff) +#define XKB_KEYCODE_INVALID (0xffffffff) #define XKB_KEYCODE_MAX (0xffffffff - 1) #define xkb_keycode_is_legal_ext(kc) (kc <= XKB_KEYCODE_MAX) @@ -560,8 +566,61 @@ xkb_key_get_syms(struct xkb_state *state, xkb_keycode_t key, xkb_keysym_t **syms_out); /** + * @defgroup components XKB state components + * Allows enumeration of state components such as modifiers and groups within + * the current keymap. + * + * @{ + */ + +/** + * Returns the number of modifiers active in the keymap. + */ +_X_EXPORT xkb_mod_index_t +xkb_map_num_mods(struct xkb_desc *xkb); + +/** + * Returns the name of the modifier specified by 'idx', or NULL if invalid. + */ +_X_EXPORT const char * +xkb_map_mod_get_name(struct xkb_desc *xkb, xkb_mod_index_t idx); + +/** + * Returns the index of the modifier specified by 'name', or NULL if invalid. + */ +_X_EXPORT xkb_mod_index_t +xkb_map_mod_get_index(struct xkb_desc *xkb, const char *name); + +/** + * Returns the number of groups active in the keymap. + */ +_X_EXPORT xkb_group_index_t +xkb_map_num_groups(struct xkb_desc *xkb); + +/** + * Returns the name of the group specified by 'idx', or NULL if invalid. + */ +_X_EXPORT const char * +xkb_map_group_get_name(struct xkb_desc *xkb, xkb_group_index_t idx); + +/** + * Returns the index of the group specified by 'name', or NULL if invalid. + */ +_X_EXPORT xkb_group_index_t +xkb_map_group_get_index(struct xkb_desc *xkb, const char *name); + +/** + * Returns the number of groups active for the specified key. + */ +_X_EXPORT xkb_group_index_t +xkb_key_num_groups(struct xkb_desc *xkb, xkb_keycode_t key); + +/** @} */ + +/** * @defgroup state XKB state objects - * Creation, destruction and manipulation of keyboard state objects, * representing modifier and group state. + * Creation, destruction and manipulation of keyboard state objects, + * representing modifier and group state. * * @{ */ @@ -591,6 +650,62 @@ xkb_state_unref(struct xkb_state *state); _X_EXPORT void xkb_state_update_key(struct xkb_state *state, xkb_keycode_t key, int down); +/** + * Modifier and group types for state objects. This enum is bitmaskable, + * e.g. (XKB_STATE_DEPRESSED | XKB_STATE_LATCHED) is valid to exclude + * locked modifiers. + */ +enum xkb_state_component { + XKB_STATE_DEPRESSED = (1 << 0), + /**< A key holding this modifier or group is currently physically + * depressed; also known as 'base'. */ + XKB_STATE_LATCHED = (1 << 1), + /**< Modifier or group is latched, i.e. will be unset after the next + * non-modifier key press. */ + XKB_STATE_LOCKED = (1 << 2), + /**< Modifier or group is locked, i.e. will be unset after the key + * provoking the lock has been pressed again. */ + XKB_STATE_EFFECTIVE = + (XKB_STATE_DEPRESSED | XKB_STATE_LATCHED | XKB_STATE_LOCKED), + /**< Combinatination of depressed, latched, and locked. */ +}; + +/** + * 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. + */ +_X_EXPORT int +xkb_state_mod_name_is_active(struct xkb_state *state, const char *name, + enum xkb_state_component type); + +/** + * 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 + * exist in the current map. + */ +_X_EXPORT 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 group specified by 'name' is active in the manner + * specified by 'type', 0 if it is unset, or -1 if the group does not + * exist in the current map. + */ +_X_EXPORT int +xkb_state_group_name_is_active(struct xkb_state *state, const char *name, + enum xkb_state_component type); + +/** + * Returns 1 if the group specified by 'idx' is active in the manner + * specified by 'type', 0 if it is unset, or -1 if the group does not + * exist in the current map. + */ +_X_EXPORT int +xkb_state_group_index_is_active(struct xkb_state *state, xkb_group_index_t idx, + enum xkb_state_component type); + /** @} */ _XFUNCPROTOEND @@ -59,6 +59,145 @@ #include <X11/X.h> /** + * Returns the total number of modifiers active in the keymap. + */ +xkb_mod_index_t +xkb_map_num_mods(struct xkb_desc *xkb) +{ + xkb_mod_index_t i; + + for (i = 0; i < XkbNumVirtualMods; i++) + if (!xkb->names->vmods[i]) + break; + + /* We always have all the core modifiers (for now), plus any virtual + * modifiers we may have defined, and then shift to one-based indexing. */ + return i + Mod5MapIndex + 1; +} + +/** + * Return the name for a given modifier. + */ +const char * +xkb_map_mod_get_name(struct xkb_desc *xkb, xkb_mod_index_t idx) +{ + if (idx >= xkb_map_num_mods(xkb)) + return NULL; + + /* First try to find a legacy modifier name. */ + switch (idx) { + case ShiftMapIndex: + return "Shift"; + case ControlMapIndex: + return "Control"; + case LockMapIndex: + return "Caps Lock"; + case Mod1MapIndex: + return "Mod1"; + case Mod2MapIndex: + return "Mod2"; + case Mod3MapIndex: + return "Mod3"; + case Mod4MapIndex: + return "Mod4"; + case Mod5MapIndex: + return "Mod5"; + default: + break; + } + + /* If that fails, try to find a virtual mod name. */ + return xkb->names->vmods[idx - Mod5MapIndex]; +} + +/** + * Returns the index for a named modifier. + */ +xkb_mod_index_t +xkb_map_mod_get_index(struct xkb_desc *xkb, const char *name) +{ + xkb_mod_index_t i; + + if (strcasecmp(name, "Shift") == 0) + return ShiftMapIndex; + if (strcasecmp(name, "Control") == 0) + return ControlMapIndex; + if (strcasecmp(name, "Caps Lock") == 0) + return LockMapIndex; + if (strcasecmp(name, "Mod1") == 0) + return Mod1MapIndex; + if (strcasecmp(name, "Mod2") == 0) + return Mod2MapIndex; + if (strcasecmp(name, "Mod3") == 0) + return Mod3MapIndex; + if (strcasecmp(name, "Mod4") == 0) + return Mod4MapIndex; + if (strcasecmp(name, "Mod5") == 0) + return Mod5MapIndex; + + for (i = 0; i < XkbNumVirtualMods && xkb->names->vmods[i]; i++) { + if (strcasecmp(name, xkb->names->vmods[i]) == 0) + return i + Mod5MapIndex; + } + + return XKB_GROUP_INVALID; +} + +/** + * Return the total number of active groups in the keymap. + */ +xkb_group_index_t +xkb_map_num_groups(struct xkb_desc *xkb) +{ + xkb_group_index_t ret = 0; + xkb_group_index_t i; + + for (i = 0; i < XkbNumKbdGroups; i++) + if (xkb->compat->groups[i].mask) + ret++; + + return ret; +} + +/** + * Returns the name for a given group. + */ +const char * +xkb_map_group_get_name(struct xkb_desc *xkb, xkb_group_index_t idx) +{ + if (idx >= xkb_map_num_groups(xkb)) + return NULL; + + return xkb->names->groups[idx]; +} + +/** + * Returns the index for a named group. + */ +xkb_group_index_t +xkb_map_group_get_index(struct xkb_desc *xkb, const char *name) +{ + xkb_group_index_t num_groups = xkb_map_num_groups(xkb); + xkb_group_index_t i; + + for (i = 0; i < num_groups; i++) { + if (strcasecmp(xkb->names->groups[i], name) == 0) + return i; + } + + return XKB_MOD_INVALID; +} + +/** + * Returns the number of groups active for a particular key. + */ +xkb_group_index_t +xkb_key_num_groups(struct xkb_desc *xkb, xkb_keycode_t key) +{ + return XkbKeyNumGroups(xkb, key); +} + +/** * Returns the level to use for the given key and state, or -1 if invalid. */ unsigned int diff --git a/src/state.c b/src/state.c index f248d0f..d63d4c9 100644 --- a/src/state.c +++ b/src/state.c @@ -461,3 +461,73 @@ xkb_state_update_key(struct xkb_state *state, xkb_keycode_t key, int down) /* FIXME: Update LED state. */ } + +/** + * Returns 1 if the given modifier is active with the specified type(s), 0 if + * not, or -1 if the modifier is invalid. + */ +int xkb_state_mod_index_is_active(struct xkb_state *state, + xkb_mod_index_t idx, + enum xkb_state_component type) +{ + int ret = 0; + + if (type & XKB_STATE_DEPRESSED) + ret |= (state->base_mods & (1 << idx)); + if (type & XKB_STATE_LATCHED) + ret |= (state->latched_mods & (1 << idx)); + if (type & XKB_STATE_LOCKED) + ret |= (state->locked_mods & (1 << idx)); + + return ret; +} + +/** + * Returns 1 if the given modifier is active with the specified type(s), 0 if + * not, or -1 if the modifier is invalid. + */ +int xkb_state_mod_name_is_active(struct xkb_state *state, const char *name, + enum xkb_state_component type) +{ + xkb_mod_index_t idx = xkb_map_mod_get_index(state->xkb, name); + + if (idx == XKB_MOD_INVALID) + return -1; + + return xkb_state_mod_index_is_active(state, idx, type); +} + +/** + * Returns 1 if the given group is active with the specified type(s), 0 if + * not, or -1 if the group is invalid. + */ +int xkb_state_group_index_is_active(struct xkb_state *state, + xkb_group_index_t idx, + enum xkb_state_component type) +{ + int ret = 0; + + if (type & XKB_STATE_DEPRESSED) + ret |= (state->base_group == idx); + if (type & XKB_STATE_LATCHED) + ret |= (state->latched_group == idx); + if (type & XKB_STATE_LOCKED) + ret |= (state->locked_group == idx); + + return ret; +} + +/** + * Returns 1 if the given modifier is active with the specified type(s), 0 if + * not, or -1 if the modifier is invalid. + */ +int xkb_state_group_name_is_active(struct xkb_state *state, const char *name, + enum xkb_state_component type) +{ + xkb_group_index_t idx = xkb_map_group_get_index(state->xkb, name); + + if (idx == XKB_GROUP_INVALID) + return -1; + + return xkb_state_group_index_is_active(state, idx, type); +} diff --git a/test/state.c b/test/state.c index 54fc8ba..6048ee7 100644 --- a/test/state.c +++ b/test/state.c @@ -38,7 +38,44 @@ * keycode set (where ESC is 9). */ #define EVDEV_OFFSET 8 -int main(int argc, char *argv[]) +static void +print_state(struct xkb_state *state) +{ + xkb_group_index_t group; + xkb_mod_index_t mod; + + if (!state->group && !state->mods) { + fprintf(stderr, "\tno state\n"); + return; + } + + for (group = 0; group < xkb_map_num_groups(state->xkb); group++) { + if (group != state->group && group != state->base_group && + group != state->latched_group && group != state->locked_group) + continue; + fprintf(stderr, "\tgroup %s (%d): %s%s%s%s\n", + xkb_map_group_get_name(state->xkb, group), + group, + (state->group == group) ? "effective " : "", + (state->base_group == group) ? "depressed " : "", + (state->latched_group == group) ? "latched " : "", + (state->locked_group == group) ? "locked " : ""); + } + + for (mod = 0; mod < xkb_map_num_mods(state->xkb); mod++) { + if (!(state->mods & (1 << mod))) + continue; + fprintf(stderr, "\tmod %s (%d): %s%s%s\n", + xkb_map_mod_get_name(state->xkb, mod), + mod, + (state->base_mods & (1 << mod)) ? "depressed " : "", + (state->latched_mods & (1 << mod)) ? "latched " : "", + (state->locked_mods & (1 << mod)) ? "locked " : ""); + } +} + +int +main(int argc, char *argv[]) { struct xkb_rule_names rmlvo; struct xkb_desc *xkb; @@ -60,40 +97,52 @@ int main(int argc, char *argv[]) } state = xkb_state_new(xkb); - assert(state->mods == 0); - assert(state->group == 0); /* LCtrl down */ xkb_state_update_key(state, KEY_LEFTCTRL + EVDEV_OFFSET, 1); - assert(state->mods & ControlMask); + fprintf(stderr, "dumping state for LCtrl down:\n"); + print_state(state); + assert(xkb_state_mod_name_is_active(state, "Control", + XKB_STATE_DEPRESSED)); /* LCtrl + RAlt down */ xkb_state_update_key(state, KEY_RIGHTALT + EVDEV_OFFSET, 1); - assert(state->mods & Mod1Mask); - assert(state->locked_mods == 0); - assert(state->latched_mods == 0); + fprintf(stderr, "dumping state for LCtrl + RAlt down:\n"); + print_state(state); + assert(xkb_state_mod_name_is_active(state, "Control", + XKB_STATE_DEPRESSED)); + assert(xkb_state_mod_name_is_active(state, "Mod1", + XKB_STATE_DEPRESSED)); /* RAlt down */ xkb_state_update_key(state, KEY_LEFTCTRL + EVDEV_OFFSET, 0); - assert(!(state->mods & ControlMask) && (state->mods & Mod1Mask)); + fprintf(stderr, "dumping state for RAlt down:\n"); + print_state(state); + assert(!xkb_state_mod_name_is_active(state, "Control", + XKB_STATE_EFFECTIVE)); + assert(xkb_state_mod_name_is_active(state, "Mod1", + XKB_STATE_DEPRESSED)); /* none down */ xkb_state_update_key(state, KEY_RIGHTALT + EVDEV_OFFSET, 0); - assert(state->mods == 0); - assert(state->group == 0); + assert(!xkb_state_mod_name_is_active(state, "Mod1", + XKB_STATE_EFFECTIVE)); /* Caps locked */ xkb_state_update_key(state, KEY_CAPSLOCK + EVDEV_OFFSET, 1); xkb_state_update_key(state, KEY_CAPSLOCK + EVDEV_OFFSET, 0); - assert(state->mods & LockMask); - assert(state->mods == state->locked_mods); + fprintf(stderr, "dumping state for Caps Lock:\n"); + print_state(state); + assert(xkb_state_mod_name_is_active(state, "Caps Lock", + XKB_STATE_LOCKED)); num_syms = xkb_key_get_syms(state, KEY_Q + EVDEV_OFFSET, &syms); assert(num_syms == 1 && syms[0] == XK_Q); /* Caps unlocked */ xkb_state_update_key(state, KEY_CAPSLOCK + EVDEV_OFFSET, 1); xkb_state_update_key(state, KEY_CAPSLOCK + EVDEV_OFFSET, 0); - assert(state->mods == 0); + assert(!xkb_state_mod_name_is_active(state, "Caps Lock", + XKB_STATE_EFFECTIVE)); num_syms = xkb_key_get_syms(state, KEY_Q + EVDEV_OFFSET, &syms); assert(num_syms == 1 && syms[0] == XK_q); diff --git a/test/state.sh b/test/state.sh new file mode 100755 index 0000000..7753426 --- /dev/null +++ b/test/state.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +srcdir=${srcdir-.} +builddir=${builddir-.} + +name=state +prog="$builddir/$name$EXEEXT" +log="$builddir/$name.log" + +rm -f "$log" +srcdir=${srcdir-.} +builddir=${builddir-.} +$builddir/$name$EXEEXT >> $log 2>&1 |