diff --git a/doc/message-registry.md b/doc/message-registry.md
index 0a5189326..01f529224 100644
--- a/doc/message-registry.md
+++ b/doc/message-registry.md
@@ -6,7 +6,7 @@ NOTE: This file has been generated automatically by “update-message-registry.p
-->
This page lists the warnings and errors generated by xkbcommon.
-There are currently 55 entries.
+There are currently 56 entries.
@todo The documentation of the log messages is a work in progress.
@@ -53,6 +53,7 @@ There are currently 55 entries.
| [XKB-623] | `invalid-real-modifier` | Invalid _real_ modifier | Error |
| [XKB-645] | `unknown-char-escape-sequence` | Warn on unknown escape sequence in string literal | Warning |
| [XKB-661] | `invalid-included-file` | The target file of an include statement could not be processed | Error |
+| [XKB-697] | `multiple-out-of-range-layout-flags` | Multiple out-of-range layout flags were set, but only one is allowed. | Error |
| [XKB-700] | `multiple-groups-at-once` | Warn if a key defines multiple groups at once | Warning |
| [XKB-711] | `unsupported-symbols-field` | A legacy X11 symbol field is not supported | Warning |
| [XKB-769] | `invalid-syntax` | The syntax is invalid and the file cannot be parsed | Error |
@@ -550,6 +551,14 @@ xkbcommon support the following escape sequences in string literals:
SummaryThe target file of an include statement could not be processed
+### XKB-697 – Multiple out of range layout flags {#XKB-697}
+
+
+ - Since
- 1.8.0
+ - Type
- Error
+ - Summary
- Multiple out-of-range layout flags were set, but only one is allowed.
+
+
### XKB-700 – Multiple groups at once {#XKB-700}
@@ -724,6 +733,7 @@ The modifiers used in `map` or `preserve` entries should be declared using the e
[XKB-623]: @ref XKB-623
[XKB-645]: @ref XKB-645
[XKB-661]: @ref XKB-661
+[XKB-697]: @ref XKB-697
[XKB-700]: @ref XKB-700
[XKB-711]: @ref XKB-711
[XKB-769]: @ref XKB-769
diff --git a/doc/message-registry.yaml b/doc/message-registry.yaml
index 6a9725fd3..05d89be56 100644
--- a/doc/message-registry.yaml
+++ b/doc/message-registry.yaml
@@ -324,6 +324,11 @@
added: ALWAYS
type: error
description: "The target file of an include statement could not be processed"
+- id: "multiple-out-of-range-layout-flags"
+ code: 697
+ added: 1.8.0
+ type: error
+ description: "Multiple out-of-range layout flags were set, but only one is allowed."
- id: "multiple-groups-at-once"
code: 700
added: ALWAYS
diff --git a/include/xkbcommon/xkbcommon.h b/include/xkbcommon/xkbcommon.h
index 219d197ba..fde93667b 100644
--- a/include/xkbcommon/xkbcommon.h
+++ b/include/xkbcommon/xkbcommon.h
@@ -875,7 +875,33 @@ xkb_context_set_log_fn(struct xkb_context *context,
/** Flags for keymap compilation. */
enum xkb_keymap_compile_flags {
/** Do not apply any flags. */
- XKB_KEYMAP_COMPILE_NO_FLAGS = 0
+ XKB_KEYMAP_COMPILE_NO_FLAGS = 0,
+ /*
+ * 4 least significant bit reserved to encode layout index (0..15) for
+ * XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT
+ */
+ /**
+ * Set the out-of-range layout action to “redirect into range”:
+ *
+ * - If the effective layout is invalid, it is set to the layout given by
+ * the 4 least significant bits of the corresponding
+ * #xkb_keymap_compile_flags value.
+ * - If the given layout is invalid, it is set to the first one (0).
+ *
+ * @since 1.8.0
+ */
+ XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT = 1 << 4,
+ /**
+ * 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
+ */
+ XKB_KEYMAP_CLAMP_OUT_OF_RANGE_LAYOUT = 1 << 5
};
/**
diff --git a/src/keymap-priv.c b/src/keymap-priv.c
index 8fdaf5b6f..55a7d683a 100644
--- a/src/keymap-priv.c
+++ b/src/keymap-priv.c
@@ -52,12 +52,36 @@ update_builtin_keymap_fields(struct xkb_keymap *keymap)
keymap->mods.num_mods = ARRAY_SIZE(builtin_mods);
}
+#define XKB_KEYMAP_OUT_OF_RANGE_LAYOUT_FLAGS \
+ (XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT | \
+ XKB_KEYMAP_CLAMP_OUT_OF_RANGE_LAYOUT)
+
struct xkb_keymap *
xkb_keymap_new(struct xkb_context *ctx,
enum xkb_keymap_format format,
enum xkb_keymap_compile_flags flags)
{
struct xkb_keymap *keymap;
+ enum xkb_range_exceed_type out_of_range_group_action;
+ xkb_layout_index_t out_of_range_group_number = 0;
+
+ switch (flags & XKB_KEYMAP_OUT_OF_RANGE_LAYOUT_FLAGS) {
+ case 0:
+ out_of_range_group_action = RANGE_WRAP;
+ break;
+ case XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT:
+ out_of_range_group_action = RANGE_REDIRECT;
+ out_of_range_group_number =
+ flags & (XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT - 1);
+ break;
+ case XKB_KEYMAP_CLAMP_OUT_OF_RANGE_LAYOUT:
+ out_of_range_group_action = RANGE_SATURATE;
+ break;
+ default:
+ log_err(ctx, XKB_ERROR_MULTIPLE_OUT_OF_RANGE_LAYOUT_FLAGS,
+ "Cannot mix out of range layout flags: %#x\n", flags);
+ return NULL;
+ }
keymap = calloc(1, sizeof(*keymap));
if (!keymap)
@@ -69,6 +93,9 @@ xkb_keymap_new(struct xkb_context *ctx,
keymap->format = format;
keymap->flags = 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;
diff --git a/src/keymap.c b/src/keymap.c
index 0291aedbb..6b8af980f 100644
--- a/src/keymap.c
+++ b/src/keymap.c
@@ -132,7 +132,7 @@ xkb_keymap_new_from_names(struct xkb_context *ctx,
return NULL;
}
- if (flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) {
+ if (flags & ~XKB_KEYMAP_FLAGS_ALL) {
log_err_func(ctx, "unrecognized flags: %#x\n", flags);
return NULL;
}
@@ -180,7 +180,7 @@ xkb_keymap_new_from_buffer(struct xkb_context *ctx,
return NULL;
}
- if (flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) {
+ if (flags & ~XKB_KEYMAP_FLAGS_ALL) {
log_err_func(ctx, "unrecognized flags: %#x\n", flags);
return NULL;
}
@@ -221,7 +221,7 @@ xkb_keymap_new_from_file(struct xkb_context *ctx,
return NULL;
}
- if (flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) {
+ if (flags & ~XKB_KEYMAP_FLAGS_ALL) {
log_err_func(ctx, "unrecognized flags: %#x\n", flags);
return NULL;
}
diff --git a/src/keymap.h b/src/keymap.h
index f7ea5bdf1..c46d61b2d 100644
--- a/src/keymap.h
+++ b/src/keymap.h
@@ -120,6 +120,13 @@ enum mod_type {
};
#define MOD_REAL_MASK_ALL ((xkb_mod_mask_t) 0x000000ff)
+#define XKB_MAX_REDIRECT_LAYOUT_INDEX (XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT - 1)
+
+#define XKB_KEYMAP_FLAGS_ALL \
+ (XKB_MAX_REDIRECT_LAYOUT_INDEX | \
+ XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT | \
+ XKB_KEYMAP_CLAMP_OUT_OF_RANGE_LAYOUT)
+
enum xkb_action_type {
ACTION_TYPE_NONE = 0,
ACTION_TYPE_MOD_SET,
@@ -392,6 +399,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:30;
+ enum xkb_range_exceed_type out_of_range_group_action:2;
struct xkb_led leds[XKB_MAX_LEDS];
unsigned int num_leds;
diff --git a/src/messages-codes.h b/src/messages-codes.h
index 3be18db02..19a9e929c 100644
--- a/src/messages-codes.h
+++ b/src/messages-codes.h
@@ -94,6 +94,8 @@ enum xkb_message_code {
XKB_WARNING_UNKNOWN_CHAR_ESCAPE_SEQUENCE = 645,
/** The target file of an include statement could not be processed */
XKB_ERROR_INVALID_INCLUDED_FILE = 661,
+ /** Multiple out-of-range layout flags were set, but only one is allowed. */
+ XKB_ERROR_MULTIPLE_OUT_OF_RANGE_LAYOUT_FLAGS = 697,
/** Warn if a key defines multiple groups at once */
XKB_WARNING_MULTIPLE_GROUPS_AT_ONCE = 700,
/** A legacy X11 symbol field is not supported */
diff --git a/src/state.c b/src/state.c
index d5a60eb57..fd2931a6f 100644
--- a/src/state.c
+++ b/src/state.c
@@ -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)
@@ -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);
@@ -829,7 +828,15 @@ 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);
+
+ // FIXME: remove log
+ log_err(state->keymap->ctx, 0,
+ "xkb_state_update_derived: base_group: %d, latched_group: %d, locked_group: %d, wrapped: %u, out_of_range_group_action: %u\n",
+ state->components.base_group, state->components.latched_group, state->components.locked_group,
+ wrapped, state->keymap->out_of_range_group_action);
+
state->components.group =
(wrapped == XKB_LAYOUT_INVALID ? 0 : wrapped);
diff --git a/src/x11/keymap.c b/src/x11/keymap.c
index 72f663927..1f58a3bf6 100644
--- a/src/x11/keymap.c
+++ b/src/x11/keymap.c
@@ -1142,7 +1142,7 @@ xkb_x11_keymap_new_from_device(struct xkb_context *ctx,
struct xkb_keymap *keymap;
const enum xkb_keymap_format format = XKB_KEYMAP_FORMAT_TEXT_V1;
- if (flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) {
+ if (flags & ~XKB_KEYMAP_FLAGS_ALL) {
log_err_func(ctx, "unrecognized flags: %#x\n", flags);
return NULL;
}
diff --git a/test/common.c b/test/common.c
index 392aacd4e..8330d6e9d 100644
--- a/test/common.c
+++ b/test/common.c
@@ -401,9 +401,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_flags(struct xkb_context *context, const char *rules,
+ const char *model, const char *layout,
+ const char *variant, const char *options,
+ enum xkb_keymap_compile_flags flags)
{
struct xkb_keymap *keymap;
struct xkb_rule_names rmlvo = {
@@ -415,9 +416,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_names(context, NULL, flags);
else
- keymap = xkb_keymap_new_from_names(context, &rmlvo, 0);
+ keymap = xkb_keymap_new_from_names(context, &rmlvo, flags);
if (!keymap) {
fprintf(stderr,
@@ -428,3 +429,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_flags(context, rules, model, layout,
+ variant, options, XKB_KEYMAP_COMPILE_NO_FLAGS);
+}
diff --git a/test/keyseq.c b/test/keyseq.c
index 17d133565..d3b0ddd8e 100644
--- a/test/keyseq.c
+++ b/test/keyseq.c
@@ -667,6 +667,58 @@ main(void)
KEY_H, BOTH, XKB_KEY_h, FINISH));
xkb_keymap_unref(keymap);
+ keymap = test_compile_rules_with_flags(
+ ctx, "evdev", "", "us,il,ru", "",
+ "grp:alt_shift_toggle_bidir,grp:menu_toggle",
+ XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT | 1);
+ assert(keymap);
+
+ /* Out-of-range group action: redirect */
+ assert(test_key_seq(keymap,
+ KEY_H, BOTH, XKB_KEY_h, NEXT,
+ KEY_LEFTSHIFT, DOWN, XKB_KEY_Shift_L, NEXT,
+ KEY_LEFTALT, BOTH, XKB_KEY_ISO_Prev_Group, NEXT,
+ KEY_LEFTSHIFT, UP, XKB_KEY_Shift_L, NEXT,
+ /* Negative group: redirect to second layout */
+ KEY_H, BOTH, XKB_KEY_hebrew_yod, NEXT,
+ KEY_COMPOSE, BOTH, XKB_KEY_ISO_Next_Group, NEXT,
+ KEY_H, BOTH, XKB_KEY_Cyrillic_er, NEXT,
+ KEY_COMPOSE, BOTH, XKB_KEY_ISO_Next_Group, NEXT,
+ /* Greater that last group: redirect to second layout */
+ KEY_H, BOTH, XKB_KEY_hebrew_yod, FINISH));
+
+ xkb_keymap_unref(keymap);
+ keymap = test_compile_rules_with_flags(
+ ctx, "evdev", "", "us,il,ru", "",
+ "grp:alt_shift_toggle_bidir,grp:menu_toggle",
+ XKB_KEYMAP_CLAMP_OUT_OF_RANGE_LAYOUT);
+ assert(keymap);
+
+ /* Out-of-range group action: clamp */
+ assert(test_key_seq(keymap,
+ KEY_H, BOTH, XKB_KEY_h, NEXT,
+ KEY_LEFTSHIFT, DOWN, XKB_KEY_Shift_L, NEXT,
+ KEY_LEFTALT, BOTH, XKB_KEY_ISO_Prev_Group, NEXT,
+ KEY_LEFTSHIFT, UP, XKB_KEY_Shift_L, NEXT,
+ /* Negative group: redirect to first layout */
+ KEY_H, BOTH, XKB_KEY_h, NEXT,
+ KEY_COMPOSE, BOTH, XKB_KEY_ISO_Next_Group, NEXT,
+ KEY_H, BOTH, XKB_KEY_hebrew_yod, NEXT,
+ KEY_COMPOSE, BOTH, XKB_KEY_ISO_Next_Group, NEXT,
+ KEY_H, BOTH, XKB_KEY_Cyrillic_er, NEXT,
+ KEY_COMPOSE, BOTH, XKB_KEY_ISO_Next_Group, NEXT,
+ KEY_COMPOSE, BOTH, XKB_KEY_ISO_Next_Group, NEXT,
+ /* Greater that last group: redirect to last layout */
+ KEY_H, BOTH, XKB_KEY_Cyrillic_er, FINISH));
+
+ xkb_keymap_unref(keymap);
+ keymap = test_compile_rules_with_flags(
+ ctx, "evdev", "", "us,il,ru", "",
+ "grp:alt_shift_toggle_bidir,grp:menu_toggle",
+ XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT | XKB_KEYMAP_CLAMP_OUT_OF_RANGE_LAYOUT);
+ /* Cannot mix out-of-range layout flags */
+ assert(!keymap);
+
keymap = test_compile_rules(ctx, "evdev", "", "us,il,ru", "",
"grp:switch,grp:lswitch,grp:menu_toggle");
assert(keymap);
diff --git a/test/test.h b/test/test.h
index 8a6e04178..55b41edb4 100644
--- a/test/test.h
+++ b/test/test.h
@@ -102,6 +102,12 @@ test_compile_rules(struct xkb_context *context, const char *rules,
const char *model, const char *layout, const char *variant,
const char *options);
+struct xkb_keymap *
+test_compile_rules_with_flags(struct xkb_context *context, const char *rules,
+ const char *model, const char *layout,
+ const char *variant, const char *options,
+ enum xkb_keymap_compile_flags flags);
+
#ifdef _WIN32
#define setenv(varname, value, overwrite) _putenv_s((varname), (value))
diff --git a/tools/messages.c b/tools/messages.c
index 8fafd158d..a21acf0c6 100644
--- a/tools/messages.c
+++ b/tools/messages.c
@@ -77,6 +77,7 @@ static const struct xkb_message_entry xkb_messages[] = {
{XKB_ERROR_INVALID_REAL_MODIFIER, "Invalid real modifier"},
{XKB_WARNING_UNKNOWN_CHAR_ESCAPE_SEQUENCE, "Unknown char escape sequence"},
{XKB_ERROR_INVALID_INCLUDED_FILE, "Invalid included file"},
+ {XKB_ERROR_MULTIPLE_OUT_OF_RANGE_LAYOUT_FLAGS, "Multiple out of range layout flags"},
{XKB_WARNING_MULTIPLE_GROUPS_AT_ONCE, "Multiple groups at once"},
{XKB_WARNING_UNSUPPORTED_SYMBOLS_FIELD, "Unsupported symbols field"},
{XKB_ERROR_INVALID_SYNTAX, "Invalid syntax"},