Skip to content

Commit

Permalink
state: support querying whether virtual modifiers are active
Browse files Browse the repository at this point in the history
Previously it was not possible to query the status of virtual
modifiers with the following functions:
- xkb_state_mod_index_is_active
- xkb_state_mod_indices_are_active
- xkb_state_mod_name_is_active
- xkb_state_mod_names_are_active

Note that it may overmatch if some modifier mappings overlap.
For example, the default “us” PC layout maps “Alt” and “Meta”
to the real modifier “Mod1”; thus “Mod1”, “Alt” and “Meta”
modifiers will return the same result with these functions.
  • Loading branch information
wismill committed Jul 4, 2023
1 parent 0d01a93 commit 2b39a25
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 16 deletions.
9 changes: 6 additions & 3 deletions include/xkbcommon/xkbcommon-names.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@
#define XKB_MOD_NAME_SHIFT "Shift"
#define XKB_MOD_NAME_CAPS "Lock"
#define XKB_MOD_NAME_CTRL "Control"
#define XKB_MOD_NAME_ALT "Mod1"
#define XKB_MOD_NAME_NUM "Mod2"
#define XKB_MOD_NAME_LOGO "Mod4"
#define XKB_MOD_NAME_ALT "Alt"
#define XKB_MOD_NAME_META "Meta"
#define XKB_MOD_NAME_LOGO "Super"
#define XKB_MOD_NAME_SUPER "Super"
#define XKB_MOD_NAME_HYPER "Hyper"
#define XKB_MOD_NAME_NUM "NumLock"

#define XKB_LED_NAME_CAPS "Caps Lock"
#define XKB_LED_NAME_NUM "Num Lock"
Expand Down
5 changes: 5 additions & 0 deletions src/keymap.h
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,11 @@ XkbWrapGroupIntoRange(int32_t group,
enum xkb_range_exceed_type out_of_range_group_action,
xkb_layout_index_t out_of_range_group_number);

static inline bool
is_real_mod(struct xkb_keymap *keymap, xkb_mod_index_t mod) {
return keymap->mods.mods[mod].type & MOD_REAL;
}

xkb_mod_mask_t
mod_mask_get_effective(struct xkb_keymap *keymap, xkb_mod_mask_t mods);

Expand Down
25 changes: 22 additions & 3 deletions src/state.c
Original file line number Diff line number Diff line change
Expand Up @@ -1138,7 +1138,13 @@ xkb_state_mod_index_is_active(struct xkb_state *state,
if (idx >= xkb_keymap_num_mods(state->keymap))
return -1;

return !!(xkb_state_serialize_mods(state, type) & (1u << idx));
if (is_real_mod(state->keymap, idx)) {
return !!(xkb_state_serialize_mods(state, type) & (1u << idx));
} else {
/* WARNING: this may overmatch */
xkb_mod_mask_t mapping = state->keymap->mods.mods[idx].mapping;
return !!((xkb_state_serialize_mods(state, type) & mapping) == mapping);
}
}

/**
Expand All @@ -1162,6 +1168,19 @@ match_mod_masks(struct xkb_state *state,
return (active & wanted) == wanted;
}

/*
* Get the mapping of a modifier.
* We cannot use `mods.mods[idx].mapping` directly, because it is
* not set for real modifiers.
*/
static inline xkb_mod_mask_t
get_mod_mapping(struct xkb_keymap *keymap, xkb_mod_index_t idx)
{
return is_real_mod(keymap, idx)
? (1u << idx)
: keymap->mods.mods[idx].mapping;
}

