Round out new state API
authorDaniel Stone <daniel@fooishbar.org>
Wed, 21 Mar 2012 15:25:32 +0000 (15:25 +0000)
committerDaniel Stone <daniel@fooishbar.org>
Wed, 21 Mar 2012 15:25:45 +0000 (15:25 +0000)
Signed-off-by: Daniel Stone <daniel@fooishbar.org>
include/xkbcommon/xkbcommon.h
src/map.c
src/state.c
test/state.c
test/state.sh [new file with mode: 0755]

index 9507d15..0c9202e 100644 (file)
@@ -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
index d5d20d7..c0020ae 100644 (file)
--- a/src/map.c
+++ b/src/map.c
 #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
index f248d0f..d63d4c9 100644 (file)
@@ -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);
+}
index 54fc8ba..6048ee7 100644 (file)
  * 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 (executable)
index 0000000..7753426
--- /dev/null
@@ -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