diff options
author | Ran Benita <ran@unusedvar.com> | 2022-12-17 13:02:41 +0200 |
---|---|---|
committer | Ran Benita <ran@unusedvar.com> | 2022-12-17 20:15:57 +0200 |
commit | 9f2bba2b3014b1a5ae9579e24e3c237f6bc3366d (patch) | |
tree | cac67dac2570abcdefd7025c9120d7aea590fb11 | |
parent | 5ba075abe04a172efe7520b51ef04b77d3a4643b (diff) | |
download | xorg-lib-libxkbcommon-update-latched-locked.tar.gz |
WIP: state: add API for updating latched and locked mods & layout in server stateupdate-latched-locked
Up to now, the "server state" xkb_state API only offered one entry point
to update the server state - `xkb_state_update_key`, which reflects the
direct keyboard keys state. But some updates come out-of-band from
keyboard input events stream, for example, a GUI layout switcher.
The X11 XKB protocol has a request which allows for such updates,
`XkbLatchLockState`[0], but xkbcommon does not have similar
functionality. So server applications ended up using
`xkb_state_update_state` for this, but that's a function intended for
client applications, not servers.
Add support for updating the latched & locked state of the mods and
layout. Note that the depressed states cannot be updated in this way --
XKB does not expect them to be updated out of band.
[0] https://www.x.org/releases/X11R7.7/doc/kbproto/xkbproto.html#Querying_and_Changing_Keyboard_State
Fixes: https://github.com/xkbcommon/libxkbcommon/issues/310
Signed-off-by: Ran Benita <ran@unusedvar.com>
-rw-r--r-- | include/xkbcommon/xkbcommon.h | 61 | ||||
-rw-r--r-- | src/state.c | 86 |
2 files changed, 145 insertions, 2 deletions
diff --git a/include/xkbcommon/xkbcommon.h b/include/xkbcommon/xkbcommon.h index a7148f3..f5cb360 100644 --- a/include/xkbcommon/xkbcommon.h +++ b/include/xkbcommon/xkbcommon.h @@ -81,9 +81,10 @@ #ifndef _XKBCOMMON_H_ #define _XKBCOMMON_H_ +#include <stdarg.h> +#include <stdbool.h> #include <stdint.h> #include <stdio.h> -#include <stdarg.h> #include <xkbcommon/xkbcommon-names.h> #include <xkbcommon/xkbcommon-keysyms.h> @@ -1442,11 +1443,67 @@ xkb_state_update_key(struct xkb_state *state, xkb_keycode_t key, enum xkb_key_direction direction); /** + * Update the keyboard state to change the latched and locked state of + * the modifiers and layout. + * + * This entry point is intended for *server* applications and should not be used + * by *client* applications; see @ref server-client-state for details. + * + * Use this function to update the latched and locked state according to + * "out of band" (non-device) inputs, such as UI layout switchers. + * + * @par Layout out of range + * @parblock + * + * If the effective layout, after taking into account the depressed, latched and + * locked layout, is out of range (negative or greater than the maximum layout), + * it is brought into range. Currently, the layout is wrapped using integer + * modulus (with negative values wrapping from the end). The wrapping behavior + * may be made configurable in the future. + * + * @endparblock + * + * @param affect_latched_mods + * @param latched_mods + * Modifiers to set as latched or unlatched. Only modifiers in + * `affect_latched_mods` are considered. + * @param affect_latched_layout + * @param latched_layout + * Layout to latch. Only considered if `affect_latched_layout` is true. + * Maybe be out of range (including negative) -- see note above. + * @param affect_locked_mods + * @param locked_mods + * Modifiers to set as locked or unlocked. Only modifiers in + * `affect_locked_mods` are considered. + * @param affect_locked_layout + * @param locked_layout + * Layout to lock. Only considered if `affect_locked_layout` is true. + * Maybe be out of range (including negative) -- see note above. + * + * @returns A mask of state components that have changed as a result of + * the update. If nothing in the state has changed, returns 0. + * + * @memberof xkb_state + * + * @sa xkb_state_update_mask() + */ +enum xkb_state_component +xkb_state_update_latched_locked(struct xkb_state *state, + xkb_mod_mask_t affect_latched_mods, + xkb_mod_mask_t latched_mods, + bool affect_latched_layout, + int32_t latched_layout, + xkb_mod_mask_t affect_locked_mods, + xkb_mod_mask_t locked_mods, + bool affect_locked_layout, + int32_t locked_layout); + +/** * Update a keyboard state from a set of explicit masks. * * This entry point is intended for *client* applications; see @ref * server-client-state for details. *Server* applications should use - * xkb_state_update_key() instead. + * xkb_state_update_key() and xkb_state_update_latched_locked() instead. * * All parameters must always be passed, or the resulting state may be * incoherent. diff --git a/src/state.c b/src/state.c index b269e6d..abdac8c 100644 --- a/src/state.c +++ b/src/state.c @@ -64,6 +64,7 @@ #include "keymap.h" #include "keysym.h" #include "utf8.h" +#include "xkbcommon/xkbcommon.h" struct xkb_filter { union xkb_action action; @@ -781,6 +782,91 @@ xkb_state_update_key(struct xkb_state *state, xkb_keycode_t kc, return get_state_component_changes(&prev_components, &state->components); } +// XXX transcription from xserver +static void +XkbLatchModifiers(struct xkb_state *state, xkb_mod_mask_t mask, xkb_mod_mask_t latches) +{ + const struct xkb_key *key = XkbKey(state->keymap, SYNTHETIC_KEYCODE); + + xkb_mod_mask_t clear = mask & (~latches); + state->components.latched_mods &= ~clear; + union xkb_action none = { + .type = ACTION_TYPE_NONE, + }; + /* Clear any pending latch to locks. */ + xkb_filter_apply_all(state, key, XKB_KEY_DOWN); + + union xkb_action latch_mods = { + .mods = { + .type = ACTION_TYPE_MOD_LATCH, + .mods = { + .mask = mask & latches, + }, + .flags = 0, + }, + }; + struct xkb_filter *filter = xkb_filter_new(state); + filter->key = key; + filter->func = filter_action_funcs[latch_mods.type].func; + filter->action = latch_mods; + xkb_filter_mod_latch_new(state, filter); + xkb_filter_mod_latch_func(state, filter, key, XKB_KEY_DOWN); + xkb_filter_mod_latch_func(state, filter, key, XKB_KEY_UP); +} + +// XXX transcription from xserver +static int +XkbLatchGroup(struct xkb_state *state, int32_t group) +{ + const struct xkb_key *key = XkbKey(state->keymap, SYNTHETIC_KEYCODE); + + union xkb_action latch_group = { + .group = { + .type = ACTION_TYPE_GROUP_LATCH, + .flags = 0, + .group = group, + }, + }; + struct xkb_filter *filter = xkb_filter_new(state); + filter->key = key; + filter->func = filter_action_funcs[latch_group.type].func; + filter->action = latch_group; + xkb_filter_group_latch_new(state, filter); + xkb_filter_group_latch_func(state, filter, key, XKB_KEY_DOWN); + xkb_filter_group_latch_func(state, filter, key, XKB_KEY_UP); +} + +XKB_EXPORT enum xkb_state_component +xkb_state_update_latched_locked(struct xkb_state *state, + xkb_mod_mask_t affect_latched_mods, + xkb_mod_mask_t latched_mods, + bool affect_latched_layout, + int32_t latched_layout, + xkb_mod_mask_t affect_locked_mods, + xkb_mod_mask_t locked_mods, + bool affect_locked_layout, + int32_t locked_layout) +{ + struct state_components prev_components = state->components; + + state->components.locked_mods &= ~affect_locked_mods; + state->components.locked_mods |= locked_mods & affect_locked_mods; + + if (affect_locked_layout) { + state->components.locked_group = locked_layout; + } + + XkbLatchModifiers(state, affect_latched_mods, latched_mods); + + if (affect_latched_layout) { + XkbLatchGroup(state, latched_layout); + } + + xkb_state_update_derived(state); + + return get_state_component_changes(&prev_components, &state->components); +} + /** * Updates the state from a set of explicit masks as gained from * xkb_state_serialize_mods and xkb_state_serialize_groups. As noted in the |