Skip to content

Commit 0a0876f

Browse files
committed
Enable out-of-range layout action configuration
Enable the configuration of out-of-range layout handling using the new function `xkb_keymap_compile_options_set_layout_out_of_range_action` and the corresponding new enumeration `xkb_keymap_out_of_range_layout_action`: - `XKB_KEYMAP_WRAP_OUT_OF_RANGE_LAYOUT`: wrap into range using integer modulus (default, as before). - `XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT`: redirect to a specific layout index. - `XKB_KEYMAP_CLAMP_OUT_OF_RANGE_LAYOUT`: clamp into range, i.e. invalid indexes are corrected to the closest valid bound (0 or highest layout index). When not specified, invalid groups are brought into range using integer modulus.
1 parent e15ad51 commit 0a0876f

File tree

11 files changed

+273
-14
lines changed

11 files changed

+273
-14
lines changed

changes/api/+keymap-compile-options.feature.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Added a new keymap compile options API:
22
- `xkb_keymap_compile_options_new`
33
- `xkb_keymap_compile_options_free`
4+
- `xkb_keymap_compile_options_set_layout_out_of_range_action`
45
- `xkb_keymap_new_from_names2`
56
- `xkb_keymap_new_from_file2`
67
- `xkb_keymap_new_from_buffer2`
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Enable the configuration of out-of-range layout handling using the new function
2+
`xkb_keymap_compile_options_set_layout_out_of_range_action` and the corresponding
3+
new enumeration `xkb_keymap_out_of_range_layout_action`:
4+
- `XKB_KEYMAP_WRAP_OUT_OF_RANGE_LAYOUT`: wrap into range using integer modulus (default).
5+
- `XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT`: redirect to a specific layout index.
6+
- `XKB_KEYMAP_CLAMP_OUT_OF_RANGE_LAYOUT`: clamp into range, i.e. invalid indexes are
7+
corrected to the closest valid bound (0 or highest layout index).
8+
9+
When not specified, invalid groups are brought into range using integer modulus.

include/xkbcommon/xkbcommon.h

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
#define _XKBCOMMON_H_
8383

8484
#include <stdint.h>
85+
#include <stdbool.h>
8586
#include <stdio.h>
8687
#include <stdarg.h>
8788

