summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRan Benita <ran@unusedvar.com>2022-12-17 13:02:41 +0200
committerRan Benita <ran@unusedvar.com>2022-12-17 20:15:57 +0200
commit9f2bba2b3014b1a5ae9579e24e3c237f6bc3366d (patch)
treecac67dac2570abcdefd7025c9120d7aea590fb11
parent5ba075abe04a172efe7520b51ef04b77d3a4643b (diff)
downloadxorg-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.h61
-rw-r--r--src/state.c86
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