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 both “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 Nov 29, 2024
1 parent 2ba9daf commit 31a841a
Show file tree
Hide file tree
Showing 7 changed files with 516 additions and 101 deletions.
9 changes: 9 additions & 0 deletions changes/api/+query-virtual-modifiers-state.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
The following functions now allow to query also *virtual* modifiers, so they work
with *any* modifiers (real *and* virtual):
- `xkb_state_mod_index_is_active`
- `xkb_state_mod_indices_are_active`
- `xkb_state_mod_name_is_active`
- `xkb_state_mod_names_are_active`

Warning: they may overmatch in case there are overlappings virtual-to-real
modifiers mappings.
36 changes: 36 additions & 0 deletions include/xkbcommon/xkbcommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -1706,10 +1706,19 @@ xkb_state_serialize_layout(struct xkb_state *state,
/**
* Test whether a modifier is active in a given keyboard state by name.
*
* @warning For [virtual modifiers], this function may *overmatch* in case
* there are virtual modifiers with overlapping mappings to [real modifiers].
*
* @returns 1 if the modifier is active, 0 if it is not. If the modifier
* name does not exist in the keymap, returns -1.
*
* @memberof xkb_state
*
* @since 0.1.0: Works only with *real* modifiers
* @since 1.8.0: Works also with *virtual* modifiers
*
* [virtual modifiers]: @ref virtual-modifier-def
* [real modifiers]: @ref real-modifier-def
*/
int
xkb_state_mod_name_is_active(struct xkb_state *state, const char *name,
Expand All @@ -1719,6 +1728,9 @@ xkb_state_mod_name_is_active(struct xkb_state *state, const char *name,
* Test whether a set of modifiers are active in a given keyboard state by
* name.
*
* @warning For [virtual modifiers], this function may *overmatch* in case
* there are virtual modifiers with overlapping mappings to [real modifiers].
*
* @param state The keyboard state.
* @param type The component of the state against which to match the
* given modifiers.
Expand All @@ -1731,6 +1743,12 @@ xkb_state_mod_name_is_active(struct xkb_state *state, const char *name,
* the modifier names do not exist in the keymap, returns -1.
*
* @memberof xkb_state
*
* @since 0.1.0: Works only with *real* modifiers
* @since 1.8.0: Works also with *virtual* modifiers
*
* [virtual modifiers]: @ref virtual-modifier-def
* [real modifiers]: @ref real-modifier-def
*/
int
xkb_state_mod_names_are_active(struct xkb_state *state,
Expand All @@ -1741,10 +1759,19 @@ xkb_state_mod_names_are_active(struct xkb_state *state,
/**
* Test whether a modifier is active in a given keyboard state by index.
*
* @warning For [virtual modifiers], this function may *overmatch* in case
* there are virtual modifiers with overlapping mappings to [real modifiers].
*
* @returns 1 if the modifier is active, 0 if it is not. If the modifier
* index is invalid in the keymap, returns -1.
*
* @memberof xkb_state
*
* @since 0.1.0: Works only with *real* modifiers
* @since 1.8.0: Works also with *virtual* modifiers
*
* [virtual modifiers]: @ref virtual-modifier-def
* [real modifiers]: @ref real-modifier-def
*/
int
xkb_state_mod_index_is_active(struct xkb_state *state, xkb_mod_index_t idx,
Expand All @@ -1754,6 +1781,9 @@ xkb_state_mod_index_is_active(struct xkb_state *state, xkb_mod_index_t idx,
* Test whether a set of modifiers are active in a given keyboard state by
* index.
*
* @warning For [virtual modifiers], this function may *overmatch* in case
* there are virtual modifiers with overlapping mappings to [real modifiers].
*
* @param state The keyboard state.
* @param type The component of the state against which to match the
* given modifiers.
Expand All @@ -1766,6 +1796,12 @@ xkb_state_mod_index_is_active(struct xkb_state *state, xkb_mod_index_t idx,
* the modifier indices are invalid in the keymap, returns -1.
*
* @memberof xkb_state
*
* @since 0.1.0: Works only with *real* modifiers
* @since 1.8.0: Works also with *virtual* modifiers
*
* [virtual modifiers]: @ref virtual-modifier-def
* [real modifiers]: @ref real-modifier-def
*/
int
xkb_state_mod_indices_are_active(struct xkb_state *state,
Expand Down
4 changes: 2 additions & 2 deletions src/keymap.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,10 @@
#define XKB_MAX_GROUPS 4

/* Don't allow more modifiers than we can hold in xkb_mod_mask_t. */
#define XKB_MAX_MODS ((xkb_mod_index_t) (sizeof(xkb_mod_mask_t) * 8))
#define XKB_MAX_MODS ((xkb_mod_index_t) (sizeof(xkb_mod_mask_t) * CHAR_BIT))

/* Don't allow more leds than we can hold in xkb_led_mask_t. */
#define XKB_MAX_LEDS ((xkb_led_index_t) (sizeof(xkb_led_mask_t) * 8))
#define XKB_MAX_LEDS ((xkb_led_index_t) (sizeof(xkb_led_mask_t) * CHAR_BIT))

/* Special value to handle modMap None {…} */
#define XKB_MOD_NONE 0xffffffffU
Expand Down
41 changes: 34 additions & 7 deletions src/state.c
Original file line number Diff line number Diff line change
Expand Up @@ -1265,6 +1265,17 @@ mod_mask_get_effective(struct xkb_keymap *keymap, xkb_mod_mask_t mods)
return mask;
}

/* Get the mapping of a modifier */
static inline xkb_mod_mask_t
mod_mapping(struct xkb_mod *mod, xkb_mod_index_t idx)
{
/*
* We cannot use `mod->mapping` directly, because it is
* not set for real modifiers.
*/
return (mod->type & MOD_REAL) ? (1u << idx) : mod->mapping;
}

/**
* Returns 1 if the given modifier is active with the specified type(s), 0 if
* not, or -1 if the modifier is invalid.
Expand All @@ -1274,10 +1285,16 @@ xkb_state_mod_index_is_active(struct xkb_state *state,
xkb_mod_index_t idx,
enum xkb_state_component type)
{
if (idx >= xkb_keymap_num_mods(state->keymap))
if (unlikely(idx >= xkb_keymap_num_mods(state->keymap)))
return -1;

return !!(xkb_state_serialize_mods(state, type) & (1u << idx));
xkb_mod_mask_t mapping = mod_mapping(&state->keymap->mods.mods[idx], idx);
if (!mapping) {
/* Modifier not mapped */
return 0;
}
/* WARNING: this may overmatch for virtual modifiers */
return !!((xkb_state_serialize_mods(state, type) & mapping) == mapping);
}

/**
Expand Down Expand Up @@ -1321,17 +1338,22 @@ xkb_state_mod_indices_are_active(struct xkb_state *state,
xkb_mod_index_t idx = va_arg(ap, xkb_mod_index_t);
if (idx == XKB_MOD_INVALID)
break;
if (idx >= num_mods) {
if (unlikely(idx >= num_mods)) {
ret = -1;
break;
}
wanted |= (1u << idx);
wanted |= mod_mapping(&state->keymap->mods.mods[idx], idx);
}
va_end(ap);

if (ret == -1)
return ret;

if (!wanted) {
/* Modifiers not mapped */
return 0;
}

return match_mod_masks(state, type, match, wanted);
}

Expand Down Expand Up @@ -1376,13 +1398,18 @@ xkb_state_mod_names_are_active(struct xkb_state *state,
ret = -1;
break;
}
wanted |= (1u << idx);
wanted |= mod_mapping(&state->keymap->mods.mods[idx], idx);
}
va_end(ap);

if (ret == -1)
return ret;

if (!wanted) {
/* Modifiers not mapped */
return 0;
}

return match_mod_masks(state, type, match, wanted);
}

Expand All @@ -1392,8 +1419,8 @@ xkb_state_mod_names_are_active(struct xkb_state *state,
*/
XKB_EXPORT int
xkb_state_layout_index_is_active(struct xkb_state *state,
xkb_layout_index_t idx,
enum xkb_state_component type)
xkb_layout_index_t idx,
enum xkb_state_component type)
{
int ret = 0;

Expand Down
2 changes: 2 additions & 0 deletions test/data/rules/evdev
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,8 @@
lv5:lwin_switch_lock_cancel = +level5(lwin_switch_lock_cancel)
lv5:rwin_switch_lock_cancel = +level5(rwin_switch_lock_cancel)
parens:swap_brackets = +parens(swap_brackets)
overlapping_modifiers:super_hyper = +overlapping_modifiers(super_hyper)
overlapping_modifiers:meta = +overlapping_modifiers(meta)


! option = compat
Expand Down
15 changes: 15 additions & 0 deletions test/data/symbols/overlapping_modifiers
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
partial modifier_keys
xkb_symbols "super_hyper" {
key <MDSW> {
type[1] = "TWO_LEVEL",
symbols[1] = [ Super_L, Hyper_L ]
};
modifier_map Mod3 { <MDSW> };
};
xkb_symbols "meta" {
key <META> {
type[1] = "ONE_LEVEL",
symbols[1] = [ Meta_L ]
};
modifier_map Mod3 { <META> };
};
Loading

0 comments on commit 31a841a

Please sign in to comment.