summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/xkbcommon/xkbcommon.h117
-rw-r--r--src/map.c139
-rw-r--r--src/state.c70
-rw-r--r--test/state.c75
-rwxr-xr-xtest/state.sh13
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
diff --git a/src/map.c b/src/map.c
index d5d20d7..c0020ae 100644
--- a/src/map.c
+++ b/src/map.c
@@ -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