diff --git a/changes/api/+keymap-compile-options.feature.md b/changes/api/+keymap-compile-options.feature.md new file mode 100644 index 000000000..6b784ab2d --- /dev/null +++ b/changes/api/+keymap-compile-options.feature.md @@ -0,0 +1,9 @@ +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` + +This interface allows to configure keymap options that cannot be passed as flags. diff --git a/changes/api/+out-of-range-layout-config.feature.md b/changes/api/+out-of-range-layout-config.feature.md new file mode 100644 index 000000000..1053b1493 --- /dev/null +++ b/changes/api/+out-of-range-layout-config.feature.md @@ -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. diff --git a/include/xkbcommon/xkbcommon.h b/include/xkbcommon/xkbcommon.h index 219d197ba..16b342f80 100644 --- a/include/xkbcommon/xkbcommon.h +++ b/include/xkbcommon/xkbcommon.h @@ -82,6 +82,7 @@ #define _XKBCOMMON_H_ #include +#include #include #include @@ -878,6 +879,109 @@ enum xkb_keymap_compile_flags { XKB_KEYMAP_COMPILE_NO_FLAGS = 0 }; +/** The possible keymap formats. */ +enum xkb_keymap_format { + /** The current/classic XKB text format, as generated by xkbcomp -xkb. */ + XKB_KEYMAP_FORMAT_TEXT_V1 = 1 +}; + +/** + * @struct xkb_keymap_compile_options + * Opaque keymap compilation options. + * + * TODO: more doc + * + * @since 1.8.0 + */ +struct xkb_keymap_compile_options; + +/** + * Create a keymap options object. + * + * TODO: more doc + * + * @since 1.8.0 + */ +struct xkb_keymap_compile_options* +xkb_keymap_compile_options_new(enum xkb_keymap_format format, + enum xkb_keymap_compile_flags flags); + +/** + * Destroy a keymap compilation options object. + * + * TODO: more doc + * + * @since 1.8.0 + */ +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. * @@ -886,7 +990,7 @@ enum xkb_keymap_compile_flags { * * @param context The context in which to create the keymap. * @param names The RMLVO names to use. See xkb_rule_names. - * @param flags Optional flags for the keymap, or 0. + * @param flags Optional flags for the keymap, or ::XKB_KEYMAP_COMPILE_NO_FLAGS. * * @returns A keymap compiled according to the RMLVO names, or NULL if * the compilation failed. @@ -899,11 +1003,28 @@ xkb_keymap_new_from_names(struct xkb_context *context, const struct xkb_rule_names *names, enum xkb_keymap_compile_flags flags); -/** The possible keymap formats. */ -enum xkb_keymap_format { - /** The current/classic XKB text format, as generated by xkbcomp -xkb. */ - XKB_KEYMAP_FORMAT_TEXT_V1 = 1 -}; +/** + * Create a keymap from RMLVO names. + * + * The primary keymap entry point: creates a new XKB keymap from a set of + * RMLVO (Rules + Model + Layouts + Variants + Options) names. + * + * @param context The context in which to create the keymap. + * @param names The RMLVO names to use. See xkb_rule_names. + * @param options The eymap compilation options. + * + * @returns A keymap compiled according to the RMLVO names, or NULL if + * the compilation failed. + * + * @sa xkb_rule_names, xkb_keymap_new_from_names() + * @memberof xkb_keymap + * + * @since 1.8.0 + */ +struct xkb_keymap * +xkb_keymap_new_from_names2(struct xkb_context *context, + const struct xkb_rule_names *names, + const struct xkb_keymap_compile_options *options); /** * Create a keymap from a keymap file. @@ -911,7 +1032,7 @@ enum xkb_keymap_format { * @param context The context in which to create the keymap. * @param file The keymap file to compile. * @param format The text format of the keymap file to compile. - * @param flags Optional flags for the keymap, or 0. + * @param flags Optional flags for the keymap, or ::XKB_KEYMAP_COMPILE_NO_FLAGS. * * @returns A keymap compiled from the given XKB keymap file, or NULL if * the compilation failed. @@ -928,6 +1049,29 @@ xkb_keymap_new_from_file(struct xkb_context *context, FILE *file, enum xkb_keymap_format format, enum xkb_keymap_compile_flags flags); +/** + * Create a keymap from a keymap file. + * + * @param context The context in which to create the keymap. + * @param file The keymap file to compile. + * @param options The Keymap compilation options. + * + * @returns A keymap compiled from the given XKB keymap file, or NULL if + * the compilation failed. + * + * The file must contain a complete keymap. For example, in the + * XKB_KEYMAP_FORMAT_TEXT_V1 format, this means the file must contain one + * top level '%xkb_keymap' section, which in turn contains other required + * sections. + * + * @since 1.8.0 + * + * @memberof xkb_keymap, xkb_keymap_new_from_file2() + */ +struct xkb_keymap * +xkb_keymap_new_from_file2(struct xkb_context *context, FILE *file, + const struct xkb_keymap_compile_options *options); + /** * Create a keymap from a keymap string. * @@ -957,6 +1101,18 @@ xkb_keymap_new_from_buffer(struct xkb_context *context, const char *buffer, size_t length, enum xkb_keymap_format format, enum xkb_keymap_compile_flags flags); +/** + * Create a keymap from a memory buffer. + * + * @see xkb_keymap_new_from_buffer() + * @memberof xkb_keymap + * @since 1.8.0 + */ +struct xkb_keymap * +xkb_keymap_new_from_buffer2(struct xkb_context *context, + const char *buffer, size_t length, + const struct xkb_keymap_compile_options *options); + /** * Take a new reference on a keymap. * diff --git a/src/keymap-priv.c b/src/keymap-priv.c index 8fdaf5b6f..6d86c2ae9 100644 --- a/src/keymap-priv.c +++ b/src/keymap-priv.c @@ -54,10 +54,36 @@ update_builtin_keymap_fields(struct xkb_keymap *keymap) struct xkb_keymap * xkb_keymap_new(struct xkb_context *ctx, - enum xkb_keymap_format format, - enum xkb_keymap_compile_flags flags) + 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) @@ -66,8 +92,11 @@ xkb_keymap_new(struct xkb_context *ctx, keymap->refcnt = 1; keymap->ctx = xkb_context_ref(ctx); - keymap->format = format; - keymap->flags = flags; + 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); diff --git a/src/keymap.c b/src/keymap.c index fd76a11c7..e640ebe04 100644 --- a/src/keymap.c +++ b/src/keymap.c @@ -55,6 +55,41 @@ #include "keymap.h" #include "text.h" +XKB_EXPORT struct xkb_keymap_compile_options* +xkb_keymap_compile_options_new(enum xkb_keymap_format format, + enum xkb_keymap_compile_flags flags) +{ + struct xkb_keymap_compile_options *options; + + options = calloc(1, sizeof(*options)); + if (!options) + return NULL; + + options->format = format; + options->flags = flags; + + return options; +} + +XKB_EXPORT void +xkb_keymap_compile_options_free(struct xkb_keymap_compile_options *options) +{ + if (options) + free(options); +} + +XKB_EXPORT 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) { @@ -116,30 +151,36 @@ get_keymap_format_ops(enum xkb_keymap_format format) return keymap_format_ops[(int) format]; } +static const struct xkb_keymap_compile_options default_keymap_compile_options = + keymap_compile_options_new(XKB_KEYMAP_FORMAT_TEXT_V1, + XKB_KEYMAP_COMPILE_NO_FLAGS); + XKB_EXPORT struct xkb_keymap * -xkb_keymap_new_from_names(struct xkb_context *ctx, - const struct xkb_rule_names *rmlvo_in, - enum xkb_keymap_compile_flags flags) +xkb_keymap_new_from_names2(struct xkb_context *ctx, + const struct xkb_rule_names *rmlvo_in, + const struct xkb_keymap_compile_options *options) { struct xkb_keymap *keymap; struct xkb_rule_names rmlvo; - const enum xkb_keymap_format format = XKB_KEYMAP_FORMAT_TEXT_V1; const struct xkb_keymap_format_ops *ops; - ops = get_keymap_format_ops(format); + if (options == NULL) + options = &default_keymap_compile_options; + + ops = get_keymap_format_ops(options->format); if (!ops || !ops->keymap_new_from_names) { log_err_func(ctx, XKB_LOG_MESSAGE_NO_ID, - "unsupported keymap format: %d\n", format); + "unsupported keymap format: %d\n", options->format); return NULL; } - if (flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) { + if (options->flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) { log_err_func(ctx, XKB_LOG_MESSAGE_NO_ID, - "unrecognized flags: %#x\n", flags); + "unrecognized flags: %#x\n", options->flags); return NULL; } - keymap = xkb_keymap_new(ctx, format, flags); + keymap = xkb_keymap_new(ctx, options); if (!keymap) return NULL; @@ -157,35 +198,48 @@ xkb_keymap_new_from_names(struct xkb_context *ctx, return keymap; } +XKB_EXPORT struct xkb_keymap * +xkb_keymap_new_from_names(struct xkb_context *ctx, + const struct xkb_rule_names *rmlvo_in, + enum xkb_keymap_compile_flags flags) +{ + struct xkb_keymap_compile_options options = + keymap_compile_options_new(XKB_KEYMAP_FORMAT_TEXT_V1, flags); + return xkb_keymap_new_from_names2(ctx, rmlvo_in, &options); +} + XKB_EXPORT struct xkb_keymap * xkb_keymap_new_from_string(struct xkb_context *ctx, const char *string, enum xkb_keymap_format format, enum xkb_keymap_compile_flags flags) { - return xkb_keymap_new_from_buffer(ctx, string, strlen(string), - format, flags); + struct xkb_keymap_compile_options options = + keymap_compile_options_new(format, flags); + return xkb_keymap_new_from_buffer2(ctx, string, strlen(string), &options); } XKB_EXPORT struct xkb_keymap * -xkb_keymap_new_from_buffer(struct xkb_context *ctx, - const char *buffer, size_t length, - enum xkb_keymap_format format, - enum xkb_keymap_compile_flags flags) +xkb_keymap_new_from_buffer2(struct xkb_context *ctx, + const char *buffer, size_t length, + const struct xkb_keymap_compile_options *options) { struct xkb_keymap *keymap; const struct xkb_keymap_format_ops *ops; - ops = get_keymap_format_ops(format); + if (options == NULL) + options = &default_keymap_compile_options; + + ops = get_keymap_format_ops(options->format); if (!ops || !ops->keymap_new_from_string) { log_err_func(ctx, XKB_LOG_MESSAGE_NO_ID, - "unsupported keymap format: %d\n", format); + "unsupported keymap format: %d\n", options->format); return NULL; } - if (flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) { + if (options->flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) { log_err_func(ctx, XKB_LOG_MESSAGE_NO_ID, - "unrecognized flags: %#x\n", flags); + "unrecognized flags: %#x\n", options->flags); return NULL; } @@ -195,7 +249,7 @@ xkb_keymap_new_from_buffer(struct xkb_context *ctx, return NULL; } - keymap = xkb_keymap_new(ctx, format, flags); + keymap = xkb_keymap_new(ctx, options); if (!keymap) return NULL; @@ -212,24 +266,36 @@ xkb_keymap_new_from_buffer(struct xkb_context *ctx, } XKB_EXPORT struct xkb_keymap * -xkb_keymap_new_from_file(struct xkb_context *ctx, - FILE *file, - enum xkb_keymap_format format, - enum xkb_keymap_compile_flags flags) +xkb_keymap_new_from_buffer(struct xkb_context *ctx, + const char *buffer, size_t length, + enum xkb_keymap_format format, + enum xkb_keymap_compile_flags flags) +{ + struct xkb_keymap_compile_options options = + keymap_compile_options_new(format, flags); + return xkb_keymap_new_from_buffer2(ctx, buffer, length, &options); +} + +XKB_EXPORT struct xkb_keymap * +xkb_keymap_new_from_file2(struct xkb_context *ctx, FILE *file, + const struct xkb_keymap_compile_options *options) { struct xkb_keymap *keymap; const struct xkb_keymap_format_ops *ops; - ops = get_keymap_format_ops(format); + if (options == NULL) + options = &default_keymap_compile_options; + + ops = get_keymap_format_ops(options->format); if (!ops || !ops->keymap_new_from_file) { log_err_func(ctx, XKB_LOG_MESSAGE_NO_ID, - "unsupported keymap format: %d\n", format); + "unsupported keymap format: %d\n", options->format); return NULL; } - if (flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) { + if (options->flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) { log_err_func(ctx, XKB_LOG_MESSAGE_NO_ID, - "unrecognized flags: %#x\n", flags); + "unrecognized flags: %#x\n", options->flags); return NULL; } @@ -239,7 +305,7 @@ xkb_keymap_new_from_file(struct xkb_context *ctx, return NULL; } - keymap = xkb_keymap_new(ctx, format, flags); + keymap = xkb_keymap_new(ctx, options); if (!keymap) return NULL; @@ -251,6 +317,17 @@ xkb_keymap_new_from_file(struct xkb_context *ctx, return keymap; } +XKB_EXPORT struct xkb_keymap * +xkb_keymap_new_from_file(struct xkb_context *ctx, + FILE *file, + enum xkb_keymap_format format, + enum xkb_keymap_compile_flags flags) +{ + struct xkb_keymap_compile_options options = + keymap_compile_options_new(format, flags); + return xkb_keymap_new_from_file2(ctx, file, &options); +} + XKB_EXPORT char * xkb_keymap_get_as_string(struct xkb_keymap *keymap, enum xkb_keymap_format format) diff --git a/src/keymap.h b/src/keymap.h index f7ea5bdf1..9b00571f0 100644 --- a/src/keymap.h +++ b/src/keymap.h @@ -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 { @@ -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; @@ -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; @@ -361,6 +370,13 @@ struct xkb_mod_set { unsigned int num_mods; }; +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 */ struct xkb_keymap { struct xkb_context *ctx; @@ -392,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; @@ -452,10 +471,14 @@ entry_is_active(const struct xkb_key_type_entry *entry) return entry->mods.mods == 0 || entry->mods.mask != 0; } +#define keymap_compile_options_new(_format, _flags) { \ + .flags = _flags, \ + .format = _format \ + } + struct xkb_keymap * xkb_keymap_new(struct xkb_context *ctx, - enum xkb_keymap_format format, - enum xkb_keymap_compile_flags flags); + const struct xkb_keymap_compile_options *options); struct xkb_key * XkbKeyByName(struct xkb_keymap *keymap, xkb_atom_t name, bool use_aliases); diff --git a/src/state.c b/src/state.c index 287f0eef8..99bbf2f20 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,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); diff --git a/src/x11/keymap.c b/src/x11/keymap.c index 0c327305c..1a2ba7424 100644 --- a/src/x11/keymap.c +++ b/src/x11/keymap.c @@ -1156,7 +1156,9 @@ xkb_x11_keymap_new_from_device(struct xkb_context *ctx, return NULL; } - keymap = xkb_keymap_new(ctx, format, flags); + struct xkb_keymap_compile_options options = + keymap_compile_options_new(format, flags); + keymap = xkb_keymap_new(ctx, &options); if (!keymap) return NULL; diff --git a/test/common.c b/test/common.c index 21e6e0098..fb5f6bd39 100644 --- a/test/common.c +++ b/test/common.c @@ -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 = { @@ -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, @@ -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); +} diff --git a/test/keyseq.c b/test/keyseq.c index 17d133565..0d72af721 100644 --- a/test/keyseq.c +++ b/test/keyseq.c @@ -667,6 +667,114 @@ main(void) KEY_H, BOTH, XKB_KEY_h, FINISH)); xkb_keymap_unref(keymap); + struct xkb_keymap_compile_options *options = + xkb_keymap_compile_options_new(XKB_KEYMAP_FORMAT_TEXT_V1, + XKB_KEYMAP_COMPILE_NO_FLAGS); + assert(options); + assert(xkb_keymap_compile_options_set_layout_out_of_range_action( + options, XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT, 1 + )); + keymap = test_compile_rules_with_options( + ctx, "evdev", "", "us,il,ru", "", + "grp:alt_shift_toggle_bidir,grp:menu_toggle", + options); + assert(keymap); + + /* Out-of-range group action: redirect to valid group */ + 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); + assert(xkb_keymap_compile_options_set_layout_out_of_range_action( + options, XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT, 4 + )); + keymap = test_compile_rules_with_options( + ctx, "evdev", "", "us,il,ru", "", + "grp:alt_shift_toggle_bidir,grp:menu_toggle", + options); + assert(keymap); + + /* Out-of-range group action: redirect to invalid group */ + 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: invalid redirect, default 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, + /* Greater that last group: invalid redirect, default to first layout */ + KEY_H, BOTH, XKB_KEY_h, FINISH)); + + xkb_keymap_unref(keymap); + assert(xkb_keymap_compile_options_set_layout_out_of_range_action( + options, XKB_KEYMAP_CLAMP_OUT_OF_RANGE_LAYOUT, 0 + )); + keymap = test_compile_rules_with_options( + ctx, "evdev", "", "us,il,ru", "", + "grp:alt_shift_toggle_bidir,grp:menu_toggle", + options); + 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); + assert(xkb_keymap_compile_options_set_layout_out_of_range_action( + options, XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT | + XKB_KEYMAP_CLAMP_OUT_OF_RANGE_LAYOUT, 0 + )); + keymap = test_compile_rules_with_options( + ctx, "evdev", "", "us,il", "", "", + options); + /* Cannot mix out-of-range layout flags */ + assert(!keymap); + /* Cannot use redirect layout index without + * XKB_KEYMAP_REDIRECT_OUT_OF_RANGE_LAYOUT */ + assert(xkb_keymap_compile_options_set_layout_out_of_range_action( + options, XKB_KEYMAP_WRAP_OUT_OF_RANGE_LAYOUT, 1 + )); + keymap = test_compile_rules_with_options( + ctx, "evdev", "", "", "", "", + options); + assert(!keymap); + assert(xkb_keymap_compile_options_set_layout_out_of_range_action( + options, XKB_KEYMAP_CLAMP_OUT_OF_RANGE_LAYOUT, 1 + )); + keymap = test_compile_rules_with_options( + ctx, "evdev", "", "", "", "", + options); + assert(!keymap); + xkb_keymap_compile_options_free(options); + 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..16c4ae064 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_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); + #ifdef _WIN32 #define setenv(varname, value, overwrite) _putenv_s((varname), (value)) diff --git a/xkbcommon.map b/xkbcommon.map index b2507272e..323852141 100644 --- a/xkbcommon.map +++ b/xkbcommon.map @@ -119,3 +119,13 @@ global: xkb_compose_table_iterator_free; xkb_compose_table_iterator_next; } V_1.0.0; + +V_1.8.0 { +global: + 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; +} V_1.6.0;