[PATCH 1/4] Add keymap generation from the xkb database
Alexey Gladkov <[email protected]> Thu, 4 Jan 2024 18:15:15 +0000
| Newsgroups | dev.linux.lists.kbd |
|---|---|
| Message-ID | <276e4450193ad26c5b3536d8421378629816edb2.1704392039.git.legion@kernel.org> |
Xorg/Wayland uses its own keyboard configuration mechanism. Because of this, the configuration of the text console and Xorg layouts may differ. Not all combinations of Xorg layouts have a keymap in the kbd database. It's inconvenient for users. To solve this we can try converting the Xorg layout to a text console keymap. To access the XKB (X Keyboard Extension) database, you can use libxkbcommon, which does not depend on the X11 libraries. libxkbcommon is a keyboard keymap compiler and support library which processes a reduced subset of keymaps as defined by the XKB specification [1]. libxkbcommon does not distribute a keymap dataset itself, other than for testing purposes. The most common dataset is xkeyboard-config, which is used by all current distributions for their X11 XKB data [2]. There is a project that uses a similar approach to unify configurations. This is console-setup [3]. I talked with Anton Zinoviev and he helped me figure out their idea of switching multiple layouts with a single key combination: > how you solved the problem of switching more than two layouts with one > key combination ? The keys with ISO_Next_Group, for example the right Alt (keynumber 100), are defined like this: keycode 100 = ShiftL_lock ShiftL keycode 100 = ShiftR_lock ShiftR keycode 100 = ShiftR_lock ShiftL ShiftR keycode 100 = ShiftL_lock So this key will work in the following way: no modifiers are active -> ShiftL is active ShiftL is active -> ShiftL and ShiftR are active ShiftL and ShiftR are active -> ShiftR is active ShiftR is active -> no modifiers are active The groups (the layouts) are placed in the following way: active modifiers 1 group 2 groups 3 groups 4 groups ---------------- ------- -------- -------- -------- no modifier group 1 group 1 group 1 group 1 ShiftL group 1 group 2 group 2 group 2 ShiftR group 1 group 2 group 3 group 4 ShiftL+ShiftR group 1 group 1 group 1 group 3 In this way we have the following cycles: 1 group: gr1->gr1->gr1->... 2 groups: gr1->gr2->gr1->gr2->... 3 groups: gr1->gr2->gr1->gr4->gr1->gr2->gr1->gr4->... 4 groups: gr1->gr2->gr3->gr4->gr1->gr2->gr3->gr4->... -- Anton Zinoviev The difference with this implementation is that I am not trying to read and parse the XKB base files. I analyze the resulting xkb keymap. [1] https://xkbcommon.org/doc/current/index.html [2] https://www.freedesktop.org/wiki/Software/XKeyboardConfig/ [3] https://salsa.debian.org/installer-team/console-setup.git Signed-off-by: Alexey Gladkov <[email protected]> --- configure.ac | 10 + src/Makefile.am | 6 + src/loadkeys.c | 64 +++++- src/loadkeys.h | 13 ++ src/xkbsupport.c | 513 +++++++++++++++++++++++++++++++++++++++++++++++ src/xkbsupport.h | 16 ++ 6 files changed, 613 insertions(+), 9 deletions(-) create mode 100644 src/loadkeys.h create mode 100644 src/xkbsupport.c create mode 100644 src/xkbsupport.h diff --git a/configure.ac b/configure.ac index a4be8383..f57317e5 100644 --- a/configure.ac +++ b/configure.ac @@ -179,6 +179,15 @@ AM_CONDITIONAL(BUILD_TESTS, test "$build_tests" != "no") AC_CHECK_PROG([HAVE_DOXYGEN], [doxygen], [yes], [no]) AM_CONDITIONAL(HAVE_DOXYGEN, test "$HAVE_DOXYGEN" = "yes") +AC_ARG_ENABLE([xkb], + [AS_HELP_STRING([--enable-xkb], + [allow to generate kernel keymaps based on xkb database @<:@default=yes@:>@])], + [USE_XKB=$enableval],[USE_XKB=yes]) +AS_IF([test "x$USE_XKB" != xno], + [PKG_CHECK_MODULES(XKBCOMMON, xkbcommon, [HAVE_XKBCOMMON=yes], [HAVE_XKBCOMMON=no])], + [USE_XKB=no]) +AM_CONDITIONAL(USE_XKB, test "$USE_XKB" = "yes") + AC_CONFIG_FILES([Makefile data/Makefile docs/Makefile @@ -228,4 +237,5 @@ AC_MSG_RESULT([ libkbdfile: ${BUILD_LIBKBDFILE} libkeymap: ${BUILD_LIBKEYMAP} libkfont: ${BUILD_LIBKFONT} + xkb support: ${USE_XKB} ]) diff --git a/src/Makefile.am b/src/Makefile.am index c495883d..7e4757e5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -78,6 +78,12 @@ LDADD = \ @LIBINTL@ \ $(CODE_COVERAGE_LIBS) +if USE_XKB +loadkeys_SOURCES += xkbsupport.c +loadkeys_CFLAGS = -DUSE_XKB @XKBCOMMON_CFLAGS@ +LDADD += @XKBCOMMON_LIBS@ +endif + install-exec-hook: for i in psfaddtable psfgettable psfstriptable; do \ rm -f $(DESTDIR)$(bindir)/$$i; \ diff --git a/src/loadkeys.c b/src/loadkeys.c index 0176c518..d0178c77 100644 --- a/src/loadkeys.c +++ b/src/loadkeys.c @@ -25,6 +25,12 @@ #include "paths.h" #include "keymap.h" +#include "loadkeys.h" + +#ifdef USE_XKB +#include "xkbsupport.h" +#endif + static const char *const dirpath1[] = { DATADIR "/" KEYMAPDIR "/**", KERNDIR "/", @@ -90,6 +96,16 @@ int main(int argc, char *argv[]) struct kbdfile_ctx *fctx; struct kbdfile *fp = NULL; +#ifdef USE_XKB + struct xkeymap_params xkeymap_params = { + .model = "pc104", + .layout = NULL, + .variant = NULL, + .options = NULL + }; + int use_xkb = 0; +#endif + set_progname(argv[0]); setuplocale(); @@ -108,9 +124,22 @@ int main(int argc, char *argv[]) { "quiet", no_argument, NULL, 'q' }, { "verbose", no_argument, NULL, 'v' }, { "version", no_argument, NULL, 'V' }, +#ifdef USE_XKB + //{ "xkb-rules", required_argument, NULL, 1 }, + { "xkb-model", required_argument, NULL, 2 }, + { "xkb-layout", required_argument, NULL, 3 }, + { "xkb-variant", required_argument, NULL, 4 }, + { "xkb-options", required_argument, NULL, 5 }, +#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.") }, +#endif { "-C, --console=DEV", _("the console device to be used.") }, { "-a, --ascii", _("force conversion to ASCII.") }, { "-b, --bkeymap", _("output a binary keymap to stdout.") }, @@ -127,15 +156,6 @@ int main(int argc, char *argv[]) { NULL, NULL } }; - enum options { - OPT_A = (1 << 1), - OPT_B = (1 << 2), - OPT_D = (1 << 3), - OPT_M = (1 << 4), - OPT_U = (1 << 5), - OPT_P = (1 << 6) - }; - ctx = lk_init(); if (!ctx) { exit(EXIT_FAILURE); @@ -146,6 +166,24 @@ int main(int argc, char *argv[]) while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { switch (c) { +#ifdef USE_XKB + case 2: + xkeymap_params.model = optarg; + use_xkb = 1; + break; + case 3: + xkeymap_params.layout = optarg; + use_xkb = 1; + break; + case 4: + xkeymap_params.variant = optarg; + use_xkb = 1; + break; + case 5: + xkeymap_params.options = optarg; + use_xkb = 1; + break; +#endif case 'a': options |= OPT_A; break; @@ -249,6 +287,14 @@ int main(int argc, char *argv[]) if (rc == -1) goto fail; +#ifdef USE_XKB + } else if (use_xkb) { + rc = convert_xkb_keymap(ctx, &xkeymap_params, options); + + if (rc == -1) + goto fail; + +#endif } else if (optind == argc) { if (!(fp = kbdfile_new(fctx))) kbd_error(EXIT_FAILURE, 0, _("Unable to create kbdfile instance: %m")); diff --git a/src/loadkeys.h b/src/loadkeys.h new file mode 100644 index 00000000..d6ff6e7c --- /dev/null +++ b/src/loadkeys.h @@ -0,0 +1,13 @@ +#ifndef __LOADKEYS_H__ +#define __LOADKEYS_H__ + +enum options { + OPT_A = (1 << 1), + OPT_B = (1 << 2), + OPT_D = (1 << 3), + OPT_M = (1 << 4), + OPT_U = (1 << 5), + OPT_P = (1 << 6) +}; + +#endif /* __LOADKEYS_H__ */ diff --git a/src/xkbsupport.c b/src/xkbsupport.c new file mode 100644 index 00000000..694d516f --- /dev/null +++ b/src/xkbsupport.c @@ -0,0 +1,513 @@ +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +#include "libcommon.h" +#include "keymap.h" +#include "loadkeys.h" +#include "xkbsupport.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +static const int layout_switch[] = { + 0, + (1 << KG_SHIFTL), + (1 << KG_SHIFTR), + (1 << KG_SHIFTL) | (1 << KG_SHIFTR) +}; + +/* + * number | + * of groups | group + * -------------------------- + * 0 | { 0, 0, 0, 0 } + * 1 | { 0, 1, 1, 0 } + * 2 | { 0, 1, 2, 0 } + * 3 | { 0, 1, 3, 2 } + */ +static const unsigned int layouts[4][4] = { + { 0, 0, 0, 0 }, + { 0, 1, 1, 0 }, + { 0, 1, 2, 0 }, + { 0, 1, 3, 2 }, +}; + +struct xkeymap { + struct xkb_context *xkb; + struct xkb_keymap *keymap; + struct lk_ctx *ctx; +}; + +/* + * Offset between evdev keycodes (where KEY_ESCAPE is 1), and the evdev XKB + * keycode set (where ESC is 9). + */ +#define EVDEV_OFFSET 8 + +/* + * Convert xkb keycode to kernel keycode. + */ +#define KERN_KEYCODE(keycode) (keycode - EVDEV_OFFSET) + +/* + * Copied from libxkbcommon. + * Don't allow more modifiers than we can hold in xkb_mod_mask_t. + */ +#define XKB_MAX_MODS ((xkb_mod_index_t)(sizeof(xkb_mod_mask_t) * 8)) + +struct xkb_mask { + xkb_mod_mask_t mask[XKB_MAX_MODS]; + size_t num; +}; + +struct modifier_mapping { + const char *xkb_mod; + const char *krn_mod; + const int bit; +}; + +static struct modifier_mapping modifier_mapping[] = { + { "Shift", "shift", (1u << KG_SHIFT) }, + { "Control", "control", (1u << KG_CTRL) }, + { "Lock", "???", 0 }, + { "Mod1", "alt", (1u << KG_ALT) }, + { "Mod2", "???", 0 }, + { "Mod3", "???", 0 }, + { "Mod4", "???", 0 }, + { "Mod5", "altgr", (1u << KG_ALT) }, + { NULL, NULL, 0 }, +}; + +static struct modifier_mapping *convert_modifier(const char *xkb_name) +{ + struct modifier_mapping *map = modifier_mapping; + + while (map->xkb_mod) { + if (!strcasecmp(map->xkb_mod, xkb_name)) + return map; + map++; + } + + return NULL; +} + +struct symbols_mapping { + const char *xkb_sym; + const char *krn_sym; +}; + +static struct symbols_mapping symbols_mapping[] = { + { "0", "zero" }, + { "1", "one" }, + { "2", "two" }, + { "3", "three" }, + { "4", "four" }, + { "5", "five" }, + { "6", "six" }, + { "7", "seven" }, + { "8", "eight" }, + { "9", "nine" }, + { "KP_Insert", "KP_0" }, + { "KP_End", "KP_1" }, + { "KP_Down", "KP_2" }, + { "KP_Next", "KP_3" }, + { "KP_Left", "KP_4" }, + { "KP_Right", "KP_6" }, + { "KP_Home", "KP_7" }, + { "KP_Up", "KP_8" }, + { "KP_Prior", "KP_9" }, + { "KP_Begin", "VoidSymbol" }, + { "KP_Delete", "VoidSymbol" }, + { "Alt_R", "Alt" }, + { "Alt_L", "Alt" }, + { "Control_R", "Control" }, + { "Control_L", "Control" }, + { "Super_R", "Alt" }, + { "Super_L", "Alt" }, + { "Hyper_R", "Alt" }, + { "Hyper_L", "Alt" }, + { "Mode_switch", "AltGr" }, + { "ISO_Group_Shift", "AltGr" }, + { "ISO_Group_Latch", "AltGr" }, + { "ISO_Group_Lock", "AltGr_Lock" }, + { "ISO_Next_Group", "AltGr_Lock" }, + { "ISO_Next_Group_Lock", "AltGr_Lock" }, + { "ISO_Prev_Group", "AltGr_Lock" }, + { "ISO_Prev_Group_Lock", "AltGr_Lock" }, + { "ISO_First_Group", "AltGr_Lock" }, + { "ISO_First_Group_Lock", "AltGr_Lock" }, + { "ISO_Last_Group", "AltGr_Lock" }, + { "ISO_Last_Group_Lock", "AltGr_Lock" }, + { "ISO_Level3_Shift", "AltGr" }, + { "ISO_Left_Tab", "Meta_Tab" }, + { "XF86Switch_VT_1", "Console_1" }, + { "XF86Switch_VT_2", "Console_2" }, + { "XF86Switch_VT_3", "Console_3" }, + { "XF86Switch_VT_4", "Console_4" }, + { "XF86Switch_VT_5", "Console_5" }, + { "XF86Switch_VT_6", "Console_6" }, + { "XF86Switch_VT_7", "Console_7" }, + { "XF86Switch_VT_8", "Console_8" }, + { "XF86Switch_VT_9", "Console_9" }, + { "XF86Switch_VT_10", "Console_10" }, + { "XF86Switch_VT_11", "Console_11" }, + { "XF86Switch_VT_12", "Console_12" }, + { "Sys_Req", "Last_Console" }, + { "Print", "Control_backslash" }, + { NULL, NULL }, +}; + +static const char *map_xkbsym_to_ksym(const char *xkb_sym) +{ + struct symbols_mapping *map = symbols_mapping; + + while (map->xkb_sym) { + if (!strcmp(map->xkb_sym, xkb_sym)) + return map->krn_sym; + map++; + } + + return NULL; +} + +static void print_modifiers(struct xkb_keymap *keymap, struct xkb_mask *mask) +{ + xkb_mod_index_t num_mods = xkb_keymap_num_mods(keymap); + + for (size_t m = 0; m < mask->num; m++) { + for (xkb_mod_index_t mod = 0; mod < num_mods; mod++) { + if ((mask->mask[m] & (1u << mod)) == 0) + continue; + + const char *modname = xkb_keymap_mod_get_name(keymap, mod); + + printf("%ld:%s(%d) ", m, modname, mod); + } + } +} + +static void xkeymap_keycode_mask(struct xkb_keymap *keymap, + xkb_layout_index_t layout, xkb_level_index_t level, xkb_keycode_t keycode, + struct xkb_mask *out) +{ + memset(out->mask, 0, sizeof(out->mask)); + out->num = xkb_keymap_key_get_mods_for_level(keymap, keycode, layout, level, out->mask, ARRAY_SIZE(out->mask)); +} + +static void xkeymap_walk_printer(struct xkeymap *xkeymap, + xkb_layout_index_t layout, xkb_level_index_t level, + xkb_keycode_t keycode, xkb_keysym_t sym) +{ + switch (sym) { + case XKB_KEY_ISO_Next_Group: + printf("* "); + break; + default: + printf(" "); + break; + } + + printf("{%-12s} %d ", xkb_keymap_layout_get_name(xkeymap->keymap, layout), layout); + printf("%d ", level); + + char s[255]; + const char *symname = NULL; + int ret, kunicode; + uint32_t unicode = xkb_keysym_to_utf32(sym); + + ret = xkb_keysym_get_name(sym, s, sizeof(s)); + + if (ret < 0 || (size_t)ret >= sizeof(s)) { + kbd_warning(0, "failed to get name of keysym"); + return; + } + + if (!(symname = map_xkbsym_to_ksym(s))) + symname = s; + + if (unicode == 0) { + if ((kunicode = lk_ksym_to_unicode(xkeymap->ctx, symname)) < 0) { + printf("keycode %3d = %s", KERN_KEYCODE(keycode), symname); + } else { + unicode = (uint32_t) kunicode; + } + } + + if (unicode > 0) + printf("keycode %3d = U+%04x %-32s", KERN_KEYCODE(keycode), unicode, symname); + + printf(" ( "); + + struct xkb_mask keycode_mask; + xkeymap_keycode_mask(xkeymap->keymap, layout, level, keycode, &keycode_mask); + + if (keycode_mask.num > 0) { + xkb_mod_index_t num_mods = xkb_keymap_num_mods(xkeymap->keymap); + + for (xkb_mod_index_t mod = 0; mod < num_mods; mod++) { + if ((keycode_mask.mask[0] & (1u << mod)) == 0) + continue; + + const struct modifier_mapping *map = convert_modifier(xkb_keymap_mod_get_name(xkeymap->keymap, mod)); + + printf("%s ", map->krn_mod); + } + } + + printf(") ( "); + print_modifiers(xkeymap->keymap, &keycode_mask); + printf(")\n"); +} + +static int parse_hexcode(const char *symname) +{ + int unicode; + size_t ret = strlen(symname); + + if (ret >= 4 && symname[0] == '0' && symname[1] == 'x') { + errno = 0; + unicode = (int) strtol(symname + 2, NULL, 16); + if (errno == ERANGE) { + kbd_warning(0, "unable to convert unnamed non-Unicode xkb symbol `%s'", symname); + return -1; + } + return unicode; + } + + if (ret >= 5 && symname[0] == 'U' && + isxdigit(symname[1]) && isxdigit(symname[2]) && + isxdigit(symname[3]) && isxdigit(symname[4])) { + errno = 0; + unicode = (int) strtol(symname + 1, NULL, 16); + if (errno == ERANGE) { + kbd_warning(0, "unable to convert unnamed unicode xkb symbol `%s'", symname); + return -1; + } + return unicode; + } + + return 0; +} + +static int xkeymap_get_code(struct lk_ctx *ctx, xkb_keysym_t symbol) +{ + uint32_t xkb_unicode; + int ret, unicode; + const char *symname; + char symbuf[BUFSIZ]; + + symbuf[0] = 0; + + ret = xkb_keysym_get_name(symbol, symbuf, sizeof(symbuf)); + + if (ret < 0 || (size_t)ret >= sizeof(symbuf)) { + kbd_warning(0, "failed to get name of keysym"); + return K_HOLE; + } + + if ((unicode = parse_hexcode(symbuf)) != 0) { + ; + } else if ((symname = map_xkbsym_to_ksym(symbuf)) != NULL) { + unicode = parse_hexcode(symname); + if (!unicode) + unicode = lk_ksym_to_unicode(ctx, symname); + + } else if ((xkb_unicode = xkb_keysym_to_utf32(symbol)) > 0) { + unicode = (int) xkb_unicode; + } else { + unicode = K_HOLE; + } + + ret = CODE_FOR_UNKNOWN_KSYM; + + if (unicode > 0) + ret = lk_convert_code(ctx, unicode, TO_AUTO); + + if (ret == CODE_FOR_UNKNOWN_KSYM) { + kbd_warning(0, "unable to convert code <%s> and replace it with VoidSymbol", symbuf); + ret = K_HOLE; + } + + return ret; +} + +static int xkeymap_get_symbol(struct xkb_keymap *keymap, + xkb_keycode_t keycode, xkb_layout_index_t layout, xkb_level_index_t level, + xkb_keysym_t *symbol) +{ + int num_syms; + const xkb_keysym_t *syms = NULL; + + if (!(num_syms = xkb_keymap_key_get_syms_by_level(keymap, keycode, layout, level, &syms))) + return 0; + + if (num_syms > 1) { + kbd_warning(0, "fixme: more %d symbol on level", num_syms); + return -1; + } + + *symbol = syms[0]; + return 1; +} + +static void xkeymap_add_value(struct xkeymap *xkeymap, int modifier, int code, int keyvalue[MAX_NR_KEYMAPS]) +{ + if (!modifier || (modifier & (1 << KG_SHIFT))) + code = lk_add_capslock(xkeymap->ctx, code); + + keyvalue[modifier] = code; +} + +static int xkeymap_walk(struct xkeymap *xkeymap) +{ + struct xkb_mask keycode_mask; + xkb_mod_index_t num_mods = xkb_keymap_num_mods(xkeymap->keymap); + xkb_keycode_t min_keycode = xkb_keymap_min_keycode(xkeymap->keymap); + xkb_keycode_t max_keycode = xkb_keymap_max_keycode(xkeymap->keymap); + + if (KERN_KEYCODE(min_keycode) >= NR_KEYS) { + kbd_warning(0, "keymap defines more keycodes than the kernel can handle."); + min_keycode = (NR_KEYS - 1) + EVDEV_OFFSET; + } + + if (KERN_KEYCODE(max_keycode) >= NR_KEYS) { + kbd_warning(0, "keymap defines more keycodes than the kernel can handle."); + max_keycode = (NR_KEYS - 1) + EVDEV_OFFSET; + } + + xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(xkeymap->keymap); + + for (xkb_keycode_t keycode = min_keycode; keycode <= max_keycode; keycode++) { + int keyvalue[MAX_NR_KEYMAPS]; + + memset(keyvalue, 0, sizeof(keyvalue)); + + for (xkb_layout_index_t layout = 0; layout < num_layouts; layout++) { + xkb_level_index_t num_levels = xkb_keymap_num_levels_for_key(xkeymap->keymap, keycode, layout); + + for (xkb_level_index_t level = 0; level < num_levels; level++) { + xkb_keysym_t sym; + int ret, value; + + if (!(ret = xkeymap_get_symbol(xkeymap->keymap, keycode, layout, level, &sym))) + continue; + else if (ret < 0) + goto err; + + if (getenv("LK_XKB_DEBUG")) { + 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"); + int shiftr_lock = lk_ksym_to_unicode(xkeymap->ctx, "ShiftR_Lock"); + + xkeymap_add_value(xkeymap, layout_switch[0], shiftl_lock, keyvalue); + xkeymap_add_value(xkeymap, layout_switch[1], shiftr_lock, keyvalue); + xkeymap_add_value(xkeymap, layout_switch[2], shiftr_lock, keyvalue); + xkeymap_add_value(xkeymap, layout_switch[3], shiftl_lock, keyvalue); + + goto process_keycode; + } + + if ((value = xkeymap_get_code(xkeymap->ctx, sym)) < 0) + continue; + + xkeymap_keycode_mask(xkeymap->keymap, layout, level, keycode, &keycode_mask); + + for (unsigned short i = 0; i < ARRAY_SIZE(layout_switch); i++) { + const struct modifier_mapping *map; + int modifier = layout_switch[i]; + + if (layout != layouts[num_layouts - 1][i]) + continue; + + for (xkb_mod_index_t mod = 0; mod < num_mods; mod++) { + if (!(keycode_mask.mask[0] & (1u << mod))) + continue; + + map = convert_modifier(xkb_keymap_mod_get_name(xkeymap->keymap, mod)); + if (map) + modifier |= map->bit; + } + + xkeymap_add_value(xkeymap, modifier, value, keyvalue); + } + } + } + + if (getenv("LK_XKB_DEBUG")) + continue; + +process_keycode: + unsigned short layout = 0; + + for (unsigned short i = 0; i < ARRAY_SIZE(keyvalue); i++) { + if (layout < ARRAY_SIZE(layout_switch) && i == layout_switch[layout + 1]) + layout++; + + if (!keyvalue[i]) { + if ((i & (1 << KG_SHIFT))) + keyvalue[i] = keyvalue[layout_switch[layout] | (1 << KG_SHIFT)]; + if (!keyvalue[i]) + keyvalue[i] = keyvalue[layout_switch[layout]]; + } + + if (lk_add_key(xkeymap->ctx, i, (int) KERN_KEYCODE(keycode), keyvalue[i]) < 0) + goto err; + } + } + + return 0; +err: + return -1; +} + + +int convert_xkb_keymap(struct lk_ctx *ctx, struct xkeymap_params *params, int options) +{ + struct xkeymap xkeymap; + int ret = -1; + + struct xkb_rule_names names = { + .rules = "evdev", + .model = params->model, + .layout = params->layout, + .variant = params->variant, + .options = params->options, + }; + + xkeymap.ctx = ctx; + + lk_set_keywords(ctx, LK_KEYWORD_ALTISMETA | LK_KEYWORD_STRASUSUAL); + + xkeymap.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!xkeymap.xkb) { + kbd_warning(0, "xkb_context_new failed"); + goto end; + } + + xkeymap.keymap = xkb_keymap_new_from_names(xkeymap.xkb, &names, XKB_KEYMAP_COMPILE_NO_FLAGS); + if (!xkeymap.keymap) { + kbd_warning(0, "xkb_keymap_new_from_names failed"); + goto end; + } + + if (xkb_keymap_num_layouts(xkeymap.keymap) > ARRAY_SIZE(layout_switch)) { + kbd_warning(0, "too many layouts specified. At the moment, you can use no more than %ld", ARRAY_SIZE(layout_switch)); + goto end; + } + + if (!(ret = xkeymap_walk(&xkeymap))) { + if (options & OPT_P) + lk_dump_keymap(ctx, stdout, LK_SHAPE_SEPARATE_LINES, 0); + } + +end: + xkb_keymap_unref(xkeymap.keymap); + xkb_context_unref(xkeymap.xkb); + + return ret; +} diff --git a/src/xkbsupport.h b/src/xkbsupport.h new file mode 100644 index 00000000..e12e39f6 --- /dev/null +++ b/src/xkbsupport.h @@ -0,0 +1,16 @@ +#ifndef _XKB_SUPPORT_H_ +#define _XKB_SUPPORT_H_ + +#include <xkbcommon/xkbcommon.h> +#include "keymap.h" + +struct xkeymap_params { + const char *model; + const char *layout; + const char *variant; + const char *options; +}; + +int convert_xkb_keymap(struct lk_ctx *ctx, struct xkeymap_params *params, int options); + +#endif /* _XKB_SUPPORT_H_ */ -- 2.43.0