Skip to content

Commit

Permalink
WIP: state: add API for updating latched and locked mods & layout in …
Browse files Browse the repository at this point in the history
…server state

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: #310
Signed-off-by: Ran Benita <[email protected]>
  • Loading branch information
bluetech committed Dec 17, 2022
1 parent 5ba075a commit 9f2bba2
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 2 deletions.
61 changes: 59 additions & 2 deletions include/xkbcommon/xkbcommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -1441,12 +1442,68 @@ enum xkb_state_component
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.
Expand Down
86 changes: 86 additions & 0 deletions src/state.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
#include "keymap.h"
#include "keysym.h"
#include "utf8.h"
#include "xkbcommon/xkbcommon.h"

struct xkb_filter {
union xkb_action action;
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 9f2bba2

Please sign in to comment.