Commit 3d672fcfea6b823db4793b9ad1c3aadc4b547a08

Daniel Stone 2012-03-22T14:32:53

Add LED state API And also convert state.c to use the state API for mods and groups, rather than testing the state members directly. Signed-off-by: Daniel Stone <daniel@fooishbar.org>

diff --git a/include/xkbcommon/xkbcommon.h b/include/xkbcommon/xkbcommon.h
index 98e8049..fe72a74 100644
--- a/include/xkbcommon/xkbcommon.h
+++ b/include/xkbcommon/xkbcommon.h
@@ -64,10 +64,12 @@ 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;
+typedef uint32_t xkb_led_index_t;
 
 #define XKB_MOD_INVALID                 (0xffffffff)
 #define XKB_GROUP_INVALID               (0xffffffff)
 #define XKB_KEYCODE_INVALID             (0xffffffff)
+#define XKB_LED_INVALID                 (0xffffffff)
 
 #define XKB_KEYCODE_MAX                 (0xffffffff - 1)
 #define xkb_keycode_is_legal_ext(kc)    (kc <= XKB_KEYCODE_MAX)
@@ -497,6 +499,8 @@ struct xkb_state {
 
 	unsigned short  ptr_buttons; /* core pointer buttons */
 
+        uint32_t        leds;
+
         int refcnt;
         void *filters;
         int num_filters;
@@ -615,6 +619,24 @@ xkb_map_group_get_index(struct xkb_desc *xkb, const char *name);
 _X_EXPORT xkb_group_index_t
 xkb_key_num_groups(struct xkb_desc *xkb, xkb_keycode_t key);
 
+/**
+ * Returns the number of LEDs in the given map.
+ */
+_X_EXPORT xkb_led_index_t
+xkb_map_num_leds(struct xkb_desc *xkb);
+
+/**
+ * Returns the name of the LED specified by 'idx', or NULL if invalid.
+ */
+_X_EXPORT const char *
+xkb_map_led_get_name(struct xkb_desc *xkb, xkb_led_index_t idx);
+
+/**
+ * Returns the index of the LED specified by 'name', or XKB_LED_INVALID.
+ */
+_X_EXPORT xkb_led_index_t
+xkb_map_led_get_index(struct xkb_desc *xkb, const char *name);
+
 /** @} */
 
 /**
@@ -706,6 +728,20 @@ _X_EXPORT int
 xkb_state_group_index_is_active(struct xkb_state *state, xkb_group_index_t idx,
                                 enum xkb_state_component type);
 
+/**
+ * Returns 1 if the LED specified by 'name' is active, 0 if it is unset, or
+ * -1 if the LED does not exist in the current map.
+ */
+_X_EXPORT int
+xkb_state_led_name_is_active(struct xkb_state *state, const char *name);
+
+/**
+ * Returns 1 if the LED specified by 'idx' is active, 0 if it is unset, or
+ * -1 if the LED does not exist in the current map.
+ */
+_X_EXPORT int
+xkb_state_led_index_is_active(struct xkb_state *state, xkb_led_index_t idx);
+
 /** @} */
 
 _XFUNCPROTOEND
diff --git a/src/map.c b/src/map.c
index 6af6d28..7f1d85a 100644
--- a/src/map.c
+++ b/src/map.c
@@ -198,6 +198,53 @@ xkb_key_num_groups(struct xkb_desc *xkb, xkb_keycode_t key)
 }
 
 /**
+ * Return the total number of active LEDs in the keymap.
+ */
+xkb_led_index_t
+xkb_map_num_leds(struct xkb_desc *xkb)
+{
+    xkb_led_index_t ret = 0;
+    xkb_led_index_t i;
+
+    for (i = 0; i < XkbNumIndicators; i++)
+        if (xkb->indicators->maps[i].which_groups ||
+            xkb->indicators->maps[i].which_mods ||
+            xkb->indicators->maps[i].ctrls)
+            ret++;
+
+    return ret;
+}
+
+/**
+ * Returns the name for a given group.
+ */
+const char *
+xkb_map_led_get_name(struct xkb_desc *xkb, xkb_led_index_t idx)
+{
+    if (idx >= xkb_map_num_leds(xkb))
+        return NULL;
+
+    return xkb->names->indicators[idx];
+}
+
+/**
+ * Returns the index for a named group.
+ */
+xkb_group_index_t
+xkb_map_led_get_index(struct xkb_desc *xkb, const char *name)
+{
+    xkb_led_index_t num_leds = xkb_map_num_leds(xkb);
+    xkb_led_index_t i;
+
+    for (i = 0; i < num_leds; i++) {
+        if (strcasecmp(xkb->names->indicators[i], name) == 0)
+            return i;
+    }
+
+    return XKB_LED_INVALID;
+}
+
+/**
  * 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 1802f84..e5dce5c 100644
--- a/src/state.c
+++ b/src/state.c
@@ -445,6 +445,55 @@ xkb_state_unref(struct xkb_state *state)
 }
 
 /**
+ * Update the LED state to match the rest of the xkb_state.
+ */
+static void
+xkb_state_led_update_all(struct xkb_state *state)
+{
+    xkb_led_index_t led;
+
+    state->leds = 0;
+
+    for (led = 0; led < XkbNumIndicators; led++) {
+        struct xkb_indicator_map *map = &state->xkb->indicators->maps[led];
+        uint32_t mod_mask = 0;
+        uint32_t group_mask = 0;
+
+        if (!map->which_mods && !map->which_groups && !map->ctrls)
+            continue;
+
+        if (map->which_mods) {
+            if (map->which_mods & XkbIM_UseBase)
+                mod_mask |= state->base_mods;
+            if (map->which_mods & XkbIM_UseLatched)
+                mod_mask |= state->latched_mods;
+            if (map->which_mods & XkbIM_UseLocked)
+                mod_mask |= state->locked_mods;
+            if (map->which_mods & XkbIM_UseEffective)
+                mod_mask |= state->mods;
+            if ((map->mods.mask & mod_mask))
+                state->leds |= (1 << led);
+        }
+        else if (map->which_groups) {
+            if (map->which_mods & XkbIM_UseBase)
+                group_mask |= (1 << state->base_group);
+            if (map->which_mods & XkbIM_UseLatched)
+                group_mask |= (1 << state->latched_group);
+            if (map->which_mods & XkbIM_UseLocked)
+                group_mask |= (1 << state->locked_group);
+            if (map->which_mods & XkbIM_UseEffective)
+                group_mask |= (1 << state->group);
+            if ((map->groups & group_mask))
+                state->leds |= (1 << led);
+        }
+        else if (map->ctrls) {
+            if ((map->ctrls & state->xkb->ctrls->enabled_ctrls))
+                state->leds |= (1 << led);
+        }
+    }
+}
+
+/**
  * Given a particular key event, updates the state structure to reflect the
  * new modifiers.
  */