@@ -915,6 +916,72 @@ xkb_keymap_compile_options_new(enum xkb_keymap_format format,
915916
void
916917
xkb_keymap_compile_options_free(struct xkb_keymap_compile_options *options);
917918

919+
/** Out-of-range layout action
920+
*
921+
* [Effective layout] index may be invalid in some contexts.
922+
* One of the following out-of-range layout action is then used to bring invalid
923+
* layout indexes back into range:
924+
*
925+
* - “redirect into range” (see: ::XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT)
926+
* - “clamp into range” (see: ::XKB_KEYMAP_CLAMP_OUT_OF_RANGE_LAYOUT)
927+
* - “wrap into range” using integer modulus (default)
928+
*
929+
* [Effective layout]: @ref ::XKB_STATE_LAYOUT_EFFECTIVE
930+
*
931+
* @since 1.8.0
932+
*/
933+
enum xkb_keymap_out_of_range_layout_action {
934+
/**
935+
* TODO: doc
936+
*/
937+
XKB_KEYMAP_WRAP_OUT_OF_RANGE_LAYOUT = 0,
938+
/**
939+
* Set the out-of-range layout action to “redirect into range”.
940+
*
941+
* - If the [effective layout] is invalid, it is set to a *target layout*.
942+
* - If the target layout is invalid, it is set to the first one (0).
943+
*
944+
* A *target layout index* (range 0..15) may be provided using the 4 least
945+
* significant bits of the corresponding #xkb_keymap_compile_flags value,
946+
* e.g.:
947+
*
948+
* ```c
949+
* // Set the target layout index to 1.
950+
* enum xkb_keymap_compile_flags flags = XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT | 1;
951+
* ```
952+
*
953+
* @since 1.8.0
954+
*
955+
* [effective layout]: @ref ::XKB_STATE_LAYOUT_EFFECTIVE
956+
*/
957+
XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT,
958+
/**
959+
* Set the out-of-range layout action to “clamp into range”: if the
960+
* [effective layout] is invalid, it is set to nearest valid layout:
961+
*
962+
* - effective layout larger than the highest supported layout are mapped to
963+
* the highest supported layout;
964+
* - effective layout less than 0 are mapped to 0.
965+
*
966+
* @since 1.8.0
967+
*
968+
* [effective layout]: @ref ::XKB_STATE_LAYOUT_EFFECTIVE
969+
*/
970+
XKB_KEYMAP_CLAMP_OUT_OF_RANGE_LAYOUT
971+
};
972+
973+
/**
974+
* TODO: more doc
975+
*
976+
* @since 1.8.0
977+
*/
978+
bool
979+
xkb_keymap_compile_options_set_layout_out_of_range_action(
980+
struct xkb_keymap_compile_options *options,
981+
enum xkb_keymap_out_of_range_layout_action,
982+
xkb_layout_index_t target
983+
);
984+
918985
/**
919986
* Create a keymap from RMLVO names.
920987
*

src/keymap-priv.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,33 @@ xkb_keymap_new(struct xkb_context *ctx,
5757
const struct xkb_keymap_compile_options *options)
5858
{
5959
struct xkb_keymap *keymap;
60+
enum xkb_range_exceed_type out_of_range_group_action;
61+
xkb_layout_index_t out_of_range_group_number = options->out_of_range_group_number;
62+
63+
switch (options->out_of_range_group_action) {
64+
case XKB_KEYMAP_WRAP_OUT_OF_RANGE_LAYOUT:
65+
out_of_range_group_action = RANGE_WRAP;
66+
break;
67+
case XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT:
68+
out_of_range_group_action = RANGE_REDIRECT;
69+
break;
70+
case XKB_KEYMAP_CLAMP_OUT_OF_RANGE_LAYOUT:
71+
out_of_range_group_action = RANGE_SATURATE;
72+
break;
73+
default:
74+
log_err(ctx, XKB_LOG_MESSAGE_NO_ID,
75+
"Unsupported \"out of range layout\" action: %#x\n",
76+
options->out_of_range_group_action);
77+
return NULL;
78+
}
79+
80+
if (out_of_range_group_number && out_of_range_group_action != RANGE_REDIRECT) {
81+
log_err(ctx, XKB_LOG_MESSAGE_NO_ID,
82+
"Redirect layout index can only be used in combination with "
83+
"XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT action, but got: %#x\n",
84+
options->out_of_range_group_action);
85+
return NULL;
86+
}
6087

6188
keymap = calloc(1, sizeof(*keymap));
6289
if (!keymap)
@@ -68,6 +95,9 @@ xkb_keymap_new(struct xkb_context *ctx,
6895
keymap->format = options->format;
6996
keymap->flags = options->flags;
7097

98+
keymap->out_of_range_group_action = out_of_range_group_action;
99+
keymap->out_of_range_group_number = out_of_range_group_number;
100+
71101
update_builtin_keymap_fields(keymap);
72102

73103
return keymap;

src/keymap.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@ xkb_keymap_compile_options_free(struct xkb_keymap_compile_options *options)
7878
free(options);
7979
}
8080

81+
XKB_EXPORT bool
82+
xkb_keymap_compile_options_set_layout_out_of_range_action(
83+
struct xkb_keymap_compile_options *options,
84+
enum xkb_keymap_out_of_range_layout_action action,
85+
xkb_layout_index_t target)
86+
{
87+
// FIXME checks
88+
options->out_of_range_group_action = action;
89+
options->out_of_range_group_number = target;
90+
return true;
91+
}
92+
8193
XKB_EXPORT struct xkb_keymap *
8294
xkb_keymap_ref(struct xkb_keymap *keymap)
8395
{

src/keymap.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ enum xkb_range_exceed_type {
307307
RANGE_WRAP = 0,
308308
RANGE_SATURATE,
309309
RANGE_REDIRECT,
310+
#define _XKB_RANGE_EXCEED_TYPE_NUM_ENTRIES 3
310311
};
311312

312313
enum xkb_explicit_components {
@@ -332,6 +333,14 @@ struct xkb_group {
332333
struct xkb_level *levels;
333334
};
334335

336+
/* Note: enum value may be interpreted as a signed int, so we need an extra bit
337+
* to store the sign. */
338+
#define OUT_OF_RANGE_GROUP_ACTION_SIZE 3
339+
#define OUT_OF_RANGE_GROUP_NUMBER_SIZE (32 - OUT_OF_RANGE_GROUP_ACTION_SIZE)
340+
#if _XKB_RANGE_EXCEED_TYPE_NUM_ENTRIES >= (1 << (OUT_OF_RANGE_GROUP_ACTION_SIZE - 1))
341+
#error "Cannot store enum xkb_range_exceed_type in bitfield out_of_range_group_number"
342+
#endif
343+
335344
struct xkb_key {
336345
xkb_keycode_t keycode;
337346
xkb_atom_t name;
@@ -343,8 +352,8 @@ struct xkb_key {
343352

344353
bool repeats;
345354

346-
enum xkb_range_exceed_type out_of_range_group_action;
347-
xkb_layout_index_t out_of_range_group_number;
355+
xkb_layout_index_t out_of_range_group_number:OUT_OF_RANGE_GROUP_NUMBER_SIZE;
356+
enum xkb_range_exceed_type out_of_range_group_action:OUT_OF_RANGE_GROUP_ACTION_SIZE;
348357

349358
xkb_layout_index_t num_groups;
350359
struct xkb_group *groups;
@@ -364,6 +373,8 @@ struct xkb_mod_set {
364373
struct xkb_keymap_compile_options {
365374
enum xkb_keymap_format format;
366375
enum xkb_keymap_compile_flags flags;
376+
xkb_layout_index_t out_of_range_group_number:OUT_OF_RANGE_GROUP_NUMBER_SIZE;
377+
enum xkb_range_exceed_type out_of_range_group_action:OUT_OF_RANGE_GROUP_ACTION_SIZE;
367378
};
368379

369380
/* Common keyboard description structure */
@@ -397,6 +408,9 @@ struct xkb_keymap {
397408
/* Not all groups must have names. */
398409
xkb_layout_index_t num_group_names;
399410
xkb_atom_t *group_names;
411+
/* groups_wrap control */
412+
xkb_layout_index_t out_of_range_group_number:OUT_OF_RANGE_GROUP_NUMBER_SIZE;
413+
enum xkb_range_exceed_type out_of_range_group_action:OUT_OF_RANGE_GROUP_ACTION_SIZE;
400414

401415
struct xkb_led leds[XKB_MAX_LEDS];
402416
unsigned int num_leds;

src/state.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,9 @@ XkbWrapGroupIntoRange(int32_t group,
173173

174174
switch (out_of_range_group_action) {
175175
case RANGE_REDIRECT:
176-
if (out_of_range_group_number >= num_groups)
177-
return 0;
178-
return out_of_range_group_number;
176+
return (out_of_range_group_number >= num_groups)
177+
? 0
178+
: out_of_range_group_number;
179179

180180
case RANGE_SATURATE:
181181
if (group < 0)
@@ -815,12 +815,11 @@ xkb_state_update_derived(struct xkb_state *state)
815815
state->components.latched_mods |
816816
state->components.locked_mods);
817817

818-
/* TODO: Use groups_wrap control instead of always RANGE_WRAP. */
819-
820818
/* Lock group must be adjusted, but not base nor latched groups */
821819
wrapped = XkbWrapGroupIntoRange(state->components.locked_group,
822820
state->keymap->num_groups,
823-
RANGE_WRAP, 0);
821+
state->keymap->out_of_range_group_action,
822+
state->keymap->out_of_range_group_number);
824823
state->components.locked_group =
825824
(wrapped == XKB_LAYOUT_INVALID ? 0 : wrapped);
826825

@@ -829,7 +828,9 @@ xkb_state_update_derived(struct xkb_state *state)
829828
state->components.latched_group +
830829
state->components.locked_group,
831830
state->keymap->num_groups,
832-
RANGE_WRAP, 0);
831+
state->keymap->out_of_range_group_action,
832+
state->keymap->out_of_range_group_number);
833+
833834
state->components.group =
834835
(wrapped == XKB_LAYOUT_INVALID ? 0 : wrapped);
835836

test/common.c

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -402,9 +402,10 @@ test_compile_buffer(struct xkb_context *context, const char *buf, size_t len)
402402
}
403403

404404
struct xkb_keymap *
405-
test_compile_rules(struct xkb_context *context, const char *rules,
406-
const char *model, const char *layout,
407-
const char *variant, const char *options)
405+
test_compile_rules_with_options(struct xkb_context *context, const char *rules,
406+
const char *model, const char *layout,
407+
const char *variant, const char *options,
408+
struct xkb_keymap_compile_options *keymap_options)
408409
{
409410
struct xkb_keymap *keymap;
410411
struct xkb_rule_names rmlvo = {
@@ -416,9 +417,9 @@ test_compile_rules(struct xkb_context *context, const char *rules,
416417
};
417418

418419
if (!rules && !model && !layout && !variant && !options)
419-
keymap = xkb_keymap_new_from_names(context, NULL, 0);
420+
keymap = xkb_keymap_new_from_names2(context, NULL, keymap_options);
420421
else
421-
keymap = xkb_keymap_new_from_names(context, &rmlvo, 0);
422+
keymap = xkb_keymap_new_from_names2(context, &rmlvo, keymap_options);
422423

423424
if (!keymap) {
424425
fprintf(stderr,
@@ -429,3 +430,12 @@ test_compile_rules(struct xkb_context *context, const char *rules,
429430

430431
return keymap;
431432
}
433+
434+
struct xkb_keymap *
435+
test_compile_rules(struct xkb_context *context, const char *rules,
436+
const char *model, const char *layout,
437+
const char *variant, const char *options)
438+
{
439+
return test_compile_rules_with_options(context, rules, model, layout,
440+
variant, options, NULL);
441+
}

0 commit comments

Comments
 (0)