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 Oct 13, 2024
1 parent 215ffb4 commit c46f9ec
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 18 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.
32 changes: 32 additions & 0 deletions include/xkbcommon/xkbcommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -1706,10 +1706,18 @@ 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
*/
int
xkb_state_mod_name_is_active(struct xkb_state *state, const char *name,
Expand All @@ -1719,6 +1727,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 +1742,11 @@ 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
*/
int
xkb_state_mod_names_are_active(struct xkb_state *state,
Expand All @@ -1741,10 +1757,18 @@ 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
*/
int
xkb_state_mod_index_is_active(struct xkb_state *state, xkb_mod_index_t idx,
Expand All @@ -1754,6 +1778,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 +1793,11 @@ 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
*/
int
xkb_state_mod_indices_are_active(struct xkb_state *state,
Expand Down
40 changes: 37 additions & 3 deletions src/state.c
Original file line number Diff line number Diff line change
Expand Up @@ -1265,6 +1265,27 @@ mod_mask_get_effective(struct xkb_keymap *keymap, xkb_mod_mask_t mods)
return mask;
}

/* [WARNING] Caller must ensure that the modifier index is valid! */
static inline bool
is_real_mod(struct xkb_keymap *keymap, xkb_mod_index_t mod) {
return !!(keymap->mods.mods[mod].type & MOD_REAL);
}

/*
* Get the mapping of a modifier.
* We cannot use `mods.mods[idx].mapping` directly, because it is
* not set for real modifiers.
*
* [WARNING] Caller must ensure that the modifier index is valid!
*/
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 given modifier is active with the specified type(s), 0 if
* not, or -1 if the modifier is invalid.
Expand All @@ -1277,7 +1298,12 @@ 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));
xkb_mod_mask_t mapping = get_mod_mapping(state->keymap, 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 @@ -1325,13 +1351,17 @@ 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);

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 +1406,17 @@ 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);

if (ret == -1)
return ret;

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

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

Expand Down
Loading

0 comments on commit c46f9ec

Please sign in to comment.