@@ -459,7 +508,7 @@ xkb_state_update_key(struct xkb_state *state, xkb_keycode_t key, int down)
                    state->latched_group;
     /* FIXME: Clamp/wrap effective group */
 
-    /* FIXME: Update LED state. */
+    xkb_state_led_update_all(state);
 }
 
 /**
@@ -537,3 +586,27 @@ int xkb_state_group_name_is_active(struct xkb_state *state, const char *name,
 
     return xkb_state_group_index_is_active(state, idx, type);
 }
+
+/**
+ * Returns 1 if the given LED is active, 0 if not, or -1 if the LED is invalid.
+ */
+int xkb_state_led_index_is_active(struct xkb_state *state, xkb_led_index_t idx)
+{
+    if (idx >= xkb_map_num_leds(state->xkb))
+        return -1;
+
+    return !!(state->leds & (1 << idx));
+}
+
+/**
+ * Returns 1 if the given LED is active, 0 if not, or -1 if the LED is invalid.
+ */
+int xkb_state_led_name_is_active(struct xkb_state *state, const char *name)
+{
+    xkb_led_index_t idx = xkb_map_led_get_index(state->xkb, name);
+
+    if (idx == XKB_LED_INVALID)
+        return -1;
+
+    return xkb_state_led_index_is_active(state, idx);
+}
diff --git a/test/state.c b/test/state.c
index 6048ee7..eea5b59 100644
--- a/test/state.c
+++ b/test/state.c
@@ -43,34 +43,49 @@ print_state(struct xkb_state *state)
 {
     xkb_group_index_t group;
     xkb_mod_index_t mod;
+    xkb_led_index_t led;
 
-    if (!state->group && !state->mods) {
+    if (!state->group && !state->mods && !state->leds) {
         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)
+        if (!xkb_state_group_index_is_active(state, group, XKB_STATE_EFFECTIVE))
             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 " : "");
+                xkb_state_group_index_is_active(state, group, XKB_STATE_EFFECTIVE) ?
+                    "effective " : "",
+                xkb_state_group_index_is_active(state, group, XKB_STATE_DEPRESSED) ?
+                    "depressed " : "",
+                xkb_state_group_index_is_active(state, group, XKB_STATE_LATCHED) ?
+                    "latched " : "",
+                xkb_state_group_index_is_active(state, group, XKB_STATE_LOCKED) ?
+                    "locked " : "");
     }
 
     for (mod = 0; mod < xkb_map_num_mods(state->xkb); mod++) {
-        if (!(state->mods & (1 << mod)))
+        if (!xkb_state_mod_index_is_active(state, mod, XKB_STATE_EFFECTIVE))
             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 " : "");
+                xkb_state_mod_index_is_active(state, mod, XKB_STATE_DEPRESSED) ?
+                    "depressed " : "",
+                xkb_state_mod_index_is_active(state, mod, XKB_STATE_LATCHED) ?
+                    "latched " : "",
+                xkb_state_mod_index_is_active(state, mod, XKB_STATE_LOCKED) ?
+                    "locked " : "");
+    }
+
+    for (led = 0; led < xkb_map_num_leds(state->xkb); led++) {
+        if (!xkb_state_led_index_is_active(state, led))
+            continue;
+        fprintf(stderr, "\tled %s (%d): active\n",
+                xkb_map_led_get_name(state->xkb, led),
+                led);
     }
 }
 
@@ -135,6 +150,7 @@ main(int argc, char *argv[])
     print_state(state);
     assert(xkb_state_mod_name_is_active(state, "Caps Lock",
                                         XKB_STATE_LOCKED));
+    assert(xkb_state_led_name_is_active(state, "Caps Lock"));
     num_syms = xkb_key_get_syms(state, KEY_Q + EVDEV_OFFSET, &syms);
     assert(num_syms == 1 && syms[0] == XK_Q);
 
@@ -143,6 +159,7 @@ main(int argc, char *argv[])
     xkb_state_update_key(state, KEY_CAPSLOCK + EVDEV_OFFSET, 0);
     assert(!xkb_state_mod_name_is_active(state, "Caps Lock",
                                          XKB_STATE_EFFECTIVE));
+    assert(!xkb_state_led_name_is_active(state, "Caps Lock"));
     num_syms = xkb_key_get_syms(state, KEY_Q + EVDEV_OFFSET, &syms);
     assert(num_syms == 1 && syms[0] == XK_q);