From 883baf0a458b191efd615b2acd7387021053ca0f Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Fri, 20 Sep 2024 18:44:35 +0200 Subject: [PATCH] wip: overlays --- include/xkbcommon/xkbcommon.h | 13 +- meson.build | 1 + src/keymap.c | 4 + src/keymap.h | 21 ++- src/state.c | 251 +++++++++++++++++++++++++++++++++- src/state.h | 10 ++ src/text.c | 35 ++++- src/text.h | 4 + src/x11/keymap.c | 4 + src/xkbcomp/action.c | 10 +- src/xkbcomp/compat.c | 20 ++- src/xkbcomp/expr.c | 49 +++++++ src/xkbcomp/expr.h | 5 + src/xkbcomp/keymap-dump.c | 22 ++- src/xkbcomp/symbols.c | 110 ++++++++++++++- tools/interactive-evdev.c | 12 +- tools/tools-common.c | 10 ++ xkbcommon.map | 5 + 18 files changed, 567 insertions(+), 19 deletions(-) create mode 100644 src/state.h diff --git a/include/xkbcommon/xkbcommon.h b/include/xkbcommon/xkbcommon.h index 219d197ba..553a4565b 100644 --- a/include/xkbcommon/xkbcommon.h +++ b/include/xkbcommon/xkbcommon.h @@ -1438,7 +1438,15 @@ enum xkb_state_component { * Use this unless you explicitly care how the state came about. */ XKB_STATE_LAYOUT_EFFECTIVE = (1 << 7), /** LEDs (derived from the other state components). */ - XKB_STATE_LEDS = (1 << 8) + XKB_STATE_LEDS = (1 << 8), + /** TODO: doc */ + XKB_STATE_OVERLAYS_SET = (1 << 9), + /** TODO: doc */ + XKB_STATE_OVERLAYS_LOCKED = (1 << 10), + /** TODO: doc */ + XKB_STATE_OVERLAYS_EFFECTIVE = (1 << 11), + /** TODO: doc */ + XKB_STATE_OVERLAID_KEYS = (1 << 12), }; /** @@ -1502,6 +1510,9 @@ xkb_state_update_mask(struct xkb_state *state, xkb_layout_index_t latched_layout, xkb_layout_index_t locked_layout); +xkb_keycode_t +xkb_state_key_get_overlay_keycode(struct xkb_state *state, xkb_keycode_t kc); + /** * Get the keysyms obtained from pressing a particular key in a given * keyboard state. diff --git a/meson.build b/meson.build index ff3a731d7..6c51bca6c 100644 --- a/meson.build +++ b/meson.build @@ -251,6 +251,7 @@ libxkbcommon_sources = [ 'src/messages-codes.h', 'src/scanner-utils.h', 'src/state.c', + 'src/state.h', 'src/text.c', 'src/text.h', 'src/utf8.c', diff --git a/src/keymap.c b/src/keymap.c index 0291aedbb..1917d474d 100644 --- a/src/keymap.c +++ b/src/keymap.c @@ -82,6 +82,9 @@ xkb_keymap_unref(struct xkb_keymap *keymap) } free(key->groups); } + if (key->overlays) { + free(key->overlays); + } } free(keymap->keys); } @@ -92,6 +95,7 @@ xkb_keymap_unref(struct xkb_keymap *keymap) } free(keymap->types); } + free(keymap->incompatible_overlays); free(keymap->sym_interprets); free(keymap->key_aliases); free(keymap->group_names); diff --git a/src/keymap.h b/src/keymap.h index f7ea5bdf1..80e09f5b9 100644 --- a/src/keymap.h +++ b/src/keymap.h @@ -120,6 +120,15 @@ enum mod_type { }; #define MOD_REAL_MASK_ALL ((xkb_mod_mask_t) 0x000000ff) +/* TODO: doc */ +#define XKB_MAX_OVERLAYS 8 + +#if XKB_MAX_OVERLAYS > 8 + #error "Cannot store overlays indexes" +#endif +typedef uint8_t xkb_overlay_index_t; +typedef uint8_t xkb_overlay_mask_t; + enum xkb_action_type { ACTION_TYPE_NONE = 0, ACTION_TYPE_MOD_SET, @@ -165,11 +174,15 @@ enum xkb_action_controls { CONTROL_AX_FEEDBACK = (1 << 8), CONTROL_BELL = (1 << 9), CONTROL_IGNORE_GROUP_LOCK = (1 << 10), +#define _CONTROL_OVERLAY1_LOG2 11 +#define CONTROL_OVERLAYS (CONTROL_OVERLAY1 | CONTROL_OVERLAY2) + CONTROL_OVERLAY1 = (1 << 11), + CONTROL_OVERLAY2 = (1 << 12), CONTROL_ALL = \ (CONTROL_REPEAT | CONTROL_SLOW | CONTROL_DEBOUNCE | CONTROL_STICKY | \ CONTROL_MOUSEKEYS | CONTROL_MOUSEKEYS_ACCEL | CONTROL_AX | \ CONTROL_AX_TIMEOUT | CONTROL_AX_FEEDBACK | CONTROL_BELL | \ - CONTROL_IGNORE_GROUP_LOCK) + CONTROL_IGNORE_GROUP_LOCK | CONTROL_OVERLAY1 | CONTROL_OVERLAY2) }; enum xkb_match_operation { @@ -201,6 +214,7 @@ struct xkb_controls_action { enum xkb_action_type type; enum xkb_action_flags flags; enum xkb_action_controls ctrls; + xkb_overlay_mask_t overlays; }; struct xkb_pointer_default_action { @@ -336,6 +350,9 @@ struct xkb_key { xkb_keycode_t keycode; xkb_atom_t name; + xkb_overlay_mask_t overlays_mask; + xkb_keycode_t *overlays; + enum xkb_explicit_components explicit; xkb_mod_mask_t modmap; @@ -370,6 +387,8 @@ struct xkb_keymap { enum xkb_keymap_format format; enum xkb_action_controls enabled_ctrls; + xkb_overlay_index_t num_overlays; + xkb_overlay_mask_t *incompatible_overlays; xkb_keycode_t min_key_code; xkb_keycode_t max_key_code; diff --git a/src/state.c b/src/state.c index d5a60eb57..81b0c4584 100644 --- a/src/state.c +++ b/src/state.c @@ -63,6 +63,7 @@ #include "keymap.h" #include "keysym.h" +#include "state.h" #include "utf8.h" struct xkb_filter { @@ -76,6 +77,14 @@ struct xkb_filter { int refcnt; }; +struct xkb_overlaid_key { + xkb_keycode_t from; + xkb_keycode_t into; + uint8_t count; +}; + +typedef darray(struct xkb_overlaid_key) xkb_overlay_keys_t; + struct state_components { /* These may be negative, because of -1 group actions. */ int32_t base_group; /**< depressed */ @@ -88,6 +97,11 @@ struct state_components { xkb_mod_mask_t locked_mods; xkb_mod_mask_t mods; /**< effective */ + xkb_overlay_mask_t base_overlays; + xkb_overlay_mask_t locked_overlays; + xkb_overlay_mask_t overlays; /**< effective */ + xkb_overlay_keys_t overlaid_keys; + xkb_led_mask_t leds; }; @@ -104,6 +118,8 @@ struct xkb_state { */ xkb_mod_mask_t set_mods; xkb_mod_mask_t clear_mods; + xkb_overlay_mask_t set_overlays; + xkb_overlay_mask_t clear_overlays; /* * We mustn't clear a base modifier if there's another depressed key @@ -112,6 +128,7 @@ struct xkb_state { * the modifier should still be set. This keeps the count. */ int16_t mod_key_count[XKB_MAX_MODS]; + int16_t overlay_key_count[XKB_MAX_OVERLAYS]; int refcnt; darray(struct xkb_filter) filters; @@ -137,6 +154,51 @@ get_entry_for_key_state(struct xkb_state *state, const struct xkb_key *key, return get_entry_for_mods(type, active_mods); } +#define key_find_active_overlay(state, key, overlays, overlay) \ + /* Find active overlay for the key (exactly one possible) */ \ + for (overlay = 0; overlay < XKB_MAX_OVERLAYS; overlay++) { \ + if ((overlays) & (1u << overlay)) \ + break; \ + } + +static inline const struct xkb_key * +XkbOverlaidKey(struct xkb_state *state, xkb_keycode_t kc) +{ + const struct xkb_key *key = XkbKey(state->keymap, kc); + if (key && key->overlays_mask) { + struct xkb_overlaid_key *overlaid_key; + xkb_overlay_mask_t overlays; + /* Check registered overlaid keys */ + darray_foreach(overlaid_key, state->components.overlaid_keys) { + if (overlaid_key->from == kc) { + /* FIXME: remove debug */ + fprintf(stderr, "XkbOverlaidKey: overlaid %u -> %u (%u)\n", + kc, overlaid_key->into, overlaid_key->count); + return &state->keymap->keys[overlaid_key->into]; + } + } + /* Check current state */ + if ((overlays = key->overlays_mask & state->components.overlays)) { + xkb_overlay_index_t overlay; + key_find_active_overlay(state, key, overlays, overlay); + /* FIXME: remove debug */ + fprintf(stderr, "XkbOverlaidKey: overlaid %u -> %u?\n", + kc, key->overlays[overlay]); + return &state->keymap->keys[key->overlays[overlay]]; + } + /* FIXME: remove debug */ + fprintf(stderr, "XkbOverlaidKey: not overlaid %u\n", kc); + } + return key; +} + +XKB_EXPORT xkb_keycode_t +xkb_state_key_get_overlay_keycode(struct xkb_state *state, xkb_keycode_t kc) +{ + const struct xkb_key *key = XkbOverlaidKey(state, kc); + return key->keycode; +} + /** * Returns the level to use for the given key and state, or * XKB_LEVEL_INVALID. @@ -638,6 +700,65 @@ xkb_filter_mod_latch_func(struct xkb_state *state, return XKB_FILTER_CONTINUE; } +static void +xkb_filter_controls_set_new(struct xkb_state *state, struct xkb_filter *filter) +{ + /* FIXME: other controls */ + if (!filter->action.ctrls.overlays) { + filter->func = NULL; + return; + } + xkb_overlay_mask_t overlays = filter->action.ctrls.overlays; + for (xkb_overlay_index_t k = 0; k < state->keymap->num_overlays; k++) { + xkb_overlay_mask_t mask = 1u << k; + if (!(mask & filter->action.ctrls.overlays)) + continue; + if (state->keymap->incompatible_overlays[k] & overlays) + overlays &= ~mask; + } + if (overlays) { + state->set_overlays = overlays; + filter->priv = overlays; + /* FIXME: remove debug */ + fprintf(stderr, "xkb_filter_controls_set_new: %u\n", overlays); + } else { + filter->func = NULL; + /* FIXME: remove debug */ + fprintf(stderr, "xkb_filter_controls_set_new: no valid overlay\n"); + } +} + +static bool +xkb_filter_controls_set_func(struct xkb_state *state, + struct xkb_filter *filter, + const struct xkb_key *key, + enum xkb_key_direction direction) +{ + if (key != filter->key) { + // FIXME + // filter->action.mods.flags &= ~ACTION_LOCK_CLEAR; + return XKB_FILTER_CONTINUE; + } + + if (direction == XKB_KEY_DOWN) { + filter->refcnt++; + return XKB_FILTER_CONSUME; + } + else if (--filter->refcnt > 0) { + return XKB_FILTER_CONSUME; + } + + state->clear_overlays = filter->priv; + /* FIXME: remove debug */ + fprintf(stderr, "xkb_filter_controls_set_func: %u\n", state->clear_overlays); + // FIXME + // if (filter->action.mods.flags & ACTION_LOCK_CLEAR) + // state->components.locked_mods &= ~filter->action.mods.mods.mask; + + filter->func = NULL; + return XKB_FILTER_CONTINUE; +} + static const struct { void (*new)(struct xkb_state *state, struct xkb_filter *filter); bool (*func)(struct xkb_state *state, struct xkb_filter *filter, @@ -655,6 +776,9 @@ static const struct { xkb_filter_group_latch_func }, [ACTION_TYPE_GROUP_LOCK] = { xkb_filter_group_lock_new, xkb_filter_group_lock_func }, + [ACTION_TYPE_CTRL_SET] = { xkb_filter_controls_set_new, + xkb_filter_controls_set_func }, + /* TODO: ACTION_TYPE_CTRL_LOCK */ }; /** @@ -739,6 +863,7 @@ xkb_state_unref(struct xkb_state *state) xkb_keymap_unref(state->keymap); darray_free(state->filters); + darray_free(state->components.overlaid_keys); free(state); } @@ -811,6 +936,9 @@ xkb_state_update_derived(struct xkb_state *state) { xkb_layout_index_t wrapped; + state->components.overlays = (state->components.base_overlays | + state->components.locked_overlays); + state->components.mods = (state->components.base_mods | state->components.latched_mods | state->components.locked_mods); @@ -858,6 +986,15 @@ get_state_component_changes(const struct state_components *a, mask |= XKB_STATE_MODS_LATCHED; if (a->locked_mods != b->locked_mods) mask |= XKB_STATE_MODS_LOCKED; + if (a->base_overlays != b->base_overlays) + mask |= XKB_STATE_OVERLAYS_SET; + if (a->locked_overlays != b->locked_overlays) + mask |= XKB_STATE_OVERLAYS_LOCKED; + if (a->base_overlays != b->base_overlays) + mask |= XKB_STATE_OVERLAYS_SET; + if (a->overlays != b->overlays) + mask |= XKB_STATE_OVERLAYS_EFFECTIVE; + // TODO overlaid keys if (a->leds != b->leds) mask |= XKB_STATE_LEDS; @@ -874,12 +1011,78 @@ xkb_state_update_key(struct xkb_state *state, xkb_keycode_t kc, { xkb_mod_index_t i; xkb_mod_mask_t bit; + xkb_overlay_index_t overlay; + xkb_overlay_mask_t obit; struct state_components prev_components; + enum xkb_state_component extra_changes = 0; const struct xkb_key *key = XkbKey(state->keymap, kc); if (!key) return 0; + if (key->overlays_mask) { + struct xkb_overlaid_key *overlaid_key; + struct xkb_key *overlay_key = NULL; + /* FIXME: remove debug */ + fprintf(stderr, "xkb_state_update_key: overlayable key %u\n", kc); + darray_foreach(overlaid_key, state->components.overlaid_keys) { + if (overlaid_key->from == kc) { + /* Key already overlaid */ + overlay_key = &state->keymap->keys[overlaid_key->into]; + /* FIXME: remove debug */ + fprintf(stderr, "xkb_state_update_key: already overlaid %u (%u)\n", + kc, overlaid_key->count); + if (direction == XKB_KEY_DOWN) { + overlaid_key->count++; + } else if (overlaid_key->count > 1) { + overlaid_key->count--; + } else { + /* FIXME: remove debug */ + fprintf(stderr, "xkb_state_update_key: stop overlay key %u\n", kc); + overlaid_key->count = 0; + overlaid_key->from = XKB_KEYCODE_INVALID; + overlaid_key->into = XKB_KEYCODE_INVALID; + } + break; + } + } + if (!overlay_key && direction == XKB_KEY_DOWN && + (obit = key->overlays_mask & state->components.overlays)) { + key_find_active_overlay(state, key, obit, overlay); + /* Overlay key */ + overlay_key = &state->keymap->keys[key->overlays[overlay]]; + /* Add to overlaid keys */ + darray_foreach(overlaid_key, state->components.overlaid_keys) { + if (overlaid_key->from == XKB_KEYCODE_INVALID) { + /* Found free spot */ + overlaid_key->from = kc; + overlaid_key->into = overlay_key->keycode; + overlaid_key->count = 1; + key = NULL; + break; + } + } + if (key) { + /* No free spot, Append new entry */ + struct xkb_overlaid_key entry = { + .from = kc, + .into = overlay_key->keycode, + .count = 1 + }; + darray_append(state->components.overlaid_keys, entry); + } + } + if (overlay_key) { + /* Apply overlay key */ + key = overlay_key; + /* FIXME: remove debug */ + fprintf(stderr, "xkb_state_update_key: %u -> %u (%u)\n", + kc, overlay_key->keycode, state->components.overlays); + kc = overlay_key->keycode; + extra_changes |= XKB_STATE_OVERLAID_KEYS; + } + } + prev_components = state->components; state->set_mods = 0; @@ -906,9 +1109,36 @@ xkb_state_update_key(struct xkb_state *state, xkb_keycode_t kc, } } + for (overlay = 0, obit = 1; state->set_overlays; overlay++, obit <<= 1) { + if (state->set_overlays & obit) { + /* FIXME: remove debug */ + fprintf(stderr, "xkb_state_update_key: set overlay %u\n", overlay); + state->overlay_key_count[overlay]++; + state->components.base_overlays |= obit; + state->set_overlays &= ~obit; + } + } + + for (overlay = 0, obit = 1; state->clear_overlays; overlay++, obit <<= 1) { + if (state->clear_overlays & obit) { + state->overlay_key_count[overlay]--; + /* FIXME: remove debug */ + fprintf(stderr, "xkb_state_update_key: overlay %u key count %i\n", + overlay, state->overlay_key_count[overlay]); + if (state->overlay_key_count[overlay] <= 0) { + /* FIXME: remove debug */ + fprintf(stderr, "xkb_state_update_key: unset overlay %u\n", overlay); + state->components.base_overlays &= ~obit; + state->overlay_key_count[overlay] = 0; + } + state->clear_overlays &= ~obit; + } + } + xkb_state_update_derived(state); - return get_state_component_changes(&prev_components, &state->components); + return get_state_component_changes(&prev_components, &state->components) | + extra_changes; } /** @@ -969,6 +1199,25 @@ xkb_state_update_mask(struct xkb_state *state, return get_state_component_changes(&prev_components, &state->components); } + +/* TODO: doc */ +xkb_overlay_mask_t +xkb_state_serialize_overlays(struct xkb_state *state, + enum xkb_state_component type) +{ + xkb_overlay_mask_t ret = 0; + + if (type & XKB_STATE_OVERLAYS_EFFECTIVE) + return state->components.overlays; + + if (type & XKB_STATE_OVERLAYS_SET) + ret |= state->components.base_overlays; + if (type & XKB_STATE_OVERLAYS_LOCKED) + ret |= state->components.locked_overlays; + + return ret; +} + /** * Provides the symbols to use for the given key and state. Returns the * number of symbols pointed to in syms_out. diff --git a/src/state.h b/src/state.h new file mode 100644 index 000000000..736329e6b --- /dev/null +++ b/src/state.h @@ -0,0 +1,10 @@ +#ifndef STATE_H +#define STATE_H + +#include "keymap.h" + +xkb_overlay_mask_t +xkb_state_serialize_overlays(struct xkb_state *state, + enum xkb_state_component type); + +#endif diff --git a/src/text.c b/src/text.c index 0cab3b722..6113c9b08 100644 --- a/src/text.c +++ b/src/text.c @@ -71,10 +71,10 @@ const LookupEntry ctrlMaskNames[] = { { "AccessXFeedback", CONTROL_AX_FEEDBACK }, { "AudibleBell", CONTROL_BELL }, { "IgnoreGroupLock", CONTROL_IGNORE_GROUP_LOCK }, + { "Overlay1", CONTROL_OVERLAY1 }, + { "Overlay2", CONTROL_OVERLAY2 }, { "all", CONTROL_ALL }, { "none", 0 }, - { "Overlay1", 0 }, - { "Overlay2", 0 }, { NULL, 0 } }; @@ -350,3 +350,34 @@ ControlMaskText(struct xkb_context *ctx, enum xkb_action_controls mask) return strcpy(xkb_context_get_buffer(ctx, pos + 1), buf); } + +const char * +ControlMaskText2(struct xkb_context *ctx, enum xkb_action_controls mask, + xkb_overlay_mask_t overlays) +{ + char buf[1024]; + size_t pos = 0; + int ret; + if (mask) { + ret = snprintf(buf, sizeof(buf), "%s", ControlMaskText(ctx, mask)); + if (ret <= 0 || pos + ret >= sizeof(buf)) + goto error; + else + pos += ret; + } + /* Only deal with Overlay3+ */ + xkb_overlay_index_t i = 2; + overlays >>= i; + for (; overlays && i < XKB_MAX_OVERLAYS; i++, overlays >>= 1) { + if (overlays & 1) { + ret = snprintf(buf + pos, sizeof(buf) - pos, "%sOverlay%u", + pos == 0 ? "" : "+", i + 1); + if (ret <= 0 || pos + ret >= sizeof(buf)) + break; + else + pos += ret; + } + } +error: + return strcpy(xkb_context_get_buffer(ctx, pos + 1), buf); +} diff --git a/src/text.h b/src/text.h index 584dea62d..1f38e0cfb 100644 --- a/src/text.h +++ b/src/text.h @@ -73,4 +73,8 @@ LedStateMaskText(struct xkb_context *ctx, enum xkb_state_component mask); const char * ControlMaskText(struct xkb_context *ctx, enum xkb_action_controls mask); +const char * +ControlMaskText2(struct xkb_context *ctx, enum xkb_action_controls mask, + xkb_overlay_mask_t overlays); + #endif /* TEXT_H */ diff --git a/src/x11/keymap.c b/src/x11/keymap.c index 72f663927..7e42b3901 100644 --- a/src/x11/keymap.c +++ b/src/x11/keymap.c @@ -152,6 +152,10 @@ translate_controls_mask(uint32_t wire) ret |= CONTROL_BELL; if (wire & XCB_XKB_BOOL_CTRL_IGNORE_GROUP_LOCK_MASK) ret |= CONTROL_IGNORE_GROUP_LOCK; + if (wire & XCB_XKB_BOOL_CTRL_OVERLAY_1_MASK) + ret |= CONTROL_OVERLAY1; + if (wire & XCB_XKB_BOOL_CTRL_OVERLAY_2_MASK) + ret |= CONTROL_OVERLAY2; /* Some controls are not supported and don't appear here. */ return ret; } diff --git a/src/xkbcomp/action.c b/src/xkbcomp/action.c index e210a0708..f052bf93a 100644 --- a/src/xkbcomp/action.c +++ b/src/xkbcomp/action.c @@ -630,15 +630,23 @@ HandleSetLockControls(struct xkb_context *ctx, const struct xkb_mod_set *mods, if (field == ACTION_FIELD_CONTROLS) { enum xkb_action_controls mask; + xkb_overlay_mask_t overlays; if (array_ndx) return ReportActionNotArray(ctx, action->type, field); - if (!ExprResolveMask(ctx, value, &mask, ctrlMaskNames)) + if (!ExprResolveControlsMask(ctx, value, &mask, &overlays)) return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, action->type, field, "controls mask"); act->ctrls = mask; + act->overlays = overlays; + + /* FIXME: remove debug */ + fprintf(stderr, + "HandleSetLockControls: ctrls: %u, overlays: %u (%u)\n", + act->ctrls, act->overlays, overlays); + return true; } else if (field == ACTION_FIELD_AFFECT) { diff --git a/src/xkbcomp/compat.c b/src/xkbcomp/compat.c index bf139aa20..3e720683d 100644 --- a/src/xkbcomp/compat.c +++ b/src/xkbcomp/compat.c @@ -814,6 +814,7 @@ HandleCompatMapFile(CompatInfo *info, XkbFile *file, enum merge_mode merge) /* Temporary struct for CopyInterps. */ struct collect { darray(struct xkb_sym_interpret) sym_interprets; + xkb_overlay_mask_t overlays; }; static void @@ -824,8 +825,12 @@ CopyInterps(CompatInfo *info, bool needSymbol, enum xkb_match_operation pred, darray_foreach(si, info->interps) if (si->interp.match == pred && - (si->interp.sym != XKB_KEY_NoSymbol) == needSymbol) + (si->interp.sym != XKB_KEY_NoSymbol) == needSymbol) { darray_append(collect->sym_interprets, si->interp); + if (si->interp.action.type == ACTION_TYPE_CTRL_SET || + si->interp.action.type == ACTION_TYPE_CTRL_LOCK) + collect->overlays |= si->interp.action.ctrls.overlays; + } } static void @@ -888,8 +893,10 @@ CopyCompatToKeymap(struct xkb_keymap *keymap, CompatInfo *info) keymap->mods = info->mods; if (!darray_empty(info->interps)) { - struct collect collect; - darray_init(collect.sym_interprets); + struct collect collect = { + .sym_interprets = darray_new(), + .overlays = 0 + }; /* Most specific to least specific. */ CopyInterps(info, true, MATCH_EXACTLY, &collect); @@ -905,6 +912,13 @@ CopyCompatToKeymap(struct xkb_keymap *keymap, CompatInfo *info) darray_steal(collect.sym_interprets, &keymap->sym_interprets, &keymap->num_sym_interprets); + + xkb_overlay_index_t overlay; + for (overlay = XKB_MAX_OVERLAYS; overlay > 0; overlay--) { + if (collect.overlays & (1u << (overlay - 1))) + break; + } + keymap->num_overlays = MAX(keymap->num_overlays, overlay); } CopyLedMapDefsToKeymap(keymap, info); diff --git a/src/xkbcomp/expr.c b/src/xkbcomp/expr.c index 2459713fd..505a70344 100644 --- a/src/xkbcomp/expr.c +++ b/src/xkbcomp/expr.c @@ -131,6 +131,45 @@ LookupModMask(struct xkb_context *ctx, const void *priv, xkb_atom_t field, return true; } +/* Data passed in the *priv argument for LookupControlsMask. */ +typedef struct { + xkb_overlay_mask_t *overlays; +} LookupControlsMaskPriv; + +static bool +LookupControlsMask(struct xkb_context *ctx, const void *priv, xkb_atom_t field, + enum expr_value_type type, xkb_mod_mask_t *val_rtrn) +{ + const LookupEntry *entry; + const LookupControlsMaskPriv *arg = priv; + + if (!priv || field == XKB_ATOM_NONE || type != EXPR_TYPE_INT) + return false; + + /* Check standard flags */ + const char *str = xkb_atom_text(ctx, field); + for (entry = ctrlMaskNames; entry && entry->name; entry++) { + if (istreq(str, entry->name)) { + *val_rtrn = entry->value; + *(arg->overlays) |= + (entry->value & CONTROL_OVERLAYS) >> _CONTROL_OVERLAY1_LOG2; + return true; + } + } + + /* Check extra overlay flags */ + if (!istreq_prefix("overlay", str)) + return false; + + char *endptr; + unsigned long overlay = strtoul(str + 7, &endptr, 10); + if (endptr[0] != '\0' || overlay <= 0 || overlay >= XKB_MAX_OVERLAYS) + return false; + *val_rtrn = 0; + *(arg->overlays) |= 1u << (overlay - 1); + return true; +} + bool ExprResolveBoolean(struct xkb_context *ctx, const ExprDef *expr, bool *set_rtrn) @@ -701,6 +740,16 @@ ExprResolveModMask(struct xkb_context *ctx, const ExprDef *expr, return ExprResolveMaskLookup(ctx, expr, mask_rtrn, LookupModMask, &priv); } +bool +ExprResolveControlsMask(struct xkb_context *ctx, const ExprDef *expr, + enum xkb_action_controls *mask_rtrn, + xkb_overlay_mask_t *overlays) +{ + *overlays = 0; + LookupControlsMaskPriv priv = { .overlays = overlays }; + return ExprResolveMaskLookup(ctx, expr, mask_rtrn, LookupControlsMask, &priv); +} + bool ExprResolveKeySym(struct xkb_context *ctx, const ExprDef *expr, xkb_keysym_t *sym_rtrn) diff --git a/src/xkbcomp/expr.h b/src/xkbcomp/expr.h index 9882b8cce..889c154ba 100644 --- a/src/xkbcomp/expr.h +++ b/src/xkbcomp/expr.h @@ -78,6 +78,11 @@ bool ExprResolveMask(struct xkb_context *ctx, const ExprDef *expr, unsigned int *mask_rtrn, const LookupEntry *values); +bool +ExprResolveControlsMask(struct xkb_context *ctx, const ExprDef *expr, + enum xkb_action_controls *mask_rtrn, + xkb_overlay_mask_t *overlays); + bool ExprResolveKeySym(struct xkb_context *ctx, const ExprDef *expr, xkb_keysym_t *sym_rtrn); diff --git a/src/xkbcomp/keymap-dump.c b/src/xkbcomp/keymap-dump.c index b7828bff6..849faba06 100644 --- a/src/xkbcomp/keymap-dump.c +++ b/src/xkbcomp/keymap-dump.c @@ -383,7 +383,7 @@ write_action(struct xkb_keymap *keymap, struct buf *buf, case ACTION_TYPE_CTRL_SET: case ACTION_TYPE_CTRL_LOCK: write_buf(buf, "%s%s(controls=%s%s)%s", prefix, type, - ControlMaskText(keymap->ctx, action->ctrls.ctrls), + ControlMaskText2(keymap->ctx, action->ctrls.ctrls, action->ctrls.overlays), (action->type == ACTION_TYPE_CTRL_LOCK) ? affect_lock_text(action->ctrls.flags, false) : "", suffix); break; @@ -557,6 +557,22 @@ write_key(struct xkb_keymap *keymap, struct buf *buf, break; } + xkb_overlay_mask_t overlays_mask = key->overlays_mask; + for (xkb_overlay_index_t overlay = 0; + overlays_mask && overlay < XKB_MAX_OVERLAYS; + overlay++, overlays_mask >>= 1) { + if (overlays_mask & 1) { + const struct xkb_key *overlay_key = XkbKey(keymap, key->overlays[overlay]); + /* FIXME: remove debug */ + fprintf(stderr, "dump overlay %u: %u (mask: %u, key ok: %u)\n", + overlay + 1, overlay_key->keycode, key->overlays_mask, !!overlay_key); + if (!overlay_key) + return false; /* impossible */ + write_buf(buf, "\n\t\toverlay%u= %s,", + overlay + 1, KeyNameText(keymap->ctx, overlay_key->name)); + } + } + show_actions = (key->explicit & EXPLICIT_INTERP); if (key->num_groups > 1 || show_actions) @@ -584,8 +600,8 @@ write_key(struct xkb_keymap *keymap, struct buf *buf, if (level != 0) write_buf(buf, ", "); write_action(keymap, buf, - &key->groups[group].levels[level].action, - NULL, NULL); + &key->groups[group].levels[level].action, + NULL, NULL); } write_buf(buf, " ]"); } diff --git a/src/xkbcomp/symbols.c b/src/xkbcomp/symbols.c index a33e033c5..9b86df534 100644 --- a/src/xkbcomp/symbols.c +++ b/src/xkbcomp/symbols.c @@ -95,6 +95,7 @@ typedef struct { xkb_atom_t name; darray(GroupInfo) groups; + darray(xkb_atom_t) overlays; enum key_repeat repeat; xkb_mod_mask_t vmodmap; @@ -157,6 +158,7 @@ ClearKeyInfo(KeyInfo *keyi) darray_foreach(groupi, keyi->groups) ClearGroupInfo(groupi); darray_free(keyi->groups); + darray_free(keyi->overlays); } /***====================================================================***/ @@ -185,6 +187,7 @@ typedef struct { darray(xkb_atom_t) group_names; darray(ModMapEntry) modmaps; struct xkb_mod_set mods; + xkb_overlay_mask_t overlays; struct xkb_context *ctx; /* Needed for AddKeySymbols. */ @@ -348,6 +351,17 @@ MergeGroups(SymbolsInfo *info, GroupInfo *into, GroupInfo *from, bool clobber, return true; } +static bool +MergeOverlays(SymbolsInfo *info, KeyInfo *into, KeyInfo *from, bool clobber, + bool report, xkb_atom_t key_name) +{ + /* FIXME: handle conflicts */ + darray_free(into->overlays); + into->overlays = from->overlays; + darray_init(from->overlays); + return true; +} + static bool UseNewKeyField(enum key_field field, enum key_field old, enum key_field new, bool clobber, bool report, enum key_field *collide) @@ -383,6 +397,9 @@ MergeKeys(SymbolsInfo *info, KeyInfo *into, KeyInfo *from, bool same_file) return true; } + /* TODO: check */ + MergeOverlays(info, into, from, clobber, report, into->name); + groups_in_both = MIN(darray_size(into->groups), darray_size(from->groups)); for (i = 0; i < groups_in_both; i++) MergeGroups(info, @@ -562,6 +579,7 @@ MergeIncludedSymbols(SymbolsInfo *into, SymbolsInfo *from, into->errorCount++; } } + into->overlays |= from->overlays; } static void @@ -799,6 +817,10 @@ AddActionsToKey(SymbolsInfo *info, KeyInfo *keyi, ExprDef *arrayNdx, "Action for group %u/level %u ignored\n", KeyInfoText(info, keyi), ndx + 1, i + 1); + if (toAct->type == ACTION_TYPE_CTRL_SET || + toAct->type == ACTION_TYPE_CTRL_LOCK) + info->overlays |= toAct->ctrls.overlays; + act = (ExprDef *) act->common.next; } @@ -894,8 +916,32 @@ SetSymbolsField(SymbolsInfo *info, KeyInfo *keyi, const char *field, "Ignoring radio group specification for key %s\n", KeyInfoText(info, keyi)); } - else if (istreq_prefix("overlay", field) || - istreq_prefix("permanentoverlay", field)) { + else if (istreq_prefix("overlay", field)) { + char *endptr; + unsigned long overlay = strtoul(field + 7, &endptr, 10); + if (endptr[0] != '\0' || overlay <= 0 || overlay > XKB_MAX_OVERLAYS) { + log_err(info->ctx, XKB_LOG_MESSAGE_NO_ID, + "Invalid overlay index: %s for key %s\n", + field, KeyInfoText(info, keyi)); + return false; + } + if (value->expr.op == EXPR_VALUE && + value->expr.value_type == EXPR_TYPE_KEYNAME) { + darray_resize0(keyi->overlays, overlay); + /* TODO: warn multiple overlay values for same index */ + /* FIXME: print */ + fprintf(stderr, "Adding %u to overlay %lu of key %s\n", + value->key_name.key_name, overlay, KeyInfoText(info, keyi)); + darray_item(keyi->overlays, overlay - 1) = value->key_name.key_name; + } else { + log_err(info->ctx, XKB_LOG_MESSAGE_NO_ID, + "Invalid overlay %lu value: %s for key %s\n", + overlay, field, KeyInfoText(info, keyi)); + return false; + } + } + else if (istreq_prefix("permanentoverlay", field)) { + /* TODO permanent flag */ log_vrb(info->ctx, 1, XKB_WARNING_UNSUPPORTED_SYMBOLS_FIELD, "Overlays not supported; " @@ -1601,6 +1647,10 @@ CopySymbolsToKeymap(struct xkb_keymap *keymap, SymbolsInfo *info) { KeyInfo *keyi; ModMapEntry *mm; + struct xkb_key *key; + xkb_overlay_index_t num_overlays = keymap->num_overlays; + xkb_overlay_index_t overlay; + darray(xkb_overlay_mask_t) incompatible_overlays = darray_new(); keymap->symbols_section_name = strdup_safe(info->name); XkbEscapeMapName(keymap->symbols_section_name); @@ -1614,9 +1664,61 @@ CopySymbolsToKeymap(struct xkb_keymap *keymap, SymbolsInfo *info) if (!CopySymbolsDefToKeymap(keymap, info, keyi)) info->errorCount++; - if (xkb_context_get_log_verbosity(keymap->ctx) > 3) { - struct xkb_key *key; + /* Overlays: from actions */ + for (overlay = XKB_MAX_OVERLAYS; overlay > 0; overlay--) { + if (info->overlays & (1u << (overlay - 1))) + break; + } + /* FIXME: remove debug*/ + fprintf(stderr, "Overlays from symbols actions: %u (%u)\n", overlay, info->overlays); + num_overlays = MAX(num_overlays, overlay); + /* Overlays: from key properties */ + darray_foreach(keyi, info->keys) { + if (!darray_size(keyi->overlays)) + continue; + num_overlays = MAX(num_overlays, darray_size(keyi->overlays)); + + darray(xkb_keycode_t) overlays = darray_new(); + darray_resize0(overlays, darray_size(keyi->overlays)); + xkb_atom_t *key_name; + darray_enumerate(overlay, key_name, keyi->overlays) { + if (*key_name == XKB_ATOM_NONE) + /* Overlay not set */ + continue; + /* Name may be an alias */ + key = XkbKeyByName(keymap, *key_name, true); + if (!key) { + log_err(info->ctx, XKB_WARNING_UNDEFINED_KEYCODE, + "Key <%s> not found in keycodes; " + "Overlay %u entry for key %s not updated\n", + xkb_atom_text(info->ctx, *key_name), overlay + 1, + KeyNameText(info->ctx, keyi->name)); + return false; + } + /* FIXME remove debug */ + fprintf(stderr, "copy overlay %u: %s\n", + overlay + 1, KeyNameText(info->ctx, *key_name)); + darray_item(overlays, overlay) = key->keycode; + /* Name cannot be an alias */ + key = XkbKeyByName(keymap, keyi->name, false); + key->overlays_mask |= 1 << overlay; + } + darray_steal(overlays, &key->overlays, NULL); + darray_resize0(incompatible_overlays, num_overlays); + for (overlay = 0; overlay < darray_size(keyi->overlays); overlay++) { + xkb_overlay_mask_t overlay_mask = 1 << overlay; + darray_item(incompatible_overlays, overlay) |= + key->overlays_mask & ~overlay_mask; + } + } + + keymap->num_overlays = num_overlays; + darray_steal(incompatible_overlays, &keymap->incompatible_overlays, NULL); + /* FIXME: remove debug */ + fprintf(stderr, "Symbols overlays: %u\n", num_overlays); + + if (xkb_context_get_log_verbosity(keymap->ctx) > 3) { xkb_keys_foreach(key, keymap) { if (key->name == XKB_ATOM_NONE) continue; diff --git a/tools/interactive-evdev.c b/tools/interactive-evdev.c index 0b7dc2fb3..895b9fde0 100644 --- a/tools/interactive-evdev.c +++ b/tools/interactive-evdev.c @@ -261,6 +261,7 @@ static void process_event(struct keyboard *kbd, uint16_t type, uint16_t code, int32_t value) { xkb_keycode_t keycode; + xkb_keycode_t keycode_old; struct xkb_keymap *keymap; enum xkb_state_component changed; enum xkb_compose_status status; @@ -268,12 +269,17 @@ process_event(struct keyboard *kbd, uint16_t type, uint16_t code, int32_t value) if (type != EV_KEY) return; - keycode = evdev_offset + code; + keycode_old = evdev_offset + code; + keycode = xkb_state_key_get_overlay_keycode(kbd->state, keycode_old); + keymap = xkb_state_get_keymap(kbd->state); if (value == KEY_STATE_REPEAT && !xkb_keymap_key_repeats(keymap, keycode)) return; + /* FIXME: remove debug */ + fprintf(stderr, "process_event: key %u -> %u\n", keycode_old, keycode); + if (with_compose && value != KEY_STATE_RELEASE) { xkb_keysym_t keysym = xkb_state_key_get_one_sym(kbd->state, keycode); xkb_compose_state_feed(kbd->compose_state, keysym); @@ -293,9 +299,9 @@ process_event(struct keyboard *kbd, uint16_t type, uint16_t code, int32_t value) } if (value == KEY_STATE_RELEASE) - changed = xkb_state_update_key(kbd->state, keycode, XKB_KEY_UP); + changed = xkb_state_update_key(kbd->state, keycode_old, XKB_KEY_UP); else - changed = xkb_state_update_key(kbd->state, keycode, XKB_KEY_DOWN); + changed = xkb_state_update_key(kbd->state, keycode_old, XKB_KEY_DOWN); if (report_state_changes) tools_print_state_changes(changed); diff --git a/tools/tools-common.c b/tools/tools-common.c index bf086a3a3..45d7dbbca 100644 --- a/tools/tools-common.c +++ b/tools/tools-common.c @@ -52,6 +52,10 @@ #include "src/keysym.h" #include "src/compose/parser.h" +#ifdef ENABLE_PRIVATE_APIS +#include "src/state.h" +#endif + static void print_keycode(struct xkb_keymap *keymap, const char* prefix, xkb_keycode_t keycode, const char *suffix) { @@ -246,6 +250,11 @@ tools_print_keycode_state(const char *prefix, } printf("] "); +#ifdef ENABLE_PRIVATE_APIS + printf("overlays [ %d ] ", + xkb_state_serialize_overlays(state, XKB_STATE_OVERLAYS_EFFECTIVE)); +#endif + printf("leds [ "); for (xkb_led_index_t led = 0; led < xkb_keymap_num_leds(keymap); led++) { if (xkb_state_led_index_is_active(state, led) <= 0) @@ -282,6 +291,7 @@ tools_print_state_changes(enum xkb_state_component changed) printf("locked-mods "); if (changed & XKB_STATE_LEDS) printf("leds "); + /* FIXME: overlays */ printf("]\n"); } diff --git a/xkbcommon.map b/xkbcommon.map index b2507272e..eb6d7c388 100644 --- a/xkbcommon.map +++ b/xkbcommon.map @@ -119,3 +119,8 @@ global: xkb_compose_table_iterator_free; xkb_compose_table_iterator_next; } V_1.0.0; + +V_1.8.0 { +global: + xkb_state_key_get_overlay_keycode; +} V_1.6.0;