summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/keymap.c33
-rw-r--r--src/keymap.h11
-rw-r--r--src/state.c11
-rw-r--r--test/keymap.c42
-rw-r--r--xkbcommon.map1
-rw-r--r--xkbcommon/xkbcommon.h42
6 files changed, 129 insertions, 11 deletions
diff --git a/src/keymap.c b/src/keymap.c
index 8e6cb67..54ac7c0 100644
--- a/src/keymap.c
+++ b/src/keymap.c
@@ -409,6 +409,39 @@ xkb_keymap_led_get_index(struct xkb_keymap *keymap, const char *name)
return XKB_LED_INVALID;
}
+XKB_EXPORT size_t
+xkb_keymap_key_get_mods_for_level(struct xkb_keymap *keymap,
+ xkb_keycode_t kc,
+ xkb_layout_index_t layout,
+ xkb_level_index_t level,
+ xkb_mod_mask_t *masks_out,
+ size_t masks_size)
+{
+ const struct xkb_key *key = XkbKey(keymap, kc);
+ if (!key)
+ return 0;
+
+ layout = XkbWrapGroupIntoRange(layout, key->num_groups,
+ key->out_of_range_group_action,
+ key->out_of_range_group_number);
+ if (layout == XKB_LAYOUT_INVALID)
+ return 0;
+
+ if (level >= XkbKeyNumLevels(key, layout))
+ return 0;
+
+ const struct xkb_key_type *type = key->groups[layout].type;
+
+ size_t count = 0;
+ for (unsigned i = 0; i < type->num_entries && count < masks_size; i++)
+ if (entry_is_active(&type->entries[i]) &&
+ type->entries[i].level == level) {
+ masks_out[count] = type->entries[i].mods.mask;
+ ++count;
+ }
+ return count;
+}
+
/**
* As below, but takes an explicit layout/level rather than state.
*/
diff --git a/src/keymap.h b/src/keymap.h
index c15052b..7c5341d 100644
--- a/src/keymap.h
+++ b/src/keymap.h
@@ -438,6 +438,17 @@ XkbKeyNumLevels(const struct xkb_key *key, xkb_layout_index_t layout)
return key->groups[layout].type->num_levels;
}
+/*
+ * If the virtual modifiers are not bound to anything, the entry
+ * is not active and should be skipped. xserver does this with
+ * cached entry->active field.
+ */
+static inline bool
+entry_is_active(const struct xkb_key_type_entry *entry)
+{
+ return entry->mods.mods == 0 || entry->mods.mask != 0;
+}
+
struct xkb_keymap *
xkb_keymap_new(struct xkb_context *ctx,
enum xkb_keymap_format format,
diff --git a/src/state.c b/src/state.c
index 2d07be4..b269e6d 100644
--- a/src/state.c
+++ b/src/state.c
@@ -118,17 +118,6 @@ struct xkb_state {
struct xkb_keymap *keymap;
};
-/*
- * If the virtual modifiers are not bound to anything, the entry
- * is not active and should be skipped. xserver does this with
- * cached entry->active field.
- */
-static bool
-entry_is_active(const struct xkb_key_type_entry *entry)
-{
- return entry->mods.mods == 0 || entry->mods.mask != 0;
-}
-
static const struct xkb_key_type_entry *
get_entry_for_mods(const struct xkb_key_type *type, xkb_mod_mask_t mods)
{
diff --git a/test/keymap.c b/test/keymap.c
index 75b92c1..75e59f3 100644
--- a/test/keymap.c
+++ b/test/keymap.c
@@ -38,6 +38,11 @@ main(void)
struct xkb_keymap *keymap;
xkb_keycode_t kc;
const char *keyname;
+ xkb_mod_mask_t masks_out[4] = { 0, 0, 0, 0 };
+ size_t mask_count;
+ xkb_mod_mask_t shift_mask;
+ xkb_mod_mask_t lock_mask;
+ xkb_mod_mask_t mod2_mask;
assert(context);
@@ -59,6 +64,43 @@ main(void)
keyname = xkb_keymap_key_get_name(keymap, kc);
assert(streq(keyname, "COMP"));
+ kc = xkb_keymap_key_by_name(keymap, "AC01");
+ assert(kc != XKB_KEYCODE_INVALID);
+
+ // AC01 level 0 ('a') requires no modifiers on us-pc104
+ mask_count = xkb_keymap_key_get_mods_for_level(keymap, kc, 0, 0, masks_out, 4);
+ assert(mask_count == 0);
+
+ shift_mask = 1 << xkb_keymap_mod_get_index(keymap, "Shift");
+ lock_mask = 1 << xkb_keymap_mod_get_index(keymap, "Lock");
+ mod2_mask = 1 << xkb_keymap_mod_get_index(keymap, "Mod2");
+
+ // AC01 level 1 ('A') requires either Shift or Lock modifiers on us-pc104
+ mask_count = xkb_keymap_key_get_mods_for_level(keymap, kc, 0, 1, masks_out, 4);
+ assert(mask_count == 2);
+ assert(masks_out[0] == shift_mask);
+ assert(masks_out[1] == lock_mask);
+
+ kc = xkb_keymap_key_by_name(keymap, "KP1");
+
+ // KP1 level 0 ('End') requires no modifiers or Shift+Mod2 on us-pc104
+ mask_count = xkb_keymap_key_get_mods_for_level(keymap, kc, 0, 0, masks_out, 4);
+ assert(mask_count == 2);
+ assert(masks_out[0] == 0);
+ assert(masks_out[1] == (shift_mask | mod2_mask));
+
+ // KP1 level 1 ('1') requires either Shift or Mod2 modifiers on us-pc104
+ mask_count = xkb_keymap_key_get_mods_for_level(keymap, kc, 0, 1, masks_out, 4);
+ assert(mask_count == 2);
+ assert(masks_out[0] == shift_mask);
+ assert(masks_out[1] == mod2_mask);
+
+ // Return key is not affected by modifiers on us-pc104
+ kc = xkb_keymap_key_by_name(keymap, "RTRN");
+ mask_count = xkb_keymap_key_get_mods_for_level(keymap, kc, 0, 0, masks_out, 4);
+ assert(mask_count == 1);
+ assert(masks_out[0] == 0);
+
xkb_keymap_unref(keymap);
xkb_context_unref(context);
}
diff --git a/xkbcommon.map b/xkbcommon.map
index eede3e7..b9bfd26 100644
--- a/xkbcommon.map
+++ b/xkbcommon.map
@@ -107,4 +107,5 @@ global:
V_0.11.0 {
global:
xkb_utf32_to_keysym;
+ xkb_keymap_key_get_mods_for_level;
} V_0.8.0;
diff --git a/xkbcommon/xkbcommon.h b/xkbcommon/xkbcommon.h
index 9d18121..5090eab 100644
--- a/xkbcommon/xkbcommon.h
+++ b/xkbcommon/xkbcommon.h
@@ -1163,6 +1163,48 @@ xkb_keymap_num_levels_for_key(struct xkb_keymap *keymap, xkb_keycode_t key,
xkb_layout_index_t layout);
/**
+ * Retrieves every possible modifier mask that produces the specified
+ * shift level for a specific key and layout.
+ *
+ * This API is useful for inverse key transformation; i.e. finding out
+ * which modifiers need to be active in order to be able to type the
+ * keysym(s) corresponding to the specific key code, layout and level.
+ *
+ * @warning It returns only up to masks_size modifier masks. If the
+ * buffer passed is too small, some of the possible modifier combinations
+ * will not be returned.
+ *
+ * @param[in] keymap The keymap.
+ * @param[in] key The keycode of the key.
+ * @param[in] layout The layout for which to get modifiers.
+ * @param[in] level The shift level in the layout for which to get the
+ * modifiers. This should be smaller than:
+ * @code xkb_keymap_num_levels_for_key(keymap, key) @endcode
+ * @param[out] masks_out A buffer in which the requested masks should be
+ * stored.
+ * @param[out] masks_size The size of the buffer pointed to by masks_out.
+ *
+ * If @c layout is out of range for this key (that is, larger or equal to
+ * the value returned by xkb_keymap_num_layouts_for_key()), it is brought
+ * back into range in a manner consistent with xkb_state_key_get_layout().
+ *
+ * @returns The number of modifier masks stored in the masks_out array.
+ * If the key is not in the keymap or if the specified shift level cannot
+ * be reached it returns 0 and does not modify the masks_out buffer.
+ *
+ * @sa xkb_level_index_t
+ * @sa xkb_mod_mask_t
+ * @memberof xkb_keymap
+ */
+size_t
+xkb_keymap_key_get_mods_for_level(struct xkb_keymap *keymap,
+ xkb_keycode_t key,
+ xkb_layout_index_t layout,
+ xkb_level_index_t level,
+ xkb_mod_mask_t *masks_out,
+ size_t masks_size);
+
+/**
* Get the keysyms obtained from pressing a key in a given layout and
* shift level.
*