[PATCH 4/4] xkbsupport: Add xkb compose
Alexey Gladkov <[email protected]> Thu, 4 Jan 2024 18:15:18 +0000
| Newsgroups | dev.linux.lists.kbd |
|---|---|
| Message-ID | <718215045881609a094090ff871742b63a693363.1704392039.git.legion@kernel.org> |
The approach to using compose differs between libxkbcommon and kernel. Compose in the kernel has stricter restrictions. 1. Length of sequence. xkb: The length of the compose sequence can be any (or very long). kbd: The length of the sequence is 2. 2. Length of result. xkb: Again, the result of the sequence can be anything. kbd: The result of compose restricted by unsigned int. 3. How to use compose. xkb: The libxkbcommon library loads the entire table for all locales (more than 5000 combinations) and as characters are typed, it narrows the number of variants until there is only one sequence left. The library is not limited by the amount of memory by the number of stored variants. kbd: Compose sequences are loaded into the kernel (ioctl KDGKBDIACR) and after that it is applied in the kernel without our participation. A table in the kernel is limited to 256 records. To reduce the number of combinations: 1. We must ignore all xkb compose sequences longer than 2. 2. We should ignore sequences that use unicodes that are not used in the generated keymap. Such tricks allow you to reduce 5000+ to ~220 (for en,ru keymap). Unfortunately, there is no guarantee yet that the amount of compose will be within the limit and there is no way to control it yet. Signed-off-by: Alexey Gladkov <[email protected]> --- src/loadkeys.c | 26 ++++++++-- src/xkbsupport.c | 126 +++++++++++++++++++++++++++++++++++++++++++++-- src/xkbsupport.h | 2 + 3 files changed, 145 insertions(+), 9 deletions(-) diff --git a/src/loadkeys.c b/src/loadkeys.c index d0178c77..c9a3304d 100644 --- a/src/loadkeys.c +++ b/src/loadkeys.c @@ -101,7 +101,8 @@ int main(int argc, char *argv[]) .model = "pc104", .layout = NULL, .variant = NULL, - .options = NULL + .options = NULL, + .locale = NULL, }; int use_xkb = 0; #endif @@ -130,15 +131,17 @@ int main(int argc, char *argv[]) { "xkb-layout", required_argument, NULL, 3 }, { "xkb-variant", required_argument, NULL, 4 }, { "xkb-options", required_argument, NULL, 5 }, + { "xkb-locale", required_argument, NULL, 6 }, #endif { NULL, 0, NULL, 0 } }; const struct kbd_help opthelp[] = { #ifdef USE_XKB - { "--xkb-model=STR", _("Specifies model used to choose component names.") }, - { "--xkb-layout=STR", _("Specifies layout used to choose component names.") }, - { "--xkb-variant=STR", _("Specifies layout variant used to choose component names.") }, - { "--xkb-options=STR", _("Adds an option used to choose component names.") }, + { "--xkb-model=STR", _("Specifies model used to choose component names.") }, + { "--xkb-layout=STR", _("Specifies layout used to choose component names.") }, + { "--xkb-variant=STR", _("Specifies layout variant used to choose component names.") }, + { "--xkb-options=STR", _("Adds an option used to choose component names.") }, + { "--xkb-locale=LOCALE", _("Use LOCALE to search an appropriate compose file.") }, #endif { "-C, --console=DEV", _("the console device to be used.") }, { "-a, --ascii", _("force conversion to ASCII.") }, @@ -183,6 +186,10 @@ int main(int argc, char *argv[]) xkeymap_params.options = optarg; use_xkb = 1; break; + case 6: + xkeymap_params.locale = optarg; + use_xkb = 1; + break; #endif case 'a': options |= OPT_A; @@ -289,6 +296,15 @@ int main(int argc, char *argv[]) #ifdef USE_XKB } else if (use_xkb) { + if (!xkeymap_params.locale || !*xkeymap_params.locale) + xkeymap_params.locale = getenv("LC_ALL"); + if (!xkeymap_params.locale || !*xkeymap_params.locale) + xkeymap_params.locale = getenv("LC_CTYPE"); + if (!xkeymap_params.locale || !*xkeymap_params.locale) + xkeymap_params.locale = getenv("LANG"); + if (!xkeymap_params.locale || !*xkeymap_params.locale) + xkeymap_params.locale = "C"; + rc = convert_xkb_keymap(ctx, &xkeymap_params, options); if (rc == -1) diff --git a/src/xkbsupport.c b/src/xkbsupport.c index 77abada0..9fb44879 100644 --- a/src/xkbsupport.c +++ b/src/xkbsupport.c @@ -42,8 +42,10 @@ struct sym_pair { }; struct xkeymap { + const char *debug; struct xkb_context *xkb; struct xkb_keymap *keymap; + struct xkb_compose_table *compose; struct lk_ctx *ctx; void *used_codes; void *syms_map; @@ -89,6 +91,12 @@ static struct modifier_mapping modifier_mapping[] = { { NULL, NULL, 0 }, }; +static int is_debug(struct xkeymap *xkeymap, const char *val) +{ + if (!xkeymap->debug || (val && strcmp(xkeymap->debug, val))) + return 0; + return 1; +} static struct modifier_mapping *convert_modifier(const char *xkb_name) { @@ -391,10 +399,8 @@ static int xkeymap_walk(struct xkeymap *xkeymap) else if (ret < 0) goto err; - if (getenv("LK_XKB_DEBUG")) { + if (is_debug(xkeymap, NULL)) xkeymap_walk_printer(xkeymap, layout, level, keycode, sym); - continue; - } if (sym == XKB_KEY_ISO_Next_Group) { int shiftl_lock = lk_ksym_to_unicode(xkeymap->ctx, "ShiftL_Lock"); @@ -513,6 +519,98 @@ err: return -1; } +static void xkeymap_compose_printer(struct xkb_compose_table_entry *entry) +{ + char buf[128]; + int offset = 20; + size_t seqlen = 0; + const xkb_keysym_t *syms = xkb_compose_table_entry_sequence(entry, &seqlen); + const char *chr = xkb_compose_table_entry_utf8(entry); + xkb_keysym_t keysym = xkb_compose_table_entry_keysym(entry); + + printf("Compose: \"%s\" (chars=%ld) ", chr, strlen(chr)); + + if (xkb_keysym_get_name(keysym, buf, sizeof(buf)) > 0) + offset -= printf("<%s>", buf); + else + offset -= printf("<?>"); + + for (; offset > 0; offset--) + printf(" "); + + printf(" -> sequence[%ld] = { ", seqlen); + for (size_t i = 0; i < seqlen; i++) { + if (xkb_keysym_get_name(syms[i], buf, sizeof(buf)) > 0) + printf("<%s> ", buf); + else + printf("<?> "); + } + printf("}\n"); +} + +static int xkeymap_compose(struct xkeymap *xkeymap) +{ + int ret = 0; + struct xkb_compose_table_iterator *iter = xkb_compose_table_iterator_new(xkeymap->compose); + struct xkb_compose_table_entry *entry; + + if (!iter) { + kbd_warning(0, "xkb_compose_table_iterator_new failed"); + return -1; + } + + while ((entry = xkb_compose_table_iterator_next(iter))) { + size_t seqlen = 0; + const xkb_keysym_t *syms = xkb_compose_table_entry_sequence(entry, &seqlen); + xkb_keysym_t keysym = xkb_compose_table_entry_keysym(entry); + + if (is_debug(xkeymap, "1")) { + xkeymap_compose_printer(entry); + continue; + } + + if (keysym == XKB_KEY_NoSymbol) + continue; + + if (seqlen == 2) { + struct lk_kbdiacr ptr; + int code; + + if ((code = xkeymap_get_code(xkeymap, syms[0])) < 0 || + !used_code(xkeymap, code)) + continue; + + ptr.diacr = (unsigned int) code; + + if ((code = xkeymap_get_code(xkeymap, syms[1])) < 0 || + !used_code(xkeymap, code)) + continue; + + ptr.base = (unsigned int) code; + + if ((code = xkeymap_get_code(xkeymap, keysym)) < 0 || + used_code(xkeymap, code)) + continue; + + ptr.result = (unsigned int) code; + + if (is_debug(xkeymap, "2")) { + xkeymap_compose_printer(entry); + continue; + } + + ret = lk_append_compose(xkeymap->ctx, &ptr); + + if (ret == -1) + break; + } + } + + xkb_compose_table_iterator_free(iter); + + return ret; +} + int convert_xkb_keymap(struct lk_ctx *ctx, struct xkeymap_params *params, int options) { FILE *fp; @@ -528,6 +626,7 @@ int convert_xkb_keymap(struct lk_ctx *ctx, struct xkeymap_params *params, int op }; xkeymap.ctx = ctx; + xkeymap.debug = getenv("LK_XKB_DEBUG"); lk_set_keywords(ctx, LK_KEYWORD_ALTISMETA | LK_KEYWORD_STRASUSUAL); @@ -548,6 +647,14 @@ int convert_xkb_keymap(struct lk_ctx *ctx, struct xkeymap_params *params, int op goto end; } + if (params->locale) { + xkeymap.compose = xkb_compose_table_new_from_locale(xkeymap.xkb, params->locale, XKB_COMPOSE_COMPILE_NO_FLAGS); + if (!xkeymap.compose) { + kbd_warning(0, "xkb_compose_table_new_from_locale failed"); + goto end; + } + } + if ((fp = fopen(DATADIR "/xkbtrans/names", "r")) != NULL) { if (parsemap(&xkeymap, fp) < 0) { kbd_warning(0, "unable to parse xkb translation names"); @@ -557,10 +664,18 @@ int convert_xkb_keymap(struct lk_ctx *ctx, struct xkeymap_params *params, int op } if (!(ret = xkeymap_walk(&xkeymap))) { - if (options & OPT_P) + if (!is_debug(&xkeymap, NULL) && (options & OPT_P)) lk_dump_keymap(ctx, stdout, LK_SHAPE_SEPARATE_LINES, 0); } + if (xkeymap.compose) { + if (xkeymap_compose(&xkeymap) < 0) { + ret = -1; + goto end; + } + if (!is_debug(&xkeymap, NULL) && (options & OPT_P)) + lk_dump_diacs(ctx, stdout); + } end: if (xkeymap.used_codes) tdestroy(xkeymap.used_codes, free); @@ -568,6 +683,9 @@ end: if (xkeymap.syms_map) tdestroy(xkeymap.syms_map, xkeymap_pair_free); + if (xkeymap.compose) + xkb_compose_table_unref(xkeymap.compose); + xkb_keymap_unref(xkeymap.keymap); xkb_context_unref(xkeymap.xkb); diff --git a/src/xkbsupport.h b/src/xkbsupport.h index e12e39f6..3d424395 100644 --- a/src/xkbsupport.h +++ b/src/xkbsupport.h @@ -2,6 +2,7 @@ #define _XKB_SUPPORT_H_ #include <xkbcommon/xkbcommon.h> +#include <xkbcommon/xkbcommon-compose.h> #include "keymap.h" struct xkeymap_params { @@ -9,6 +10,7 @@ struct xkeymap_params { const char *layout; const char *variant; const char *options; + const char *locale; }; int convert_xkb_keymap(struct lk_ctx *ctx, struct xkeymap_params *params, int options); -- 2.43.0