diff --git a/src/keymap-priv.c b/src/keymap-priv.c index 2a970c399..1d5ed5883 100644 --- a/src/keymap-priv.c +++ b/src/keymap-priv.c @@ -150,3 +150,91 @@ XkbLevelsSameSyms(const struct xkb_level *a, const struct xkb_level *b) return a->s.sym == b->s.sym; return memcmp(a->s.syms, b->s.syms, sizeof(*a->s.syms) * a->num_syms) == 0; } + +bool +XkbLevelHasNoAction(const struct xkb_level *level) +{ + if (level->num_syms == 0) + return true; + if (level->num_syms == 1) + return level->a.action.type == ACTION_TYPE_NONE; + for (unsigned int k = 0; k < level->num_syms; k++) { + if (level->a.actions[k].type != ACTION_TYPE_NONE) + return false; + } + return true; +} + +xkb_layout_index_t +XkbWrapGroupIntoRange(int32_t group, + xkb_layout_index_t num_groups, + enum xkb_range_exceed_type out_of_range_group_action, + xkb_layout_index_t out_of_range_group_number) +{ + if (num_groups == 0) + return XKB_LAYOUT_INVALID; + + if (group >= 0 && (xkb_layout_index_t) group < num_groups) + return group; + + switch (out_of_range_group_action) { + case RANGE_REDIRECT: + if (out_of_range_group_number >= num_groups) + return 0; + return out_of_range_group_number; + + case RANGE_SATURATE: + if (group < 0) + return 0; + else + return num_groups - 1; + + case RANGE_WRAP: + default: + /* + * C99 says a negative dividend in a modulo operation always + * gives a negative result. + */ + if (group < 0) + return ((int) num_groups + (group % (int) num_groups)); + else + return group % num_groups; + } +} + +unsigned int +xkb_keymap_key_get_actions_by_level(struct xkb_keymap *keymap, + xkb_keycode_t kc, + xkb_layout_index_t layout, + xkb_level_index_t level, + const union xkb_action **actions) +{ + const struct xkb_key *key = XkbKey(keymap, kc); + if (!key) + goto err; + + layout = XkbWrapGroupIntoRange(layout, key->num_groups, + key->out_of_range_group_action, + key->out_of_range_group_number); + if (layout == XKB_LAYOUT_INVALID) + goto err; + + if (level >= XkbKeyNumLevels(key, layout)) + goto err; + + const unsigned int count = key->groups[layout].levels[level].num_syms; + switch (count) { + case 0: + goto err; + case 1: + *actions = &key->groups[layout].levels[level].a.action; + break; + default: + *actions = key->groups[layout].levels[level].a.actions; + } + return count; + +err: + *actions = NULL; + return 0; +} diff --git a/src/keymap.c b/src/keymap.c index 6e43e7571..cf83c19c1 100644 --- a/src/keymap.c +++ b/src/keymap.c @@ -75,8 +75,10 @@ xkb_keymap_unref(struct xkb_keymap *keymap) for (unsigned i = 0; i < key->num_groups; i++) { if (key->groups[i].levels) { for (unsigned j = 0; j < XkbKeyNumLevels(key, i); j++) - if (key->groups[i].levels[j].num_syms > 1) + if (key->groups[i].levels[j].num_syms > 1) { free(key->groups[i].levels[j].s.syms); + free(key->groups[i].levels[j].a.actions); + } free(key->groups[i].levels); } } diff --git a/src/keymap.h b/src/keymap.h index b8bd847be..57cebaf5a 100644 --- a/src/keymap.h +++ b/src/keymap.h @@ -316,13 +316,18 @@ enum xkb_explicit_components { }; struct xkb_level { - union xkb_action action; + /* Count of keysyms/actions */ unsigned int num_syms; /* Keysyms */ union { - xkb_keysym_t sym; /* num_syms == 1 */ - xkb_keysym_t *syms; /* num_syms > 1 */ + xkb_keysym_t sym; /* num_syms == 1 */ + xkb_keysym_t *syms; /* num_syms > 1 */ } s; + /* Actions */ + union { + union xkb_action action; /* num_syms == 1 */ + union xkb_action *actions; /* num_syms > 1 */ + } a; }; /** @@ -493,6 +498,9 @@ XkbModNameToIndex(const struct xkb_mod_set *mods, xkb_atom_t name, bool XkbLevelsSameSyms(const struct xkb_level *a, const struct xkb_level *b); +bool +XkbLevelHasNoAction(const struct xkb_level *level); + xkb_layout_index_t XkbWrapGroupIntoRange(int32_t group, xkb_layout_index_t num_groups, @@ -502,6 +510,13 @@ XkbWrapGroupIntoRange(int32_t group, xkb_mod_mask_t mod_mask_get_effective(struct xkb_keymap *keymap, xkb_mod_mask_t mods); +unsigned int +xkb_keymap_key_get_actions_by_level(struct xkb_keymap *keymap, + xkb_keycode_t kc, + xkb_layout_index_t layout, + xkb_level_index_t level, + const union xkb_action **actions); + struct xkb_keymap_format_ops { bool (*keymap_new_from_names)(struct xkb_keymap *keymap, const struct xkb_rule_names *names); diff --git a/src/state.c b/src/state.c index 287f0eef8..103ba4ddc 100644 --- a/src/state.c +++ b/src/state.c @@ -159,43 +159,6 @@ xkb_state_key_get_level(struct xkb_state *state, xkb_keycode_t kc, return entry->level; } -xkb_layout_index_t -XkbWrapGroupIntoRange(int32_t group, - xkb_layout_index_t num_groups, - enum xkb_range_exceed_type out_of_range_group_action, - xkb_layout_index_t out_of_range_group_number) -{ - if (num_groups == 0) - return XKB_LAYOUT_INVALID; - - if (group >= 0 && (xkb_layout_index_t) group < num_groups) - return group; - - switch (out_of_range_group_action) { - case RANGE_REDIRECT: - if (out_of_range_group_number >= num_groups) - return 0; - return out_of_range_group_number; - - case RANGE_SATURATE: - if (group < 0) - return 0; - else - return num_groups - 1; - - case RANGE_WRAP: - default: - /* - * C99 says a negative dividend in a modulo operation always - * gives a negative result. - */ - if (group < 0) - return ((int) num_groups + (group % (int) num_groups)); - else - return group % num_groups; - } -} - /** * Returns the layout to use for the given key and state, taking * wrapping/clamping/etc into account, or XKB_LAYOUT_INVALID. @@ -213,23 +176,27 @@ xkb_state_key_get_layout(struct xkb_state *state, xkb_keycode_t kc) key->out_of_range_group_number); } -static const union xkb_action * -xkb_key_get_action(struct xkb_state *state, const struct xkb_key *key) +static unsigned int +xkb_key_get_actions(struct xkb_state *state, const struct xkb_key *key, + const union xkb_action **actions) { - static const union xkb_action dummy = { .type = ACTION_TYPE_NONE }; - xkb_layout_index_t layout; xkb_level_index_t level; layout = xkb_state_key_get_layout(state, key->keycode); if (layout == XKB_LAYOUT_INVALID) - return &dummy; + goto err; level = xkb_state_key_get_level(state, key->keycode, layout); if (level == XKB_LEVEL_INVALID) - return &dummy; + goto err; - return &key->groups[layout].levels[level].action; + return xkb_keymap_key_get_actions_by_level(state->keymap, key->keycode, + layout, level, actions); + +err: + *actions = NULL; + return 0; } static struct xkb_filter * @@ -414,34 +381,37 @@ xkb_filter_group_latch_func(struct xkb_state *state, * keypress, then either break the latch if any random key is pressed, * or promote it to a lock or plain base set if it's the same * group delta & flags. */ - const union xkb_action *action = xkb_key_get_action(state, key); - if (action->type == ACTION_TYPE_GROUP_LATCH && - action->group.group == filter->action.group.group && - action->group.flags == filter->action.group.flags) { - filter->action = *action; - if (filter->action.group.flags & ACTION_LATCH_TO_LOCK && - filter->action.group.group != 0) { - /* Promote to lock */ - filter->action.type = ACTION_TYPE_GROUP_LOCK; - filter->func = xkb_filter_group_lock_func; - xkb_filter_group_lock_new(state, filter); + const union xkb_action *actions = NULL; + unsigned int count = xkb_key_get_actions(state, key, &actions); + for (unsigned int k = 0; k < count; k++) { + if (actions[k].type == ACTION_TYPE_GROUP_LATCH && + actions[k].group.group == filter->action.group.group && + actions[k].group.flags == filter->action.group.flags) { + filter->action = actions[k]; + if (filter->action.group.flags & ACTION_LATCH_TO_LOCK && + filter->action.group.group != 0) { + /* Promote to lock */ + filter->action.type = ACTION_TYPE_GROUP_LOCK; + filter->func = xkb_filter_group_lock_func; + xkb_filter_group_lock_new(state, filter); + } + else { + /* Degrade to plain set */ + filter->action.type = ACTION_TYPE_GROUP_SET; + filter->func = xkb_filter_group_set_func; + xkb_filter_group_set_new(state, filter); + } + filter->key = key; + state->components.latched_group -= priv.group_delta; + /* XXX beep beep! */ + return XKB_FILTER_CONSUME; } - else { - /* Degrade to plain set */ - filter->action.type = ACTION_TYPE_GROUP_SET; - filter->func = xkb_filter_group_set_func; - xkb_filter_group_set_new(state, filter); + else if (xkb_action_breaks_latch(&(actions[k]))) { + /* Breaks the latch */ + state->components.latched_group = 0; + filter->func = NULL; + return XKB_FILTER_CONTINUE; } - filter->key = key; - state->components.latched_group -= priv.group_delta; - /* XXX beep beep! */ - return XKB_FILTER_CONSUME; - } - else if (xkb_action_breaks_latch(action)) { - /* Breaks the latch */ - state->components.latched_group = 0; - filter->func = NULL; - return XKB_FILTER_CONTINUE; } } else if (direction == XKB_KEY_UP && key == filter->key) { @@ -570,32 +540,35 @@ xkb_filter_mod_latch_func(struct xkb_state *state, * keypress, then either break the latch if any random key is pressed, * or promote it to a lock or plain base set if it's the same * modifier. */ - const union xkb_action *action = xkb_key_get_action(state, key); - if (action->type == ACTION_TYPE_MOD_LATCH && - action->mods.flags == filter->action.mods.flags && - action->mods.mods.mask == filter->action.mods.mods.mask) { - filter->action = *action; - if (filter->action.mods.flags & ACTION_LATCH_TO_LOCK) { - filter->action.type = ACTION_TYPE_MOD_LOCK; - filter->func = xkb_filter_mod_lock_func; - state->components.locked_mods |= filter->action.mods.mods.mask; + const union xkb_action *actions = NULL; + unsigned int count = xkb_key_get_actions(state, key, &actions); + for (unsigned int k = 0; k < count; k++) { + if (actions[k].type == ACTION_TYPE_MOD_LATCH && + actions[k].mods.flags == filter->action.mods.flags && + actions[k].mods.mods.mask == filter->action.mods.mods.mask) { + filter->action = actions[k]; + if (filter->action.mods.flags & ACTION_LATCH_TO_LOCK) { + filter->action.type = ACTION_TYPE_MOD_LOCK; + filter->func = xkb_filter_mod_lock_func; + state->components.locked_mods |= filter->action.mods.mods.mask; + } + else { + filter->action.type = ACTION_TYPE_MOD_SET; + filter->func = xkb_filter_mod_set_func; + state->set_mods = filter->action.mods.mods.mask; + } + filter->key = key; + state->components.latched_mods &= ~filter->action.mods.mods.mask; + /* XXX beep beep! */ + return XKB_FILTER_CONSUME; } - else { - filter->action.type = ACTION_TYPE_MOD_SET; - filter->func = xkb_filter_mod_set_func; - state->set_mods = filter->action.mods.mods.mask; + else if (xkb_action_breaks_latch(&(actions[k]))) { + /* XXX: This may be totally broken, we might need to break the + * latch in the next run after this press? */ + state->components.latched_mods &= ~filter->action.mods.mods.mask; + filter->func = NULL; + return XKB_FILTER_CONTINUE; } - filter->key = key; - state->components.latched_mods &= ~filter->action.mods.mods.mask; - /* XXX beep beep! */ - return XKB_FILTER_CONSUME; - } - else if (xkb_action_breaks_latch(action)) { - /* XXX: This may be totally broken, we might need to break the - * latch in the next run after this press? */ - state->components.latched_mods &= ~filter->action.mods.mods.mask; - filter->func = NULL; - return XKB_FILTER_CONTINUE; } } else if (direction == XKB_KEY_UP && key == filter->key) { @@ -668,7 +641,8 @@ xkb_filter_apply_all(struct xkb_state *state, enum xkb_key_direction direction) { struct xkb_filter *filter; - const union xkb_action *action; + const union xkb_action *actions = NULL; + unsigned int count; bool consumed; /* First run through all the currently active filters and see if any of @@ -684,29 +658,38 @@ xkb_filter_apply_all(struct xkb_state *state, if (consumed || direction == XKB_KEY_UP) return; - /* No filter consumed this event, so proceed with the key action */ - action = xkb_key_get_action(state, key); + /* No filter consumed this event, so proceed with the key actions */ + count = xkb_key_get_actions(state, key, &actions); /* - * It's possible for the keymap to set action->type explicitly, like so: - * interpret XF86_Next_VMode { - * action = Private(type=0x86, data="+VMode"); - * }; - * We don't handle those. + * Process actions sequentially. + * + * NOTE: We rely on the parser to disallow multiple modifier or group + * actions (see `CheckMultipleActionsCategories`). Allowing multiple such + * actions requires a refactor of the state handling. */ - if (action->type >= _ACTION_TYPE_NUM_ENTRIES) - return; + for (unsigned int k = 0; k < count; k++) { + /* + * It's possible for the keymap to set action->type explicitly, like so: + * interpret XF86_Next_VMode { + * action = Private(type=0x86, data="+VMode"); + * }; + * We don't handle those. + */ + if (actions[k].type >= _ACTION_TYPE_NUM_ENTRIES) + continue; - /* Return if no corresponding action */ - if (!filter_action_funcs[action->type].new) - return; + /* Go to next action if no corresponding action handler */ + if (!filter_action_funcs[actions[k].type].new) + continue; - /* Add a new filter and run the corresponding initial action */ - filter = xkb_filter_new(state); - filter->key = key; - filter->func = filter_action_funcs[action->type].func; - filter->action = *action; - filter_action_funcs[action->type].new(state, filter); + /* Add a new filter and run the corresponding initial action */ + filter = xkb_filter_new(state); + filter->key = key; + filter->func = filter_action_funcs[actions[k].type].func; + filter->action = actions[k]; + filter_action_funcs[actions[k].type].new(state, filter); + } } XKB_EXPORT struct xkb_state * diff --git a/src/x11/keymap.c b/src/x11/keymap.c index 61bdf13d0..e30925c2f 100644 --- a/src/x11/keymap.c +++ b/src/x11/keymap.c @@ -529,7 +529,7 @@ get_actions(struct xkb_keymap *keymap, xcb_connection_t *conn, xcb_xkb_action_t *wire_action = acts_iter.data; if (level < key->groups[group].type->num_levels) { - union xkb_action *action = &key->groups[group].levels[level].action; + union xkb_action *action = &key->groups[group].levels[level].a.action; translate_action(action, wire_action); } diff --git a/src/xkbcomp/ast-build.c b/src/xkbcomp/ast-build.c index 99ff53666..1d2ae4a79 100644 --- a/src/xkbcomp/ast-build.c +++ b/src/xkbcomp/ast-build.c @@ -200,7 +200,57 @@ ExprCreateActionList(ExprDef *actions) ExprDef *expr = ExprCreate(EXPR_ACTION_LIST, EXPR_TYPE_ACTIONS, sizeof(ExprActionList)); if (!expr) return NULL; - expr->actions.actions = actions; + + darray_init(expr->actions.actions); + darray_init(expr->actions.actionsMapIndex); + darray_init(expr->actions.actionsNumEntries); + + darray_append(expr->actions.actions, actions); + darray_append(expr->actions.actionsMapIndex, 0); + darray_append(expr->actions.actionsNumEntries, 1); + return expr; +} + +ExprDef * +ExprCreateMultiActionList(ExprDef *expr) +{ + unsigned nLevels = darray_size(expr->actions.actionsMapIndex); + + darray_resize(expr->actions.actionsMapIndex, 1); + darray_resize(expr->actions.actionsNumEntries, 1); + darray_item(expr->actions.actionsMapIndex, 0) = 0; + darray_item(expr->actions.actionsNumEntries, 0) = nLevels; + + return expr; +} + +ExprDef * +ExprAppendActionList(ExprDef *expr, ExprDef *action) +{ + unsigned nSyms = darray_size(expr->actions.actions); + + darray_append(expr->actions.actionsMapIndex, nSyms); + darray_append(expr->actions.actionsNumEntries, 1); + darray_append(expr->actions.actions, action); + + return expr; +} + +ExprDef * +ExprAppendMultiActionList(ExprDef *expr, ExprDef *append) +{ + unsigned nSyms = darray_size(expr->actions.actions); + unsigned numEntries = darray_size(append->actions.actions); + + darray_append(expr->actions.actionsMapIndex, nSyms); + darray_append(expr->actions.actionsNumEntries, numEntries); + + /* Steal */ + darray_concat(expr->actions.actions, append->actions.actions); + darray_free(append->actions.actions); + + FreeStmt((ParseCommon *) append); + return expr; } @@ -598,6 +648,8 @@ FreeExpr(ExprDef *expr) if (!expr) return; + ExprDef** action; + switch (expr->expr.op) { case EXPR_NEGATE: case EXPR_UNARY_PLUS: @@ -620,7 +672,12 @@ FreeExpr(ExprDef *expr) break; case EXPR_ACTION_LIST: - FreeStmt((ParseCommon *) expr->actions.actions); + darray_foreach(action, expr->actions.actions) { + FreeStmt((ParseCommon *) *action); + } + darray_free(expr->actions.actions); + darray_free(expr->actions.actionsMapIndex); + darray_free(expr->actions.actionsNumEntries); break; case EXPR_ARRAY_REF: diff --git a/src/xkbcomp/ast-build.h b/src/xkbcomp/ast-build.h index 3afb08038..8f0c17ee3 100644 --- a/src/xkbcomp/ast-build.h +++ b/src/xkbcomp/ast-build.h @@ -64,6 +64,15 @@ ExprCreateAction(xkb_atom_t name, ExprDef *args); ExprDef * ExprCreateActionList(ExprDef *actions); +ExprDef * +ExprCreateMultiActionList(ExprDef *expr); + +ExprDef * +ExprAppendActionList(ExprDef *expr, ExprDef *action); + +ExprDef * +ExprAppendMultiActionList(ExprDef *expr, ExprDef *append); + ExprDef * ExprCreateMultiKeysymList(ExprDef *list); diff --git a/src/xkbcomp/ast.h b/src/xkbcomp/ast.h index 8b0901f94..453d3831c 100644 --- a/src/xkbcomp/ast.h +++ b/src/xkbcomp/ast.h @@ -231,13 +231,21 @@ typedef struct { typedef struct { ExprCommon expr; - ExprDef *actions; + /* List of actions for all levels, flattened */ + darray(ExprDef*) actions; + /* List of start index in `actions`, per level */ + darray(unsigned int) actionsMapIndex; + /* List of number of actions, per level */ + darray(unsigned int) actionsNumEntries; } ExprActionList; typedef struct { ExprCommon expr; + /* List of keysym for all levels, flattened */ darray(xkb_keysym_t) syms; + /* List of start index in `syms`, per level */ darray(unsigned int) symsMapIndex; + /* List of number of keysyms, per level */ darray(unsigned int) symsNumEntries; } ExprKeysymList; diff --git a/src/xkbcomp/keymap-dump.c b/src/xkbcomp/keymap-dump.c index 428ad5627..3fc730d9e 100644 --- a/src/xkbcomp/keymap-dump.c +++ b/src/xkbcomp/keymap-dump.c @@ -292,6 +292,9 @@ affect_lock_text(enum xkb_action_flags flags, bool show_both) return ""; } +#define SYMBOL_PADDING 15 +#define ACTION_PADDING 30 + static bool write_action(struct xkb_keymap *keymap, struct buf *buf, const union xkb_action *action, @@ -406,6 +409,59 @@ write_action(struct xkb_keymap *keymap, struct buf *buf, return true; } +static bool +write_actions(struct xkb_keymap *keymap, struct buf *buf, struct buf *buf2, + const struct xkb_key *key, xkb_layout_index_t group) +{ + static const union xkb_action noAction = { .type = ACTION_TYPE_NONE }; + + for (xkb_level_index_t level = 0; level < XkbKeyNumLevels(key, group); + level++) { + const union xkb_action *actions; + int count; + + if (level != 0) + write_buf(buf, ", "); + + count = xkb_keymap_key_get_actions_by_level(keymap, key->keycode, + group, level, &actions); + buf2->size = 0; + if (count == 0) { + if (!write_action(keymap, buf2, &noAction, NULL, NULL)) + return false; + write_buf(buf, "%*s", ACTION_PADDING, buf2->buf); + } + else if (count == 1) { + if (!write_action(keymap, buf2, &(actions[0]), NULL, NULL)) + return false; + write_buf(buf, "%*s", ACTION_PADDING, buf2->buf); + } + else { + write_buf(buf2, "{ "); + for (int k = 0; k < count; k++) { + if (k != 0) + write_buf(buf2, ", "); + size_t old_size = buf2->size; + if (!write_action(keymap, buf2, &(actions[k]), NULL, NULL)) + return false; + /* Check if padding is necessary */ + if (buf2->size >= old_size + ACTION_PADDING) + continue; + /* Compute and write padding, then write the action again */ + unsigned int padding = old_size + ACTION_PADDING - buf2->size; + buf2->size = old_size; + write_buf(buf2, "%*s", padding, ""); + if (!write_action(keymap, buf2, &(actions[k]), NULL, NULL)) + return false; + } + write_buf(buf2, " }"); + write_buf(buf, "%*s", ACTION_PADDING, buf2->buf); + } + } + + return true; +} + static bool write_compat(struct xkb_keymap *keymap, struct buf *buf) { @@ -456,9 +512,11 @@ write_compat(struct xkb_keymap *keymap, struct buf *buf) } static bool -write_keysyms(struct xkb_keymap *keymap, struct buf *buf, - const struct xkb_key *key, xkb_layout_index_t group) +write_keysyms(struct xkb_keymap *keymap, struct buf *buf, struct buf *buf2, + const struct xkb_key *key, xkb_layout_index_t group, + bool show_actions) { + unsigned int padding = show_actions ? ACTION_PADDING : SYMBOL_PADDING; for (xkb_level_index_t level = 0; level < XkbKeyNumLevels(key, group); level++) { const xkb_keysym_t *syms; @@ -470,19 +528,22 @@ write_keysyms(struct xkb_keymap *keymap, struct buf *buf, num_syms = xkb_keymap_key_get_syms_by_level(keymap, key->keycode, group, level, &syms); if (num_syms == 0) { - write_buf(buf, "%15s", "NoSymbol"); + write_buf(buf, "%*s", padding, "NoSymbol"); } else if (num_syms == 1) { - write_buf(buf, "%15s", KeysymText(keymap->ctx, syms[0])); + write_buf(buf, "%*s", padding, KeysymText(keymap->ctx, syms[0])); } else { - write_buf(buf, "{ "); + buf2->size = 0; + write_buf(buf2, "{ "); for (int s = 0; s < num_syms; s++) { if (s != 0) - write_buf(buf, ", "); - write_buf(buf, "%s", KeysymText(keymap->ctx, syms[s])); + write_buf(buf2, ", "); + write_buf(buf2, "%*s", (show_actions ? padding : 0), + KeysymText(keymap->ctx, syms[s])); } - write_buf(buf, " }"); + write_buf(buf2, " }"); + write_buf(buf, "%*s", padding, buf2->buf); } } @@ -490,7 +551,7 @@ write_keysyms(struct xkb_keymap *keymap, struct buf *buf, } static bool -write_key(struct xkb_keymap *keymap, struct buf *buf, +write_key(struct xkb_keymap *keymap, struct buf *buf, struct buf *buf2, const struct xkb_key *key) { xkb_layout_index_t group; @@ -577,29 +638,22 @@ write_key(struct xkb_keymap *keymap, struct buf *buf, if (simple) { write_buf(buf, "\t[ "); - if (!write_keysyms(keymap, buf, key, 0)) + if (!write_keysyms(keymap, buf, buf2, key, 0, false)) return false; write_buf(buf, " ] };\n"); } else { - xkb_level_index_t level; - for (group = 0; group < key->num_groups; group++) { if (group != 0) write_buf(buf, ","); write_buf(buf, "\n\t\tsymbols[Group%u]= [ ", group + 1); - if (!write_keysyms(keymap, buf, key, group)) + if (!write_keysyms(keymap, buf, buf2, key, group, show_actions)) return false; write_buf(buf, " ]"); if (show_actions) { write_buf(buf, ",\n\t\tactions[Group%u]= [ ", group + 1); - for (level = 0; level < XkbKeyNumLevels(key, group); level++) { - if (level != 0) - write_buf(buf, ", "); - write_action(keymap, buf, - &key->groups[group].levels[level].action, - NULL, NULL); - } + if (!write_actions(keymap, buf, buf2, key, group)) + return false; write_buf(buf, " ]"); } } @@ -631,9 +685,16 @@ write_symbols(struct xkb_keymap *keymap, struct buf *buf) if (group > 0) write_buf(buf, "\n"); - xkb_keys_foreach(key, keymap) - if (key->num_groups > 0) - write_key(keymap, buf, key); + struct buf buf2 = { NULL, 0, 0 }; + xkb_keys_foreach(key, keymap) { + if (key->num_groups > 0) { + if (!write_key(keymap, buf, &buf2, key)) { + free(buf2.buf); + return false; + } + } + } + free(buf2.buf); xkb_mods_enumerate(i, mod, &keymap->mods) { bool had_any = false; diff --git a/src/xkbcomp/keymap.c b/src/xkbcomp/keymap.c index a0e442828..caf7c52a0 100644 --- a/src/xkbcomp/keymap.c +++ b/src/xkbcomp/keymap.c @@ -29,7 +29,9 @@ #include "config.h" +#include "darray.h" #include "xkbcomp-priv.h" +#include "text.h" static void ComputeEffectiveMask(struct xkb_keymap *keymap, struct xkb_mods *mods) @@ -63,22 +65,25 @@ static const struct xkb_sym_interpret default_interpret = { .action = { .type = ACTION_TYPE_NONE }, }; +typedef darray(const struct xkb_sym_interpret*) xkb_sym_interprets; + /** * Find an interpretation which applies to this particular level, either by * finding an exact match for the symbol and modifier combination, or a * generic XKB_KEY_NoSymbol match. */ -static const struct xkb_sym_interpret * +static bool FindInterpForKey(struct xkb_keymap *keymap, const struct xkb_key *key, - xkb_layout_index_t group, xkb_level_index_t level) + xkb_layout_index_t group, xkb_level_index_t level, + xkb_sym_interprets *interprets) { const xkb_keysym_t *syms; int num_syms; num_syms = xkb_keymap_key_get_syms_by_level(keymap, key->keycode, group, level, &syms); - if (num_syms == 0) - return NULL; + if (num_syms <= 0) + return false; /* * There may be multiple matchings interprets; we should always return @@ -86,44 +91,72 @@ FindInterpForKey(struct xkb_keymap *keymap, const struct xkb_key *key, * sym_interprets array from the most specific to the least specific, * such that when we find a match we return immediately. */ - for (unsigned i = 0; i < keymap->num_sym_interprets; i++) { - const struct xkb_sym_interpret *interp = &keymap->sym_interprets[i]; - - xkb_mod_mask_t mods; + for (int s = 0; s < num_syms; s++) { bool found = false; + for (unsigned int i = 0; i < keymap->num_sym_interprets; i++) { + const struct xkb_sym_interpret *interp = &keymap->sym_interprets[i]; + xkb_mod_mask_t mods; - if ((num_syms > 1 || interp->sym != syms[0]) && - interp->sym != XKB_KEY_NoSymbol) - continue; + found = false; - if (interp->level_one_only && level != 0) - mods = 0; - else - mods = key->modmap; - - switch (interp->match) { - case MATCH_NONE: - found = !(interp->mods & mods); - break; - case MATCH_ANY_OR_NONE: - found = (!mods || (interp->mods & mods)); - break; - case MATCH_ANY: - found = (interp->mods & mods); - break; - case MATCH_ALL: - found = ((interp->mods & mods) == interp->mods); - break; - case MATCH_EXACTLY: - found = (interp->mods == mods); - break; - } + if (interp->sym != syms[s] && interp->sym != XKB_KEY_NoSymbol) + continue; - if (found) - return interp; - } + if (interp->level_one_only && level != 0) + mods = 0; + else + mods = key->modmap; + + switch (interp->match) { + case MATCH_NONE: + found = !(interp->mods & mods); + break; + case MATCH_ANY_OR_NONE: + found = (!mods || (interp->mods & mods)); + break; + case MATCH_ANY: + found = (interp->mods & mods); + break; + case MATCH_ALL: + found = ((interp->mods & mods) == interp->mods); + break; + case MATCH_EXACTLY: + found = (interp->mods == mods); + break; + } - return &default_interpret; + if (found && i > 0 && interp->sym == XKB_KEY_NoSymbol) { + /* + * For an interpretation matching Any keysym, we may get the + * same interpretation for multiple keysyms. This may result in + * unwanted duplicate actions. So set this interpretation only + * if no previous keysym was matched with this interpret at this + * level, else set the default interpretation. + */ + struct xkb_sym_interpret const **previous_interp; + darray_foreach(previous_interp, *interprets) { + if (*previous_interp == interp) { + found = false; + log_warn(keymap->ctx, XKB_LOG_MESSAGE_NO_ID, + "Repeated interpretation ignored for keysym " + "#%u \"%s\" at level %u/group %u on key %s.\n", + s + 1, KeysymText(keymap->ctx, syms[s]), + level + 1, group + 1, + KeyNameText(keymap->ctx, key->name)); + goto not_found; + } + } + } + if (found) { + darray_append(*interprets, interp); + break; + } + } + if (!found) +not_found: + darray_append(*interprets, &default_interpret); + } + return true; } static bool @@ -139,23 +172,36 @@ ApplyInterpsToKey(struct xkb_keymap *keymap, struct xkb_key *key) continue; for (level = 0; level < XkbKeyNumLevels(key, group); level++) { + const struct xkb_sym_interpret **interp_iter; const struct xkb_sym_interpret *interp; + size_t k; + xkb_sym_interprets interprets = darray_new(); - interp = FindInterpForKey(keymap, key, group, level); - if (!interp) + bool found = FindInterpForKey(keymap, key, group, level, &interprets); + if (!found) continue; - /* Infer default key behaviours from the base level. */ - if (group == 0 && level == 0) - if (!(key->explicit & EXPLICIT_REPEAT) && interp->repeat) - key->repeats = true; - - if ((group == 0 && level == 0) || !interp->level_one_only) - if (interp->virtual_mod != XKB_MOD_INVALID) - vmodmap |= (1u << interp->virtual_mod); + darray_enumerate(k, interp_iter, interprets) { + interp = *interp_iter; + /* Infer default key behaviours from the base level. */ + if (group == 0 && level == 0) + if (!(key->explicit & EXPLICIT_REPEAT) && interp->repeat) + key->repeats = true; + + if ((group == 0 && level == 0) || !interp->level_one_only) + if (interp->virtual_mod != XKB_MOD_INVALID) + vmodmap |= (1u << interp->virtual_mod); + + if (interp->action.type != ACTION_TYPE_NONE) { + if (darray_size(interprets) == 1) { + key->groups[group].levels[level].a.action = interp->action; + } else { + key->groups[group].levels[level].a.actions[k] = interp->action; + } + } + } - if (interp->action.type != ACTION_TYPE_NONE) - key->groups[group].levels[level].action = interp->action; + darray_free(interprets); } } @@ -165,6 +211,59 @@ ApplyInterpsToKey(struct xkb_keymap *keymap, struct xkb_key *key) return true; } +static inline bool +is_mod_action(union xkb_action *action) +{ + return action->type == ACTION_TYPE_MOD_SET || + action->type == ACTION_TYPE_MOD_LATCH || + action->type == ACTION_TYPE_MOD_LOCK; +} + +static inline bool +is_group_action(union xkb_action *action) +{ + return action->type == ACTION_TYPE_GROUP_SET || + action->type == ACTION_TYPE_GROUP_LATCH || + action->type == ACTION_TYPE_GROUP_LOCK; +} + +/* Check for mixing actions of the same category. + * We do not support that yet, because it needs a careful refactor of the state + * handling. See: `xkb_filter_apply_all`. */ +static void +CheckMultipleActionsCategories(struct xkb_keymap *keymap, struct xkb_key *key) +{ + for (xkb_layout_index_t g = 0; g < key->num_groups; g++) { + for (xkb_level_index_t l = 0; l < XkbKeyNumLevels(key, g); l++) { + struct xkb_level *level = &key->groups[g].levels[l]; + if (level->num_syms <= 1) + continue; + for (unsigned i = 0; i < level->num_syms; i++) { + union xkb_action *action1 = &level->a.actions[i]; + bool mod_action = is_mod_action(action1); + bool group_action = is_group_action(action1); + if (!(mod_action || group_action)) + continue; + for (unsigned j = i + 1; j < level->num_syms; j++) { + union xkb_action *action2 = &level->a.actions[j]; + if ((mod_action && is_mod_action(action2)) || + (group_action && is_group_action(action2))) + { + log_err(keymap->ctx, XKB_LOG_MESSAGE_NO_ID, + "Cannot use multiple %s actions " + "in the same level. Action #%u " + "for key %s in group %u/level %u ignored.\n", + (mod_action ? "modifiers" : "group"), + j + 1, KeyNameText(keymap->ctx, key->name), + g + 1, l + 1); + action2->type = ACTION_TYPE_NONE; + } + } + } + } + } +} + /** * This collects a bunch of disparate functions which was done in the server * at various points that really should've been done within xkbcomp. Turns out @@ -177,13 +276,15 @@ UpdateDerivedKeymapFields(struct xkb_keymap *keymap) struct xkb_key *key; struct xkb_mod *mod; struct xkb_led *led; - unsigned int i, j; + unsigned int i, j, k; /* Find all the interprets for the key and bind them to actions, * which will also update the vmodmap. */ - xkb_keys_foreach(key, keymap) + xkb_keys_foreach(key, keymap) { if (!ApplyInterpsToKey(keymap, key)) return false; + CheckMultipleActionsCategories(keymap, key); + } /* Update keymap->mods, the virtual -> real mod mapping. */ xkb_keys_foreach(key, keymap) @@ -204,9 +305,18 @@ UpdateDerivedKeymapFields(struct xkb_keymap *keymap) /* Update action modifiers. */ xkb_keys_foreach(key, keymap) for (i = 0; i < key->num_groups; i++) - for (j = 0; j < XkbKeyNumLevels(key, i); j++) - UpdateActionMods(keymap, &key->groups[i].levels[j].action, - key->modmap); + for (j = 0; j < XkbKeyNumLevels(key, i); j++) { + if (key->groups[i].levels[j].num_syms == 1) { + UpdateActionMods(keymap, &key->groups[i].levels[j].a.action, + key->modmap); + } else { + for (k = 0; k < key->groups[i].levels[j].num_syms; k++) { + UpdateActionMods(keymap, + &key->groups[i].levels[j].a.actions[k], + key->modmap); + } + } + } /* Update vmod -> led maps. */ xkb_leds_foreach(led, keymap) diff --git a/src/xkbcomp/parser.y b/src/xkbcomp/parser.y index a779de852..80420aae0 100644 --- a/src/xkbcomp/parser.y +++ b/src/xkbcomp/parser.y @@ -206,9 +206,9 @@ resolve_keysym(struct parser_param *param, const char *name, xkb_keysym_t *sym_r %type KeySym %type Decl %type DeclList -%type Expr Term Lhs Terminal ArrayInit KeySyms -%type OptKeySymList MultiKeySymList KeySymList Action Coord CoordList -%type OptExprList ExprList ActionList +%type Expr Term Lhs Terminal ArrayInit Actions KeySyms +%type MultiKeySymList KeySymList MultiActionList ActionList Action Coord CoordList +%type OptExprList ExprList %type VarDecl SymbolsVarDecl %type VarDeclList SymbolsBody %type VModDef @@ -476,10 +476,12 @@ SymbolsVarDecl : Lhs EQUALS Expr { $$ = VarCreate($1, $3); } | ArrayInit { $$ = VarCreate(NULL, $1); } ; -ArrayInit : OBRACKET OptKeySymList CBRACKET +ArrayInit : OBRACKET MultiKeySymList CBRACKET { $$ = $2; } - | OBRACKET ActionList CBRACKET - { $$ = ExprCreateActionList($2.head); } + | OBRACKET MultiActionList CBRACKET + { $$ = $2; } + | OBRACKET CBRACKET + { $$ = NULL; } ; GroupCompatDecl : GROUP Integer EQUALS Expr SEMI @@ -678,10 +680,27 @@ Term : MINUS Term { $$ = $2; } ; -ActionList : ActionList COMMA Action - { $$.head = $1.head; $$.last->common.next = &$3->common; $$.last = $3; } +MultiActionList : MultiActionList COMMA Action + { $$ = ExprAppendActionList($1, $3); } + | MultiActionList COMMA Actions + { $$ = ExprAppendMultiActionList($1, $3); } | Action - { $$.head = $$.last = $1; } + { $$ = ExprCreateActionList($1); } + | Actions + { $$ = ExprCreateMultiActionList($1); } + ; + +ActionList : ActionList COMMA Action + { $$ = ExprAppendActionList($1, $3); } + | Action COMMA Action + { + $$ = ExprCreateActionList($1); + $$ = ExprAppendActionList($$, $3); + } + ; + +Actions : OBRACE ActionList CBRACE + { $$ = $2; } ; Action : FieldSpec OPAREN OptExprList CPAREN @@ -708,10 +727,6 @@ Terminal : String { $$ = ExprCreateKeyName($1); } ; -OptKeySymList : MultiKeySymList { $$ = $1; } - | { $$ = NULL; } - ; - MultiKeySymList : MultiKeySymList COMMA KeySym { $$ = ExprAppendKeysymList($1, $3); } | MultiKeySymList COMMA KeySyms diff --git a/src/xkbcomp/symbols.c b/src/xkbcomp/symbols.c index 6725e6acb..fed778537 100644 --- a/src/xkbcomp/symbols.c +++ b/src/xkbcomp/symbols.c @@ -107,8 +107,26 @@ typedef struct { static void ClearLevelInfo(struct xkb_level *leveli) { - if (leveli->num_syms > 1) + if (leveli->num_syms > 1) { free(leveli->s.syms); + free(leveli->a.actions); + } +} + +static void +StealLevelInfo(struct xkb_level *into, struct xkb_level *from) +{ + ClearLevelInfo(into); + if (from->num_syms > 1) { + /* Steal */ + into->s.syms = steal(&from->s.syms); + into->a.actions = steal(&from->a.actions); + } else { + into->s.sym = from->s.sym; + into->a.action = from->a.action; + } + into->num_syms = from->num_syms; + from->num_syms = 0; } static void @@ -134,11 +152,16 @@ CopyGroupInfo(GroupInfo *to, const GroupInfo *from) darray_init(to->levels); darray_copy(to->levels, from->levels); for (xkb_level_index_t j = 0; j < darray_size(to->levels); j++) - if (darray_item(from->levels, j).num_syms > 1) + if (darray_item(from->levels, j).num_syms > 1) { darray_item(to->levels, j).s.syms = memdup(darray_item(from->levels, j).s.syms, darray_item(from->levels, j).num_syms, sizeof(xkb_keysym_t)); + darray_item(to->levels, j).a.actions = + memdup(darray_item(from->levels, j).a.actions, + darray_item(from->levels, j).num_syms, + sizeof(union xkb_action)); + } } static void @@ -279,58 +302,92 @@ MergeGroups(SymbolsInfo *info, GroupInfo *into, GroupInfo *from, bool clobber, struct xkb_level *intoLevel = &darray_item(into->levels, i); struct xkb_level *fromLevel = &darray_item(from->levels, i); - if (fromLevel->action.type == ACTION_TYPE_NONE) { - /* it's empty for consistency with other comparisons */ - } - else if (intoLevel->action.type == ACTION_TYPE_NONE) { - intoLevel->action = fromLevel->action; - } - else { - union xkb_action *use, *ignore; - use = (clobber ? &fromLevel->action : &intoLevel->action); - ignore = (clobber ? &intoLevel->action : &fromLevel->action); - - if (report) { - log_warn(info->ctx, XKB_WARNING_CONFLICTING_KEY_ACTION, - "Multiple actions for level %d/group %u on key %s; " - "Using %s, ignoring %s\n", - i + 1, group + 1, KeyNameText(info->ctx, key_name), - ActionTypeText(use->type), - ActionTypeText(ignore->type)); - } - - intoLevel->action = *use; - } - if (fromLevel->num_syms == 0) { /* it's empty for consistency with other comparisons */ } else if (intoLevel->num_syms == 0) { - intoLevel->num_syms = fromLevel->num_syms; - if (fromLevel->num_syms > 1) - intoLevel->s.syms = fromLevel->s.syms; - else - intoLevel->s.sym = fromLevel->s.sym; - fromLevel->num_syms = 0; + StealLevelInfo(intoLevel, fromLevel); } - else if (!XkbLevelsSameSyms(fromLevel, intoLevel)) { - if (report) { - log_warn(info->ctx, XKB_WARNING_CONFLICTING_KEY_SYMBOL, - "Multiple symbols for level %d/group %u on key %s; " - "Using %s, ignoring %s\n", - i + 1, group + 1, KeyNameText(info->ctx, key_name), - (clobber ? "from" : "to"), - (clobber ? "to" : "from")); + else { + bool actions_replaced = false; + /* Handle keysyms */ + if (!XkbLevelsSameSyms(fromLevel, intoLevel)) { + if (report) { + log_warn(info->ctx, XKB_WARNING_CONFLICTING_KEY_SYMBOL, + "Multiple symbols for level %d/group %u on key %s; " + "Using %s, ignoring %s\n", + i + 1, group + 1, KeyNameText(info->ctx, key_name), + (clobber ? "from" : "to"), + (clobber ? "to" : "from")); + } + + if (clobber) { + /* Use `from` keysyms and actions */ + if (report && !XkbLevelHasNoAction(intoLevel)) { + if (fromLevel->num_syms > 1) { + log_warn( + info->ctx, XKB_WARNING_CONFLICTING_KEY_ACTION, + "Multiple actions for level %d/group %u " + "on key %s; Using from, ignoring to\n", + i + 1, group + 1, + KeyNameText(info->ctx, key_name)); + } else { + log_warn( + info->ctx, XKB_WARNING_CONFLICTING_KEY_ACTION, + "Multiple actions for level %d/group %u " + "on key %s; Using %s, ignoring %s\n", + i + 1, group + 1, + KeyNameText(info->ctx, key_name), + ActionTypeText(fromLevel->a.action.type), + ActionTypeText(intoLevel->a.action.type)); + } + } + StealLevelInfo(intoLevel, fromLevel); + actions_replaced = true; + } } - if (clobber) { - ClearLevelInfo(intoLevel); - intoLevel->num_syms = fromLevel->num_syms; - if (fromLevel->num_syms > 1) - intoLevel->s.syms = fromLevel->s.syms; - else - intoLevel->s.sym = fromLevel->s.sym; - fromLevel->num_syms = 0; + /* Handle actions */ + if (actions_replaced) { + /* Already handled, included incompatible keysyms/actions count */ + } else if (XkbLevelHasNoAction(fromLevel)) { + /* It's empty for consistency with other comparisons */ + } else if (XkbLevelHasNoAction(intoLevel)) { + /* Take actions from `from` */ + assert(intoLevel->num_syms == fromLevel->num_syms); + StealLevelInfo(intoLevel, fromLevel); + } else { + /* Incompatible actions */ + assert(intoLevel->num_syms == fromLevel->num_syms); + if (report) { + if (intoLevel->num_syms > 1) { + log_warn( + info->ctx, XKB_WARNING_CONFLICTING_KEY_ACTION, + "Multiple actions for level %d/group %u on key %s; " + "%s\n", + i + 1, group + 1, KeyNameText(info->ctx, key_name), + clobber ? "Using from, ignoring to" + : "Using to, ignoring from"); + } else { + union xkb_action *use, *ignore; + use = clobber + ? &fromLevel->a.action + : &intoLevel->a.action; + ignore = clobber + ? &intoLevel->a.action + : &fromLevel->a.action; + + log_warn(info->ctx, XKB_WARNING_CONFLICTING_KEY_ACTION, + "Multiple actions for level %d/group %u " + "on key %s; Using %s, ignoring %s\n", + i + 1, group + 1, + KeyNameText(info->ctx, key_name), + ActionTypeText(use->type), + ActionTypeText(ignore->type)); + } + } + if (clobber) + StealLevelInfo(intoLevel, fromLevel); } } } @@ -722,22 +779,41 @@ AddSymbolsToKey(SymbolsInfo *info, KeyInfo *keyi, ExprDef *arrayNdx, struct xkb_level *leveli = &darray_item(groupi->levels, i); sym_index = darray_item(value->keysym_list.symsMapIndex, i); - leveli->num_syms = darray_item(value->keysym_list.symsNumEntries, i); - if (leveli->num_syms > 1) - leveli->s.syms = calloc(leveli->num_syms, sizeof(*leveli->s.syms)); - - for (unsigned j = 0; j < leveli->num_syms; j++) { - xkb_keysym_t keysym = darray_item(value->keysym_list.syms, - sym_index + j); - - if (leveli->num_syms == 1) { - if (keysym == XKB_KEY_NoSymbol) - leveli->num_syms = 0; - else - leveli->s.sym = keysym; + if (leveli->num_syms == 0) { + leveli->num_syms = darray_item(value->keysym_list.symsNumEntries, i); + if (leveli->num_syms > 1) { + /* Allocate keysyms */ + leveli->s.syms = + calloc(leveli->num_syms, sizeof(*leveli->s.syms)); + /* Initialize actions */ + leveli->a.actions = + calloc(leveli->num_syms, sizeof(*leveli->a.actions)); + static const union xkb_action dummy = + { .type = ACTION_TYPE_NONE }; + for (unsigned j = 0; j < leveli->num_syms; j++) { + leveli->a.actions[j] = dummy; + } } - else if (leveli->num_syms > 1) { - leveli->s.syms[j] = keysym; + } else if (leveli->num_syms != + darray_item(value->keysym_list.symsNumEntries, i)) + { + log_err(info->ctx, XKB_LOG_MESSAGE_NO_ID, // FIXME + "Symbols for key %s, group %u, level %u must have the same " + "number of keysyms than the corresponding actions. " + "Expected %u, got: %u. Ignoring duplicate definition\n", + KeyInfoText(info, keyi), ndx + 1, i + 1, leveli->num_syms, + darray_item(value->keysym_list.symsMapIndex, i)); + continue; + } + + if (leveli->num_syms <= 1) { + leveli->s.sym = darray_item(value->keysym_list.syms, sym_index); + if (leveli->s.sym == XKB_KEY_NoSymbol) + leveli->num_syms = 0; + } else { + for (unsigned j = 0; j < leveli->num_syms; j++) { + leveli->s.syms[j] = + darray_item(value->keysym_list.syms, sym_index + j); } } } @@ -751,8 +827,7 @@ AddActionsToKey(SymbolsInfo *info, KeyInfo *keyi, ExprDef *arrayNdx, { xkb_layout_index_t ndx; GroupInfo *groupi; - unsigned int nActs; - ExprDef *act; + xkb_level_index_t nLevels; if (!GetGroupIndex(info, keyi, arrayNdx, ACTIONS, &ndx)) return false; @@ -779,26 +854,56 @@ AddActionsToKey(SymbolsInfo *info, KeyInfo *keyi, ExprDef *arrayNdx, return false; } - nActs = 0; - for (act = value->actions.actions; act; act = (ExprDef *) act->common.next) - nActs++; - - if (darray_size(groupi->levels) < nActs) - darray_resize0(groupi->levels, nActs); + nLevels = darray_size(value->actions.actionsMapIndex); + if (darray_size(groupi->levels) < nLevels) + darray_resize0(groupi->levels, nLevels); groupi->defined |= GROUP_FIELD_ACTS; - act = value->actions.actions; - for (unsigned i = 0; i < nActs; i++) { - union xkb_action *toAct = &darray_item(groupi->levels, i).action; + for (unsigned i = 0; i < nLevels; i++) { + unsigned int act_index; + struct xkb_level *leveli = &darray_item(groupi->levels, i); - if (!HandleActionDef(info->ctx, info->actions, &info->mods, act, toAct)) - log_err(info->ctx, XKB_ERROR_INVALID_VALUE, - "Illegal action definition for %s; " - "Action for group %u/level %u ignored\n", - KeyInfoText(info, keyi), ndx + 1, i + 1); + act_index = darray_item(value->actions.actionsMapIndex, i); + if (leveli->num_syms == 0) { + leveli->num_syms = darray_item(value->actions.actionsNumEntries, i); + if (leveli->num_syms > 1) { + /* Allocate actions */ + leveli->a.actions = + calloc(leveli->num_syms, sizeof(*leveli->a.actions)); + /* Initialize keysyms */ + leveli->s.syms = + calloc(leveli->num_syms, sizeof(*leveli->s.syms)); + for (unsigned j = 0; j < leveli->num_syms; j++) { + leveli->s.syms[j] = XKB_KEY_NoSymbol; + } + } + } else if (leveli->num_syms != + darray_item(value->actions.actionsNumEntries, i)) + { + log_err(info->ctx, XKB_LOG_MESSAGE_NO_ID, // FIXME + "Symbols for key %s, group %u, level %u must have the same " + "number of actions than the corresponding keysyms. " + "Expected %u, got: %u. Ignoring duplicate definition\n", + KeyInfoText(info, keyi), ndx + 1, i + 1, leveli->num_syms, + darray_item(value->actions.actionsNumEntries, i)); + continue; + } - act = (ExprDef *) act->common.next; + for (unsigned j = 0; j < leveli->num_syms; j++) { + ExprDef *act = darray_item(value->actions.actions, act_index + j); + union xkb_action *toAct; + if (leveli->num_syms > 1) { + toAct = &darray_item(groupi->levels, i).a.actions[j]; + } else { + toAct = &darray_item(groupi->levels, i).a.action; + } + if (!HandleActionDef(info->ctx, info->actions, &info->mods, act, toAct)) + log_err(info->ctx, XKB_ERROR_INVALID_VALUE, + "Illegal action definition for %s; " + "Action for group %u/level %u ignored\n", + KeyInfoText(info, keyi), ndx + 1, i + 1); + } } return true; @@ -1298,9 +1403,16 @@ FindKeyForSymbol(struct xkb_keymap *keymap, xkb_keysym_t sym) if (group < key->num_groups && level < XkbKeyNumLevels(key, group)) { got_one_group = got_one_level = true; - if (key->groups[group].levels[level].num_syms == 1 && - key->groups[group].levels[level].s.sym == sym) + unsigned int num_syms = key->groups[group].levels[level].num_syms; + if (num_syms > 1) { + for (unsigned int k = 0; k < num_syms; k++) { + if (key->groups[group].levels[level].s.syms[k] == sym) + return key; + } + } else if (num_syms && + key->groups[group].levels[level].s.sym == sym) { return key; + } } } level++; diff --git a/test/data/keymaps/explicit-actions.xkb b/test/data/keymaps/explicit-actions.xkb index 8d1e74efb..dbfeb8b26 100644 --- a/test/data/keymaps/explicit-actions.xkb +++ b/test/data/keymaps/explicit-actions.xkb @@ -288,12 +288,12 @@ xkb_symbols "(unnamed)" { //// using explicit actions makes the key keep the initial value, //// i.e. repeat=No. //repeat= Yes, - symbols[Group1]= [ t, T ], - //actions[Group1]= [ NoAction(), NoAction() ], - symbols[Group2]= [ Cyrillic_ie, Cyrillic_IE ], - actions[Group2]= [ NoAction(), NoAction() ], - symbols[Group3]= [ w, W ]//, - //actions[Group3]= [ NoAction(), NoAction() ] + symbols[Group1]= [ t, T ], + //actions[Group1]= [ NoAction(), NoAction() ], + symbols[Group2]= [ Cyrillic_ie, Cyrillic_IE ], + actions[Group2]= [ NoAction(), NoAction() ], + symbols[Group3]= [ w, W ]//, + //actions[Group3]= [ NoAction(), NoAction() ] }; key { type[Group3]= "EIGHT_LEVEL_ALPHABETIC_LEVEL_FIVE_LOCK", @@ -315,11 +315,11 @@ xkb_symbols "(unnamed)" { type[Group2]= "TWO_LEVEL", type[Group3]= "TWO_LEVEL", //repeat= No, - symbols[Group1]= [ Shift_L ], + symbols[Group1]= [ Shift_L ], //actions[Group1]= [ SetMods(modifiers=Shift,clearLocks) ], - symbols[Group2]= [ ISO_Level3_Shift, Multi_key ], - actions[Group2]= [ SetMods(modifiers=LevelThree), NoAction() ], - symbols[Group3]= [ ISO_Level5_Shift, ISO_Level3_Shift ]//, + symbols[Group2]= [ ISO_Level3_Shift, Multi_key ], + actions[Group2]= [ SetMods(modifiers=LevelThree), NoAction() ], + symbols[Group3]= [ ISO_Level5_Shift, ISO_Level3_Shift ]//, //actions[Group3]= [ SetMods(modifiers=LevelFive,clearLocks), SetMods(modifiers=LevelThree,clearLocks) ] }; key { @@ -329,11 +329,11 @@ xkb_symbols "(unnamed)" { //// disable them. //repeat= No, //virtualMods= LevelThree, - symbols[Group1]= [ ISO_Level3_Shift ], + symbols[Group1]= [ ISO_Level3_Shift ], //actions[Group1]= [ SetMods(modifiers=LevelThree,clearLocks) ], - symbols[Group2]= [ ISO_Level3_Shift ], - actions[Group2]= [ SetMods(modifiers=LevelThree) ], - symbols[Group3]= [ ISO_Level3_Shift ]//, + symbols[Group2]= [ ISO_Level3_Shift ], + actions[Group2]= [ SetMods(modifiers=LevelThree) ], + symbols[Group3]= [ ISO_Level3_Shift ]//, //actions[Group3]= [ SetMods(modifiers=LevelThree,clearLocks) ] }; key {