From 1fc45ee6209f3a815cc3791a19f4d8c1b2880415 Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Thu, 4 Jul 2024 19:06:40 +0200 Subject: [PATCH 1/9] keymap: Add support for querying supported formats Add the following API: - `xkb_keymap_supported_formats` - `xkb_keymap_is_supported_format` --- include/xkbcommon/xkbcommon.h | 36 +++++++++++++++++++++++++++++------ src/keymap.c | 25 ++++++++++++++++++++++++ test/keymap.c | 18 ++++++++++++++++++ xkbcommon.map | 6 ++++++ 4 files changed, 79 insertions(+), 6 deletions(-) diff --git a/include/xkbcommon/xkbcommon.h b/include/xkbcommon/xkbcommon.h index e3631df8e..456abdabb 100644 --- a/include/xkbcommon/xkbcommon.h +++ b/include/xkbcommon/xkbcommon.h @@ -82,6 +82,7 @@ #define _XKBCOMMON_H_ #include +#include #include #include @@ -866,6 +867,35 @@ 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 +}; + +/** + * Provide an array of the supported keymap formats, sorted in ascending order + * (newest last). + * + * @param[out] formats An array of the supported keymap formats. + * + * @returns The size of the array. + * + * @memberof xkb_keymap + */ +size_t +xkb_keymap_supported_formats(const enum xkb_keymap_format **formats); + +/** + * Check if the given keymap format is supported. + * + * @param[in] format A keymap format to test. + * + * @memberof xkb_keymap + */ +bool +xkb_keymap_is_supported_format(enum xkb_keymap_format format); + /** * Create a keymap from RMLVO names. * @@ -887,12 +917,6 @@ 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 a keymap file. * diff --git a/src/keymap.c b/src/keymap.c index 0291aedbb..4d744aa06 100644 --- a/src/keymap.c +++ b/src/keymap.c @@ -116,6 +116,31 @@ get_keymap_format_ops(enum xkb_keymap_format format) return keymap_format_ops[(int) format]; } +/* Warning: must be in ascending order */ +static const enum xkb_keymap_format supported_keymap_formats[] = { + XKB_KEYMAP_FORMAT_TEXT_V1 +}; + +XKB_EXPORT size_t +xkb_keymap_supported_formats(const enum xkb_keymap_format **formats) +{ + *formats = supported_keymap_formats; + return ARRAY_SIZE(supported_keymap_formats); +} + +XKB_EXPORT bool +xkb_keymap_is_supported_format(enum xkb_keymap_format format) +{ + for (size_t k = 0; k < ARRAY_SIZE(supported_keymap_formats); k++) { + if (supported_keymap_formats[k] == format) + return true; + /* Short-circuit because array is sorted */ + if (supported_keymap_formats[k] > format) + return false; + } + return false; +} + XKB_EXPORT struct xkb_keymap * xkb_keymap_new_from_names(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo_in, diff --git a/test/keymap.c b/test/keymap.c index 7b02bd942..2ae42e60d 100644 --- a/test/keymap.c +++ b/test/keymap.c @@ -32,6 +32,23 @@ #include "test.h" #include "keymap.h" +static void +test_supported_formats(void) +{ + const enum xkb_keymap_format *formats; + assert(!xkb_keymap_is_supported_format(-1)); + assert(!xkb_keymap_is_supported_format(0)); + assert(!xkb_keymap_is_supported_format(100000000)); + size_t count = xkb_keymap_supported_formats(&formats); + assert(count); + enum xkb_keymap_format previous = 0; + for (size_t k = 0; k < count; k++) { + assert(previous < formats[k]); + assert(xkb_keymap_is_supported_format(formats[k])); + previous = formats[k]; + } +} + static void test_garbage_key(void) { @@ -235,6 +252,7 @@ main(void) { test_init(); + test_supported_formats(); test_garbage_key(); test_keymap(); test_numeric_keysyms(); diff --git a/xkbcommon.map b/xkbcommon.map index b2507272e..7822efd96 100644 --- a/xkbcommon.map +++ b/xkbcommon.map @@ -119,3 +119,9 @@ global: xkb_compose_table_iterator_free; xkb_compose_table_iterator_next; } V_1.0.0; + +V_1.8.0 { +global: + xkb_keymap_supported_formats; + xkb_keymap_is_supported_format; +} V_1.6.0; From 1ac679298513084d1aae0f56084908a347c4175e Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Thu, 4 Jul 2024 19:08:43 +0200 Subject: [PATCH 2/9] keymap: Add xkb_keymap_new_from_names2 Currently `xkb_keymap_new_from_names` does not allow to specify the keymap format, so provide a new function `xkb_keymap_new_from_names2`. --- include/xkbcommon/xkbcommon.h | 25 ++++++++++++++++++++++++- src/keymap.c | 17 +++++++++++++---- xkbcommon.map | 1 + 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/include/xkbcommon/xkbcommon.h b/include/xkbcommon/xkbcommon.h index 456abdabb..54c208dcb 100644 --- a/include/xkbcommon/xkbcommon.h +++ b/include/xkbcommon/xkbcommon.h @@ -909,7 +909,7 @@ xkb_keymap_is_supported_format(enum xkb_keymap_format format); * @returns A keymap compiled according to the RMLVO names, or NULL if * the compilation failed. * - * @sa xkb_rule_names + * @sa xkb_rule_names xkb_keymap_new_from_names2 * @memberof xkb_keymap */ struct xkb_keymap * @@ -917,6 +917,29 @@ xkb_keymap_new_from_names(struct xkb_context *context, const struct xkb_rule_names *names, enum xkb_keymap_compile_flags flags); +/** + * Create a keymap from RMLVO names using a specific keymap format. + * + * 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 format The text format of the keymap file to compile. + * @param flags Optional flags for the keymap, or 0. + * + * @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 + */ +struct xkb_keymap * +xkb_keymap_new_from_names2(struct xkb_context *context, + const struct xkb_rule_names *names, + enum xkb_keymap_format format, + enum xkb_keymap_compile_flags flags); + /** * Create a keymap from a keymap file. * diff --git a/src/keymap.c b/src/keymap.c index 4d744aa06..21ae89b26 100644 --- a/src/keymap.c +++ b/src/keymap.c @@ -142,13 +142,13 @@ xkb_keymap_is_supported_format(enum xkb_keymap_format format) } 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, + enum xkb_keymap_format format, + enum xkb_keymap_compile_flags flags) { 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); @@ -180,6 +180,15 @@ 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) +{ + return xkb_keymap_new_from_names2(ctx, rmlvo_in, + XKB_KEYMAP_FORMAT_TEXT_V1, flags); +} + XKB_EXPORT struct xkb_keymap * xkb_keymap_new_from_string(struct xkb_context *ctx, const char *string, diff --git a/xkbcommon.map b/xkbcommon.map index 7822efd96..e0d2528be 100644 --- a/xkbcommon.map +++ b/xkbcommon.map @@ -124,4 +124,5 @@ V_1.8.0 { global: xkb_keymap_supported_formats; xkb_keymap_is_supported_format; + xkb_keymap_new_from_names2; } V_1.6.0; From 030210aa33b4a09b0ab45adc612e79824d585506 Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Fri, 5 Jul 2024 06:58:03 +0200 Subject: [PATCH 3/9] Introduce XKB_KEYMAP_FORMAT_TEXT_V1_1 --- include/xkbcommon/xkbcommon.h | 5 ++++- src/keymap.c | 6 ++++-- src/keymap.h | 3 ++- src/xkbcomp/keymap-dump.c | 8 +++++--- src/xkbcomp/xkbcomp-priv.h | 3 ++- test/stringcomp.c | 2 +- 6 files changed, 18 insertions(+), 9 deletions(-) diff --git a/include/xkbcommon/xkbcommon.h b/include/xkbcommon/xkbcommon.h index 54c208dcb..886a1a682 100644 --- a/include/xkbcommon/xkbcommon.h +++ b/include/xkbcommon/xkbcommon.h @@ -870,7 +870,10 @@ enum xkb_keymap_compile_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 + XKB_KEYMAP_FORMAT_TEXT_V1 = 1, + /** Extension that introduces the options `lockOnRelease` and + * `unlockOnPress`. */ + XKB_KEYMAP_FORMAT_TEXT_V1_1 = 2 }; /** diff --git a/src/keymap.c b/src/keymap.c index 21ae89b26..d3d84e4fc 100644 --- a/src/keymap.c +++ b/src/keymap.c @@ -108,6 +108,7 @@ get_keymap_format_ops(enum xkb_keymap_format format) { static const struct xkb_keymap_format_ops *keymap_format_ops[] = { [XKB_KEYMAP_FORMAT_TEXT_V1] = &text_v1_keymap_format_ops, + [XKB_KEYMAP_FORMAT_TEXT_V1_1] = &text_v1_keymap_format_ops, }; if ((int) format < 0 || (int) format >= (int) ARRAY_SIZE(keymap_format_ops)) @@ -118,7 +119,8 @@ get_keymap_format_ops(enum xkb_keymap_format format) /* Warning: must be in ascending order */ static const enum xkb_keymap_format supported_keymap_formats[] = { - XKB_KEYMAP_FORMAT_TEXT_V1 + XKB_KEYMAP_FORMAT_TEXT_V1, + XKB_KEYMAP_FORMAT_TEXT_V1_1 }; XKB_EXPORT size_t @@ -292,7 +294,7 @@ xkb_keymap_get_as_string(struct xkb_keymap *keymap, return NULL; } - return ops->keymap_get_as_string(keymap); + return ops->keymap_get_as_string(keymap, format); } /** diff --git a/src/keymap.h b/src/keymap.h index f7ea5bdf1..15f9edd6e 100644 --- a/src/keymap.h +++ b/src/keymap.h @@ -488,7 +488,8 @@ struct xkb_keymap_format_ops { bool (*keymap_new_from_string)(struct xkb_keymap *keymap, const char *string, size_t length); bool (*keymap_new_from_file)(struct xkb_keymap *keymap, FILE *file); - char *(*keymap_get_as_string)(struct xkb_keymap *keymap); + char *(*keymap_get_as_string)(struct xkb_keymap *keymap, + enum xkb_keymap_format format); }; extern const struct xkb_keymap_format_ops text_v1_keymap_format_ops; diff --git a/src/xkbcomp/keymap-dump.c b/src/xkbcomp/keymap-dump.c index b7828bff6..a73bb18ae 100644 --- a/src/xkbcomp/keymap-dump.c +++ b/src/xkbcomp/keymap-dump.c @@ -644,7 +644,8 @@ write_symbols(struct xkb_keymap *keymap, struct buf *buf) } static bool -write_keymap(struct xkb_keymap *keymap, struct buf *buf) +write_keymap(struct xkb_keymap *keymap, enum xkb_keymap_format format, + struct buf *buf) { return (check_write_buf(buf, "xkb_keymap {\n") && write_keycodes(keymap, buf) && @@ -655,11 +656,12 @@ write_keymap(struct xkb_keymap *keymap, struct buf *buf) } char * -text_v1_keymap_get_as_string(struct xkb_keymap *keymap) +text_v1_keymap_get_as_string(struct xkb_keymap *keymap, + enum xkb_keymap_format format) { struct buf buf = { NULL, 0, 0 }; - if (!write_keymap(keymap, &buf)) { + if (!write_keymap(keymap, format, &buf)) { free(buf.buf); return NULL; } diff --git a/src/xkbcomp/xkbcomp-priv.h b/src/xkbcomp/xkbcomp-priv.h index 3de8d8ddd..5fb4e18f3 100644 --- a/src/xkbcomp/xkbcomp-priv.h +++ b/src/xkbcomp/xkbcomp-priv.h @@ -38,7 +38,8 @@ struct xkb_component_names { }; char * -text_v1_keymap_get_as_string(struct xkb_keymap *keymap); +text_v1_keymap_get_as_string(struct xkb_keymap *keymap, + enum xkb_keymap_format format); XkbFile * XkbParseFile(struct xkb_context *ctx, FILE *file, diff --git a/test/stringcomp.c b/test/stringcomp.c index 470a56cc6..8ac740da1 100644 --- a/test/stringcomp.c +++ b/test/stringcomp.c @@ -93,7 +93,7 @@ main(int argc, char *argv[]) /* Test response to invalid formats and flags. */ assert(!xkb_keymap_new_from_string(ctx, dump, 0, 0)); assert(!xkb_keymap_new_from_string(ctx, dump, -1, 0)); - assert(!xkb_keymap_new_from_string(ctx, dump, XKB_KEYMAP_FORMAT_TEXT_V1+1, 0)); + assert(!xkb_keymap_new_from_string(ctx, dump, XKB_KEYMAP_FORMAT_TEXT_V1+1000000, 0)); assert(!xkb_keymap_new_from_string(ctx, dump, XKB_KEYMAP_FORMAT_TEXT_V1, -1)); assert(!xkb_keymap_new_from_string(ctx, dump, XKB_KEYMAP_FORMAT_TEXT_V1, 1414)); assert(!xkb_keymap_get_as_string(keymap, 0)); From 112e6552a101bbb19be65eaeaa093893c6d89001 Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Fri, 5 Jul 2024 11:02:12 +0200 Subject: [PATCH 4/9] messages: Add new error "incompatible-keymap-export-format" --- doc/message-registry.md | 12 +++++++++++- doc/message-registry.yaml | 5 +++++ src/messages-codes.h | 2 ++ tools/messages.c | 1 + 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/doc/message-registry.md b/doc/message-registry.md index 0a5189326..41fa5c61b 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-689] | `incompatible-keymap-export-format` | The keymap has features that cannot be exported in the target format | 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:
Summary
The target file of an include statement could not be processed
+### XKB-689 – Incompatible keymap export format {#XKB-689} + +
+
Since
1.8.0
+
Type
Error
+
Summary
The keymap has features that cannot be exported in the target format
+
+ ### 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-689]: @ref XKB-689 [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..afe0c0666 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: "incompatible-keymap-export-format" + code: 689 + added: 1.8.0 + type: error + description: "The keymap has features that cannot be exported in the target format" - id: "multiple-groups-at-once" code: 700 added: ALWAYS diff --git a/src/messages-codes.h b/src/messages-codes.h index 3be18db02..205ed367c 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, + /** The keymap has features that cannot be exported in the target format */ + XKB_ERROR_INCOMPATIBLE_KEYMAP_EXPORT_FORMAT = 689, /** 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/tools/messages.c b/tools/messages.c index 8fafd158d..d73995cb6 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_INCOMPATIBLE_KEYMAP_EXPORT_FORMAT, "Incompatible keymap export format"}, {XKB_WARNING_MULTIPLE_GROUPS_AT_ONCE, "Multiple groups at once"}, {XKB_WARNING_UNSUPPORTED_SYMBOLS_FIELD, "Unsupported symbols field"}, {XKB_ERROR_INVALID_SYNTAX, "Invalid syntax"}, From 0d0a33038f836ba1c652d0147e70e7e42ed05ae2 Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Fri, 5 Jul 2024 07:23:37 +0200 Subject: [PATCH 5/9] actions: Add format for parsing actions --- src/xkbcomp/action.c | 39 +++++++++++++++++++++++++-------------- src/xkbcomp/action.h | 7 ++++--- src/xkbcomp/compat.c | 16 ++++++++++------ src/xkbcomp/symbols.c | 7 ++++--- 4 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/xkbcomp/action.c b/src/xkbcomp/action.c index e210a0708..f55b7dc03 100644 --- a/src/xkbcomp/action.c +++ b/src/xkbcomp/action.c @@ -224,7 +224,8 @@ ReportActionNotArray(struct xkb_context *ctx, enum xkb_action_type action, } static bool -HandleNoAction(struct xkb_context *ctx, const struct xkb_mod_set *mods, +HandleNoAction(struct xkb_context *ctx, enum xkb_keymap_format format, + const struct xkb_mod_set *mods, union xkb_action *action, enum action_field field, const ExprDef *array_ndx, const ExprDef *value) @@ -312,7 +313,8 @@ CheckAffectField(struct xkb_context *ctx, enum xkb_action_type action, } static bool -HandleSetLatchLockMods(struct xkb_context *ctx, const struct xkb_mod_set *mods, +HandleSetLatchLockMods(struct xkb_context *ctx, enum xkb_keymap_format format, + const struct xkb_mod_set *mods, union xkb_action *action, enum action_field field, const ExprDef *array_ndx, const ExprDef *value) { @@ -379,7 +381,8 @@ CheckGroupField(struct xkb_context *ctx, enum xkb_action_type action, } static bool -HandleSetLatchLockGroup(struct xkb_context *ctx, const struct xkb_mod_set *mods, +HandleSetLatchLockGroup(struct xkb_context *ctx, enum xkb_keymap_format format, + const struct xkb_mod_set *mods, union xkb_action *action, enum action_field field, const ExprDef *array_ndx, const ExprDef *value) { @@ -404,7 +407,8 @@ HandleSetLatchLockGroup(struct xkb_context *ctx, const struct xkb_mod_set *mods, } static bool -HandleMovePtr(struct xkb_context *ctx, const struct xkb_mod_set *mods, +HandleMovePtr(struct xkb_context *ctx, enum xkb_keymap_format format, + const struct xkb_mod_set *mods, union xkb_action *action, enum action_field field, const ExprDef *array_ndx, const ExprDef *value) { @@ -453,7 +457,8 @@ HandleMovePtr(struct xkb_context *ctx, const struct xkb_mod_set *mods, } static bool -HandlePtrBtn(struct xkb_context *ctx, const struct xkb_mod_set *mods, +HandlePtrBtn(struct xkb_context *ctx, enum xkb_keymap_format format, + const struct xkb_mod_set *mods, union xkb_action *action, enum action_field field, const ExprDef *array_ndx, const ExprDef *value) { @@ -516,7 +521,8 @@ static const LookupEntry ptrDflts[] = { }; static bool -HandleSetPtrDflt(struct xkb_context *ctx, const struct xkb_mod_set *mods, +HandleSetPtrDflt(struct xkb_context *ctx, enum xkb_keymap_format format, + const struct xkb_mod_set *mods, union xkb_action *action, enum action_field field, const ExprDef *array_ndx, const ExprDef *value) { @@ -575,7 +581,8 @@ HandleSetPtrDflt(struct xkb_context *ctx, const struct xkb_mod_set *mods, } static bool -HandleSwitchScreen(struct xkb_context *ctx, const struct xkb_mod_set *mods, +HandleSwitchScreen(struct xkb_context *ctx, enum xkb_keymap_format format, + const struct xkb_mod_set *mods, union xkb_action *action, enum action_field field, const ExprDef *array_ndx, const ExprDef *value) { @@ -622,7 +629,8 @@ HandleSwitchScreen(struct xkb_context *ctx, const struct xkb_mod_set *mods, } static bool -HandleSetLockControls(struct xkb_context *ctx, const struct xkb_mod_set *mods, +HandleSetLockControls(struct xkb_context *ctx, enum xkb_keymap_format format, + const struct xkb_mod_set *mods, union xkb_action *action, enum action_field field, const ExprDef *array_ndx, const ExprDef *value) { @@ -650,7 +658,8 @@ HandleSetLockControls(struct xkb_context *ctx, const struct xkb_mod_set *mods, } static bool -HandlePrivate(struct xkb_context *ctx, const struct xkb_mod_set *mods, +HandlePrivate(struct xkb_context *ctx, enum xkb_keymap_format format, + const struct xkb_mod_set *mods, union xkb_action *action, enum action_field field, const ExprDef *array_ndx, const ExprDef *value) { @@ -757,6 +766,7 @@ HandlePrivate(struct xkb_context *ctx, const struct xkb_mod_set *mods, } typedef bool (*actionHandler)(struct xkb_context *ctx, + enum xkb_keymap_format format, const struct xkb_mod_set *mods, union xkb_action *action, enum action_field field, @@ -785,7 +795,8 @@ static const actionHandler handleAction[_ACTION_TYPE_NUM_ENTRIES] = { /***====================================================================***/ bool -HandleActionDef(struct xkb_context *ctx, ActionsInfo *info, +HandleActionDef(struct xkb_context *ctx, enum xkb_keymap_format format, + ActionsInfo *info, const struct xkb_mod_set *mods, ExprDef *def, union xkb_action *action) { @@ -855,7 +866,7 @@ HandleActionDef(struct xkb_context *ctx, ActionsInfo *info, return false; } - if (!handleAction[handler_type](ctx, mods, action, fieldNdx, + if (!handleAction[handler_type](ctx, format, mods, action, fieldNdx, arrayRtrn, value)) return false; } @@ -864,8 +875,8 @@ HandleActionDef(struct xkb_context *ctx, ActionsInfo *info, } bool -SetActionField(struct xkb_context *ctx, ActionsInfo *info, - struct xkb_mod_set *mods, const char *elem, +SetActionField(struct xkb_context *ctx, enum xkb_keymap_format format, + ActionsInfo *info, struct xkb_mod_set *mods, const char *elem, const char *field, ExprDef *array_ndx, ExprDef *value) { enum xkb_action_type action; @@ -880,6 +891,6 @@ SetActionField(struct xkb_context *ctx, ActionsInfo *info, return false; } - return handleAction[action](ctx, mods, &info->actions[action], + return handleAction[action](ctx, format, mods, &info->actions[action], action_field, array_ndx, value); } diff --git a/src/xkbcomp/action.h b/src/xkbcomp/action.h index 1f92e7b38..2ffce4f3b 100644 --- a/src/xkbcomp/action.h +++ b/src/xkbcomp/action.h @@ -43,13 +43,14 @@ void FreeActionsInfo(ActionsInfo *info); bool -HandleActionDef(struct xkb_context *ctx, ActionsInfo *info, +HandleActionDef(struct xkb_context *ctx, enum xkb_keymap_format format, + ActionsInfo *info, const struct xkb_mod_set *mods, ExprDef *def, union xkb_action *action); bool -SetActionField(struct xkb_context *ctx, ActionsInfo *info, - struct xkb_mod_set *mods, const char *elem, +SetActionField(struct xkb_context *ctx, enum xkb_keymap_format format, + ActionsInfo *info, struct xkb_mod_set *mods, const char *elem, const char *field, ExprDef *array_ndx, ExprDef *value); diff --git a/src/xkbcomp/compat.c b/src/xkbcomp/compat.c index bf139aa20..1fc9bd64e 100644 --- a/src/xkbcomp/compat.c +++ b/src/xkbcomp/compat.c @@ -97,6 +97,7 @@ typedef struct { struct xkb_mod_set mods; struct xkb_context *ctx; + enum xkb_keymap_format format; } CompatInfo; static const char * @@ -150,11 +151,13 @@ ReportLedNotArray(CompatInfo *info, LedInfo *ledi, const char *field) static void InitCompatInfo(CompatInfo *info, struct xkb_context *ctx, + enum xkb_keymap_format format, unsigned int include_depth, ActionsInfo *actions, const struct xkb_mod_set *mods) { memset(info, 0, sizeof(*info)); info->ctx = ctx; + info->format = format; info->include_depth = include_depth; info->actions = actions; info->mods = *mods; @@ -438,7 +441,7 @@ HandleIncludeCompatMap(CompatInfo *info, IncludeStmt *include) return false; } - InitCompatInfo(&included, info->ctx, 0 /* unused */, + InitCompatInfo(&included, info->ctx, info->format, 0 /* unused */, info->actions, &info->mods); included.name = steal(&include->stmt); @@ -453,7 +456,8 @@ HandleIncludeCompatMap(CompatInfo *info, IncludeStmt *include) return false; } - InitCompatInfo(&next_incl, info->ctx, info->include_depth + 1, + InitCompatInfo(&next_incl, info->ctx, info->format, + info->include_depth + 1, info->actions, &included.mods); next_incl.default_interp = info->default_interp; next_incl.default_interp.merge = stmt->merge; @@ -484,7 +488,7 @@ SetInterpField(CompatInfo *info, SymInterpInfo *si, const char *field, if (arrayNdx) return ReportSINotArray(info, si, field); - if (!HandleActionDef(info->ctx, info->actions, &info->mods, + if (!HandleActionDef(info->ctx, info->format, info->actions, &info->mods, value, &si->interp.action)) return false; @@ -655,8 +659,8 @@ HandleGlobalVar(CompatInfo *info, VarDef *stmt) ret = SetLedMapField(info, &info->default_led, field, ndx, stmt->value); else - ret = SetActionField(info->ctx, info->actions, &info->mods, - elem, field, ndx, stmt->value); + ret = SetActionField(info->ctx, info->format, info->actions, + &info->mods, elem, field, ndx, stmt->value); return ret; } @@ -923,7 +927,7 @@ CompileCompatMap(XkbFile *file, struct xkb_keymap *keymap, if (!actions) return false; - InitCompatInfo(&info, keymap->ctx, 0, actions, &keymap->mods); + InitCompatInfo(&info, keymap->ctx, keymap->format, 0, actions, &keymap->mods); info.default_interp.merge = merge; info.default_led.merge = merge; diff --git a/src/xkbcomp/symbols.c b/src/xkbcomp/symbols.c index 659e66019..bbf45e4e3 100644 --- a/src/xkbcomp/symbols.c +++ b/src/xkbcomp/symbols.c @@ -792,7 +792,8 @@ AddActionsToKey(SymbolsInfo *info, KeyInfo *keyi, ExprDef *arrayNdx, for (unsigned i = 0; i < nActs; i++) { union xkb_action *toAct = &darray_item(groupi->levels, i).action; - if (!HandleActionDef(info->ctx, info->actions, &info->mods, act, toAct)) + if (!HandleActionDef(info->ctx, info->keymap->format, info->actions, + &info->mods, act, toAct)) log_err(info->ctx, XKB_ERROR_INVALID_VALUE, "Illegal action definition for %s; " @@ -1079,8 +1080,8 @@ HandleGlobalVar(SymbolsInfo *info, VarDef *stmt) ret = true; } else { - ret = SetActionField(info->ctx, info->actions, &info->mods, - elem, field, arrayNdx, stmt->value); + ret = SetActionField(info->ctx, info->keymap->format, info->actions, + &info->mods, elem, field, arrayNdx, stmt->value); } return ret; From 2004b8459c283c4158fcc2141243feb518a64f2e Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Fri, 5 Jul 2024 10:39:50 +0200 Subject: [PATCH 6/9] tools: Add --format* options Allow to choose the exact formats for parsing and serializing. --- tools/compile-keymap.c | 76 ++++++++++++++++++++++++++------ tools/interactive-evdev.c | 19 ++++++-- tools/xkbcli-compile-keymap.1 | 9 ++++ tools/xkbcli-interactive-evdev.1 | 3 ++ 4 files changed, 90 insertions(+), 17 deletions(-) diff --git a/tools/compile-keymap.c b/tools/compile-keymap.c index 034f3899c..2feeada3a 100644 --- a/tools/compile-keymap.c +++ b/tools/compile-keymap.c @@ -39,6 +39,7 @@ #include "tools-common.h" #define DEFAULT_INCLUDE_PATH_PLACEHOLDER "__defaults__" +#define DEFAULT_KEYMAP_FORMAT XKB_KEYMAP_FORMAT_TEXT_V1 static bool verbose = false; static enum output_format { @@ -95,15 +96,26 @@ usage(char **argv) " The XKB layout variant (default: '%s')\n" " --options \n" " The XKB options (default: '%s')\n" + " --input-format \n" + " The keymap format to use for parsing (default: '%d')\n" + " --output-format \n" + " The keymap format to use for serializing (default: '%d')\n" + " --format \n" + " The keymap format to use for both parsing and serializing (default: '%d')\n" "\n", argv[0], DEFAULT_XKB_RULES, DEFAULT_XKB_MODEL, DEFAULT_XKB_LAYOUT, DEFAULT_XKB_VARIANT ? DEFAULT_XKB_VARIANT : "", - DEFAULT_XKB_OPTIONS ? DEFAULT_XKB_OPTIONS : ""); + DEFAULT_XKB_OPTIONS ? DEFAULT_XKB_OPTIONS : "", + DEFAULT_KEYMAP_FORMAT, + DEFAULT_KEYMAP_FORMAT, + DEFAULT_KEYMAP_FORMAT); } static bool -parse_options(int argc, char **argv, struct xkb_rule_names *names) +parse_options(int argc, char **argv, struct xkb_rule_names *names, + enum xkb_keymap_format *keymap_input_format, + enum xkb_keymap_format *keymap_output_format) { enum options { OPT_VERBOSE, @@ -117,6 +129,9 @@ parse_options(int argc, char **argv, struct xkb_rule_names *names) OPT_LAYOUT, OPT_VARIANT, OPT_OPTION, + OPT_FORMAT, + OPT_INPUT_FORMAT, + OPT_OUTPUT_FORMAT, }; static struct option opts[] = { {"help", no_argument, 0, 'h'}, @@ -133,6 +148,9 @@ parse_options(int argc, char **argv, struct xkb_rule_names *names) {"layout", required_argument, 0, OPT_LAYOUT}, {"variant", required_argument, 0, OPT_VARIANT}, {"options", required_argument, 0, OPT_OPTION}, + {"format", required_argument, 0, OPT_FORMAT}, + {"input-format", required_argument, 0, OPT_INPUT_FORMAT}, + {"output-format", required_argument, 0, OPT_OUTPUT_FORMAT}, {0, 0, 0, 0}, }; @@ -188,6 +206,31 @@ parse_options(int argc, char **argv, struct xkb_rule_names *names) case OPT_OPTION: names->options = optarg; break; + case OPT_FORMAT: + *keymap_input_format = atoi(optarg); + if (!xkb_keymap_is_supported_format(*keymap_input_format)) { + fprintf(stderr, "error: invalid --format: \"%s\"\n", optarg); + usage(argv); + exit(EXIT_INVALID_USAGE); + } + *keymap_output_format = *keymap_input_format; + break; + case OPT_INPUT_FORMAT: + *keymap_input_format = atoi(optarg); + if (!xkb_keymap_is_supported_format(*keymap_input_format)) { + fprintf(stderr, "error: invalid --input-format: \"%s\"\n", optarg); + usage(argv); + exit(EXIT_INVALID_USAGE); + } + break; + case OPT_OUTPUT_FORMAT: + *keymap_output_format = atoi(optarg); + if (!xkb_keymap_is_supported_format(*keymap_input_format)) { + fprintf(stderr, "error: invalid --output-format: \"%s\"\n", optarg); + usage(argv); + exit(EXIT_INVALID_USAGE); + } + break; default: usage(argv); exit(EXIT_INVALID_USAGE); @@ -236,15 +279,18 @@ print_kccgst(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo) } static bool -print_keymap(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo) +print_keymap(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo, + enum xkb_keymap_format keymap_input_format, + enum xkb_keymap_format keymap_output_format) { struct xkb_keymap *keymap; - keymap = xkb_keymap_new_from_names(ctx, rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS); + keymap = xkb_keymap_new_from_names2(ctx, rmlvo, keymap_input_format, + XKB_KEYMAP_COMPILE_NO_FLAGS); if (keymap == NULL) return false; - char *buf = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1); + char *buf = xkb_keymap_get_as_string(keymap, keymap_output_format); printf("%s\n", buf); free(buf); @@ -253,7 +299,9 @@ print_keymap(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo) } static bool -print_keymap_from_file(struct xkb_context *ctx) +print_keymap_from_file(struct xkb_context *ctx, + enum xkb_keymap_format keymap_input_format, + enum xkb_keymap_format keymap_output_format) { struct xkb_keymap *keymap = NULL; char *keymap_string = NULL; @@ -286,14 +334,12 @@ print_keymap_from_file(struct xkb_context *ctx) break; } fseek(file, 0, SEEK_SET); - keymap = xkb_keymap_new_from_file(ctx, file, - XKB_KEYMAP_FORMAT_TEXT_V1, 0); + keymap = xkb_keymap_new_from_file(ctx, file, keymap_input_format, 0); if (!keymap) { fprintf(stderr, "Couldn't create xkb keymap\n"); goto out; } - - keymap_string = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1); + keymap_string = xkb_keymap_get_as_string(keymap, keymap_output_format); if (!keymap_string) { fprintf(stderr, "Couldn't get the keymap string\n"); goto out; @@ -324,6 +370,8 @@ main(int argc, char **argv) .variant = NULL, .options = DEFAULT_XKB_OPTIONS, }; + enum xkb_keymap_format keymap_input_format = DEFAULT_KEYMAP_FORMAT; + enum xkb_keymap_format keymap_output_format = DEFAULT_KEYMAP_FORMAT; int rc = 1; if (argc < 1) { @@ -331,7 +379,8 @@ main(int argc, char **argv) return EXIT_INVALID_USAGE; } - if (!parse_options(argc, argv, &names)) + if (!parse_options(argc, argv, &names, + &keymap_input_format, &keymap_output_format)) return EXIT_INVALID_USAGE; /* Now fill in the layout */ @@ -366,11 +415,12 @@ main(int argc, char **argv) if (output_format == FORMAT_RMLVO) { rc = print_rmlvo(ctx, &names) ? EXIT_SUCCESS : EXIT_FAILURE; } else if (output_format == FORMAT_KEYMAP) { - rc = print_keymap(ctx, &names) ? EXIT_SUCCESS : EXIT_FAILURE; + rc = print_keymap(ctx, &names, keymap_input_format, keymap_output_format) + ? EXIT_SUCCESS : EXIT_FAILURE; } else if (output_format == FORMAT_KCCGST) { rc = print_kccgst(ctx, &names) ? EXIT_SUCCESS : EXIT_FAILURE; } else if (output_format == FORMAT_KEYMAP_FROM_XKB) { - rc = print_keymap_from_file(ctx); + rc = print_keymap_from_file(ctx, keymap_input_format, keymap_output_format); } xkb_context_unref(ctx); diff --git a/tools/interactive-evdev.c b/tools/interactive-evdev.c index 0b7dc2fb3..5b8625c84 100644 --- a/tools/interactive-evdev.c +++ b/tools/interactive-evdev.c @@ -385,6 +385,7 @@ usage(FILE *fp, char *progname) progname); fprintf(fp, "For both:\n" " --verbose (enable verbose debugging output)\n" + " --format F (use keymap format F)\n" #ifdef ENABLE_PRIVATE_APIS " --print-modmaps (print real & virtual key modmaps)\n" #endif @@ -408,6 +409,7 @@ main(int argc, char *argv[]) struct xkb_compose_table *compose_table = NULL; const char *includes[64]; size_t num_includes = 0; + enum xkb_keymap_format format = XKB_KEYMAP_FORMAT_TEXT_V1; const char *rules = NULL; const char *model = NULL; const char *layout = NULL; @@ -426,6 +428,7 @@ main(int argc, char *argv[]) OPT_VARIANT, OPT_OPTION, OPT_KEYMAP, + OPT_FORMAT, OPT_WITHOUT_X11_OFFSET, OPT_CONSUMED_MODE, OPT_COMPOSE, @@ -446,6 +449,7 @@ main(int argc, char *argv[]) {"variant", required_argument, 0, OPT_VARIANT}, {"options", required_argument, 0, OPT_OPTION}, {"keymap", required_argument, 0, OPT_KEYMAP}, + {"format", required_argument, 0, OPT_FORMAT}, {"consumed-mode", required_argument, 0, OPT_CONSUMED_MODE}, {"enable-compose", no_argument, 0, OPT_COMPOSE}, {"short", no_argument, 0, OPT_SHORT}, @@ -503,6 +507,14 @@ main(int argc, char *argv[]) case OPT_KEYMAP: keymap_path = optarg; break; + case OPT_FORMAT: + format = atoi(optarg); + if (!xkb_keymap_is_supported_format(format)) { + fprintf(stderr, "error: invalid --format \"%s\"\n", optarg); + usage(stderr, argv[0]); + return EXIT_INVALID_USAGE; + } + break; case OPT_WITHOUT_X11_OFFSET: evdev_offset = 0; break; @@ -569,8 +581,7 @@ main(int argc, char *argv[]) keymap_path, strerror(errno)); goto out; } - keymap = xkb_keymap_new_from_file(ctx, file, - XKB_KEYMAP_FORMAT_TEXT_V1, + keymap = xkb_keymap_new_from_file(ctx, file, format, XKB_KEYMAP_COMPILE_NO_FLAGS); fclose(file); } @@ -584,9 +595,9 @@ main(int argc, char *argv[]) }; if (!rules && !model && !layout && !variant && !options) - keymap = xkb_keymap_new_from_names(ctx, NULL, 0); + keymap = xkb_keymap_new_from_names2(ctx, NULL, format, 0); else - keymap = xkb_keymap_new_from_names(ctx, &rmlvo, 0); + keymap = xkb_keymap_new_from_names2(ctx, &rmlvo, format, 0); if (!keymap) { fprintf(stderr, diff --git a/tools/xkbcli-compile-keymap.1 b/tools/xkbcli-compile-keymap.1 index 74c08826e..5006ee01e 100644 --- a/tools/xkbcli-compile-keymap.1 +++ b/tools/xkbcli-compile-keymap.1 @@ -55,6 +55,15 @@ The XKB layout variant . .It Fl \-options Ar options The XKB options +. +.It Fl \-input\-format Ar format +The keymap format (numeric) for parsing +. +.It Fl \-output\-format Ar format +The keymap format (numeric) for serializing +. +.It Fl \-format Ar format +The keymap format (numeric) for both parsing and serializing .El . .Sh SEE ALSO diff --git a/tools/xkbcli-interactive-evdev.1 b/tools/xkbcli-interactive-evdev.1 index 25d698e0a..95410c557 100644 --- a/tools/xkbcli-interactive-evdev.1 +++ b/tools/xkbcli-interactive-evdev.1 @@ -68,6 +68,9 @@ The XKB options Specify a keymap path. This option is mutually exclusive with the RMLVO options. . +.It Fl \-format Ar format +Specify the keymap format (numeric). +. .It Fl \-short Do not print layout nor Unicode keysym translation. . From 30596bc6d8e5e434db920e8d506cea0ac607b388 Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Fri, 5 Jul 2024 08:25:15 +0200 Subject: [PATCH 7/9] test: Use explicit format version in test_compile_rules --- bench/key-proc.c | 3 ++- bench/rulescomp.c | 3 ++- test/buffercomp.c | 2 +- test/common.c | 8 ++++---- test/keymap.c | 12 ++++++++---- test/keyseq.c | 18 ++++++++++++------ test/rulescomp.c | 4 ++-- test/state.c | 6 ++++-- test/stringcomp.c | 3 ++- test/test.h | 6 +++--- 10 files changed, 40 insertions(+), 25 deletions(-) diff --git a/bench/key-proc.c b/bench/key-proc.c index 1d2924322..641ac4563 100644 --- a/bench/key-proc.c +++ b/bench/key-proc.c @@ -65,7 +65,8 @@ main(void) ctx = test_get_context(0); assert(ctx); - keymap = test_compile_rules(ctx, "evdev", "pc104", "us,ru,il,de", + keymap = test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, + "evdev", "pc104", "us,ru,il,de", ",,,neo", "grp:menu_toggle"); assert(keymap); diff --git a/bench/rulescomp.c b/bench/rulescomp.c index 0da4d3cca..f97367e76 100644 --- a/bench/rulescomp.c +++ b/bench/rulescomp.c @@ -47,7 +47,8 @@ main(int argc, char *argv[]) bench_start(&bench); for (i = 0; i < BENCHMARK_ITERATIONS; i++) { - keymap = test_compile_rules(ctx, "evdev", "evdev", "us", "", ""); + keymap = test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, + "evdev", "evdev", "us", "", ""); assert(keymap); xkb_keymap_unref(keymap); } diff --git a/test/buffercomp.c b/test/buffercomp.c index fe6037b46..f6d100255 100644 --- a/test/buffercomp.c +++ b/test/buffercomp.c @@ -218,7 +218,7 @@ main(int argc, char *argv[]) assert(test_encodings(ctx)); /* Make sure we can recompile our output for a normal keymap from rules. */ - keymap = test_compile_rules(ctx, NULL, NULL, + keymap = test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, NULL, NULL, "ru,ca,de,us", ",multix,neo,intl", NULL); assert(keymap); dump = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_USE_ORIGINAL_FORMAT); diff --git a/test/common.c b/test/common.c index 392aacd4e..ae64bcb25 100644 --- a/test/common.c +++ b/test/common.c @@ -401,8 +401,8 @@ 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, +test_compile_rules(struct xkb_context *context, enum xkb_keymap_format format, + const char *rules, const char *model, const char *layout, const char *variant, const char *options) { struct xkb_keymap *keymap; @@ -415,9 +415,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, format, 0); else - keymap = xkb_keymap_new_from_names(context, &rmlvo, 0); + keymap = xkb_keymap_new_from_names2(context, &rmlvo, format, 0); if (!keymap) { fprintf(stderr, diff --git a/test/keymap.c b/test/keymap.c index 2ae42e60d..1ad63d5bd 100644 --- a/test/keymap.c +++ b/test/keymap.c @@ -62,7 +62,8 @@ test_garbage_key(void) assert(context); - keymap = test_compile_rules(context, NULL, NULL, "garbage", NULL, NULL); + keymap = test_compile_rules(context, XKB_KEYMAP_FORMAT_TEXT_V1, + NULL, NULL, "garbage", NULL, NULL); assert(keymap); /* TLDE uses the 'us' sym on the first level and is thus [grave, exclam] */ @@ -107,7 +108,8 @@ test_keymap(void) assert(context); - keymap = test_compile_rules(context, "evdev", "pc104", "us,ru", NULL, "grp:menu_toggle"); + keymap = test_compile_rules(context, XKB_KEYMAP_FORMAT_TEXT_V1, "evdev", + "pc104", "us,ru", NULL, "grp:menu_toggle"); assert(keymap); kc = xkb_keymap_key_by_name(keymap, "AE09"); @@ -184,7 +186,8 @@ test_numeric_keysyms(void) assert(context); - keymap = test_compile_rules(context, "evdev", "pc104", "numeric_keysyms", NULL, NULL); + keymap = test_compile_rules(context, XKB_KEYMAP_FORMAT_TEXT_V1, "evdev", + "pc104", "numeric_keysyms", NULL, NULL); assert(keymap); kc = xkb_keymap_key_by_name(keymap, "AD01"); @@ -227,7 +230,8 @@ test_multiple_keysyms_per_level(void) assert(context); - keymap = test_compile_rules(context, "evdev", "pc104", "awesome", NULL, NULL); + keymap = test_compile_rules(context, XKB_KEYMAP_FORMAT_TEXT_V1, + "evdev", "pc104", "awesome", NULL, NULL); assert(keymap); kc = xkb_keymap_key_by_name(keymap, "AD01"); diff --git a/test/keyseq.c b/test/keyseq.c index 53de4232e..2fe3f12b3 100644 --- a/test/keyseq.c +++ b/test/keyseq.c @@ -35,7 +35,8 @@ main(void) struct xkb_keymap *keymap; assert(ctx); - keymap = test_compile_rules(ctx, "evdev", "evdev", + keymap = test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, + "evdev", "pc105", "us,il,ru,de", ",,phonetic,neo", "grp:alt_shift_toggle,grp:menu_toggle"); assert(keymap); @@ -312,7 +313,8 @@ main(void) KEY_V, BOTH, XKB_KEY_p, FINISH)); xkb_keymap_unref(keymap); - keymap = test_compile_rules(ctx, "evdev", "", "de", "neo", ""); + keymap = test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, + "evdev", "", "de", "neo", ""); assert(keymap); assert(test_key_seq(keymap, /* Level 5. */ @@ -364,7 +366,8 @@ main(void) xkb_keymap_unref(keymap); - keymap = test_compile_rules(ctx, "evdev", "", "us,il,ru", "", + keymap = test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, + "evdev", "", "us,il,ru", "", "grp:alt_shift_toggle_bidir,grp:menu_toggle"); assert(keymap); @@ -403,7 +406,8 @@ main(void) KEY_H, BOTH, XKB_KEY_h, FINISH)); xkb_keymap_unref(keymap); - keymap = test_compile_rules(ctx, "evdev", "", "us,il,ru", "", + keymap = test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, + "evdev", "", "us,il,ru", "", "grp:switch,grp:lswitch,grp:menu_toggle"); assert(keymap); @@ -446,7 +450,8 @@ main(void) KEY_H, BOTH, XKB_KEY_h, FINISH)); xkb_keymap_unref(keymap); - keymap = test_compile_rules(ctx, "evdev", "", "us", "euro", ""); + keymap = test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, + "evdev", "", "us", "euro", ""); assert(keymap); assert(test_key_seq(keymap, @@ -466,7 +471,8 @@ main(void) KEY_Z, BOTH, XKB_KEY_y, FINISH)); xkb_keymap_unref(keymap); - keymap = test_compile_rules(ctx, "evdev", "applealu_ansi", "us", "", + keymap = test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, + "evdev", "applealu_ansi", "us", "", "terminate:ctrl_alt_bksp"); assert(keymap); diff --git a/test/rulescomp.c b/test/rulescomp.c index 4933dd883..6957bf380 100644 --- a/test/rulescomp.c +++ b/test/rulescomp.c @@ -34,8 +34,8 @@ test_rmlvo_va(struct xkb_context *context, const char *rules, struct xkb_keymap *keymap; int ret; - keymap = test_compile_rules(context, rules, model, layout, variant, - options); + keymap = test_compile_rules(context, XKB_KEYMAP_FORMAT_TEXT_V1, + rules, model, layout, variant, options); if (!keymap) return 0; diff --git a/test/state.c b/test/state.c index 283c47163..752d7789a 100644 --- a/test/state.c +++ b/test/state.c @@ -724,7 +724,8 @@ main(void) xkb_keymap_unref(NULL); xkb_state_unref(NULL); - keymap = test_compile_rules(context, "evdev", "pc104", "us,ru", NULL, "grp:menu_toggle"); + keymap = test_compile_rules(context, XKB_KEYMAP_FORMAT_TEXT_V1, + "evdev", "pc104", "us,ru", NULL, "grp:menu_toggle"); assert(keymap); test_update_key(keymap); @@ -737,7 +738,8 @@ main(void) test_ctrl_string_transformation(keymap); xkb_keymap_unref(keymap); - keymap = test_compile_rules(context, "evdev", NULL, "ch", "fr", NULL); + keymap = test_compile_rules(context, XKB_KEYMAP_FORMAT_TEXT_V1, + "evdev", NULL, "ch", "fr", NULL); assert(keymap); test_caps_keysym_transformation(keymap); diff --git a/test/stringcomp.c b/test/stringcomp.c index 8ac740da1..c275a5577 100644 --- a/test/stringcomp.c +++ b/test/stringcomp.c @@ -77,7 +77,8 @@ main(int argc, char *argv[]) assert(!keymap); /* Make sure we can recompile our output for a normal keymap from rules. */ - keymap = test_compile_rules(ctx, NULL, NULL, + keymap = test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, + NULL, NULL, "ru,ca,de,us", ",multix,neo,intl", NULL); assert(keymap); dump = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_USE_ORIGINAL_FORMAT); diff --git a/test/test.h b/test/test.h index 8a6e04178..569fd0512 100644 --- a/test/test.h +++ b/test/test.h @@ -98,9 +98,9 @@ struct xkb_keymap * 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(struct xkb_context *context, enum xkb_keymap_format format, + const char *rules, const char *model, const char *layout, + const char *variant, const char *options); #ifdef _WIN32 From 679bb57910d83dc0a4db6cb9e5f8b82569d63100 Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Fri, 5 Jul 2024 09:36:53 +0200 Subject: [PATCH 8/9] keymap: Add option "lockOnRelease" for LockGroup action This is an extension to XKB, to allow to use the combination Control + Shift *alone* to switch layouts, while keeping the use of Control + Shift + some other key (typically for keyboard shortcuts). This is really useful for people coming from other platform, such as Windows. --- src/keymap.h | 1 + src/state.c | 22 ++++++- src/xkbcomp/action.c | 105 ++++++++++++++++++++++------------ src/xkbcomp/action.h | 4 +- src/xkbcomp/compat.c | 2 +- src/xkbcomp/keymap-dump.c | 36 +++++++----- src/xkbcomp/symbols.c | 2 +- test/data/compat/iso9995-v1.1 | 58 +++++++++++++++++++ test/data/rules/evdev | 2 + test/keyseq.c | 66 +++++++++++++++++++++ 10 files changed, 245 insertions(+), 53 deletions(-) create mode 100644 test/data/compat/iso9995-v1.1 diff --git a/src/keymap.h b/src/keymap.h index 15f9edd6e..d6f10ea66 100644 --- a/src/keymap.h +++ b/src/keymap.h @@ -151,6 +151,7 @@ enum xkb_action_flags { ACTION_ABSOLUTE_Y = (1 << 7), ACTION_ACCEL = (1 << 8), ACTION_SAME_SCREEN = (1 << 9), + ACTION_LOCK_ON_RELEASE = (1 << 10), }; enum xkb_action_controls { diff --git a/src/state.c b/src/state.c index 543658bef..03351f693 100644 --- a/src/state.c +++ b/src/state.c @@ -311,6 +311,11 @@ xkb_filter_group_set_func(struct xkb_state *state, static void xkb_filter_group_lock_new(struct xkb_state *state, struct xkb_filter *filter) { + /* XKB extension: lock on release + * Do nothing on press */ + if (filter->action.group.flags & ACTION_LOCK_ON_RELEASE) + return; + if (filter->action.group.flags & ACTION_ABSOLUTE_SWITCH) state->components.locked_group = filter->action.group.group; else @@ -323,8 +328,15 @@ xkb_filter_group_lock_func(struct xkb_state *state, const struct xkb_key *key, enum xkb_key_direction direction) { - if (key != filter->key) + if (key != filter->key) { + if (filter->action.group.flags & ACTION_LOCK_ON_RELEASE && + direction == XKB_KEY_DOWN) { + /* XKB extension: lock on release + * Another key has been pressed: cancel group change on release */ + filter->action.group.flags &= ~ACTION_LOCK_ON_RELEASE; + } return XKB_FILTER_CONTINUE; + } if (direction == XKB_KEY_DOWN) { filter->refcnt++; @@ -333,6 +345,14 @@ xkb_filter_group_lock_func(struct xkb_state *state, if (--filter->refcnt > 0) return XKB_FILTER_CONSUME; + if (filter->action.group.flags & ACTION_LOCK_ON_RELEASE) { + /* XKB extension: lock on release */ + if (filter->action.group.flags & ACTION_ABSOLUTE_SWITCH) + state->components.locked_group = filter->action.group.group; + else + state->components.locked_group += filter->action.group.group; + } + filter->func = NULL; return XKB_FILTER_CONTINUE; } diff --git a/src/xkbcomp/action.c b/src/xkbcomp/action.c index f55b7dc03..eea4928f4 100644 --- a/src/xkbcomp/action.c +++ b/src/xkbcomp/action.c @@ -100,10 +100,11 @@ enum action_field { ACTION_FIELD_DEVICE, ACTION_FIELD_KEYCODE, ACTION_FIELD_MODS_TO_CLEAR, + ACTION_FIELD_LOCK_ON_RELEASE, }; ActionsInfo * -NewActionsInfo(void) +NewActionsInfo(enum xkb_keymap_format format) { enum xkb_action_type type; ActionsInfo *info; @@ -123,6 +124,11 @@ NewActionsInfo(void) info->actions[ACTION_TYPE_PTR_MOVE].ptr.flags = ACTION_ACCEL; info->actions[ACTION_TYPE_SWITCH_VT].screen.flags = ACTION_SAME_SCREEN; + if (isGroupLockOnReleaseSupported(format)) { + info->actions[ACTION_TYPE_GROUP_LOCK].group.flags = + ACTION_LOCK_ON_RELEASE; + } + return info; } @@ -133,40 +139,41 @@ FreeActionsInfo(ActionsInfo *info) } static const LookupEntry fieldStrings[] = { - { "clearLocks", ACTION_FIELD_CLEAR_LOCKS }, - { "latchToLock", ACTION_FIELD_LATCH_TO_LOCK }, - { "genKeyEvent", ACTION_FIELD_GEN_KEY_EVENT }, - { "generateKeyEvent", ACTION_FIELD_GEN_KEY_EVENT }, - { "report", ACTION_FIELD_REPORT }, - { "default", ACTION_FIELD_DEFAULT }, - { "affect", ACTION_FIELD_AFFECT }, - { "increment", ACTION_FIELD_INCREMENT }, - { "modifiers", ACTION_FIELD_MODIFIERS }, - { "mods", ACTION_FIELD_MODIFIERS }, - { "group", ACTION_FIELD_GROUP }, - { "x", ACTION_FIELD_X }, - { "y", ACTION_FIELD_Y }, - { "accel", ACTION_FIELD_ACCEL }, - { "accelerate", ACTION_FIELD_ACCEL }, - { "repeat", ACTION_FIELD_ACCEL }, - { "button", ACTION_FIELD_BUTTON }, - { "value", ACTION_FIELD_VALUE }, - { "controls", ACTION_FIELD_CONTROLS }, - { "ctrls", ACTION_FIELD_CONTROLS }, - { "type", ACTION_FIELD_TYPE }, - { "count", ACTION_FIELD_COUNT }, - { "screen", ACTION_FIELD_SCREEN }, - { "same", ACTION_FIELD_SAME }, - { "sameServer", ACTION_FIELD_SAME }, - { "data", ACTION_FIELD_DATA }, - { "device", ACTION_FIELD_DEVICE }, - { "dev", ACTION_FIELD_DEVICE }, - { "key", ACTION_FIELD_KEYCODE }, - { "keycode", ACTION_FIELD_KEYCODE }, - { "kc", ACTION_FIELD_KEYCODE }, - { "clearmods", ACTION_FIELD_MODS_TO_CLEAR }, - { "clearmodifiers", ACTION_FIELD_MODS_TO_CLEAR }, - { NULL, 0 } + { "clearLocks", ACTION_FIELD_CLEAR_LOCKS }, + { "latchToLock", ACTION_FIELD_LATCH_TO_LOCK }, + { "genKeyEvent", ACTION_FIELD_GEN_KEY_EVENT }, + { "generateKeyEvent", ACTION_FIELD_GEN_KEY_EVENT }, + { "report", ACTION_FIELD_REPORT }, + { "default", ACTION_FIELD_DEFAULT }, + { "affect", ACTION_FIELD_AFFECT }, + { "increment", ACTION_FIELD_INCREMENT }, + { "modifiers", ACTION_FIELD_MODIFIERS }, + { "mods", ACTION_FIELD_MODIFIERS }, + { "group", ACTION_FIELD_GROUP }, + { "x", ACTION_FIELD_X }, + { "y", ACTION_FIELD_Y }, + { "accel", ACTION_FIELD_ACCEL }, + { "accelerate", ACTION_FIELD_ACCEL }, + { "repeat", ACTION_FIELD_ACCEL }, + { "button", ACTION_FIELD_BUTTON }, + { "value", ACTION_FIELD_VALUE }, + { "controls", ACTION_FIELD_CONTROLS }, + { "ctrls", ACTION_FIELD_CONTROLS }, + { "type", ACTION_FIELD_TYPE }, + { "count", ACTION_FIELD_COUNT }, + { "screen", ACTION_FIELD_SCREEN }, + { "same", ACTION_FIELD_SAME }, + { "sameServer", ACTION_FIELD_SAME }, + { "data", ACTION_FIELD_DATA }, + { "device", ACTION_FIELD_DEVICE }, + { "dev", ACTION_FIELD_DEVICE }, + { "key", ACTION_FIELD_KEYCODE }, + { "keycode", ACTION_FIELD_KEYCODE }, + { "kc", ACTION_FIELD_KEYCODE }, + { "clearmods", ACTION_FIELD_MODS_TO_CLEAR }, + { "clearmodifiers", ACTION_FIELD_MODS_TO_CLEAR }, + { "lockOnRelease", ACTION_FIELD_LOCK_ON_RELEASE }, + { NULL, 0 } }; static bool @@ -201,6 +208,17 @@ ReportMismatch(struct xkb_context *ctx, xkb_message_code_t code, return false; } +static inline bool +ReportFormatVersionMismatch(struct xkb_context *ctx, enum xkb_action_type action, + enum action_field field, const char *versions) +{ + log_err(ctx, XKB_LOG_MESSAGE_NO_ID, + "Field %s for an action of type %s requires keymap format %s; " + "Action definition ignored\n", + fieldText(field), ActionTypeText(action), versions); + return false; +} + static inline bool ReportIllegal(struct xkb_context *ctx, enum xkb_action_type action, enum action_field field) @@ -380,6 +398,12 @@ CheckGroupField(struct xkb_context *ctx, enum xkb_action_type action, return true; } +inline bool +isGroupLockOnReleaseSupported(enum xkb_keymap_format format) { + /* Lax bound */ + return format >= XKB_KEYMAP_FORMAT_TEXT_V1_1; +} + static bool HandleSetLatchLockGroup(struct xkb_context *ctx, enum xkb_keymap_format format, const struct xkb_mod_set *mods, @@ -402,6 +426,17 @@ HandleSetLatchLockGroup(struct xkb_context *ctx, enum xkb_keymap_format format, return CheckBooleanFlag(ctx, action->type, field, ACTION_LATCH_TO_LOCK, array_ndx, value, &act->flags); + if (type == ACTION_TYPE_GROUP_LOCK && + field == ACTION_FIELD_LOCK_ON_RELEASE) { + if (isGroupLockOnReleaseSupported(format)) { + return CheckBooleanFlag(ctx, action->type, field, + ACTION_LOCK_ON_RELEASE, array_ndx, value, + &act->flags); + } else { + return ReportFormatVersionMismatch(ctx, action->type, field, + ">= 1.1"); + } + } return ReportIllegal(ctx, action->type, field); } diff --git a/src/xkbcomp/action.h b/src/xkbcomp/action.h index 2ffce4f3b..a622b717a 100644 --- a/src/xkbcomp/action.h +++ b/src/xkbcomp/action.h @@ -37,7 +37,7 @@ typedef struct { } ActionsInfo; ActionsInfo * -NewActionsInfo(void); +NewActionsInfo(enum xkb_keymap_format format); void FreeActionsInfo(ActionsInfo *info); @@ -53,5 +53,7 @@ SetActionField(struct xkb_context *ctx, enum xkb_keymap_format format, ActionsInfo *info, struct xkb_mod_set *mods, const char *elem, const char *field, ExprDef *array_ndx, ExprDef *value); +bool +isGroupLockOnReleaseSupported(enum xkb_keymap_format format); #endif diff --git a/src/xkbcomp/compat.c b/src/xkbcomp/compat.c index 1fc9bd64e..6155eec13 100644 --- a/src/xkbcomp/compat.c +++ b/src/xkbcomp/compat.c @@ -923,7 +923,7 @@ CompileCompatMap(XkbFile *file, struct xkb_keymap *keymap, CompatInfo info; ActionsInfo *actions; - actions = NewActionsInfo(); + actions = NewActionsInfo(keymap->format); if (!actions) return false; diff --git a/src/xkbcomp/keymap-dump.c b/src/xkbcomp/keymap-dump.c index a73bb18ae..f1d1b2be3 100644 --- a/src/xkbcomp/keymap-dump.c +++ b/src/xkbcomp/keymap-dump.c @@ -52,6 +52,7 @@ #include "config.h" #include "xkbcomp-priv.h" +#include "action.h" #include "text.h" #define BUF_CHUNK_SIZE 4096 @@ -293,8 +294,8 @@ affect_lock_text(enum xkb_action_flags flags, bool show_both) } static bool -write_action(struct xkb_keymap *keymap, struct buf *buf, - const union xkb_action *action, +write_action(struct xkb_keymap *keymap, enum xkb_keymap_format format, + struct buf *buf, const union xkb_action *action, const char *prefix, const char *suffix) { const char *type; @@ -326,11 +327,16 @@ write_action(struct xkb_keymap *keymap, struct buf *buf, case ACTION_TYPE_GROUP_SET: case ACTION_TYPE_GROUP_LATCH: case ACTION_TYPE_GROUP_LOCK: - write_buf(buf, "%s%s(group=%s%d%s%s)%s", prefix, type, + if (action->type == ACTION_TYPE_GROUP_LOCK && (action->group.flags & ACTION_LOCK_ON_RELEASE) && !isGroupLockOnReleaseSupported(format)) { + log_err(keymap->ctx, XKB_ERROR_INCOMPATIBLE_KEYMAP_EXPORT_FORMAT, + "Cannot use \"lockOnRelease=true\" in keymap format %d\n", format); + } + write_buf(buf, "%s%s(group=%s%d%s%s%s)%s", prefix, type, (!(action->group.flags & ACTION_ABSOLUTE_SWITCH) && action->group.group > 0) ? "+" : "", (action->group.flags & ACTION_ABSOLUTE_SWITCH) ? action->group.group + 1 : action->group.group, (action->type != ACTION_TYPE_GROUP_LOCK && (action->group.flags & ACTION_LOCK_CLEAR)) ? ",clearLocks" : "", (action->type != ACTION_TYPE_GROUP_LOCK && (action->group.flags & ACTION_LATCH_TO_LOCK)) ? ",latchToLock" : "", + (action->type == ACTION_TYPE_GROUP_LOCK && (!(action->group.flags & ACTION_LOCK_ON_RELEASE)) && isGroupLockOnReleaseSupported(format)) ? ",lockOnRelease=false" : "", suffix); break; @@ -407,7 +413,8 @@ write_action(struct xkb_keymap *keymap, struct buf *buf, } static bool -write_compat(struct xkb_keymap *keymap, struct buf *buf) +write_compat(struct xkb_keymap *keymap, enum xkb_keymap_format format, + struct buf *buf) { const struct xkb_led *led; @@ -441,7 +448,7 @@ write_compat(struct xkb_keymap *keymap, struct buf *buf) if (si->repeat) write_buf(buf, "\t\trepeat= True;\n"); - write_action(keymap, buf, &si->action, "\t\taction= ", ";\n"); + write_action(keymap, format, buf, &si->action, "\t\taction= ", ";\n"); write_buf(buf, "\t};\n"); } @@ -490,8 +497,8 @@ write_keysyms(struct xkb_keymap *keymap, struct buf *buf, } static bool -write_key(struct xkb_keymap *keymap, struct buf *buf, - const struct xkb_key *key) +write_key(struct xkb_keymap *keymap, enum xkb_keymap_format format, + struct buf *buf, const struct xkb_key *key) { xkb_layout_index_t group; bool simple = true; @@ -583,9 +590,9 @@ write_key(struct xkb_keymap *keymap, struct buf *buf, for (level = 0; level < XkbKeyNumLevels(key, group); level++) { if (level != 0) write_buf(buf, ", "); - write_action(keymap, buf, - &key->groups[group].levels[level].action, - NULL, NULL); + write_action(keymap, format, buf, + &key->groups[group].levels[level].action, + NULL, NULL); } write_buf(buf, " ]"); } @@ -597,7 +604,8 @@ write_key(struct xkb_keymap *keymap, struct buf *buf, } static bool -write_symbols(struct xkb_keymap *keymap, struct buf *buf) +write_symbols(struct xkb_keymap *keymap, enum xkb_keymap_format format, + struct buf *buf) { const struct xkb_key *key; xkb_layout_index_t group; @@ -620,7 +628,7 @@ write_symbols(struct xkb_keymap *keymap, struct buf *buf) xkb_keys_foreach(key, keymap) if (key->num_groups > 0) - write_key(keymap, buf, key); + write_key(keymap, format, buf, key); xkb_mods_enumerate(i, mod, &keymap->mods) { bool had_any = false; @@ -650,8 +658,8 @@ write_keymap(struct xkb_keymap *keymap, enum xkb_keymap_format format, return (check_write_buf(buf, "xkb_keymap {\n") && write_keycodes(keymap, buf) && write_types(keymap, buf) && - write_compat(keymap, buf) && - write_symbols(keymap, buf) && + write_compat(keymap, format, buf) && + write_symbols(keymap, format, buf) && check_write_buf(buf, "};\n")); } diff --git a/src/xkbcomp/symbols.c b/src/xkbcomp/symbols.c index bbf45e4e3..0ea434f20 100644 --- a/src/xkbcomp/symbols.c +++ b/src/xkbcomp/symbols.c @@ -1644,7 +1644,7 @@ CompileSymbols(XkbFile *file, struct xkb_keymap *keymap, SymbolsInfo info; ActionsInfo *actions; - actions = NewActionsInfo(); + actions = NewActionsInfo(keymap->format); if (!actions) return false; diff --git a/test/data/compat/iso9995-v1.1 b/test/data/compat/iso9995-v1.1 new file mode 100644 index 000000000..c18876677 --- /dev/null +++ b/test/data/compat/iso9995-v1.1 @@ -0,0 +1,58 @@ +// Fairly complete set of symbol interpretations +// to provide reasonable default behavior. + +default partial +xkb_compatibility "lockOnRelease" { + + virtual_modifiers AltGr; + + interpret.repeat= False; + + interpret ISO_Next_Group { + useModMapMods= level1; + virtualModifier= AltGr; + action= LockGroup(group=+1, lockOnRelease); + }; + + interpret ISO_Prev_Group { + useModMapMods= level1; + virtualModifier= AltGr; + action= LockGroup(group=-1, lockOnRelease); + }; + + interpret ISO_First_Group { + action= LockGroup(group=1, lockOnRelease); + }; + + interpret ISO_Last_Group { + action= LockGroup(group=2, lockOnRelease); + }; +}; + +partial +xkb_compatibility "lockOnPress" { + + virtual_modifiers AltGr; + + interpret.repeat= False; + + interpret ISO_Next_Group { + useModMapMods= level1; + virtualModifier= AltGr; + action= LockGroup(group=+1, lockOnRelease=false); + }; + + interpret ISO_Prev_Group { + useModMapMods= level1; + virtualModifier= AltGr; + action= LockGroup(group=-1, lockOnRelease=false); + }; + + interpret ISO_First_Group { + action= LockGroup(group=1, lockOnRelease=false); + }; + + interpret ISO_Last_Group { + action= LockGroup(group=2, lockOnRelease=false); + }; +}; diff --git a/test/data/rules/evdev b/test/data/rules/evdev index 32c8ed3ef..2e9476759 100644 --- a/test/data/rules/evdev +++ b/test/data/rules/evdev @@ -1161,6 +1161,8 @@ japan:kana_lock = +japan(kana_lock) caps:shiftlock = +ledcaps(shift_lock) grab:break_actions = +xfree86(grab_break) + grp:lockOnRelease = +iso9995-v1.1(lockOnRelease) + grp:lockOnPress = +iso9995-v1.1(lockOnPress) ! option = types diff --git a/test/keyseq.c b/test/keyseq.c index 2fe3f12b3..95cfe6787 100644 --- a/test/keyseq.c +++ b/test/keyseq.c @@ -492,6 +492,72 @@ main(void) KEY_A, BOTH, XKB_KEY_a, FINISH)); xkb_keymap_unref(keymap); + + /* Group lock on press for format V1 (implicit, XKB spec) */ + keymap = test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, + "evdev", "pc105", + "us,de", "", + "grp:alt_shift_toggle"); + assert(keymap); + +#define test_group_lock_on_press(keymap_) \ + assert(test_key_seq(keymap_, \ + KEY_Y, BOTH, XKB_KEY_y, NEXT, \ + KEY_LEFTALT, DOWN, XKB_KEY_Alt_L, NEXT, \ + KEY_LEFTSHIFT, BOTH, XKB_KEY_ISO_Next_Group, NEXT, \ + /* Group change on press */ \ + KEY_Y, BOTH, XKB_KEY_z, NEXT, \ + KEY_LEFTSHIFT, DOWN, XKB_KEY_ISO_Next_Group, NEXT, \ + /* Group change on press */ \ + KEY_Y, BOTH, XKB_KEY_y, NEXT, \ + KEY_LEFTSHIFT, UP, XKB_KEY_ISO_Next_Group, NEXT, \ + KEY_Y, BOTH, XKB_KEY_y, NEXT, \ + KEY_LEFTALT, UP, XKB_KEY_Alt_L, FINISH)) + test_group_lock_on_press(keymap); + + xkb_keymap_unref(keymap); + + /* Group lock on release for format V1_1 (implicit, using defaults) */ + keymap = test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1_1, + "evdev", "pc105", + "us,de", "", + "grp:alt_shift_toggle"); + assert(keymap); + +#define test_group_lock_on_relase(keymap_) \ + assert(test_key_seq(keymap_, \ + KEY_Y, BOTH, XKB_KEY_y, NEXT, \ + KEY_LEFTALT, DOWN, XKB_KEY_Alt_L, NEXT, \ + KEY_LEFTSHIFT, BOTH, XKB_KEY_ISO_Next_Group, NEXT, \ + /* Group lock on release */ \ + KEY_Y, BOTH, XKB_KEY_z, NEXT, \ + KEY_LEFTSHIFT, DOWN, XKB_KEY_ISO_Next_Group, NEXT, \ + /* Key not released, no group change */ \ + KEY_Y, BOTH, XKB_KEY_z, NEXT, \ + KEY_LEFTSHIFT, UP, XKB_KEY_ISO_Next_Group, NEXT, \ + /* Group lock cancelled by intermediate key press */\ + KEY_Y, BOTH, XKB_KEY_z, NEXT, \ + KEY_LEFTALT, UP, XKB_KEY_Alt_L, FINISH)) + test_group_lock_on_relase(keymap); + + xkb_keymap_unref(keymap); + + /* Group lock on release for format V1_1 (explicit lockOnRelease=true) */ + keymap = test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1_1, + "evdev", "pc105", + "us,de", "", + "grp:alt_shift_toggle,grp:lockOnRelease"); + test_group_lock_on_relase(keymap); + xkb_keymap_unref(keymap); + + /* Group lock on press for format V1_1 (explicit lockOnRelease=false, XKB spec) */ + keymap = test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1_1, + "evdev", "pc105", + "us,de", "", + "grp:alt_shift_toggle,grp:lockOnPress"); + test_group_lock_on_press(keymap); + xkb_keymap_unref(keymap); + xkb_context_unref(ctx); return 0; } From 2a47e137c3c54f2c96dd4b0a844bcad72b9cd80a Mon Sep 17 00:00:00 2001 From: Pierre Le Marre Date: Thu, 8 Feb 2024 17:28:10 +0100 Subject: [PATCH 9/9] keymap: Add option "unlockOnPress" for LockMods action This is an extensions to XKB. It intends to allow to deactivate CapsLock on press rather than on release, as in other platforms such as Windows. --- src/keymap.h | 1 + src/state.c | 14 ++++++++--- src/xkbcomp/action.c | 31 ++++++++++++++++++++--- src/xkbcomp/action.h | 3 +++ src/xkbcomp/keymap-dump.c | 11 +++++++- test/data/compat/caps | 12 +++++++++ test/data/rules/evdev | 2 ++ test/keyseq.c | 53 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 119 insertions(+), 8 deletions(-) diff --git a/src/keymap.h b/src/keymap.h index d6f10ea66..9ba8579c0 100644 --- a/src/keymap.h +++ b/src/keymap.h @@ -152,6 +152,7 @@ enum xkb_action_flags { ACTION_ACCEL = (1 << 8), ACTION_SAME_SCREEN = (1 << 9), ACTION_LOCK_ON_RELEASE = (1 << 10), + ACTION_UNLOCK_ON_PRESS = (1 << 11), }; enum xkb_action_controls { diff --git a/src/state.c b/src/state.c index 03351f693..bce8b0746 100644 --- a/src/state.c +++ b/src/state.c @@ -395,9 +395,17 @@ xkb_filter_mod_lock_new(struct xkb_state *state, struct xkb_filter *filter) { filter->priv = (state->components.locked_mods & filter->action.mods.mods.mask); - state->set_mods |= filter->action.mods.mods.mask; - if (!(filter->action.mods.flags & ACTION_LOCK_NO_LOCK)) - state->components.locked_mods |= filter->action.mods.mods.mask; + if (filter->priv && (filter->action.mods.flags & ACTION_UNLOCK_ON_PRESS)) { + /* XKB extension: Unlock on second press */ + state->clear_mods |= filter->action.mods.mods.mask; + if (!(filter->action.mods.flags & ACTION_LOCK_NO_UNLOCK)) + state->components.locked_mods &= ~filter->priv; + filter->priv = 0; + } else { + state->set_mods |= filter->action.mods.mods.mask; + if (!(filter->action.mods.flags & ACTION_LOCK_NO_LOCK)) + state->components.locked_mods |= filter->action.mods.mods.mask; + } } static bool diff --git a/src/xkbcomp/action.c b/src/xkbcomp/action.c index eea4928f4..640f1195f 100644 --- a/src/xkbcomp/action.c +++ b/src/xkbcomp/action.c @@ -101,6 +101,7 @@ enum action_field { ACTION_FIELD_KEYCODE, ACTION_FIELD_MODS_TO_CLEAR, ACTION_FIELD_LOCK_ON_RELEASE, + ACTION_FIELD_UNLOCK_ON_PRESS, }; ActionsInfo * @@ -124,6 +125,10 @@ NewActionsInfo(enum xkb_keymap_format format) info->actions[ACTION_TYPE_PTR_MOVE].ptr.flags = ACTION_ACCEL; info->actions[ACTION_TYPE_SWITCH_VT].screen.flags = ACTION_SAME_SCREEN; + if (isModUnlockOnPressSupported(format)) { + info->actions[ACTION_TYPE_MOD_LOCK].group.flags = + ACTION_UNLOCK_ON_PRESS; + } if (isGroupLockOnReleaseSupported(format)) { info->actions[ACTION_TYPE_GROUP_LOCK].group.flags = ACTION_LOCK_ON_RELEASE; @@ -173,6 +178,7 @@ static const LookupEntry fieldStrings[] = { { "clearmods", ACTION_FIELD_MODS_TO_CLEAR }, { "clearmodifiers", ACTION_FIELD_MODS_TO_CLEAR }, { "lockOnRelease", ACTION_FIELD_LOCK_ON_RELEASE }, + { "unlockOnPress", ACTION_FIELD_UNLOCK_ON_PRESS }, { NULL, 0 } }; @@ -330,6 +336,12 @@ CheckAffectField(struct xkb_context *ctx, enum xkb_action_type action, return true; } +inline bool +isModUnlockOnPressSupported(enum xkb_keymap_format format) { + /* Lax bound */ + return format >= XKB_KEYMAP_FORMAT_TEXT_V1_1; +} + static bool HandleSetLatchLockMods(struct xkb_context *ctx, enum xkb_keymap_format format, const struct xkb_mod_set *mods, @@ -352,10 +364,21 @@ HandleSetLatchLockMods(struct xkb_context *ctx, enum xkb_keymap_format format, return CheckBooleanFlag(ctx, action->type, field, ACTION_LATCH_TO_LOCK, array_ndx, value, &act->flags); - if (type == ACTION_TYPE_MOD_LOCK && - field == ACTION_FIELD_AFFECT) - return CheckAffectField(ctx, action->type, array_ndx, value, - &act->flags); + if (type == ACTION_TYPE_MOD_LOCK) { + if (field == ACTION_FIELD_AFFECT) + return CheckAffectField(ctx, action->type, array_ndx, value, + &act->flags); + if (field == ACTION_FIELD_UNLOCK_ON_PRESS) { + if (isModUnlockOnPressSupported(format)) { + return CheckBooleanFlag(ctx, action->type, field, + ACTION_UNLOCK_ON_PRESS, array_ndx, value, + &act->flags); + } else { + return ReportFormatVersionMismatch(ctx, action->type, field, + ">= 1.1"); + } + } + } return ReportIllegal(ctx, action->type, field); } diff --git a/src/xkbcomp/action.h b/src/xkbcomp/action.h index a622b717a..f49dfd36d 100644 --- a/src/xkbcomp/action.h +++ b/src/xkbcomp/action.h @@ -53,6 +53,9 @@ SetActionField(struct xkb_context *ctx, enum xkb_keymap_format format, ActionsInfo *info, struct xkb_mod_set *mods, const char *elem, const char *field, ExprDef *array_ndx, ExprDef *value); +bool +isModUnlockOnPressSupported(enum xkb_keymap_format format); + bool isGroupLockOnReleaseSupported(enum xkb_keymap_format format); diff --git a/src/xkbcomp/keymap-dump.c b/src/xkbcomp/keymap-dump.c index f1d1b2be3..ef4e4b5ad 100644 --- a/src/xkbcomp/keymap-dump.c +++ b/src/xkbcomp/keymap-dump.c @@ -317,10 +317,17 @@ write_action(struct xkb_keymap *keymap, enum xkb_keymap_format format, else args = ModMaskText(keymap->ctx, &keymap->mods, action->mods.mods.mods); - write_buf(buf, "%s%s(modifiers=%s%s%s%s)%s", prefix, type, args, + if (action->type == ACTION_TYPE_MOD_LOCK && (action->mods.flags & ACTION_UNLOCK_ON_PRESS) && !isModUnlockOnPressSupported(format)) { + /* Default is true in format ≥ 1.1 */ + log_err(keymap->ctx, XKB_ERROR_INCOMPATIBLE_KEYMAP_EXPORT_FORMAT, + "Cannot use \"unlockOnPress=true\" in keymap format %d\n", format); + } + write_buf(buf, "%s%s(modifiers=%s%s%s%s%s)%s", prefix, type, args, (action->type != ACTION_TYPE_MOD_LOCK && (action->mods.flags & ACTION_LOCK_CLEAR)) ? ",clearLocks" : "", (action->type != ACTION_TYPE_MOD_LOCK && (action->mods.flags & ACTION_LATCH_TO_LOCK)) ? ",latchToLock" : "", (action->type == ACTION_TYPE_MOD_LOCK) ? affect_lock_text(action->mods.flags, false) : "", + /* Default is true in format ≥ 1.1 */ + (action->type == ACTION_TYPE_MOD_LOCK && (!(action->mods.flags & ACTION_UNLOCK_ON_PRESS)) && isModUnlockOnPressSupported(format)) ? ",unlockOnPress=false" : "", suffix); break; @@ -328,6 +335,7 @@ write_action(struct xkb_keymap *keymap, enum xkb_keymap_format format, case ACTION_TYPE_GROUP_LATCH: case ACTION_TYPE_GROUP_LOCK: if (action->type == ACTION_TYPE_GROUP_LOCK && (action->group.flags & ACTION_LOCK_ON_RELEASE) && !isGroupLockOnReleaseSupported(format)) { + /* Default is true in format ≥ 1.1 */ log_err(keymap->ctx, XKB_ERROR_INCOMPATIBLE_KEYMAP_EXPORT_FORMAT, "Cannot use \"lockOnRelease=true\" in keymap format %d\n", format); } @@ -336,6 +344,7 @@ write_action(struct xkb_keymap *keymap, enum xkb_keymap_format format, (action->group.flags & ACTION_ABSOLUTE_SWITCH) ? action->group.group + 1 : action->group.group, (action->type != ACTION_TYPE_GROUP_LOCK && (action->group.flags & ACTION_LOCK_CLEAR)) ? ",clearLocks" : "", (action->type != ACTION_TYPE_GROUP_LOCK && (action->group.flags & ACTION_LATCH_TO_LOCK)) ? ",latchToLock" : "", + /* Default is true in format ≥ 1.1 */ (action->type == ACTION_TYPE_GROUP_LOCK && (!(action->group.flags & ACTION_LOCK_ON_RELEASE)) && isGroupLockOnReleaseSupported(format)) ? ",lockOnRelease=false" : "", suffix); break; diff --git a/test/data/compat/caps b/test/data/compat/caps index 4793051ec..524ea157c 100644 --- a/test/data/compat/caps +++ b/test/data/compat/caps @@ -9,3 +9,15 @@ partial xkb_compatibility "caps_lock" { action = LockMods(modifiers = Lock); }; }; + +partial xkb_compatibility "unlock_on_press" { + interpret Caps_Lock { + action = LockMods(modifiers = Lock, unlockOnPress); + }; +}; + +partial xkb_compatibility "unlock_on_release" { + interpret Caps_Lock { + action = LockMods(modifiers = Lock, unlockOnPress=false); + }; +}; diff --git a/test/data/rules/evdev b/test/data/rules/evdev index 2e9476759..6e88e265d 100644 --- a/test/data/rules/evdev +++ b/test/data/rules/evdev @@ -1160,6 +1160,8 @@ mod_led:compose = +ledcompose(compose) japan:kana_lock = +japan(kana_lock) caps:shiftlock = +ledcaps(shift_lock) + caps:unlock_on_press = +caps(unlock_on_press) + caps:unlock_on_release = +caps(unlock_on_release) grab:break_actions = +xfree86(grab_break) grp:lockOnRelease = +iso9995-v1.1(lockOnRelease) grp:lockOnPress = +iso9995-v1.1(lockOnPress) diff --git a/test/keyseq.c b/test/keyseq.c index 95cfe6787..dcd105140 100644 --- a/test/keyseq.c +++ b/test/keyseq.c @@ -558,6 +558,59 @@ main(void) test_group_lock_on_press(keymap); xkb_keymap_unref(keymap); + /* Caps unlocks on release for format V1 (implicit, XKB spec) */ + keymap = test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1, + "evdev", "pc105", "us", "", ""); + assert(keymap); + +#define test_caps_unlocks_on_release(keymap_) \ + assert(test_key_seq(keymap_, \ + KEY_Y, BOTH, XKB_KEY_y, NEXT, \ + KEY_CAPSLOCK, BOTH, XKB_KEY_Caps_Lock, NEXT, \ + KEY_Y, BOTH, XKB_KEY_Y, NEXT, \ + KEY_CAPSLOCK, DOWN, XKB_KEY_Caps_Lock, NEXT, \ + /* No unlock on press */ \ + KEY_Y, BOTH, XKB_KEY_Y, NEXT, \ + KEY_CAPSLOCK, UP, XKB_KEY_Caps_Lock, NEXT, \ + /* Unlock on release */ \ + KEY_Y, BOTH, XKB_KEY_y, FINISH)) + test_caps_unlocks_on_release(keymap); + + xkb_keymap_unref(keymap); + + /* Caps unlocks on press for format V1.1 (implicit, default) */ + keymap = test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1_1, + "evdev", "pc105", "us", "", ""); + assert(keymap); + +#define test_caps_unlocks_on_press(keymap_) \ + assert(test_key_seq(keymap_, \ + KEY_Y, BOTH, XKB_KEY_y, NEXT, \ + KEY_CAPSLOCK, BOTH, XKB_KEY_Caps_Lock, NEXT, \ + KEY_Y, BOTH, XKB_KEY_Y, NEXT, \ + KEY_CAPSLOCK, DOWN, XKB_KEY_Caps_Lock, NEXT, \ + /* Unlock on press */ \ + KEY_Y, BOTH, XKB_KEY_y, NEXT, \ + KEY_CAPSLOCK, UP, XKB_KEY_Caps_Lock, NEXT, \ + KEY_Y, BOTH, XKB_KEY_y, FINISH)) + test_caps_unlocks_on_press(keymap); + + xkb_keymap_unref(keymap); + + /* Caps unlocks on press for format V1.1 (explicit unlockOnPress=true) */ + keymap = test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1_1, + "evdev", "pc105", "us", "", "caps:unlock_on_press"); + assert(keymap); + test_caps_unlocks_on_press(keymap); + xkb_keymap_unref(keymap); + + /* Caps unlocks on press for format V1.1 (explicit unlockOnPress=false) */ + keymap = test_compile_rules(ctx, XKB_KEYMAP_FORMAT_TEXT_V1_1, + "evdev", "pc105", "us", "", "caps:unlock_on_release"); + assert(keymap); + test_caps_unlocks_on_release(keymap); + xkb_keymap_unref(keymap); + xkb_context_unref(ctx); return 0; }