Skip to content

Commit 80c9844

Browse files
committed
actions: Add support for multiple actions per level
groups_wrap + tests
1 parent a4f62fc commit 80c9844

File tree

18 files changed

+751
-268
lines changed

18 files changed

+751
-268
lines changed

include/xkbcommon/xkbcommon.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -863,7 +863,13 @@ xkb_context_set_log_fn(struct xkb_context *context,
863863
/** Flags for keymap compilation. */
864864
enum xkb_keymap_compile_flags {
865865
/** Do not apply any flags. */
866-
XKB_KEYMAP_COMPILE_NO_FLAGS = 0
866+
XKB_KEYMAP_COMPILE_NO_FLAGS = 0,
867+
XKB_KEYMAP_COMPILE_RANGE_REDIRECT_TO_0 = (1 << 0),
868+
XKB_KEYMAP_COMPILE_RANGE_SATURATE = (1 << 1),
869+
_XKB_KEYMAP_COMPILE_ALL = \
870+
( XKB_KEYMAP_COMPILE_NO_FLAGS \
871+
| XKB_KEYMAP_COMPILE_RANGE_REDIRECT_TO_0 \
872+
| XKB_KEYMAP_COMPILE_RANGE_SATURATE )
867873
};
868874

869875
/**

src/keymap-priv.c

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,16 @@ xkb_keymap_new(struct xkb_context *ctx,
6969
keymap->format = format;
7070
keymap->flags = flags;
7171

72+
if (flags & XKB_KEYMAP_COMPILE_RANGE_REDIRECT_TO_0) {
73+
keymap->out_of_range_group_action = RANGE_REDIRECT;
74+
keymap->out_of_range_group_number = 0;
75+
} else if (flags & XKB_KEYMAP_COMPILE_RANGE_SATURATE) {
76+
keymap->out_of_range_group_action = RANGE_SATURATE;
77+
} else {
78+
keymap->out_of_range_group_action = RANGE_WRAP;
79+
}
80+
log_err(ctx, 0, "xkb_keymap_new: flags: %d, out_of_range_group_action=%u\n", flags, keymap->out_of_range_group_action);
81+
7282
update_builtin_keymap_fields(keymap);
7383

7484
return keymap;
@@ -147,6 +157,95 @@ XkbLevelsSameSyms(const struct xkb_level *a, const struct xkb_level *b)
147157
if (a->num_syms != b->num_syms)
148158
return false;
149159
if (a->num_syms <= 1)
150-
return a->u.sym == b->u.sym;
151-
return memcmp(a->u.syms, b->u.syms, sizeof(*a->u.syms) * a->num_syms) == 0;
160+
return a->s.sym == b->s.sym;
161+
return memcmp(a->s.syms, b->s.syms, sizeof(*a->s.syms) * a->num_syms) == 0;
162+
}
163+
164+
bool
165+
XkbLevelHasNoAction(const struct xkb_level *level)
166+
{
167+
if (level->num_syms == 0)
168+
return true;
169+
if (level->num_syms == 1)
170+
return level->a.action.type == ACTION_TYPE_NONE;
171+
for (unsigned int k = 0; k < level->num_syms; k++) {
172+
if (level->a.actions[k].type != ACTION_TYPE_NONE)
173+
return false;
174+
}
175+
return true;
176+
}
177+
178+
xkb_layout_index_t
179+
XkbWrapGroupIntoRange(int32_t group,
180+
xkb_layout_index_t num_groups,
181+
enum xkb_range_exceed_type out_of_range_group_action,
182+
xkb_layout_index_t out_of_range_group_number)
183+
{
184+
if (num_groups == 0)
185+
return XKB_LAYOUT_INVALID;
186+
187+
if (group >= 0 && (xkb_layout_index_t) group < num_groups)
188+
return group;
189+
190+
switch (out_of_range_group_action) {
191+
case RANGE_REDIRECT:
192+
if (out_of_range_group_number >= num_groups)
193+
return 0;
194+
return out_of_range_group_number;
195+
196+
case RANGE_SATURATE:
197+
if (group < 0)
198+
return 0;
199+
else
200+
return num_groups - 1;
201+
202+
case RANGE_WRAP:
203+
default:
204+
/*
205+
* C99 says a negative dividend in a modulo operation always
206+
* gives a negative result.
207+
*/
208+
if (group < 0)
209+
return ((int) num_groups + (group % (int) num_groups));
210+
else
211+
return group % num_groups;
212+
}
213+
}
214+
215+
unsigned int
216+
xkb_keymap_key_get_actions_by_level(struct xkb_keymap *keymap,
217+
xkb_keycode_t kc,
218+
xkb_layout_index_t layout,
219+
xkb_level_index_t level,
220+
const union xkb_action **actions)
221+
{
222+
const struct xkb_key *key = XkbKey(keymap, kc);
223+
unsigned int count;
224+
225+
if (!key)
226+
goto err;
227+
228+
layout = XkbWrapGroupIntoRange(layout, key->num_groups,
229+
key->out_of_range_group_action,
230+
key->out_of_range_group_number);
231+
if (layout == XKB_LAYOUT_INVALID)
232+
goto err;
233+
234+
if (level >= XkbKeyNumLevels(key, layout))
235+
goto err;
236+
237+
count = key->groups[layout].levels[level].num_syms;
238+
if (count == 0)
239+
goto err;
240+
241+
if (count == 1)
242+
*actions = &key->groups[layout].levels[level].a.action;
243+
else
244+
*actions = key->groups[layout].levels[level].a.actions;
245+
246+
return count;
247+
248+
err:
249+
*actions = NULL;
250+
return 0;
152251
}

