#include <string.h>
#include <sys/ioctl.h>
#include <linux/input.h>
+#include <X11/extensions/XKBcommon.h>
-#include "input_xkb.h"
+#include "kbd.h"
#include "log.h"
#include "imKStoUCS.h"
-/* internal procedures prototypes */
-static void init_compat(struct xkb_desc *desc);
-static void init_key_types(struct xkb_desc *desc);
-static void init_actions(struct xkb_desc *desc);
-static void init_indicators(struct xkb_desc *desc);
-static void init_autorepeat(struct xkb_desc *desc);
-static int init_compat_for_keycode(struct xkb_desc *desc, KeyCode keycode);
-static int init_compat_for_keysym(struct xkb_desc *desc, KeyCode keycode,
- uint8_t group, uint16_t level);
-static int allocate_key_acts(struct xkb_desc *desc, uint8_t keycode);
-static struct xkb_sym_interpret *find_sym_interpret(struct xkb_desc *desc,
- uint32_t sym, uint16_t level, uint8_t key_modmap);
-static bool are_modifiers_matching(uint8_t mods, unsigned char match,
- uint8_t to_mods);
-static uint8_t virtual_and_real_to_mask(struct xkb_desc *desc, uint16_t vmods,
- uint8_t real_mods);
-static uint8_t virtual_to_real_mods(struct xkb_desc *desc, uint16_t vmods);
-static void init_action(struct xkb_desc *desc, union xkb_action *action);
+struct kmscon_kbd_desc {
+ unsigned long ref;
-static bool process_action(struct xkb_desc *desc, struct xkb_state *state,
- KeyCode keycode, enum kmscon_key_state key_state,
- union xkb_action *action);
-static bool process_mod_action(struct xkb_desc *desc, struct xkb_state *state,
- KeyCode keycode, enum kmscon_key_state key_state,
- struct xkb_mod_action *action);
-static bool process_group_action(struct xkb_desc *desc, struct xkb_state *state,
- KeyCode keycode, enum kmscon_key_state key_state,
- struct xkb_group_action *action);
+ struct xkb_desc *desc;
+};
-static bool should_key_repeat(struct xkb_desc *desc, KeyCode keycode);
-static uint8_t wrap_group_keycode(struct xkb_desc *desc, KeyCode keycode,
- int16_t group);
-static uint8_t wrap_group_control(struct xkb_desc *desc, int16_t group);
-static void update_effective_mods(struct xkb_desc *desc,
- struct xkb_state *state);
-static void update_effective_group(struct xkb_desc *desc,
- struct xkb_state *state);
-static struct xkb_indicator_map *find_indicator_map(struct xkb_desc *desc,
- const char *indicator_name);
+struct kmscon_kbd {
+ unsigned long ref;
+ struct kmscon_kbd_desc *desc;
-/*
- * Create a ready-to-use xkb description object. This is used in most places
- * having to do with XKB.
- */
-int kmscon_xkb_new_desc(const char *layout, const char *variant,
- const char *options,
- struct xkb_desc **out)
+ struct xkb_state state;
+};
+
+int kmscon_kbd_new(struct kmscon_kbd **out, struct kmscon_kbd_desc *desc)
{
- struct xkb_desc *desc;
+ struct kmscon_kbd *kbd;
- struct xkb_rule_names rmlvo = {
- .rules = "evdev",
- .model = "evdev",
- .layout = layout,
- .variant = variant,
- .options = options,
- };
+ kbd = malloc(sizeof(*kbd));
+ if (!kbd)
+ return -ENOMEM;
- desc = xkb_compile_keymap_from_rules(&rmlvo);
- if (!desc)
- return -EFAULT;
+ memset(kbd, 0, sizeof(*kbd));
- /* The order of these is important! */
- init_compat(desc);
- init_key_types(desc);
- init_actions(desc);
- init_indicators(desc);
- init_autorepeat(desc);
+ kbd->desc = desc;
+ kmscon_kbd_desc_ref(desc);
- *out = desc;
+ *out = kbd;
return 0;
}
-void kmscon_xkb_free_desc(struct xkb_desc *desc)
+void kmscon_kbd_ref(struct kmscon_kbd *kbd)
{
- if (!desc)
+ if (!kbd)
return;
- /*
- * XXX: Seems this doesn't really free everything, valgrind shows some
- * big leaks from libxkbcommon. Hopefully we use just one up until we
- * exit.
- */
- xkb_free_keymap(desc);
+ ++kbd->ref;
}
-/*
- * This mostly fills out the keycode-action mapping and puts the virtual
- * modifier mappings in the right place.
- */
-static void init_compat(struct xkb_desc *desc)
+void kmscon_kbd_unref(struct kmscon_kbd *kbd)
{
- /* If we use KeyCode it overflows. */
- unsigned int keycode;
+ if (!kbd || !kbd->ref)
+ return;
- for (keycode = desc->min_key_code; keycode <= desc->max_key_code; keycode++)
- init_compat_for_keycode(desc, keycode);
+ if (--kbd->ref)
+ return;
+
+ kmscon_kbd_desc_unref(kbd->desc);
+ free(kbd);
}
-static int init_compat_for_keycode(struct xkb_desc *desc, KeyCode keycode)
+static uint8_t virtual_to_real_mods(struct xkb_desc *desc, uint16_t vmods)
{
- int ret;
- int i, bit;
-
- uint8_t group;
- uint16_t level;
- int num_groups;
- int num_levels;
-
- /*
- * It's possible that someone had set some actions for the keycode
- * through the symbols file, and so we shouldn't override with the
- * compat. This is very uncommon though, only used by the breaks_caps
- * option here.
- */
- if (XkbKeyHasActions(desc, keycode))
- return 0;
+ int i;
+ uint32_t bit;
+ uint8_t mods;
- num_groups = XkbKeyNumGroups(desc, keycode);
+ mods = 0x00;
- /*
- * We need to track the sym level in order to support LevelOneOnly,
- * which is used in some symbol interpretations.
- */
+ for (i=0, bit=0x01; i < XkbNumVirtualMods; i++, bit<<=1)
+ if (vmods & bit)
+ mods |= desc->server->vmods[i];
- for (group=0, i=0; group < num_groups; group++) {
- num_levels = XkbKeyGroupWidth(desc, keycode, group);
+ return mods;
+}
- for (level=0; level < num_levels; level++) {
- ret = init_compat_for_keysym(desc, keycode,
- group, level);
- if (ret)
- return ret;
- }
- }
+static uint8_t virtual_and_real_to_mask(struct xkb_desc *desc,
+ uint16_t vmods, uint8_t real_mods)
+{
+ uint8_t mods = 0x00;
- /*
- * Translate the virtual modifiers bound to this key to the real
- * modifiers bound to this key.
- * See [Lib] 17.4 for vmodmap and friends.
- */
- for (i=0, bit=0x01; i < XkbNumVirtualMods; i++, bit<<=1)
- if (bit&desc->server->vmodmap[keycode])
- desc->server->vmods[i] |= desc->map->modmap[keycode];
+ mods |= real_mods;
+ mods |= virtual_to_real_mods(desc, vmods);
- return 0;
+ return mods;
}
-static int init_compat_for_keysym(struct xkb_desc *desc, KeyCode keycode,
- uint8_t group, uint16_t level)
+/*
+ * Helper function for the wrap_group_* functions.
+ * See [Lib] 11.7.1 for the rules.
+ */
+static uint8_t wrap_group(int16_t group, int num_groups, uint8_t group_info)
{
- int ret;
- uint8_t key_modmap;
- uint32_t sym;
- struct xkb_sym_interpret *si;
- union xkb_action *action;
-
- key_modmap = desc->map->modmap[keycode];
- sym = XkbKeySymEntry(desc, keycode, level, group);
- si = find_sym_interpret(desc, sym, level, key_modmap);
+ /* No need for wrapping. */
+ if (XkbIsLegalGroup(group) && group < num_groups)
+ return group;
- if (!si)
- return 0;
+ switch (XkbOutOfRangeGroupAction(group_info)) {
+ case XkbWrapIntoRange:
+ /*
+ * C99 says a negative dividend in a modulo operation
+ * will always give a negative result.
+ */
+ if (group < 0)
+ return num_groups + (group % num_groups);
+ else
+ return group % num_groups;
- /* Set the key action mapping. */
- if (si->act.type != XkbSA_NoAction) {
- ret = allocate_key_acts(desc, keycode);
- if (ret)
- return ret;
+ case XkbClampIntoRange:
+ /* This one seems to be unused. */
+ return num_groups - 1;
- action = XkbKeyActionEntry(desc, keycode, level, group);
- *action = (union xkb_action)si->act;
+ case XkbRedirectIntoRange:
+ /* This one seems to be unused. */
+ group = XkbOutOfRangeGroupNumber(group_info);
+ /* If it's _still_ out of range, use the first group. */
+ if (group >= num_groups)
+ return 0;
}
- /* Set the key virtual modifier mapping. */
- if (si->virtual_mod != XkbNoModifier)
- desc->server->vmodmap[keycode] |= 0x01 << si->virtual_mod;
-
return 0;
}
/*
- * Allocate slots for a keycode in the key-action mapping array. xkbcommon
- * doesn't do this by itself for actions from compat (that is almost all of
- * them).
- * See [xserver] XKBMAlloc.c:XkbResizeKeyActions() for the equivalent.
+ * Wrap an arbitrary group into a legal effective global group according to
+ * the GroupsWrap control.
+ * (Group actions mostly act on the group number in a relative manner [e.g.
+ * +1, -1]. So if we have N groups, the effective group is N-1, and we get a
+ * SetGroup +1, this tells us what to do.)
*/
-static int allocate_key_acts(struct xkb_desc *desc, uint8_t keycode)
+static uint8_t wrap_group_control(struct xkb_desc *desc, int16_t group)
{
- unsigned short index;
- union xkb_action *acts;
- struct xkb_server_map *server;
- int sym_count;
- int new_needed;
- unsigned short new_num_acts;
- unsigned short new_size_acts;
-
- server = desc->server;
- sym_count = XkbKeyNumSyms(desc, keycode);
-
- /*
- * num_acts is the occupied slots, size_acts is the current total
- * capacity.
- */
-
- if (XkbKeyHasActions(desc, keycode)) {
- /* An array is already allocated for this key. */
+ int num_groups;
+ uint8_t group_info;
- /* index = server->key_acts[keycode]; */
- } else if (server->num_acts + sym_count <= server->size_acts) {
- /* There's enough left over space; use it. */
+ num_groups = desc->ctrls->num_groups;
+ group_info = desc->ctrls->groups_wrap;
- index = server->num_acts;
- server->key_acts[keycode] = index;
- server->num_acts += sym_count;
- } else {
- /* Need to allocate new space. */
+ return wrap_group(group, num_groups, group_info);
+}
- index = server->num_acts;
- new_num_acts = server->num_acts + sym_count;
- new_needed = sym_count - (server->size_acts - new_num_acts);
- /* Add some extra to avoid repeated reallocs. */
- new_size_acts = server->size_acts + new_needed + 8;
+/*
+ * Wrap the effective global group to a legal group for the keycode, according
+ * to the rule specified for the key.
+ * (Some keycodes may have more groups than others, and so the effective
+ * group may not make sense for a certain keycode).
+ */
+static uint8_t wrap_group_keycode(struct xkb_desc *desc, KeyCode keycode,
+ int16_t group)
+{
+ int num_groups;
+ uint8_t group_info;
- acts = realloc(server->acts,
- sizeof(union xkb_action) * new_size_acts);
- if (!acts)
- return -ENOMEM;
+ num_groups = XkbKeyNumGroups(desc, keycode);
+ group_info = XkbKeyGroupInfo(desc, keycode);
- /* XkbSA_NoAction is 0x00 so we're good. */
- memset(acts+index, 0, sym_count);
- server->key_acts[keycode] = index;
- server->num_acts = new_num_acts;
- server->size_acts = new_size_acts;
- server->acts = acts;
- }
+ return wrap_group(group, num_groups, group_info);
+}
- return 0;
+/*
+ * Need to update the effective mods after any changes to the base, latched or
+ * locked mods.
+ */
+static void update_effective_mods(struct xkb_desc *desc,
+ struct xkb_state *state)
+{
+ state->mods = state->base_mods | state->latched_mods |
+ state->locked_mods;
}
/*
- * Look for the most specific symbol interpretation for the keysym.
- * See [xserver] XKBMisc.c:_XkbFindMatchingInterp() for the equivalent.
+ * Need to update the effective group after any changes to the base, latched or
+ * locked group.
*/
-static struct xkb_sym_interpret *find_sym_interpret(struct xkb_desc *desc,
- uint32_t sym, uint16_t level, uint8_t key_modmap)
+static void update_effective_group(struct xkb_desc *desc,
+ struct xkb_state *state)
{
- int i;
- struct xkb_sym_interpret *si;
- struct xkb_sym_interpret *all_syms_si;
+ int16_t group;
- all_syms_si = NULL;
+ /* Update the effective group. */
+ group = state->base_group + state->locked_group + state->latched_group;
+ state->group = wrap_group_control(desc, group);
+}
+
+/*
+ * Updates the group state.
+ * See [Lib] Table 17.4 for logic.
+ */
+static bool process_group_action(struct xkb_desc *desc, struct xkb_state *state,
+ KeyCode keycode, enum kmscon_key_state key_state,
+ struct xkb_group_action *action)
+{
+ int16_t group = action->group;
+ uint8_t flags = action->flags;
/*
- * If we find a matching interpret specific to our symbol, we return
- * it immediatly.
- * If we didn't find any, we return the first matching all-catching
- * interpret.
+ * action->group is signed and may be negative if GroupAbsolute
+ * is not set. A group itself cannot be negative and is unsigend.
+ * Therefore we extend these to int16 to avoid underflow and
+ * signedness issues. Be careful!
*/
+ int16_t base_group = state->base_group;
+ int16_t latched_group = state->latched_group;
+ int16_t locked_group = state->locked_group;
- for (i=0; i < desc->compat->num_si; i++) {
- si = &desc->compat->sym_interpret[i];
-
- if (si->sym != sym && si->sym != 0)
- continue;
-
- /*
- * If the interpret specified UseModMapMods=level1, the sym
- * must be in the first level of its group.
- * Note: [xserver] and [Lib] do different things here, and it
- * doesn't seem to matter much. So it's commented for now.
- */
- /* if (si->match&XkbSI_LevelOneOnly && level != 0) */
- /* continue; */
+ /*
+ * FIXME: Some actions here should be conditioned "and no keys are
+ * physically depressed when this key is released".
+ */
- if (!are_modifiers_matching(si->mods, si->match, key_modmap))
- continue;
+ switch (action->type) {
+ case XkbSA_SetGroup:
+ if (key_state == KMSCON_KEY_PRESSED) {
+ if (flags & XkbSA_GroupAbsolute)
+ base_group = group;
+ else
+ base_group += group;
+ } else if (key_state == KMSCON_KEY_RELEASED) {
+ if (flags & XkbSA_ClearLocks)
+ locked_group = 0;
+ }
- if (si->sym != 0)
- return si;
- else if (all_syms_si == NULL)
- all_syms_si = si;
- }
+ break;
+ case XkbSA_LatchGroup:
+ if (key_state == KMSCON_KEY_PRESSED) {
+ if (flags & XkbSA_GroupAbsolute)
+ base_group = group;
+ else
+ base_group += group;
+ } else if (key_state == KMSCON_KEY_RELEASED) {
+ if ((flags & XkbSA_LatchToLock) && latched_group) {
+ locked_group += group;
+ latched_group -= group;
+ } else {
+ latched_group += group;
+ }
+ }
- return all_syms_si;
-}
+ break;
+ case XkbSA_LockGroup:
+ if (key_state == KMSCON_KEY_PRESSED) {
+ if (flags & XkbSA_GroupAbsolute)
+ locked_group = group;
+ else
+ locked_group += group;
+ }
-/*
- * Check a sym interpret match condition.
- * See [Lib] Table 18.1 for the logic.
- */
-static bool are_modifiers_matching(uint8_t mods, unsigned char match,
- uint8_t to_mods)
-{
- switch (match & XkbSI_OpMask) {
- case XkbSI_NoneOf:
- return (mods & to_mods) == 0;
- case XkbSI_AnyOfOrNone:
- return true;
- case XkbSI_AnyOf:
- return (mods & to_mods) != 0;
- case XkbSI_AllOf:
- return (mods & to_mods) == mods;
- case XkbSI_Exactly:
- return mods == to_mods;
+ break;
}
- return false;
+ /* Bring what was changed back into range. */
+ state->base_group = wrap_group_control(desc, base_group);
+ state->locked_group = wrap_group_control(desc, locked_group);
+ state->latched_group = wrap_group_control(desc, latched_group);
+ update_effective_group(desc, state);
+ return true;
}
/*
- * After we figured out the virtual mods from the compat component, we update
- * the effective modifiers in the key_types component accordingly, because we
- * use it extensively to find the correct shift level.
- */
-static void init_key_types(struct xkb_desc *desc)
+ * Updates the modifiers state.
+ * See [Lib] Table 17.1 for logic.
+ * */
+static bool process_mod_action(struct xkb_desc *desc, struct xkb_state *state,
+ KeyCode keycode, enum kmscon_key_state key_state,
+ struct xkb_mod_action *action)
{
- int i, j;
- struct xkb_key_type *type;
- struct xkb_kt_map_entry *entry;
- struct xkb_mods *mods;
-
- for (i=0; i < desc->map->num_types; i++) {
- type = &desc->map->types[i];
- mods = &type->mods;
+ uint8_t mods;
+ uint8_t saved_mods;
+ uint8_t flags = action->flags;
- mods->mask = virtual_and_real_to_mask(desc, mods->vmods,
- mods->real_mods);
+ if (flags & XkbSA_UseModMapMods)
+ mods = desc->map->modmap[keycode];
+ else
+ mods = action->mask;
- for (j=0; j < type->map_count; j++) {
- entry = &type->map[j];
- mods = &entry->mods;
+ /*
+ * FIXME: Some actions here should be conditioned "and no keys are
+ * physically depressed when this key is released".
+ */
- mods->mask = virtual_and_real_to_mask(desc,
- mods->vmods, mods->real_mods);
+ switch (action->type) {
+ case XkbSA_SetMods:
+ if (key_state == KMSCON_KEY_PRESSED) {
+ state->base_mods |= mods;
+ } else if (key_state == KMSCON_KEY_RELEASED) {
+ state->base_mods &= ~mods;
+ if (flags & XkbSA_ClearLocks)
+ state->locked_mods &= ~mods;
+ }
- /*
- * If the entry's vmods are bound to something, it
- * should be active.
- */
- if (virtual_to_real_mods(desc, mods->vmods))
- entry->active = true;
+ break;
+ case XkbSA_LatchMods:
+ if (key_state == KMSCON_KEY_PRESSED) {
+ state->base_mods |= mods;
+ } else if (key_state == KMSCON_KEY_RELEASED) {
+ if (flags & XkbSA_ClearLocks) {
+ saved_mods = state->locked_mods;
+ state->locked_mods &= ~mods;
+ mods &= ~(mods & saved_mods);
+ }
+ if (flags & XkbSA_LatchToLock) {
+ saved_mods = mods;
+ mods = (mods & state->latched_mods);
+ state->locked_mods |= mods;
+ state->latched_mods &= ~mods;
+ mods = saved_mods & (~mods);
+ }
+ state->latched_mods |= mods;
}
- }
-}
-/*
- * Update the effective modifer mask of the various action objects after we
- * initialized the virtual modifiers from compat. The only actions we change
- * here are the mod_action types.
- */
-static void init_actions(struct xkb_desc *desc)
-{
- int i;
- union xkb_action *action;
- struct xkb_sym_interpret *si;
+ break;
+ case XkbSA_LockMods:
+ /* We fake a little here and toggle both on and off on keypress. */
+ if (key_state == KMSCON_KEY_PRESSED) {
+ state->base_mods |= mods;
+ state->locked_mods ^= mods;
+ } else if (key_state == KMSCON_KEY_RELEASED) {
+ state->base_mods &= ~mods;
+ }
- for (i=0; i < desc->server->num_acts; i++) {
- action = &desc->server->acts[i];
- init_action(desc, action);
+ break;
}
- for (i=0; i < desc->compat->num_si; i++) {
- si = &desc->compat->sym_interpret[i];
- action = (union xkb_action *)&si->act;
- init_action(desc, action);
- }
+ update_effective_mods(desc, state);
+ return true;
}
-static void init_action(struct xkb_desc *desc, union xkb_action *action)
+/*
+ * An action dispatcher. The return value indicates whether the keyboard state
+ * was changed.
+ */
+static bool process_action(struct xkb_desc *desc, struct xkb_state *state,
+ KeyCode keycode, enum kmscon_key_state key_state,
+ union xkb_action *action)
{
- struct xkb_mod_action *mod_act;
+ if (!action)
+ return false;
switch (action->type) {
+ case XkbSA_NoAction:
+ break;
case XkbSA_SetMods:
case XkbSA_LatchMods:
case XkbSA_LockMods:
- mod_act = &action->mods;
-
- mod_act->mask = virtual_and_real_to_mask(desc, mod_act->vmods,
- mod_act->real_mods);
+ return process_mod_action(desc, state, keycode, key_state,
+ &action->mods);
+ break;
+ case XkbSA_SetGroup:
+ case XkbSA_LatchGroup:
+ case XkbSA_LockGroup:
+ return process_group_action(desc, state, keycode, key_state,
+ &action->group);
+ break;
+ default:
+ /*
+ * Don't handle other actions.
+ * Note: There may be useful stuff here, like TerminateServer
+ * or SwitchScreen.
+ */
break;
}
+
+ return false;
}
/*
- * Update to the effective modifier mask of the indicator objects. We use them
- * to dicover which modifiers to match with which leds.
+ * The shift level to use for the keycode (together with the group) is
+ * determined by the modifier state. There are various "types" of ways to use
+ * the modifiers to shift the keycode; this is determined by the key_type
+ * object mapped to the (keycode, group) pair.
*/
-static void init_indicators(struct xkb_desc *desc)
+static uint16_t find_shift_level(struct xkb_desc *desc, KeyCode keycode,
+ uint8_t mods, uint8_t group)
{
int i;
- struct xkb_indicator_map *im;
- struct xkb_mods *mods;
+ struct xkb_key_type *type;
+ struct xkb_kt_map_entry *entry;
+ uint8_t masked_mods;
- for (i=0; i < XkbNumIndicators; i++) {
- im = &desc->indicators->maps[i];
- mods = &im->mods;
+ type = XkbKeyKeyType(desc, keycode, group);
- mods->mask = virtual_and_real_to_mask(desc, mods->vmods,
- mods->real_mods);
- }
-}
+ masked_mods = type->mods.mask & mods;
-/*
- * We don't do soft repeat currently, but we use the controls to filter out
- * which evdev repeats to send.
- */
-static void init_autorepeat(struct xkb_desc *desc)
-{
- /*
- * This is taken from <xserver>/include/site.h
- * If a bit is off for a keycode, it should not repeat.
- */
- static const char DEFAULT_AUTOREPEATS[XkbPerKeyBitArraySize] = {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ for (i=0; i < type->map_count; i++) {
+ entry = &type->map[i];
- memcpy(desc->ctrls->per_key_repeat,
- DEFAULT_AUTOREPEATS, XkbPerKeyBitArraySize);
+ if (!entry->active)
+ continue;
- desc->ctrls->enabled_ctrls |= XkbRepeatKeysMask;
+ /*
+ * Must match exactly after we masked it with the key_type's
+ * mask.
+ */
+ if (entry->mods.mask == masked_mods)
+ return entry->level;
+ }
+
+ /* The default is LevelOne. */
+ return 0;
}
/* Whether to send out a repeat event for the key. */
return true;
}
-static uint8_t virtual_to_real_mods(struct xkb_desc *desc, uint16_t vmods)
+int kmscon_kbd_process_key(struct kmscon_kbd *kbd,
+ enum kmscon_key_state key_state,
+ uint16_t code,
+ struct kmscon_input_event *out)
{
- int i;
- uint32_t bit;
- uint8_t mods;
+ struct xkb_desc *desc;
+ struct xkb_state *state;
+ KeyCode keycode;
+ uint8_t group;
+ uint16_t shift_level;
+ uint32_t sym;
+ union xkb_action *action;
+ bool state_changed, event_filled;
- mods = 0x00;
+ if (!kbd)
+ return -EINVAL;
- for (i=0, bit=0x01; i < XkbNumVirtualMods; i++, bit<<=1)
- if (vmods & bit)
- mods |= desc->server->vmods[i];
+ desc = kbd->desc->desc;
+ state = &kbd->state;
- return mods;
+ keycode = code + desc->min_key_code;
+
+ /* Valid keycode. */
+ if (!XkbKeycodeInRange(desc, keycode))
+ return -ENOKEY;
+ /* Active keycode. */
+ if (XkbKeyNumSyms(desc, keycode) == 0)
+ return -ENOKEY;
+ /* Unwanted repeat. */
+ if (key_state == KMSCON_KEY_REPEATED &&
+ !should_key_repeat(desc, keycode))
+ return -ENOKEY;
+
+ group = wrap_group_keycode(desc, keycode, state->group);
+ shift_level = find_shift_level(desc, keycode, state->mods, group);
+ sym = XkbKeySymEntry(desc, keycode, shift_level, group);
+
+ state_changed = false;
+ if (key_state != KMSCON_KEY_REPEATED) {
+ action = XkbKeyActionEntry(desc, keycode, shift_level, group);
+ state_changed = process_action(desc, state, keycode,
+ key_state, action);
+ }
+
+ event_filled = false;
+ if (key_state != KMSCON_KEY_RELEASED) {
+ out->keycode = code;
+ out->keysym = sym;
+ /* 1-to-1 match - this might change. */
+ out->mods = state->mods;
+ out->unicode = KeysymToUcs4(sym);
+
+ if (out->unicode == 0)
+ out->unicode = KMSCON_INPUT_INVALID;
+
+ event_filled = true;
+ }
+
+ if (state_changed) {
+ /* Release latches. */
+ state->latched_mods = 0;
+ update_effective_mods(desc, state);
+ state->latched_group = 0;
+ update_effective_group(desc, state);
+ }
+
+ return event_filled ? 0 : -ENOKEY;
}
-static uint8_t virtual_and_real_to_mask(struct xkb_desc *desc,
- uint16_t vmods, uint8_t real_mods)
+static struct xkb_indicator_map *find_indicator_map(struct xkb_desc *desc,
+ const char *indicator_name)
{
- uint8_t mods = 0x00;
+ int i;
+ uint32_t atom;
- mods |= real_mods;
- mods |= virtual_to_real_mods(desc, vmods);
+ atom = xkb_intern_atom(indicator_name);
- return mods;
+ for (i=0; i < XkbNumIndicators; i++)
+ if (desc->names->indicators[i] == atom)
+ return &desc->indicators->maps[i];
+
+ return NULL;
}
/*
* We don't reset the locked group, this should survive a VT switch, etc. The
* locked modifiers are reset according to the keyboard LEDs.
*/
-void kmscon_xkb_reset_state(struct xkb_desc *desc, struct xkb_state *state,
- int evdev_fd)
+void kmscon_kbd_reset(struct kmscon_kbd *kbd, int evdev_fd)
{
int i;
+ struct xkb_desc *desc;
+ struct xkb_state *state;
/* One long should be enough (LED_MAX is currently 16). */
unsigned long leds, bit;
struct xkb_indicator_map *im;
+ if (!kbd)
+ return;
+
+ desc = kbd->desc->desc;
+ state = &kbd->state;
+
state->group = 0;
state->base_group = 0;
state->latched_group = 0;
errno = 0;
ioctl(evdev_fd, EVIOCGLED(sizeof(leds)), &leds);
if (errno) {
- log_warning("input: couldn't discover modifiers state: %m\n");
+ log_warning("kbd-xkb: cannot discover modifiers state: %m\n");
return;
}
break;
}
- /* Only locked modifiers really matter here. */
- if (im && im->which_mods == XkbIM_UseLocked)
- state->locked_mods |= im->mods.mask;
- }
+ /* Only locked modifiers really matter here. */
+ if (im && im->which_mods == XkbIM_UseLocked)
+ state->locked_mods |= im->mods.mask;
+ }
+
+ update_effective_mods(desc, state);
+ update_effective_group(desc, state);
+}
+
+/*
+ * We don't do soft repeat currently, but we use the controls to filter out
+ * which evdev repeats to send.
+ */
+static void init_autorepeat(struct xkb_desc *desc)
+{
+ /*
+ * This is taken from <xserver>/include/site.h
+ * If a bit is off for a keycode, it should not repeat.
+ */
+ static const char DEFAULT_AUTOREPEATS[XkbPerKeyBitArraySize] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ memcpy(desc->ctrls->per_key_repeat,
+ DEFAULT_AUTOREPEATS, XkbPerKeyBitArraySize);
+
+ desc->ctrls->enabled_ctrls |= XkbRepeatKeysMask;
+}
+
+/*
+ * Update to the effective modifier mask of the indicator objects. We use them
+ * to dicover which modifiers to match with which leds.
+ */
+static void init_indicators(struct xkb_desc *desc)
+{
+ int i;
+ struct xkb_indicator_map *im;
+ struct xkb_mods *mods;
+
+ for (i=0; i < XkbNumIndicators; i++) {
+ im = &desc->indicators->maps[i];
+ mods = &im->mods;
+
+ mods->mask = virtual_and_real_to_mask(desc, mods->vmods,
+ mods->real_mods);
+ }
+}
+
+static void init_action(struct xkb_desc *desc, union xkb_action *action)
+{
+ struct xkb_mod_action *mod_act;
+
+ switch (action->type) {
+ case XkbSA_SetMods:
+ case XkbSA_LatchMods:
+ case XkbSA_LockMods:
+ mod_act = &action->mods;
+
+ mod_act->mask = virtual_and_real_to_mask(desc, mod_act->vmods,
+ mod_act->real_mods);
+ break;
+ }
+}
+
+/*
+ * Update the effective modifer mask of the various action objects after we
+ * initialized the virtual modifiers from compat. The only actions we change
+ * here are the mod_action types.
+ */
+static void init_actions(struct xkb_desc *desc)
+{
+ int i;
+ union xkb_action *action;
+ struct xkb_sym_interpret *si;
+
+ for (i=0; i < desc->server->num_acts; i++) {
+ action = &desc->server->acts[i];
+ init_action(desc, action);
+ }
+
+ for (i=0; i < desc->compat->num_si; i++) {
+ si = &desc->compat->sym_interpret[i];
+ action = (union xkb_action *)&si->act;
+ init_action(desc, action);
+ }
+}
+
+/*
+ * After we figured out the virtual mods from the compat component, we update
+ * the effective modifiers in the key_types component accordingly, because we
+ * use it extensively to find the correct shift level.
+ */
+static void init_key_types(struct xkb_desc *desc)
+{
+ int i, j;
+ struct xkb_key_type *type;
+ struct xkb_kt_map_entry *entry;
+ struct xkb_mods *mods;
+
+ for (i=0; i < desc->map->num_types; i++) {
+ type = &desc->map->types[i];
+ mods = &type->mods;
+
+ mods->mask = virtual_and_real_to_mask(desc, mods->vmods,
+ mods->real_mods);
+
+ for (j=0; j < type->map_count; j++) {
+ entry = &type->map[j];
+ mods = &entry->mods;
+
+ mods->mask = virtual_and_real_to_mask(desc,
+ mods->vmods, mods->real_mods);
- update_effective_mods(desc, state);
- update_effective_group(desc, state);
+ /*
+ * If the entry's vmods are bound to something, it
+ * should be active.
+ */
+ if (virtual_to_real_mods(desc, mods->vmods))
+ entry->active = true;
+ }
+ }
}
-static struct xkb_indicator_map *find_indicator_map(struct xkb_desc *desc,
- const char *indicator_name)
+/*
+ * Check a sym interpret match condition.
+ * See [Lib] Table 18.1 for the logic.
+ */
+static bool are_modifiers_matching(uint8_t mods, unsigned char match,
+ uint8_t to_mods)
{
- int i;
- uint32_t atom;
-
- atom = xkb_intern_atom(indicator_name);
-
- for (i=0; i < XkbNumIndicators; i++)
- if (desc->names->indicators[i] == atom)
- return &desc->indicators->maps[i];
+ switch (match & XkbSI_OpMask) {
+ case XkbSI_NoneOf:
+ return (mods & to_mods) == 0;
+ case XkbSI_AnyOfOrNone:
+ return true;
+ case XkbSI_AnyOf:
+ return (mods & to_mods) != 0;
+ case XkbSI_AllOf:
+ return (mods & to_mods) == mods;
+ case XkbSI_Exactly:
+ return mods == to_mods;
+ }
- return NULL;
+ return false;
}
/*
- * The shift level to use for the keycode (together with the group) is
- * determined by the modifier state. There are various "types" of ways to use
- * the modifiers to shift the keycode; this is determined by the key_type
- * object mapped to the (keycode, group) pair.
+ * Look for the most specific symbol interpretation for the keysym.
+ * See [xserver] XKBMisc.c:_XkbFindMatchingInterp() for the equivalent.
*/
-static uint16_t find_shift_level(struct xkb_desc *desc, KeyCode keycode,
- uint8_t mods, uint8_t group)
+static struct xkb_sym_interpret *find_sym_interpret(struct xkb_desc *desc,
+ uint32_t sym, uint16_t level, uint8_t key_modmap)
{
int i;
- struct xkb_key_type *type;
- struct xkb_kt_map_entry *entry;
- uint8_t masked_mods;
+ struct xkb_sym_interpret *si;
+ struct xkb_sym_interpret *all_syms_si;
- type = XkbKeyKeyType(desc, keycode, group);
+ all_syms_si = NULL;
- masked_mods = type->mods.mask & mods;
+ /*
+ * If we find a matching interpret specific to our symbol, we return
+ * it immediatly.
+ * If we didn't find any, we return the first matching all-catching
+ * interpret.
+ */
- for (i=0; i < type->map_count; i++) {
- entry = &type->map[i];
+ for (i=0; i < desc->compat->num_si; i++) {
+ si = &desc->compat->sym_interpret[i];
- if (!entry->active)
+ if (si->sym != sym && si->sym != 0)
continue;
/*
- * Must match exactly after we masked it with the key_type's
- * mask.
+ * If the interpret specified UseModMapMods=level1, the sym
+ * must be in the first level of its group.
+ * Note: [xserver] and [Lib] do different things here, and it
+ * doesn't seem to matter much. So it's commented for now.
*/
- if (entry->mods.mask == masked_mods)
- return entry->level;
+ /* if (si->match&XkbSI_LevelOneOnly && level != 0) */
+ /* continue; */
+
+ if (!are_modifiers_matching(si->mods, si->match, key_modmap))
+ continue;
+
+ if (si->sym != 0)
+ return si;
+ else if (all_syms_si == NULL)
+ all_syms_si = si;
}
- /* The default is LevelOne. */
- return 0;
+ return all_syms_si;
}
/*
- * This is the entry point to the XKB processing.
- * We get an evdev scancode and the keyboard state, and should put out a
- * proper input event.
- * Some evdev input events shouldn't result in us sending an input event
- * (e.g. a key release). The return value indicated whether the input_event
- * was filled out or not.
+ * Allocate slots for a keycode in the key-action mapping array. xkbcommon
+ * doesn't do this by itself for actions from compat (that is almost all of
+ * them).
+ * See [xserver] XKBMAlloc.c:XkbResizeKeyActions() for the equivalent.
*/
-bool kmscon_xkb_process_evdev_key(struct xkb_desc *desc,
- struct xkb_state *state,
- enum kmscon_key_state key_state,
- uint16_t code,
- struct kmscon_input_event *out)
+static int allocate_key_acts(struct xkb_desc *desc, uint8_t keycode)
{
- KeyCode keycode;
- uint8_t group;
- uint16_t shift_level;
- uint32_t sym;
- union xkb_action *action;
- bool state_changed, event_filled;
-
- keycode = code + desc->min_key_code;
-
- /* Valid keycode. */
- if (!XkbKeycodeInRange(desc, keycode))
- return false;
- /* Active keycode. */
- if (XkbKeyNumSyms(desc, keycode) == 0)
- return false;
- /* Unwanted repeat. */
- if (key_state == KMSCON_KEY_REPEATED &&
- !should_key_repeat(desc, keycode))
- return false;
-
- group = wrap_group_keycode(desc, keycode, state->group);
- shift_level = find_shift_level(desc, keycode, state->mods, group);
- sym = XkbKeySymEntry(desc, keycode, shift_level, group);
+ unsigned short index;
+ union xkb_action *acts;
+ struct xkb_server_map *server;
+ int sym_count;
+ int new_needed;
+ unsigned short new_num_acts;
+ unsigned short new_size_acts;
- state_changed = false;
- if (key_state != KMSCON_KEY_REPEATED) {
- action = XkbKeyActionEntry(desc, keycode, shift_level, group);
- state_changed = process_action(desc, state, keycode,
- key_state, action);
- }
+ server = desc->server;
+ sym_count = XkbKeyNumSyms(desc, keycode);
- event_filled = false;
- if (key_state != KMSCON_KEY_RELEASED && !state_changed) {
- out->keycode = code;
- out->keysym = sym;
- /* 1-to-1 match - this might change. */
- out->mods = state->mods;
- out->unicode = KeysymToUcs4(sym);
+ /*
+ * num_acts is the occupied slots, size_acts is the current total
+ * capacity.
+ */
- if (out->unicode == 0)
- out->unicode = KMSCON_INPUT_INVALID;
+ if (XkbKeyHasActions(desc, keycode)) {
+ /* An array is already allocated for this key. */
- event_filled = true;
- }
+ /* index = server->key_acts[keycode]; */
+ } else if (server->num_acts + sym_count <= server->size_acts) {
+ /* There's enough left over space; use it. */
- if (state_changed) {
- /* Release latches. */
- state->latched_mods = 0;
- update_effective_mods(desc, state);
- state->latched_group = 0;
- update_effective_group(desc, state);
- }
+ index = server->num_acts;
+ server->key_acts[keycode] = index;
+ server->num_acts += sym_count;
+ } else {
+ /* Need to allocate new space. */
- return event_filled;
-}
+ index = server->num_acts;
+ new_num_acts = server->num_acts + sym_count;
+ new_needed = sym_count - (server->size_acts - new_num_acts);
+ /* Add some extra to avoid repeated reallocs. */
+ new_size_acts = server->size_acts + new_needed + 8;
-/*
- * An action dispatcher. The return value indicates whether the keyboard state
- * was changed.
- */
-static bool process_action(struct xkb_desc *desc, struct xkb_state *state,
- KeyCode keycode, enum kmscon_key_state key_state,
- union xkb_action *action)
-{
- if (!action)
- return false;
+ acts = realloc(server->acts,
+ sizeof(union xkb_action) * new_size_acts);
+ if (!acts)
+ return -ENOMEM;
- switch (action->type) {
- case XkbSA_NoAction:
- break;
- case XkbSA_SetMods:
- case XkbSA_LatchMods:
- case XkbSA_LockMods:
- return process_mod_action(desc, state, keycode, key_state,
- &action->mods);
- break;
- case XkbSA_SetGroup:
- case XkbSA_LatchGroup:
- case XkbSA_LockGroup:
- return process_group_action(desc, state, keycode, key_state,
- &action->group);
- break;
- default:
- /*
- * Don't handle other actions.
- * Note: There may be useful stuff here, like TerminateServer
- * or SwitchScreen.
- */
- break;
+ /* XkbSA_NoAction is 0x00 so we're good. */
+ memset(acts+index, 0, sym_count);
+ server->key_acts[keycode] = index;
+ server->num_acts = new_num_acts;
+ server->size_acts = new_size_acts;
+ server->acts = acts;
}
- return false;
+ return 0;
}
-/*
- * Updates the modifiers state.
- * See [Lib] Table 17.1 for logic.
- * */
-static bool process_mod_action(struct xkb_desc *desc, struct xkb_state *state,
- KeyCode keycode, enum kmscon_key_state key_state,
- struct xkb_mod_action *action)
+static int init_compat_for_keysym(struct xkb_desc *desc, KeyCode keycode,
+ uint8_t group, uint16_t level)
{
- uint8_t mods;
- uint8_t saved_mods;
- uint8_t flags = action->flags;
-
- if (flags & XkbSA_UseModMapMods)
- mods = desc->map->modmap[keycode];
- else
- mods = action->mask;
-
- /*
- * FIXME: Some actions here should be conditioned "and no keys are
- * physically depressed when this key is released".
- */
-
- switch (action->type) {
- case XkbSA_SetMods:
- if (key_state == KMSCON_KEY_PRESSED) {
- state->base_mods |= mods;
- } else if (key_state == KMSCON_KEY_RELEASED) {
- state->base_mods &= ~mods;
- if (flags & XkbSA_ClearLocks)
- state->locked_mods &= ~mods;
- }
+ int ret;
+ uint8_t key_modmap;
+ uint32_t sym;
+ struct xkb_sym_interpret *si;
+ union xkb_action *action;
- break;
- case XkbSA_LatchMods:
- if (key_state == KMSCON_KEY_PRESSED) {
- state->base_mods |= mods;
- } else if (key_state == KMSCON_KEY_RELEASED) {
- if (flags & XkbSA_ClearLocks) {
- saved_mods = state->locked_mods;
- state->locked_mods &= ~mods;
- mods &= ~(mods & saved_mods);
- }
- if (flags & XkbSA_LatchToLock) {
- saved_mods = mods;
- mods = (mods & state->latched_mods);
- state->locked_mods |= mods;
- state->latched_mods &= ~mods;
- mods = saved_mods & (~mods);
- }
- state->latched_mods |= mods;
- }
+ key_modmap = desc->map->modmap[keycode];
+ sym = XkbKeySymEntry(desc, keycode, level, group);
+ si = find_sym_interpret(desc, sym, level, key_modmap);
- break;
- case XkbSA_LockMods:
- /* We fake a little here and toggle both on and off on keypress. */
- if (key_state == KMSCON_KEY_PRESSED) {
- state->base_mods |= mods;
- state->locked_mods ^= mods;
- } else if (key_state == KMSCON_KEY_RELEASED) {
- state->base_mods &= ~mods;
- }
+ if (!si)
+ return 0;
- break;
+ /* Set the key action mapping. */
+ if (si->act.type != XkbSA_NoAction) {
+ ret = allocate_key_acts(desc, keycode);
+ if (ret)
+ return ret;
+
+ action = XkbKeyActionEntry(desc, keycode, level, group);
+ *action = (union xkb_action)si->act;
}
- update_effective_mods(desc, state);
- return true;
+ /* Set the key virtual modifier mapping. */
+ if (si->virtual_mod != XkbNoModifier)
+ desc->server->vmodmap[keycode] |= 0x01 << si->virtual_mod;
+
+ return 0;
}
-/*
- * Updates the group state.
- * See [Lib] Table 17.4 for logic.
- */
-static bool process_group_action(struct xkb_desc *desc, struct xkb_state *state,
- KeyCode keycode, enum kmscon_key_state key_state,
- struct xkb_group_action *action)
+static int init_compat_for_keycode(struct xkb_desc *desc, KeyCode keycode)
{
- int16_t group = action->group;
- uint8_t flags = action->flags;
+ int ret;
+ int i, bit;
+
+ uint8_t group;
+ uint16_t level;
+ int num_groups;
+ int num_levels;
/*
- * action->group is signed and may be negative if GroupAbsolute
- * is not set. A group itself cannot be negative and is unsigend.
- * Therefore we extend these to int16 to avoid underflow and
- * signedness issues. Be careful!
+ * It's possible that someone had set some actions for the keycode
+ * through the symbols file, and so we shouldn't override with the
+ * compat. This is very uncommon though, only used by the breaks_caps
+ * option here.
*/
- int16_t base_group = state->base_group;
- int16_t latched_group = state->latched_group;
- int16_t locked_group = state->locked_group;
+ if (XkbKeyHasActions(desc, keycode))
+ return 0;
+
+ num_groups = XkbKeyNumGroups(desc, keycode);
/*
- * FIXME: Some actions here should be conditioned "and no keys are
- * physically depressed when this key is released".
+ * We need to track the sym level in order to support LevelOneOnly,
+ * which is used in some symbol interpretations.
*/
- switch (action->type) {
- case XkbSA_SetGroup:
- if (key_state == KMSCON_KEY_PRESSED) {
- if (flags & XkbSA_GroupAbsolute)
- base_group = group;
- else
- base_group += group;
- } else if (key_state == KMSCON_KEY_RELEASED) {
- if (flags & XkbSA_ClearLocks)
- locked_group = 0;
- }
+ for (group=0, i=0; group < num_groups; group++) {
+ num_levels = XkbKeyGroupWidth(desc, keycode, group);
- break;
- case XkbSA_LatchGroup:
- if (key_state == KMSCON_KEY_PRESSED) {
- if (flags & XkbSA_GroupAbsolute)
- base_group = group;
- else
- base_group += group;
- } else if (key_state == KMSCON_KEY_RELEASED) {
- if ((flags & XkbSA_LatchToLock) && latched_group) {
- locked_group += group;
- latched_group -= group;
- } else {
- latched_group += group;
- }
+ for (level=0; level < num_levels; level++) {
+ ret = init_compat_for_keysym(desc, keycode,
+ group, level);
+ if (ret)
+ return ret;
}
+ }
- break;
- case XkbSA_LockGroup:
- if (key_state == KMSCON_KEY_PRESSED) {
- if (flags & XkbSA_GroupAbsolute)
- locked_group = group;
- else
- locked_group += group;
- }
+ /*
+ * Translate the virtual modifiers bound to this key to the real
+ * modifiers bound to this key.
+ * See [Lib] 17.4 for vmodmap and friends.
+ */
+ for (i=0, bit=0x01; i < XkbNumVirtualMods; i++, bit<<=1)
+ if (bit&desc->server->vmodmap[keycode])
+ desc->server->vmods[i] |= desc->map->modmap[keycode];
- break;
- }
+ return 0;
+}
- /* Bring what was changed back into range. */
- state->base_group = wrap_group_control(desc, base_group);
- state->locked_group = wrap_group_control(desc, locked_group);
- state->latched_group = wrap_group_control(desc, latched_group);
- update_effective_group(desc, state);
- return true;
+/*
+ * This mostly fills out the keycode-action mapping and puts the virtual
+ * modifier mappings in the right place.
+ */
+static void init_compat(struct xkb_desc *desc)
+{
+ /* If we use KeyCode it overflows. */
+ unsigned int keycode;
+
+ for (keycode = desc->min_key_code; keycode <= desc->max_key_code; keycode++)
+ init_compat_for_keycode(desc, keycode);
}
/*
- * Helper function for the wrap_group_* functions.
- * See [Lib] 11.7.1 for the rules.
+ * Create a ready-to-use xkb description object. It is used in most places
+ * having to do with XKB.
*/
-static uint8_t wrap_group(int16_t group, int num_groups, uint8_t group_info)
+int kmscon_kbd_desc_new(struct kmscon_kbd_desc **out, const char *layout,
+ const char *variant, const char *options)
{
- /* No need for wrapping. */
- if (XkbIsLegalGroup(group) && group < num_groups)
- return group;
+ struct kmscon_kbd_desc *desc;
- switch (XkbOutOfRangeGroupAction(group_info)) {
- case XkbWrapIntoRange:
- /*
- * C99 says a negative dividend in a modulo operation
- * will always give a negative result.
- */
- if (group < 0)
- return num_groups + (group % num_groups);
- else
- return group % num_groups;
+ if (!out)
+ return -EINVAL;
- case XkbClampIntoRange:
- /* This one seems to be unused. */
- return num_groups - 1;
+ log_debug("kbd-xkb: new keyboard description (%s, %s, %s)\n",
+ layout, variant, options);
- case XkbRedirectIntoRange:
- /* This one seems to be unused. */
- group = XkbOutOfRangeGroupNumber(group_info);
- /* If it's _still_ out of range, use the first group. */
- if (group >= num_groups)
- return 0;
+ desc = malloc(sizeof(*desc));
+ if (!desc)
+ return -ENOMEM;
+
+ memset(desc, 0, sizeof(*desc));
+ desc->ref = 1;
+
+ struct xkb_rule_names rmlvo = {
+ .rules = "evdev",
+ .model = "evdev",
+ .layout = layout,
+ .variant = variant,
+ .options = options,
+ };
+
+ desc->desc = xkb_compile_keymap_from_rules(&rmlvo);
+ if (!desc->desc) {
+ log_err("kbd-xkb: cannot compile keymap from rules\n");
+ free(desc);
+ return -EFAULT;
}
+ /* The order of these is important! */
+ init_compat(desc->desc);
+ init_key_types(desc->desc);
+ init_actions(desc->desc);
+ init_indicators(desc->desc);
+ init_autorepeat(desc->desc);
+
+ *out = desc;
return 0;
}
-/*
- * Wrap an arbitrary group into a legal effective global group according to
- * the GroupsWrap control.
- * (Group actions mostly act on the group number in a relative manner [e.g.
- * +1, -1]. So if we have N groups, the effective group is N-1, and we get a
- * SetGroup +1, this tells us what to do.)
- */
-static uint8_t wrap_group_control(struct xkb_desc *desc, int16_t group)
+void kmscon_kbd_desc_ref(struct kmscon_kbd_desc *desc)
{
- int num_groups;
- uint8_t group_info;
-
- num_groups = desc->ctrls->num_groups;
- group_info = desc->ctrls->groups_wrap;
+ if (!desc)
+ return;
- return wrap_group(group, num_groups, group_info);
+ ++desc->ref;
}
-/*
- * Wrap the effective global group to a legal group for the keycode, according
- * to the rule specified for the key.
- * (Some keycodes may have more groups than others, and so the effective
- * group may not make sense for a certain keycode).
- */
-static uint8_t wrap_group_keycode(struct xkb_desc *desc, KeyCode keycode,
- int16_t group)
+void kmscon_kbd_desc_unref(struct kmscon_kbd_desc *desc)
{
- int num_groups;
- uint8_t group_info;
+ if (!desc || !desc->ref)
+ return;
- num_groups = XkbKeyNumGroups(desc, keycode);
- group_info = XkbKeyGroupInfo(desc, keycode);
+ if (--desc->ref)
+ return;
- return wrap_group(group, num_groups, group_info);
-}
+ log_debug("kbd-xkb: destroying keyboard description\n");
-/*
- * Need to update the effective mods after any changes to the base, latched or
- * locked mods.
- */
-static void update_effective_mods(struct xkb_desc *desc,
- struct xkb_state *state)
-{
- state->mods = state->base_mods | state->latched_mods |
- state->locked_mods;
+ /*
+ * XXX: Seems this doesn't really free everything, valgrind shows some
+ * big leaks from libxkbcommon. Hopefully we use just one up until we
+ * exit.
+ */
+ xkb_free_keymap(desc->desc);
+ free(desc);
}
-/*
- * Need to update the effective group after any changes to the base, latched or
- * locked group.
- */
-static void update_effective_group(struct xkb_desc *desc,
- struct xkb_state *state)
+void kmscon_kbd_keysym_to_string(uint32_t keysym, char *str, size_t size)
{
- int16_t group;
-
- /* Update the effective group. */
- group = state->base_group + state->locked_group + state->latched_group;
- state->group = wrap_group_control(desc, group);
+ xkb_keysym_to_string(keysym, str, size);
}