Skip to content

Commit

Permalink
Enable out-of-range layout action configuration
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
wismill committed Sep 28, 2024
1 parent e15ad51 commit 58348ec
Show file tree
Hide file tree
Showing 11 changed files with 273 additions and 14 deletions.
1 change: 1 addition & 0 deletions changes/api/+keymap-compile-options.feature.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Added a new keymap compile options API:
- `xkb_keymap_compile_options_new`
- `xkb_keymap_compile_options_free`
- `xkb_keymap_compile_options_set_layout_out_of_range_action`
- `xkb_keymap_new_from_names2`
- `xkb_keymap_new_from_file2`
- `xkb_keymap_new_from_buffer2`
Expand Down
9 changes: 9 additions & 0 deletions changes/api/+out-of-range-layout-config.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
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).
- `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.
67 changes: 67 additions & 0 deletions include/xkbcommon/xkbcommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
#define _XKBCOMMON_H_

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdarg.h>

Expand Down Expand Up @@ -915,6 +916,72 @@ xkb_keymap_compile_options_new(enum xkb_keymap_format format,
void
xkb_keymap_compile_options_free(struct xkb_keymap_compile_options *options);

/** Out-of-range layout action
*
* [Effective layout] index may be invalid in some contexts.
* One of the following out-of-range layout action is then used to bring invalid
* layout indexes back into range:
*
* - “redirect into range” (see: ::XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT)
* - “clamp into range” (see: ::XKB_KEYMAP_CLAMP_OUT_OF_RANGE_LAYOUT)
* - “wrap into range” using integer modulus (default)
*
* [Effective layout]: @ref ::XKB_STATE_LAYOUT_EFFECTIVE
*
* @since 1.8.0
*/
enum xkb_keymap_out_of_range_layout_action {
/**
* TODO: doc
*/
XKB_KEYMAP_WRAP_OUT_OF_RANGE_LAYOUT = 0,
/**
* Set the out-of-range layout action to “redirect into range”.
*
* - If the [effective layout] is invalid, it is set to a *target layout*.
* - If the target layout is invalid, it is set to the first one (0).
*
* A *target layout index* (range 0..15) may be provided using the 4 least
* significant bits of the corresponding #xkb_keymap_compile_flags value,
* e.g.:
*
* ```c
* // Set the target layout index to 1.
* enum xkb_keymap_compile_flags flags = XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT | 1;
* ```
*
* @since 1.8.0
*
* [effective layout]: @ref ::XKB_STATE_LAYOUT_EFFECTIVE
*/
XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT,
/**
* Set the out-of-range layout action to “clamp into range”: if the
* [effective layout] is invalid, it is set to nearest valid layout:
*
* - effective layout larger than the highest supported layout are mapped to
* the highest supported layout;
* - effective layout less than 0 are mapped to 0.
*
* @since 1.8.0
*
* [effective layout]: @ref ::XKB_STATE_LAYOUT_EFFECTIVE
*/
XKB_KEYMAP_CLAMP_OUT_OF_RANGE_LAYOUT
};

/**
* TODO: more doc
*
* @since 1.8.0
*/
bool
xkb_keymap_compile_options_set_layout_out_of_range_action(
struct xkb_keymap_compile_options *options,
enum xkb_keymap_out_of_range_layout_action,
xkb_layout_index_t target
);

/**
* Create a keymap from RMLVO names.
*
Expand Down
30 changes: 30 additions & 0 deletions src/keymap-priv.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,33 @@ xkb_keymap_new(struct xkb_context *ctx,
const struct xkb_keymap_compile_options *options)
{
struct xkb_keymap *keymap;
enum xkb_range_exceed_type out_of_range_group_action;
xkb_layout_index_t out_of_range_group_number = options->out_of_range_group_number;

switch (options->out_of_range_group_action) {
case XKB_KEYMAP_WRAP_OUT_OF_RANGE_LAYOUT:
out_of_range_group_action = RANGE_WRAP;
break;
case XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT:
out_of_range_group_action = RANGE_REDIRECT;
break;
case XKB_KEYMAP_CLAMP_OUT_OF_RANGE_LAYOUT:
out_of_range_group_action = RANGE_SATURATE;
break;
default:
log_err(ctx, XKB_LOG_MESSAGE_NO_ID,
"Unsupported \"out of range layout\" action: %#x\n",
options->out_of_range_group_action);
return NULL;
}

if (out_of_range_group_number && out_of_range_group_action != RANGE_REDIRECT) {
log_err(ctx, XKB_LOG_MESSAGE_NO_ID,
"Redirect layout index can only be used in combination with "
"XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT action, but got: %#x\n",
options->out_of_range_group_action);
return NULL;
}

keymap = calloc(1, sizeof(*keymap));
if (!keymap)
Expand All @@ -68,6 +95,9 @@ xkb_keymap_new(struct xkb_context *ctx,
keymap->format = options->format;
keymap->flags = options->flags;

keymap->out_of_range_group_action = out_of_range_group_action;
keymap->out_of_range_group_number = out_of_range_group_number;

update_builtin_keymap_fields(keymap);

return keymap;
Expand Down
12 changes: 12 additions & 0 deletions src/keymap.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ xkb_keymap_compile_options_free(struct xkb_keymap_compile_options *options)
free(options);
}

bool
xkb_keymap_compile_options_set_layout_out_of_range_action(
struct xkb_keymap_compile_options *options,
enum xkb_keymap_out_of_range_layout_action action,
xkb_layout_index_t target)
{
// FIXME checks
options->out_of_range_group_action = action;
options->out_of_range_group_number = target;
return true;
}

XKB_EXPORT struct xkb_keymap *
xkb_keymap_ref(struct xkb_keymap *keymap)
{
Expand Down
18 changes: 16 additions & 2 deletions src/keymap.h
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ enum xkb_range_exceed_type {
RANGE_WRAP = 0,
RANGE_SATURATE,
RANGE_REDIRECT,
#define _XKB_RANGE_EXCEED_TYPE_NUM_ENTRIES 3
};

enum xkb_explicit_components {
Expand All @@ -332,6 +333,14 @@ struct xkb_group {
struct xkb_level *levels;
};

/* Note: enum value may be interpreted as a signed int, so we need an extra bit
* to store the sign. */
#define OUT_OF_RANGE_GROUP_ACTION_SIZE 3
#define OUT_OF_RANGE_GROUP_NUMBER_SIZE (32 - OUT_OF_RANGE_GROUP_ACTION_SIZE)
#if _XKB_RANGE_EXCEED_TYPE_NUM_ENTRIES >= (1 << (OUT_OF_RANGE_GROUP_ACTION_SIZE - 1))
#error "Cannot store enum xkb_range_exceed_type in bitfield out_of_range_group_number"
#endif

struct xkb_key {
xkb_keycode_t keycode;
xkb_atom_t name;
Expand All @@ -343,8 +352,8 @@ struct xkb_key {

bool repeats;

enum xkb_range_exceed_type out_of_range_group_action;
xkb_layout_index_t out_of_range_group_number;
xkb_layout_index_t out_of_range_group_number:OUT_OF_RANGE_GROUP_NUMBER_SIZE;
enum xkb_range_exceed_type out_of_range_group_action:OUT_OF_RANGE_GROUP_ACTION_SIZE;

xkb_layout_index_t num_groups;
struct xkb_group *groups;
Expand All @@ -364,6 +373,8 @@ struct xkb_mod_set {
struct xkb_keymap_compile_options {
enum xkb_keymap_format format;
enum xkb_keymap_compile_flags flags;
xkb_layout_index_t out_of_range_group_number:OUT_OF_RANGE_GROUP_NUMBER_SIZE;
enum xkb_range_exceed_type out_of_range_group_action:OUT_OF_RANGE_GROUP_ACTION_SIZE;
};

/* Common keyboard description structure */
Expand Down Expand Up @@ -397,6 +408,9 @@ struct xkb_keymap {
/* Not all groups must have names. */
xkb_layout_index_t num_group_names;
xkb_atom_t *group_names;
/* groups_wrap control */
xkb_layout_index_t out_of_range_group_number:OUT_OF_RANGE_GROUP_NUMBER_SIZE;
enum xkb_range_exceed_type out_of_range_group_action:OUT_OF_RANGE_GROUP_ACTION_SIZE;

struct xkb_led leds[XKB_MAX_LEDS];
unsigned int num_leds;
Expand Down
15 changes: 8 additions & 7 deletions src/state.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,9 @@ XkbWrapGroupIntoRange(int32_t 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;
return (out_of_range_group_number >= num_groups)
? 0
: out_of_range_group_number;

case RANGE_SATURATE:
if (group < 0)
Expand Down Expand Up @@ -815,12 +815,11 @@ xkb_state_update_derived(struct xkb_state *state)
state->components.latched_mods |
state->components.locked_mods);

/* TODO: Use groups_wrap control instead of always RANGE_WRAP. */

/* Lock group must be adjusted, but not base nor latched groups */
wrapped = XkbWrapGroupIntoRange(state->components.locked_group,
state->keymap->num_groups,
RANGE_WRAP, 0);
state->keymap->out_of_range_group_action,
state->keymap->out_of_range_group_number);
state->components.locked_group =
(wrapped == XKB_LAYOUT_INVALID ? 0 : wrapped);

Expand All @@ -829,7 +828,9 @@ xkb_state_update_derived(struct xkb_state *state)
state->components.latched_group +
state->components.locked_group,
state->keymap->num_groups,
RANGE_WRAP, 0);
state->keymap->out_of_range_group_action,
state->keymap->out_of_range_group_number);

state->components.group =
(wrapped == XKB_LAYOUT_INVALID ? 0 : wrapped);

Expand Down
20 changes: 15 additions & 5 deletions test/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -402,9 +402,10 @@ test_compile_buffer(struct xkb_context *context, const char *buf, size_t len)
}

struct xkb_keymap *
test_compile_rules(struct xkb_context *context, const char *rules,
const char *model, const char *layout,
const char *variant, const char *options)
test_compile_rules_with_options(struct xkb_context *context, const char *rules,
const char *model, const char *layout,
const char *variant, const char *options,
struct xkb_keymap_compile_options *keymap_options)
{
struct xkb_keymap *keymap;
struct xkb_rule_names rmlvo = {
Expand All @@ -416,9 +417,9 @@ test_compile_rules(struct xkb_context *context, const char *rules,
};

if (!rules && !model && !layout && !variant && !options)
keymap = xkb_keymap_new_from_names(context, NULL, 0);
keymap = xkb_keymap_new_from_names2(context, NULL, keymap_options);
else
keymap = xkb_keymap_new_from_names(context, &rmlvo, 0);
keymap = xkb_keymap_new_from_names2(context, &rmlvo, keymap_options);

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

return keymap;
}

struct xkb_keymap *
test_compile_rules(struct xkb_context *context, const char *rules,
const char *model, const char *layout,
const char *variant, const char *options)
{
return test_compile_rules_with_options(context, rules, model, layout,
variant, options, NULL);
}
Loading

0 comments on commit 58348ec

Please sign in to comment.