src/keymap.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,10 @@ xkb_keymap_unref(struct xkb_keymap *keymap)
7575
for (unsigned i = 0; i < key->num_groups; i++) {
7676
if (key->groups[i].levels) {
7777
for (unsigned j = 0; j < XkbKeyNumLevels(key, i); j++)
78-
if (key->groups[i].levels[j].num_syms > 1)
79-
free(key->groups[i].levels[j].u.syms);
78+
if (key->groups[i].levels[j].num_syms > 1) {
79+
free(key->groups[i].levels[j].s.syms);
80+
free(key->groups[i].levels[j].a.actions);
81+
}
8082
free(key->groups[i].levels);
8183
}
8284
}
@@ -132,7 +134,7 @@ xkb_keymap_new_from_names(struct xkb_context *ctx,
132134
return NULL;
133135
}
134136

135-
if (flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) {
137+
if (flags & ~(_XKB_KEYMAP_COMPILE_ALL)) {
136138
log_err_func(ctx, "unrecognized flags: %#x\n", flags);
137139
return NULL;
138140
}
@@ -180,7 +182,7 @@ xkb_keymap_new_from_buffer(struct xkb_context *ctx,
180182
return NULL;
181183
}
182184

183-
if (flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) {
185+
if (flags & ~(_XKB_KEYMAP_COMPILE_ALL)) {
184186
log_err_func(ctx, "unrecognized flags: %#x\n", flags);
185187
return NULL;
186188
}
@@ -221,7 +223,7 @@ xkb_keymap_new_from_file(struct xkb_context *ctx,
221223
return NULL;
222224
}
223225

224-
if (flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) {
226+
if (flags & ~(_XKB_KEYMAP_COMPILE_ALL)) {
225227
log_err_func(ctx, "unrecognized flags: %#x\n", flags);
226228
return NULL;
227229
}
@@ -503,9 +505,9 @@ xkb_keymap_key_get_syms_by_level(struct xkb_keymap *keymap,
503505
goto err;
504506

505507
if (num_syms == 1)
506-
*syms_out = &key->groups[layout].levels[level].u.sym;
508+
*syms_out = &key->groups[layout].levels[level].s.sym;
507509
else
508-
*syms_out = key->groups[layout].levels[level].u.syms;
510+
*syms_out = key->groups[layout].levels[level].s.syms;
509511

510512
return num_syms;
511513

src/keymap.h

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,12 +316,15 @@ enum xkb_explicit_components {
316316
};
317317

318318
struct xkb_level {
319-
union xkb_action action;
320319
unsigned int num_syms;
321320
union {
322321
xkb_keysym_t sym; /* num_syms == 1 */
323322
xkb_keysym_t *syms; /* num_syms > 1 */
324-
} u;
323+
} s;
324+
union {
325+
union xkb_action action; /* num_syms == 1 */
326+
union xkb_action *actions; /* num_syms > 1 */
327+
} a;
325328
};
326329

327330
struct xkb_group {
@@ -370,6 +373,9 @@ struct xkb_keymap {
370373
enum xkb_keymap_format format;
371374

372375
enum xkb_action_controls enabled_ctrls;
376+
/* groups_wrap control */
377+
enum xkb_range_exceed_type out_of_range_group_action;
378+
xkb_layout_index_t out_of_range_group_number;
373379

374380
xkb_keycode_t min_key_code;
375381
xkb_keycode_t max_key_code;
@@ -473,6 +479,9 @@ XkbModNameToIndex(const struct xkb_mod_set *mods, xkb_atom_t name,
473479
bool
474480
XkbLevelsSameSyms(const struct xkb_level *a, const struct xkb_level *b);
475481

482+
bool
483+
XkbLevelHasNoAction(const struct xkb_level *level);
484+
476485
xkb_layout_index_t
477486
XkbWrapGroupIntoRange(int32_t group,
478487
xkb_layout_index_t num_groups,
@@ -482,6 +491,13 @@ XkbWrapGroupIntoRange(int32_t group,
482491
xkb_mod_mask_t
483492
mod_mask_get_effective(struct xkb_keymap *keymap, xkb_mod_mask_t mods);
484493

494+
unsigned int
495+
xkb_keymap_key_get_actions_by_level(struct xkb_keymap *keymap,
496+
xkb_keycode_t kc,
497+
xkb_layout_index_t layout,
498+
xkb_level_index_t level,
499+
const union xkb_action **actions);
500+
485501
struct xkb_keymap_format_ops {
486502
bool (*keymap_new_from_names)(struct xkb_keymap *keymap,
487503
const struct xkb_rule_names *names);

0 commit comments

Comments
 (0)