/**
* Returns 1 if the modifiers are active with the specified type(s), 0 if
* not, or -1 if any of the modifiers are invalid.
Expand All @@ -1186,7 +1205,7 @@ xkb_state_mod_indices_are_active(struct xkb_state *state,
ret = -1;
break;
}
wanted |= (1u << idx);
wanted |= get_mod_mapping(state->keymap, idx);
}
va_end(ap);

Expand Down Expand Up @@ -1237,7 +1256,7 @@ xkb_state_mod_names_are_active(struct xkb_state *state,
ret = -1;
break;
}
wanted |= (1u << idx);
wanted |= get_mod_mapping(state->keymap, idx);
}
va_end(ap);

Expand Down
77 changes: 67 additions & 10 deletions test/state.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,56 +126,104 @@ test_update_key(struct xkb_keymap *keymap)
print_state(state);
assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL,
XKB_STATE_MODS_DEPRESSED) > 0);
assert(xkb_state_mod_name_is_active(state, "Mod1",
XKB_STATE_MODS_DEPRESSED) > 0);
assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT,
XKB_STATE_MODS_DEPRESSED) > 0);
assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_META,
XKB_STATE_MODS_DEPRESSED) > 0);
assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED,
XKB_STATE_MATCH_ALL,
XKB_MOD_NAME_CTRL,
"Mod1",
XKB_MOD_NAME_ALT,
XKB_MOD_NAME_META,
NULL) > 0);
assert(xkb_state_mod_indices_are_active(state, XKB_STATE_MODS_DEPRESSED,
XKB_STATE_MATCH_ALL,
xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CTRL),
xkb_keymap_mod_get_index(keymap, "Mod1"),
xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_ALT),
xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_META),
XKB_MOD_INVALID) > 0);
assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED,
XKB_STATE_MATCH_ALL,
"Mod1",
NULL) == 0);
assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED,
XKB_STATE_MATCH_ALL,
XKB_MOD_NAME_ALT,
NULL) == 0);
assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED,
XKB_STATE_MATCH_ALL,
XKB_MOD_NAME_META,
NULL) == 0);
assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED,
XKB_STATE_MATCH_ALL |
XKB_STATE_MATCH_NON_EXCLUSIVE,
"Mod1",
NULL) > 0);
assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED,
XKB_STATE_MATCH_ALL |
XKB_STATE_MATCH_NON_EXCLUSIVE,
XKB_MOD_NAME_ALT,
NULL) > 0);
assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED,
XKB_STATE_MATCH_ALL |
XKB_STATE_MATCH_NON_EXCLUSIVE,
XKB_MOD_NAME_META,
NULL) > 0);
assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED,
(XKB_STATE_MATCH_ANY |
XKB_STATE_MATCH_NON_EXCLUSIVE),
"Mod1",
NULL) > 0);
assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED,
(XKB_STATE_MATCH_ANY |
XKB_STATE_MATCH_NON_EXCLUSIVE),
XKB_MOD_NAME_ALT,
NULL) > 0);
assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED,
(XKB_STATE_MATCH_ANY |
XKB_STATE_MATCH_NON_EXCLUSIVE),
XKB_MOD_NAME_META,
NULL) > 0);

/* RAlt down */
xkb_state_update_key(state, KEY_LEFTCTRL + EVDEV_OFFSET, XKB_KEY_UP);
fprintf(stderr, "dumping state for RAlt down:\n");
print_state(state);
assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL,
XKB_STATE_MODS_EFFECTIVE) == 0);
assert(xkb_state_mod_name_is_active(state, "Mod1",
XKB_STATE_MODS_DEPRESSED) > 0);
assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT,
XKB_STATE_MODS_DEPRESSED) > 0);
assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_META,
XKB_STATE_MODS_DEPRESSED) > 0);
assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_DEPRESSED,
XKB_STATE_MATCH_ANY,
XKB_MOD_NAME_CTRL,
"Mod1",
XKB_MOD_NAME_ALT,
XKB_MOD_NAME_META,
NULL) > 0);
assert(xkb_state_mod_names_are_active(state, XKB_STATE_MODS_LATCHED,
XKB_STATE_MATCH_ANY,
XKB_MOD_NAME_CTRL,
"Mod1",
XKB_MOD_NAME_ALT,
XKB_MOD_NAME_META,
NULL) == 0);

/* none down */
xkb_state_update_key(state, KEY_RIGHTALT + EVDEV_OFFSET, XKB_KEY_UP);
assert(xkb_state_mod_name_is_active(state, "Mod1",
XKB_STATE_MODS_EFFECTIVE) == 0);
assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT,
XKB_STATE_MODS_EFFECTIVE) == 0);
assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_META,
XKB_STATE_MODS_EFFECTIVE) == 0);

