Allow to convert xkb keymap to kernel keymap
cover Allow to convert xkb keymap to kernel keymap
This patchset adds the ability to use keymaps from libxkbcommon. It gives the loadkeys utility additional options for passing xkb-specific parameters.
The idea is to make it possible to unify the keymap configuration between linux console and xorg/wayland and unification of the keymaps configuration method.
Unfortunately, we cannot use xkb keymap one to one. The linux kernel has a number of limitations that allow the use of xkb keymaps.
Many thanks to Anton Zinoviev who helped me figure out how console-setup solved the problems of converting xkb to linux keymaps.
Alexey Gladkov (4):
Add keymap generation from the xkb database
xkbsupport: Create a list of used codes
xkbsupport: Extract translation table
xkbsupport: Add xkb compose
configure.ac | 10 + data/xkbtrans/names | 154 ++++++++++ src/Makefile.am | 6 + src/loadkeys.c | 80 ++++- src/loadkeys.h | 13 + src/xkbsupport.c | 693 ++++++++++++++++++++++++++++++++++++++++++++ src/xkbsupport.h | 18 ++ 7 files changed, 965 insertions(+), 9 deletions(-) create mode 100644 data/xkbtrans/names create mode 100644 src/loadkeys.h create mode 100644 src/xkbsupport.c create mode 100644 src/xkbsupport.h
Signature · 1 line
2.43.0
PATCH 1/4 Add keymap generation from the xkb database
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:
Quoted 2 lines
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
--- 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
configure.ac +10 -0
@@ -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} ])
src/Makefile.am +6 -0
@@ -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; \
src/loadkeys.c +55 -9
103 lines not on this page · raw message
src/loadkeys.h new file +13 -0
@@ -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__ */
src/xkbsupport.c new file +513 -0
513 lines not on this page · raw message
src/xkbsupport.h new file +16 -0
@@ -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_ */
Signature · 1 line
2.43.0
PATCH 2/4 xkbsupport: Create a list of used codes
In reply to Alexey GladkovThis is preparation with the following changes. We create a binary search tree with all the unicodes that are used in the generated keymap so that we can later check whether the unicode symbol is used or not.
--- src/xkbsupport.c | 50 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-)
src/xkbsupport.c +47 -3
@@ -1,3 +1,6 @@ +#define _GNU_SOURCE +#include <search.h> +#include <stdlib.h> #include <string.h> #include <errno.h>
@@ -37,6 +40,7 @@ struct xkeymap {struct xkb_context *xkb;struct xkb_keymap *keymap;struct lk_ctx *ctx; +void *used_codes; };/*
@@ -171,6 +175,15 @@ static const char *map_xkbsym_to_ksym(const char *xkb_sym)return NULL ; } +static int compare_codes (const void *pa,const void *pb) +{ +if (*(int *) pa < *(int *) pb) +return -1 ; +if (*(int *) pa > *(int *) pb) +return 1 ; +return 0 ; +} +static void print_modifiers (struct xkb_keymap *keymap,struct xkb_mask *mask) {xkb_mod_index_t num_mods =xkb_keymap_num_mods (keymap);
@@ -351,6 +364,32 @@ static int xkeymap_get_symbol(struct xkb_keymap *keymap,return 1 ; } +static int used_code (struct xkeymap *xkeymap,int code) +{ +return tfind (&code, &xkeymap->used_codes, compare_codes) !=NULL ; +} + +static void remember_code (struct xkeymap *xkeymap,int code) +{ +int *ptr, **val; + +if (used_code (xkeymap, code)) +return ; + + ptr =malloc (sizeof (code)); +if (!ptr) +kbd_error (1 , errno,"out of memory" ); + + *ptr = code; + + val =tsearch (ptr, &xkeymap->used_codes, compare_codes); +if (!val) +kbd_error (1 , errno,"out of memory" ); + +if (*val != ptr) +free (ptr); +} +static void xkeymap_add_value (struct xkeymap *xkeymap,int modifier,int code,int keyvalue[MAX_NR_KEYMAPS ]) {if (!modifier || (modifier & (1 <<KG_SHIFT )))
@@ -438,9 +477,6 @@ static int xkeymap_walk(struct xkeymap *xkeymap) } } -if (getenv ("LK_XKB_DEBUG" )) -continue ; - process_keycode:unsigned short layout =0 ;
@@ -455,6 +491,11 @@ process_keycode: keyvalue[i] = keyvalue[layout_switch[layout]]; } +remember_code (xkeymap, keyvalue[i]); + +if (getenv ("LK_XKB_DEBUG" )) +continue ; +if (lk_add_key (xkeymap->ctx, i, (int )KERN_KEYCODE (keycode), keyvalue[i]) <0 ) goto err; }
@@ -506,6 +547,9 @@ int convert_xkb_keymap(struct lk_ctx *ctx, struct xkeymap_params *params, int op } end: +if (xkeymap.used_codes) +tdestroy (xkeymap.used_codes, free); +xkb_keymap_unref (xkeymap.keymap);xkb_context_unref (xkeymap.xkb);
Signature · 1 line
2.43.0
PATCH 3/4 xkbsupport: Extract translation table
In reply to Alexey GladkovThis will allow us to expand the xkb to kbd keys translation table.
--- data/xkbtrans/names | 154 ++++++++++++++++++++++++++++++++++++++ src/xkbsupport.c | 176 ++++++++++++++++++++++++-------------------- 2 files changed, 251 insertions(+), 79 deletions(-) create mode 100644 data/xkbtrans/names
data/xkbtrans/names new file +154 -0
@@ -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
src/xkbsupport.c +97 -79
256 lines not on this page · raw message
Signature · 1 line
2.43.0
PATCH 4/4 xkbsupport: Add xkb compose
In reply to Alexey GladkovThe 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.
--- src/loadkeys.c | 26 ++++++++-- src/xkbsupport.c | 126 +++++++++++++++++++++++++++++++++++++++++++++-- src/xkbsupport.h | 2 + 3 files changed, 145 insertions(+), 9 deletions(-)
src/loadkeys.c +21 -5
@@ -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 )
src/xkbsupport.c +122 -4
180 lines not on this page · raw message
src/xkbsupport.h +2 -0
@@ -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);
Signature · 1 line
2.43.0