custom keyboard layouts with xkb
x keyboard extension (aka: xkb), which was made several decades ago, is still in active use today; here we show how to use xkb to alter system keyboard layout and create our own; in this example, we want to switch comma and period, for no reason but a demo;
load, change, save
xkb comes with a tool called xkbcomp
, which is used to compile xkb keymap
descriptions; a great feature of this “compiler” is that it can talk to the
“live” x server to load and save keymap; this makes our customization an easy
3-step process:
-
load keymap from x server;
xkbcomp "$DISPLAY" custom.xkb
-
change keymap;
vim custom.xkb
assuming we are using layout
us
; we look for sectionxkb_symbols
then we can find these two lines:key <AB08> { [ comma, less ] }; key <AB09> { [ period, greater ] };
as you can guess, to switch these two keys, we simply switch their values:
key <AB08> { [ period, greater ] }; key <AB09> { [ comma, less ] };
-
save keymap to x server;
xkbcomp custom.xkb "$DISPLAY"
this is pretty easy and works well;
change partials
the above approach involves a monolithic keymap file, which may seem daunting;
we can do it a bit more elegant, by editing partial files; to do this, we step
into /usr/share/X11/xkb
and we find subdirs here such as keycodes
, symbols
,
types
, etc.; we are still interested symbols files so we look into symbols
subdir; for simplicity we open symbols/us
, which contains this part:
default partial alphanumeric_keys modifier_keys
xkb_symbols "basic" {
name[Group1]= "English (US)";
key <TLDE> { [ grave, asciitilde ] };
key <AE01> { [ 1, exclam ] };
key <AE02> { [ 2, at ] };
key <AE03> { [ 3, numbersign ] };
key <AE04> { [ 4, dollar ] };
key <AE05> { [ 5, percent ] };
key <AE06> { [ 6, asciicircum ] };
key <AE07> { [ 7, ampersand ] };
key <AE08> { [ 8, asterisk ] };
key <AE09> { [ 9, parenleft ] };
key <AE10> { [ 0, parenright ] };
key <AE11> { [ minus, underscore ] };
key <AE12> { [ equal, plus ] };
key <AD01> { [ q, Q ] };
key <AD02> { [ w, W ] };
key <AD03> { [ e, E ] };
key <AD04> { [ r, R ] };
key <AD05> { [ t, T ] };
key <AD06> { [ y, Y ] };
key <AD07> { [ u, U ] };
key <AD08> { [ i, I ] };
key <AD09> { [ o, O ] };
key <AD10> { [ p, P ] };
key <AD11> { [ bracketleft, braceleft ] };
key <AD12> { [ bracketright, braceright ] };
key <AC01> { [ a, A ] };
key <AC02> { [ s, S ] };
key <AC03> { [ d, D ] };
key <AC04> { [ f, F ] };
key <AC05> { [ g, G ] };
key <AC06> { [ h, H ] };
key <AC07> { [ j, J ] };
key <AC08> { [ k, K ] };
key <AC09> { [ l, L ] };
key <AC10> { [ semicolon, colon ] };
key <AC11> { [ apostrophe, quotedbl ] };
key <AB01> { [ z, Z ] };
key <AB02> { [ x, X ] };
key <AB03> { [ c, C ] };
key <AB04> { [ v, V ] };
key <AB05> { [ b, B ] };
key <AB06> { [ n, N ] };
key <AB07> { [ m, M ] };
key <AB08> { [ comma, less ] };
key <AB09> { [ period, greater ] };
key <AB10> { [ slash, question ] };
key <BKSL> { [ backslash, bar ] };
};
it is straightforward to change this file and save as a custom copy; some
articles may then tell you to add this custom copy to rules by editing some file;
but we will not go that way here, cuz that needs to write under /usr/share
,
which should not be touched by normal users; instead, we do these steps:
-
save our modified symbols file in a dir that can be written by normal user:
mkdir -p "${xkb}/symbols" echo "$symbols" > "${xkb}/symbols/custom"
-
print current keymap using
setxkbmap
:setxkbmap -print > "${xkb}/custom.xkb"
compare this with:
xkbcomp "$DISPLAY" "${xkb}/custom.xkb"
they both generate a keymap file, but
setxkbmap
generates it succinct:xkb_keymap { ... xkb_symbols { include "pc+us" }; ... };
-
customize curent keymap:
sed -i "s/+us/+custom/" "${xkb}/custom.xkb"
-
call
xkbcomp
to apply our change on x display:xkbcomp -I"$xkb" "${xkb}/custom.xkb" "$DISPLAY"
we need
-I
because our$xkb
is not a standard dir searched byxkbcomp
;
print keyboard layout
when our work is done, we probably want to know how our keymap looks like; for this we can use xkbprint:
xkbprint -color "$DISPLAY"
this outputs a .ps
file, which we can pipe to get a .pdf
file:
xkbprint -color "$DISPLAY" - | ps2pdf - custom.pdf
now comes the problem: xkbprint
may not be available in your linux distro; for
example, it has been retired in fedora; seriously, i could not believe it,
but we are gonna help ourselves; on fedora, since xkbprint
is no longer
available, i pulled the xkbcomp
srpm, changed all names and versions to those
of xkbprint
, fixed some obvious bugs in spec file, then fetched the xkbprint
source tarball from here, finally it built into a binary;
references
this is a huge topic, many not covered here; but we got these great articles:
- https://www.charvolant.org/doug/xkb/html/node5.html
- https://wiki.archlinux.org/title/X_keyboard_extension
- https://colmanlayout.wordpress.com/download/
- https://wiki.linuxquestions.org/wiki/Altering_or_Creating_Keyboard_Maps
- https://askubuntu.com/questions/1003404/is-there-an-official-folder-in-the-home-directory-for-storing-keymaps
- https://www.vinc17.net/unix/xkb.en.html
- https://bugs.launchpad.net/ubuntu/+source/xkeyboard-config/+bug/1738630
it seems xkb configuration can only read from /usr/share
, not
/usr/local/share
or other dirs; this sucks, really;