Commit 2f1f1bca5b28807293920e613de6c9ff705853bf

Daniel Stone 2012-08-08T14:26:23

Add xkb_map_mod_mask_remove_consumed A fairly simple helper which, given an xkb_mod_mask_t, removes all modifiers which are consumed during processing of a particular key. Signed-off-by: Daniel Stone <daniel@fooishbar.org>

diff --git a/src/map.c b/src/map.c
index aa6b08b..4a7ce31 100644
--- a/src/map.c
+++ b/src/map.c
@@ -346,6 +346,24 @@ xkb_key_repeats(struct xkb_keymap *keymap, xkb_keycode_t kc)
     return XkbKey(keymap, kc)->repeats;
 }
 
+static struct xkb_kt_map_entry *
+get_entry_for_key_state(struct xkb_state *state, struct xkb_key_type *type,
+                        xkb_keycode_t kc)
+{
+    xkb_mod_mask_t active_mods;
+    unsigned int i;
+
+    active_mods = xkb_state_serialize_mods(state, XKB_STATE_EFFECTIVE);
+    active_mods &= type->mods.mask;
+
+    for (i = 0; i < type->num_entries; i++) {
+        if (type->map[i].mods.mask == active_mods)
+            return &type->map[i];
+    }
+
+    return NULL;
+}
+
 /**
  * Tests to see if a modifier is used up by our translation of a
  * keycode to keysyms, taking note of the current modifier state and
@@ -363,29 +381,47 @@ xkb_key_mod_index_is_consumed(struct xkb_state *state, xkb_keycode_t kc,
                               xkb_mod_index_t idx)
 {
     struct xkb_keymap *keymap = xkb_state_get_map(state);
-    xkb_group_index_t group;
     struct xkb_key_type *type;
-    unsigned int i;
-    struct xkb_kt_map_entry *entry = NULL;
-    xkb_mod_mask_t active_mods;
+    struct xkb_kt_map_entry *entry;
+    xkb_group_index_t group;
 
     if (!XkbKeycodeInRange(keymap, kc))
         return 0;
 
     group = xkb_key_get_group(state, kc);
     type = XkbKeyType(keymap, XkbKey(keymap, kc), group);
-    active_mods = xkb_state_serialize_mods(state, XKB_STATE_EFFECTIVE);
-    active_mods &= type->mods.mask;
+    entry = get_entry_for_key_state(state, type, kc);
+    if (!entry)
+        return 0;
 
-    for (i = 0; i < type->num_entries; i++) {
-        if (type->map[i].mods.mask == active_mods) {
-            entry = &type->map[i];
-            break;
-        }
-    }
+    return !!((type->mods.mask & (~entry->preserve.mask)) & (1 << idx));
+}
 
+/**
+ * Calculates which modifiers should be consumed during key processing,
+ * and returns the mask with all these modifiers removed.  e.g. if
+ * given a state of Alt and Shift active for a two-level alphabetic
+ * key containing plus and equal on the first and second level
+ * respectively, will return a mask of only Alt, as Shift has been
+ * consumed by the type handling.
+ */
+XKB_EXPORT xkb_mod_mask_t
+xkb_key_mod_mask_remove_consumed(struct xkb_state *state, xkb_keycode_t kc,
+                                 xkb_mod_mask_t mask)
+{
+    struct xkb_keymap *keymap = xkb_state_get_map(state);
+    struct xkb_key_type *type;
+    struct xkb_kt_map_entry *entry;
+    xkb_group_index_t group;
+
+    if (!XkbKeycodeInRange(keymap, kc))
+        return 0;
+
+    group = xkb_key_get_group(state, kc);
+    type = XkbKeyType(keymap, XkbKey(keymap, kc), group);
+    entry = get_entry_for_key_state(state, type, kc);
     if (!entry)
         return 0;
 
-    return !!((type->mods.mask & (~entry->preserve.mask)) & (1 << idx));
+    return mask & ~(type->mods.mask & ~entry->preserve.mask);
 }
diff --git a/test/state.c b/test/state.c
index 4eccb34..d4f61ef 100644
--- a/test/state.c
+++ b/test/state.c
@@ -236,6 +236,36 @@ test_repeat(struct xkb_keymap *keymap)
     assert(xkb_key_repeats(keymap, KEY_KBDILLUMDOWN + 8));
 }
 
+static void
+test_consume(struct xkb_keymap *keymap)
+{
+    struct xkb_state *state = xkb_state_new(keymap);
+    xkb_mod_index_t alt, shift;
+    xkb_mod_mask_t mask;
+
+    assert(state);
+
+    alt = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_ALT);
+    assert(alt != XKB_MOD_INVALID);
+    shift = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_SHIFT);
+    assert(shift != XKB_MOD_INVALID);
+
+    xkb_state_update_key(state, KEY_LEFTALT + EVDEV_OFFSET, XKB_KEY_DOWN);
+    xkb_state_update_key(state, KEY_LEFTSHIFT + EVDEV_OFFSET, XKB_KEY_DOWN);
+    xkb_state_update_key(state, KEY_EQUAL + EVDEV_OFFSET, XKB_KEY_DOWN);
+
+    fprintf(stderr, "dumping state for Alt-Shift-+\n");
+    print_state(state);
+
+    mask = xkb_state_serialize_mods(state, XKB_STATE_EFFECTIVE);
+    assert(mask == ((1 << alt) | (1 << shift)));
+    mask = xkb_key_mod_mask_remove_consumed(state, KEY_EQUAL + EVDEV_OFFSET,
+                                            mask);
+    assert(mask == (1 << alt));
+
+    xkb_state_unref(state);
+}
+
 int
 main(void)
 {
@@ -250,6 +280,7 @@ main(void)
     test_update_key(keymap);
     test_serialisation(keymap);
     test_repeat(keymap);
+    test_consume(keymap);
 
     xkb_map_unref(keymap);
     xkb_context_unref(context);
diff --git a/xkbcommon/xkbcommon.h b/xkbcommon/xkbcommon.h
index 6afe5e1..0c2f06f 100644
--- a/xkbcommon/xkbcommon.h
+++ b/xkbcommon/xkbcommon.h
@@ -657,6 +657,15 @@ xkb_key_mod_index_is_consumed(struct xkb_state *state, xkb_keycode_t key,
                               xkb_mod_index_t idx);
 
 /**
+ * Takes the given modifier mask, and removes all modifiers which are
+ * marked as 'consumed' (see xkb_key_mod_index_is_consumed definition)
+ * for that particular key.
+ */
+xkb_mod_mask_t
+xkb_key_mod_mask_remove_consumed(struct xkb_state *state, xkb_keycode_t key,
+                                 xkb_mod_mask_t mask);
+
+/**
  * 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