Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support querying whether virtual modifiers are active (alternative 2) #512

Merged
merged 5 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions changes/api/+new-modifiers-names.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Added the following modifiers names definitions in `xkbcommon-names.h`:
- `XKB_MOD_NAME_MOD1`
- `XKB_MOD_NAME_MOD2`
- `XKB_MOD_NAME_MOD3`
- `XKB_MOD_NAME_MOD4`
- `XKB_MOD_NAME_MOD5`
- `XKB_VMOD_NAME_ALT`
- `XKB_VMOD_NAME_META`
- `XKB_VMOD_NAME_NUM`
- `XKB_VMOD_NAME_SUPER`
- `XKB_VMOD_NAME_HYPER`
- `XKB_VMOD_NAME_LEVEL3`
- `XKB_VMOD_NAME_LEVEL5`
- `XKB_VMOD_NAME_SCROLL`
12 changes: 12 additions & 0 deletions changes/api/+query-virtual-modifiers-state.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
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`
- `xkb_state_mod_index_is_consumed`
- `xkb_state_mod_index_is_consumed2`
- `xkb_state_mod_mask_remove_consumed`

Warning: they may overmatch in case there are overlappings virtual-to-real
modifiers mappings.
27 changes: 23 additions & 4 deletions include/xkbcommon/xkbcommon-names.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,32 @@
* @brief Predefined names for common modifiers and LEDs.
*/

/* Real modifiers names are hardcoded in libxkbcommon */
/* *Real* modifiers names are hardcoded in libxkbcommon */
#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_MOD1 "Mod1"
#define XKB_MOD_NAME_MOD2 "Mod2"
#define XKB_MOD_NAME_MOD3 "Mod3"
#define XKB_MOD_NAME_MOD4 "Mod4"
#define XKB_MOD_NAME_MOD5 "Mod5"

/* Usual virtual modifiers mappings to real modifiers */
#define XKB_MOD_NAME_ALT "Mod1" /* Alt */
#define XKB_MOD_NAME_LOGO "Mod4" /* Super */
#define XKB_MOD_NAME_NUM "Mod2" /* NumLock */

/* Common *virtual* modifiers, encoded in xkeyboard-config in the compat and
* symbols files. They have been stable since the beginning of the project and
* are unlikely to ever change. */
#define XKB_VMOD_NAME_ALT "Alt"
#define XKB_VMOD_NAME_HYPER "Hyper"
#define XKB_VMOD_NAME_LEVEL3 "LevelThree"
#define XKB_VMOD_NAME_LEVEL5 "LevelFive"
#define XKB_VMOD_NAME_META "Meta"
#define XKB_VMOD_NAME_NUM "NumLock"
#define XKB_VMOD_NAME_SCROLL "ScrollLock"
#define XKB_VMOD_NAME_SUPER "Super"

/* LEDs names are encoded in xkeyboard-config, in the keycodes and compat files.
* They have been stable since the beginning of the project and are unlikely to
Expand Down
64 changes: 61 additions & 3 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 Expand Up @@ -1882,10 +1918,12 @@ enum xkb_consumed_mode {
* @param key The keycode of the key.
* @param mode The consumed modifiers mode to use; see enum description.
*
* @returns a mask of the consumed modifiers.
* @returns a mask of the consumed [real modifiers] modifiers.
*
* @memberof xkb_state
* @since 0.7.0
*
* [real modifiers]: @ref real-modifier-def
*/
xkb_mod_mask_t
xkb_state_key_get_consumed_mods2(struct xkb_state *state, xkb_keycode_t key,
Expand All @@ -1904,6 +1942,9 @@ xkb_state_key_get_consumed_mods(struct xkb_state *state, xkb_keycode_t key);
* Test whether a modifier is consumed by keyboard state translation for
* a key.
*
* @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 key The keycode of the key.
* @param idx The index of the modifier to check.
Expand All @@ -1915,7 +1956,11 @@ xkb_state_key_get_consumed_mods(struct xkb_state *state, xkb_keycode_t key);
* @sa xkb_state_mod_mask_remove_consumed()
* @sa xkb_state_key_get_consumed_mods()
* @memberof xkb_state
* @since 0.7.0
* @since 0.7.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_consumed2(struct xkb_state *state,
Expand All @@ -1926,8 +1971,15 @@ xkb_state_mod_index_is_consumed2(struct xkb_state *state,
/**
* Same as xkb_state_mod_index_is_consumed2() with mode XKB_CONSUMED_MOD_XKB.
*
* @warning For [virtual modifiers], this function may *overmatch* in case
* there are virtual modifiers with overlapping mappings to [real modifiers].
*
* @memberof xkb_state
* @since 0.4.1
* @since 0.4.1: 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_consumed(struct xkb_state *state, xkb_keycode_t key,
Expand All @@ -1941,8 +1993,14 @@ xkb_state_mod_index_is_consumed(struct xkb_state *state, xkb_keycode_t key,
* Takes the given modifier mask, and removes all modifiers which are
* consumed for that particular key (as in xkb_state_mod_index_is_consumed()).
*
* @returns a mask of [real modifiers] modifiers.
*
* @sa xkb_state_mod_index_is_consumed()
* @memberof xkb_state
* @since 0.5.0: Works only with *real* modifiers
* @since 1.8.0: Works also with *virtual* modifiers
*
* [real modifiers]: @ref real-modifier-def
*/
xkb_mod_mask_t
xkb_state_mod_mask_remove_consumed(struct xkb_state *state, xkb_keycode_t key,
Expand Down
17 changes: 9 additions & 8 deletions src/keymap-priv.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,22 @@

#include "config.h"

#include "xkbcommon/xkbcommon-names.h"
#include "keymap.h"

static void
update_builtin_keymap_fields(struct xkb_keymap *keymap)
{
/* Predefined (AKA real, core, X11) modifiers. The order is important! */
static const char *const builtin_mods[] = {
[0] = "Shift",
[1] = "Lock",
[2] = "Control",
[3] = "Mod1",
[4] = "Mod2",
[5] = "Mod3",
[6] = "Mod4",
[7] = "Mod5"
[0] = XKB_MOD_NAME_SHIFT,
[1] = XKB_MOD_NAME_CAPS,
[2] = XKB_MOD_NAME_CTRL,
[3] = XKB_MOD_NAME_MOD1,
[4] = XKB_MOD_NAME_MOD2,
[5] = XKB_MOD_NAME_MOD3,
[6] = XKB_MOD_NAME_MOD4,
[7] = XKB_MOD_NAME_MOD5
};

for (unsigned i = 0; i < ARRAY_SIZE(builtin_mods); i++) {
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
53 changes: 43 additions & 10 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 Expand Up @@ -1520,10 +1547,15 @@ xkb_state_mod_index_is_consumed2(struct xkb_state *state, xkb_keycode_t kc,
{
const struct xkb_key *key = XkbKey(state->keymap, kc);

if (!key || idx >= xkb_keymap_num_mods(state->keymap))
if (unlikely(!key || idx >= xkb_keymap_num_mods(state->keymap)))
return -1;

return !!((1u << idx) & key_get_consumed(state, key, mode));
xkb_mod_mask_t mapping = mod_mapping(&state->keymap->mods.mods[idx], idx);
if (!mapping) {
/* Modifier not mapped */
return 0;
wismill marked this conversation as resolved.
Show resolved Hide resolved
}
return !!((mapping & key_get_consumed(state, key, mode)) == mapping);
}

XKB_EXPORT int
Expand All @@ -1543,7 +1575,8 @@ xkb_state_mod_mask_remove_consumed(struct xkb_state *state, xkb_keycode_t kc,
if (!key)
return 0;

return mask & ~key_get_consumed(state, key, XKB_CONSUMED_MODE_XKB);
return mod_mask_get_effective(state->keymap, mask) &
~key_get_consumed(state, key, XKB_CONSUMED_MODE_XKB);
}

XKB_EXPORT xkb_mod_mask_t
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
Loading
Loading