/* Caps locked */
xkb_state_update_key(state, KEY_CAPSLOCK + EVDEV_OFFSET, XKB_KEY_DOWN);
Expand All @@ -201,6 +249,8 @@ test_update_key(struct xkb_keymap *keymap)
XKB_STATE_MODS_LOCKED) > 0);
assert(xkb_state_mod_name_is_active(state, "Mod2",
XKB_STATE_MODS_LOCKED) > 0);
assert(xkb_state_mod_name_is_active(state, XKB_MOD_NAME_NUM,
XKB_STATE_MODS_LOCKED) > 0);
num_syms = xkb_state_key_get_syms(state, KEY_KP1 + EVDEV_OFFSET, &syms);
assert(num_syms == 1 && syms[0] == XKB_KEY_KP_1);
assert(xkb_state_led_name_is_active(state, XKB_LED_NAME_NUM) > 0);
Expand Down Expand Up @@ -302,7 +352,7 @@ static void
test_update_mask_mods(struct xkb_keymap *keymap)
{
struct xkb_state *state = xkb_state_new(keymap);
xkb_mod_index_t caps, shift, num, alt, mod1, mod2;
xkb_mod_index_t caps, shift, num, alt, meta, mod1, mod2;
enum xkb_state_component changed;

assert(state);
Expand All @@ -315,6 +365,8 @@ test_update_mask_mods(struct xkb_keymap *keymap)
assert(num != XKB_MOD_INVALID);
alt = xkb_keymap_mod_get_index(keymap, "Alt");
assert(alt != XKB_MOD_INVALID);
meta = xkb_keymap_mod_get_index(keymap, "Meta");
assert(alt != XKB_MOD_INVALID);
mod1 = xkb_keymap_mod_get_index(keymap, "Mod1");
assert(mod1 != XKB_MOD_INVALID);
mod2 = xkb_keymap_mod_get_index(keymap, "Mod2");
Expand Down Expand Up @@ -346,6 +398,11 @@ test_update_mask_mods(struct xkb_keymap *keymap)
assert(xkb_state_serialize_mods(state, XKB_STATE_MODS_EFFECTIVE) ==
((1u << alt) | (1u << mod1)));

changed = xkb_state_update_mask(state, (1 << meta), 0, 0, 0, 0, 0);
assert(changed == (XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_EFFECTIVE));
assert(xkb_state_serialize_mods(state, XKB_STATE_MODS_EFFECTIVE) ==
((1u << meta) | (1u << mod1)));

changed = xkb_state_update_mask(state, 0, 0, (1 << num), 0, 0, 0);
assert(changed == (XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LOCKED |
XKB_STATE_MODS_EFFECTIVE | XKB_STATE_LEDS));
Expand Down Expand Up @@ -381,20 +438,20 @@ static void
test_consume(struct xkb_keymap *keymap)
{
struct xkb_state *state;
xkb_mod_index_t alt, shift, caps, ctrl, mod5;
xkb_mod_index_t shift, caps, ctrl, mod1, mod5;
xkb_mod_mask_t mask;

state = xkb_state_new(keymap);
assert(state);

alt = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_ALT);
assert(alt != XKB_MOD_INVALID);
shift = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_SHIFT);
assert(shift != XKB_MOD_INVALID);
caps = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CAPS);
assert(caps != XKB_MOD_INVALID);
ctrl = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CTRL);
assert(ctrl != XKB_MOD_INVALID);
mod1 = xkb_keymap_mod_get_index(keymap, "Mod1");
assert(mod1 != XKB_MOD_INVALID);
mod5 = xkb_keymap_mod_get_index(keymap, "Mod5");
assert(mod5 != XKB_MOD_INVALID);

Expand All @@ -407,10 +464,10 @@ test_consume(struct xkb_keymap *keymap)
print_state(state);

mask = xkb_state_serialize_mods(state, XKB_STATE_MODS_EFFECTIVE);
assert(mask == ((1U << alt) | (1U << shift)));
assert(mask == ((1U << mod1) | (1U << shift)));
mask = xkb_state_mod_mask_remove_consumed(state, KEY_EQUAL + EVDEV_OFFSET,
mask);
assert(mask == (1U << alt));
assert(mask == (1U << mod1));

/* Test get_consumed_mods() */
mask = xkb_state_key_get_consumed_mods(state, KEY_EQUAL + EVDEV_OFFSET);
Expand Down Expand Up @@ -450,16 +507,16 @@ test_consume(struct xkb_keymap *keymap)
assert(state);

mask = xkb_state_key_get_consumed_mods(state, KEY_F1 + EVDEV_OFFSET);
assert(mask == ((1U << shift) | (1U << alt) | (1U << ctrl) | (1U << mod5)));
assert(mask == ((1U << shift) | (1U << mod1) | (1U << ctrl) | (1U << mod5)));

/* Shift is preserved. */
xkb_state_update_key(state, KEY_LEFTSHIFT + EVDEV_OFFSET, XKB_KEY_DOWN);
mask = xkb_state_key_get_consumed_mods(state, KEY_F1 + EVDEV_OFFSET);
assert(mask == ((1U << alt) | (1U << ctrl) | (1U << mod5)));
assert(mask == ((1U << mod1) | (1U << ctrl) | (1U << mod5)));
xkb_state_update_key(state, KEY_LEFTSHIFT + EVDEV_OFFSET, XKB_KEY_UP);

mask = xkb_state_key_get_consumed_mods(state, KEY_F1 + EVDEV_OFFSET);
assert(mask == ((1U << shift) | (1U << alt) | (1U << ctrl) | (1U << mod5)));
assert(mask == ((1U << shift) | (1U << mod1) | (1U << ctrl) | (1U << mod5)));

xkb_state_unref(state);

Expand All @@ -479,7 +536,7 @@ test_consume(struct xkb_keymap *keymap)
xkb_state_update_key(state, KEY_LEFTALT + EVDEV_OFFSET, XKB_KEY_DOWN);
mask = xkb_state_key_get_consumed_mods2(state, KEY_F1 + EVDEV_OFFSET,
XKB_CONSUMED_MODE_GTK);
assert(mask == ((1U << alt) | (1U << ctrl)));
assert(mask == ((1U << mod1) | (1U << ctrl)));

xkb_state_unref(state);

Expand Down

0 comments on commit 2b39a25

Please sign in to comment.