* [PATCH 1/4] Add keymap generation from the xkb database
2024-01-04 18:15 [PATCH 0/4] Allow to convert xkb keymap to kernel keymap Alexey Gladkov
@ 2024-01-04 18:15 ` Alexey Gladkov
2024-01-04 18:15 ` [PATCH 2/4] xkbsupport: Create a list of used codes Alexey Gladkov
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Alexey Gladkov @ 2024-01-04 18:15 UTC (permalink / raw)
To: kbd
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 <legion@kernel.org>
---
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
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 3/4] xkbsupport: Extract translation table
2024-01-04 18:15 [PATCH 0/4] Allow to convert xkb keymap to kernel keymap Alexey Gladkov
2024-01-04 18:15 ` [PATCH 1/4] Add keymap generation from the xkb database Alexey Gladkov
2024-01-04 18:15 ` [PATCH 2/4] xkbsupport: Create a list of used codes Alexey Gladkov
@ 2024-01-04 18:15 ` Alexey Gladkov
2024-01-04 18:15 ` [PATCH 4/4] xkbsupport: Add xkb compose Alexey Gladkov
3 siblings, 0 replies; 5+ messages in thread
From: Alexey Gladkov @ 2024-01-04 18:15 UTC (permalink / raw)
To: kbd
This will allow us to expand the xkb to kbd keys translation table.
Signed-off-by: Alexey Gladkov <legion@kernel.org>
---
data/xkbtrans/names | 154 ++++++++++++++++++++++++++++++++++++++
src/xkbsupport.c | 176 ++++++++++++++++++++++++--------------------
2 files changed, 251 insertions(+), 79 deletions(-)
create mode 100644 data/xkbtrans/names
diff --git a/data/xkbtrans/names b/data/xkbtrans/names
new file mode 100644
index 00000000..f39e8456
--- /dev/null
+++ b/data/xkbtrans/names
@@ -0,0 +1,154 @@
+0 zero
+1 one
+2 two
+3 three
+4 four
+5 five
+6 six
+7 seven
+8 eight
+9 nine
+Alt_L Alt
+Alt_R Alt
+Begin Begin
+Break Break
+Control_L Control
+Control_R Control
+Down Down
+End End
+F1 F1
+F10 F10
+F11 F11
+F12 F12
+F13 F13
+F14 F14
+F15 F15
+F16 F16
+F17 F17
+F18 F18
+F19 F19
+F2 F2
+F20 F20
+F21 F21
+F22 F22
+F23 F23
+F24 F24
+F25 F25
+F26 F26
+F27 F27
+F28 F28
+F29 F29
+F3 F3
+F30 F30
+F31 F31
+F32 F32
+F33 F33
+F34 F34
+F35 F35
+F4 F4
+F5 F5
+F6 F6
+F7 F7
+F8 F8
+F9 F9
+Find Find
+Help Help
+Home Home
+Hyper_L Alt
+Hyper_R Alt
+ISO_First_Group AltGr_Lock
+ISO_First_Group_Lock AltGr_Lock
+ISO_Group_Latch AltGr
+ISO_Group_Lock AltGr_Lock
+ISO_Group_Shift AltGr
+ISO_Last_Group AltGr_Lock
+ISO_Last_Group_Lock AltGr_Lock
+ISO_Left_Tab Meta_Tab
+ISO_Level2_Latch Shift
+ISO_Level3_Latch AltGr
+ISO_Level3_Lock AltGr_Lock
+ISO_Level3_Shift AltGr
+ISO_Level5_Latch AltGr
+ISO_Level5_Lock AltGr_Lock
+ISO_Level5_Shift AltGr
+ISO_Lock Caps_Lock
+ISO_Next_Group AltGr_Lock
+ISO_Next_Group_Lock AltGr_Lock
+ISO_Prev_Group AltGr_Lock
+ISO_Prev_Group_Lock AltGr_Lock
+Insert Insert
+KP_Down KP_2
+KP_End KP_1
+KP_Home KP_7
+KP_Insert KP_0
+KP_Left KP_4
+KP_Next KP_3
+KP_Prior KP_9
+KP_Right KP_6
+KP_Up KP_8
+L1 F11
+L10 F20
+L2 F12
+L3 F13
+L4 F14
+L5 F15
+L6 F16
+L7 F17
+L8 F18
+L9 F19
+Left Left
+Meta_L Alt
+Meta_R Alt
+Mode_switch AltGr
+Multi_key Compose
+Next Next
+Num_Lock Num_Lock
+Pause Pause
+Print Control_backslash
+Prior Prior
+R1 F21
+R10 F30
+R11 F31
+R12 F32
+R13 F33
+R14 F34
+R15 F35
+R2 F22
+R3 F23
+R4 F24
+R5 F25
+R6 F26
+R7 F27
+R8 F28
+R9 F29
+Right Right
+Scroll_Lock Scroll_Lock
+Shift_L Shift
+Shift_R Shift
+Super_L Alt
+Super_R Alt
+Sys_Req Last_Console
+Up Up
+XF86Switch_VT_1 Console_1
+XF86Switch_VT_10 Console_10
+XF86Switch_VT_11 Console_11
+XF86Switch_VT_12 Console_12
+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
+dead_V dead_caron
+dead_acute dead_acute
+dead_breve dead_breve
+dead_caron dead_caron
+dead_cedilla dead_cedilla
+dead_circumflex dead_circumflex
+dead_diaeresis dead_diaeresis
+dead_doubleacute dead_doubleacute
+dead_grave dead_grave
+dead_ogonek dead_ogonek
+dead_tilde dead_tilde
diff --git a/src/xkbsupport.c b/src/xkbsupport.c
index 3caf8465..77abada0 100644
--- a/src/xkbsupport.c
+++ b/src/xkbsupport.c
@@ -36,11 +36,17 @@ static const unsigned int layouts[4][4] = {
{ 0, 1, 3, 2 },
};
+struct sym_pair {
+ char *xkb_sym;
+ char *krn_sym;
+};
+
struct xkeymap {
struct xkb_context *xkb;
struct xkb_keymap *keymap;
struct lk_ctx *ctx;
void *used_codes;
+ void *syms_map;
};
/*
@@ -83,6 +89,7 @@ static struct modifier_mapping modifier_mapping[] = {
{ NULL, NULL, 0 },
};
+
static struct modifier_mapping *convert_modifier(const char *xkb_name)
{
struct modifier_mapping *map = modifier_mapping;
@@ -96,82 +103,32 @@ static struct modifier_mapping *convert_modifier(const char *xkb_name)
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 void xkeymap_pair_free(void *pa)
+{
+ struct sym_pair *pair = pa;
+ if (!pair)
+ return;
+ free(pair->xkb_sym);
+ free(pair->krn_sym);
+ free(pair);
+}
-static const char *map_xkbsym_to_ksym(const char *xkb_sym)
+static int compare_by_xkbsym(const void *pa, const void *pb)
{
- struct symbols_mapping *map = symbols_mapping;
+ return strcmp(((struct sym_pair *)pa)->xkb_sym,
+ ((struct sym_pair *)pb)->xkb_sym);
+}
- while (map->xkb_sym) {
- if (!strcmp(map->xkb_sym, xkb_sym))
- return map->krn_sym;
- map++;
- }
+static const char *map_xkbsym_to_ksym(struct xkeymap *xkeymap, char *xkb_sym)
+{
+ struct sym_pair **val;
+ struct sym_pair pair = {
+ .xkb_sym = xkb_sym,
+ };
+ val = tfind(&pair, &xkeymap->syms_map, compare_by_xkbsym);
+ if (val)
+ return ((struct sym_pair *) *val)->krn_sym;
return NULL;
}
@@ -236,7 +193,7 @@ static void xkeymap_walk_printer(struct xkeymap *xkeymap,
return;
}
- if (!(symname = map_xkbsym_to_ksym(s)))
+ if (!(symname = map_xkbsym_to_ksym(xkeymap, s)))
symname = s;
if (unicode == 0) {
@@ -303,7 +260,7 @@ static int parse_hexcode(const char *symname)
return 0;
}
-static int xkeymap_get_code(struct lk_ctx *ctx, xkb_keysym_t symbol)
+static int xkeymap_get_code(struct xkeymap *xkeymap, xkb_keysym_t symbol)
{
uint32_t xkb_unicode;
int ret, unicode;
@@ -321,10 +278,10 @@ static int xkeymap_get_code(struct lk_ctx *ctx, xkb_keysym_t symbol)
if ((unicode = parse_hexcode(symbuf)) != 0) {
;
- } else if ((symname = map_xkbsym_to_ksym(symbuf)) != NULL) {
+ } else if ((symname = map_xkbsym_to_ksym(xkeymap, symbuf)) != NULL) {
unicode = parse_hexcode(symname);
if (!unicode)
- unicode = lk_ksym_to_unicode(ctx, symname);
+ unicode = lk_ksym_to_unicode(xkeymap->ctx, symname);
} else if ((xkb_unicode = xkb_keysym_to_utf32(symbol)) > 0) {
unicode = (int) xkb_unicode;
@@ -335,7 +292,7 @@ static int xkeymap_get_code(struct lk_ctx *ctx, xkb_keysym_t symbol)
ret = CODE_FOR_UNKNOWN_KSYM;
if (unicode > 0)
- ret = lk_convert_code(ctx, unicode, TO_AUTO);
+ ret = lk_convert_code(xkeymap->ctx, unicode, TO_AUTO);
if (ret == CODE_FOR_UNKNOWN_KSYM) {
kbd_warning(0, "unable to convert code <%s> and replace it with VoidSymbol", symbuf);
@@ -451,7 +408,7 @@ static int xkeymap_walk(struct xkeymap *xkeymap)
goto process_keycode;
}
- if ((value = xkeymap_get_code(xkeymap->ctx, sym)) < 0)
+ if ((value = xkeymap_get_code(xkeymap, sym)) < 0)
continue;
xkeymap_keycode_mask(xkeymap->keymap, layout, level, keycode, &keycode_mask);
@@ -506,10 +463,60 @@ err:
return -1;
}
+static int parsemap(struct xkeymap *xkeymap, FILE *fp)
+{
+ char buffer[BUFSIZ];
+ char *p, *q, *xkb_sym = NULL, *krn_sym = NULL;
+ struct sym_pair **val, *pair = NULL;
+
+ while (fgets(buffer, sizeof(buffer) - 1, fp)) {
+ for (p = buffer; isspace(*p); p++);
+
+ if (*p == '#' || *p == '\0')
+ continue;
+
+ for (q = p; *q != '\0' && !isspace(*q); q++);
+ *q = '\0';
+
+ xkb_sym = p;
+
+ if (p != q)
+ for(p = ++q; isspace(*p); p++);
+
+ for (q = p; *q != '\0' && !isspace(*q); q++);
+ *q = '\0';
+
+ krn_sym = p;
+
+ pair = calloc(1, sizeof(*pair));
+ if (!pair)
+ goto err;
+
+ pair->xkb_sym = strdup(xkb_sym);
+ pair->krn_sym = strdup(krn_sym);
+
+ if (!pair->xkb_sym || !pair->krn_sym)
+ goto err;
+
+ val = tsearch(pair, &xkeymap->syms_map, compare_by_xkbsym);
+ if (!val)
+ goto err;
+
+ if (*val != pair)
+ xkeymap_pair_free(pair);
+
+ pair = NULL;
+ }
+ return 0;
+err:
+ xkeymap_pair_free(pair);
+ return -1;
+}
int convert_xkb_keymap(struct lk_ctx *ctx, struct xkeymap_params *params, int options)
{
- struct xkeymap xkeymap;
+ FILE *fp;
+ struct xkeymap xkeymap = { 0 };
int ret = -1;
struct xkb_rule_names names = {
@@ -541,6 +548,14 @@ int convert_xkb_keymap(struct lk_ctx *ctx, struct xkeymap_params *params, int op
goto end;
}
+ if ((fp = fopen(DATADIR "/xkbtrans/names", "r")) != NULL) {
+ if (parsemap(&xkeymap, fp) < 0) {
+ kbd_warning(0, "unable to parse xkb translation names");
+ goto end;
+ }
+ fclose(fp);
+ }
+
if (!(ret = xkeymap_walk(&xkeymap))) {
if (options & OPT_P)
lk_dump_keymap(ctx, stdout, LK_SHAPE_SEPARATE_LINES, 0);
@@ -550,6 +565,9 @@ end:
if (xkeymap.used_codes)
tdestroy(xkeymap.used_codes, free);
+ if (xkeymap.syms_map)
+ tdestroy(xkeymap.syms_map, xkeymap_pair_free);
+
xkb_keymap_unref(xkeymap.keymap);
xkb_context_unref(xkeymap.xkb);
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 4/4] xkbsupport: Add xkb compose
2024-01-04 18:15 [PATCH 0/4] Allow to convert xkb keymap to kernel keymap Alexey Gladkov
` (2 preceding siblings ...)
2024-01-04 18:15 ` [PATCH 3/4] xkbsupport: Extract translation table Alexey Gladkov
@ 2024-01-04 18:15 ` Alexey Gladkov
3 siblings, 0 replies; 5+ messages in thread
From: Alexey Gladkov @ 2024-01-04 18:15 UTC (permalink / raw)
To: kbd
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 <legion@kernel.org>
---
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
^ permalink raw reply related [flat|nested] 5+ messages in thread