I have a Thinkpad and I also have two external keyboards I occasionally use with it: one at home when I decide to work at my desk with a bigger monitor, another one at a shared office I rent. They have quite different physical layouts: the one I use at home is a Thinkpad keyboard which roughly mirrors the layout of Thinkpad X230, the other one is Sun Type 6. I want to be able to use any of them while having my own custom keyboard layout too.
Before I started customising layouts, I typically has three layouts I’d normally use: US English, Russian and Belarusian. Three layouts are not a very optimal setup, especially if there’s no LRU/MRU ordering. Also, Russian and Belarusian are almost the same except a couple of letters, which makes it a bit inconvenient having to switch between them; also typing in a wrong one cannot immediately be seen until you type a letter which is missing in the current layout.
Hence, my first step was unifying Belarusian and Russian adding the missing letters to the third level (AltGr) of each other. I initially tried it with just one layout, but it made typing in one of the two languages for longer periods of time more difficult.
The second step I undertook was stateless switching. I found it much easier to always switch to a Latin-based layout as the first thing before typing anything, switching to a Cyrillic-based layout as I need to. I configured a hotkey to switch to the first layout (English), another one to switch to the last layout (Russian), and yet two to switch to the next or previous (so that I could cycle between layouts if I really needed to).
Finally, I heavily customised the English layout, adding all sorts of special symbols and letters for various Slavic languages (initially those were Latin alphabet for Belarusian and Polish, then I gradually replaced most of those with Slovak letters since I type in Slovak much more that in either Polish or Belarusian). Currently my Latin layout is based on the Belarusian Latin alphabet replacing quite a lot of letters, so I probably should switch to a different layout as the basis for mine anyway.
My setup consists of the following ~/.xkb
hierarchy of directories and files:
~/.xkb
├── compat
│ └── custom
├── geometry
│ └── custom
├── keycodes
│ └── custom
└── symbols
└── custom
Over the time as I experimented with settings half of the files became empty except these.
keycodes/custom
:
xkb_keycodes "sun" {
// include "sun(type6_usb)"
<LSGT> = 94;
<LALT> = 133;
<RWIN> = 105;
<LWIN> = 64;
<RALT> = 134;
<RCTL> = 108;
<COMP> = 135;
//indicator 5 = "Compose";
};
I found the swapped Alt and Meta on the Sun keyboard too difficult to get used when I frequently switch keyboards, so I swapped them to match my other two keyboards. I tried to make the Compose indicator work, but to no avail.
symbols/custom
:
partial alphanumeric_keys
xkb_symbols "andrewsh" {
key <LWIN> {
//type[group1]= "FOUR_LEVEL",
//symbols[Group1] = [ Super_L ],
symbols[Group1] = [ ISO_First_Group, ISO_Last_Group ]
//actions[Group1] = [ LockGroup(group=1), LockGroup(group=2), LockGroup(group=+1), LockGroup(group=-1) ]
};
modifier_map Mod4 { Super_L };
key <MENU> { [ ISO_Next_Group, ISO_Prev_Group ] };
key <AE11> {
symbols[Group1]= [ minus, underscore, emdash, endash ],
symbols[Group2]= [ minus, underscore, emdash, endash ],
symbols[Group3]= [ minus, underscore, emdash, endash ]
};
key <BKSL> {
type[group1]= "FOUR_LEVEL",
symbols[Group1]= [ backslash, bar, dead_grave, dead_breve ],
symbols[Group2]= [ slash, backslash ],
symbols[Group3]= [ slash, bar ]
};
key <TLDE> {
type[group1]= "FOUR_LEVEL",
type[group2]= "FOUR_LEVEL",
type[group3]= "FOUR_LEVEL",
symbols[Group1]= [ grave, asciitilde, U0301, U0301 ],
symbols[Group2]= [ Cyrillic_io, Cyrillic_IO, U0301, U0301 ],
symbols[Group3]= [ Cyrillic_io, Cyrillic_IO, U0301, U0301 ]
};
key <AE04> {
type[group1]= "FOUR_LEVEL",
symbols[Group1]= [ 4, dollar, ssharp, EuroSign ],
symbols[Group2]= [ 4, semicolon ],
symbols[Group3]= [ 4, semicolon ]
};
key <AE05> {
type[group1]= "FOUR_LEVEL",
symbols[Group1]= [ 5, percent, onehalf, onequarter ],
symbols[Group2]= [ 5, percent ],
symbols[Group3]= [ 5, percent ]
};
key <AD01> {
type[group1]= "FOUR_LEVEL_SEMIALPHABETIC",
type[group2]= "FOUR_LEVEL_ALPHABETIC",
type[group3]= "FOUR_LEVEL_ALPHABETIC",
symbols[Group1]= [ q, Q, at, U00A7 ],
symbols[Group2]= [ Cyrillic_shorti, Cyrillic_SHORTI, Ukrainian_yi, Ukrainian_YI ],
symbols[Group3]= [ Cyrillic_shorti, Cyrillic_SHORTI, Ukrainian_yi, Ukrainian_YI ]
};
key <AD05> {
type= "FOUR_LEVEL_ALPHABETIC",
symbols[Group1]= [ t, T, tcaron, Tcaron ],
symbols[Group2]= [ Cyrillic_ie, Cyrillic_IE, Ukrainian_ie, Ukrainian_IE ],
symbols[Group3]= [ Cyrillic_ie, Cyrillic_IE, Ukrainian_ie, Ukrainian_IE ]
};
key <AD09> {
type[group1]= "FOUR_LEVEL_ALPHABETIC",
type[group2]= "FOUR_LEVEL_ALPHABETIC",
type[group3]= "FOUR_LEVEL_ALPHABETIC",
symbols[Group1]= [ o, O, oslash, Oslash ],
symbols[Group2]= [ Cyrillic_shcha, Cyrillic_SHCHA, Byelorussian_shortu, Byelorussian_SHORTU ],
symbols[Group3]= [ Byelorussian_shortu, Byelorussian_SHORTU, Cyrillic_shcha, Cyrillic_SHCHA ]
};
key <AD12> {
type[group1]= "FOUR_LEVEL",
type[group2]= "FOUR_LEVEL_ALPHABETIC",
type[group3]= "FOUR_LEVEL_ALPHABETIC",
symbols[Group1]= [ bracketright, braceright, dead_tilde, dead_macron ],
symbols[Group2]= [ Cyrillic_hardsign, Cyrillic_HARDSIGN, apostrophe, apostrophe ],
symbols[Group3]= [ apostrophe, apostrophe, Cyrillic_hardsign, Cyrillic_HARDSIGN ]
};
key <AC02> {
type[group1]= "FOUR_LEVEL_ALPHABETIC",
type[group2]= "ALPHABETIC",
type[group3]= "ALPHABETIC",
symbols[Group1]= [ s, S, scaron, Scaron ],
symbols[Group2]= [ Cyrillic_yeru, Cyrillic_YERU ],
symbols[Group3]= [ Cyrillic_yeru, Cyrillic_YERU ]
};
key <AC03> {
type[group1]= "FOUR_LEVEL_ALPHABETIC",
type[group2]= "ALPHABETIC",
type[group3]= "ALPHABETIC",
symbols[Group1]= [ d, D, dcaron, Dcaron ],
symbols[Group2]= [ Cyrillic_ve, Cyrillic_VE ],
symbols[Group3]= [ Cyrillic_ve, Cyrillic_VE ]
};
key <AC09> {
type[group1]= "FOUR_LEVEL_ALPHABETIC",
type[group2]= "ALPHABETIC",
type[group3]= "ALPHABETIC",
symbols[Group1]= [ l, L, lcaron, Lcaron ],
symbols[Group2]= [ Cyrillic_de, Cyrillic_DE ],
symbols[Group3]= [ Cyrillic_de, Cyrillic_DE ]
};
key <AB01> {
type[group1]= "FOUR_LEVEL_ALPHABETIC",
type[group2]= "ALPHABETIC",
type[group3]= "ALPHABETIC",
symbols[Group1]= [ z, Z, zcaron, Zcaron ],
symbols[Group2]= [ Cyrillic_ya, Cyrillic_YA ],
symbols[Group3]= [ Cyrillic_ya, Cyrillic_YA ]
};
key <AB03> {
type[group1]= "FOUR_LEVEL_ALPHABETIC",
type[group2]= "ALPHABETIC",
type[group3]= "ALPHABETIC",
symbols[Group1]= [ c, C, ccaron, Ccaron ],
symbols[Group2]= [ Cyrillic_es, Cyrillic_ES ],
symbols[Group3]= [ Cyrillic_es, Cyrillic_ES ]
};
key <AB06> {
type[group1]= "FOUR_LEVEL_ALPHABETIC",
type[group2]= "ALPHABETIC",
type[group3]= "ALPHABETIC",
symbols[Group1]= [ n, N, ncaron, Ncaron ],
symbols[Group2]= [ Cyrillic_te, Cyrillic_TE ],
symbols[Group3]= [ Cyrillic_te, Cyrillic_TE ]
};
key <AB08> {
type[group1]= "FOUR_LEVEL",
type[group2]= "ALPHABETIC",
type[group3]= "ALPHABETIC",
symbols[Group1]= [ comma, less, U2212, multiply ],
symbols[Group2]= [ Cyrillic_be, Cyrillic_BE ],
symbols[Group3]= [ Cyrillic_be, Cyrillic_BE ]
};
key <AB05> {
type[group1]= "FOUR_LEVEL_SEMIALPHABETIC",
type[group2]= "FOUR_LEVEL_ALPHABETIC",
type[group3]= "FOUR_LEVEL_ALPHABETIC",
symbols[Group1]= [ b, B, U2033, U2032 ],
symbols[Group2]= [ Cyrillic_i, Cyrillic_I, Ukrainian_i, Ukrainian_I ],
symbols[Group3]= [ Ukrainian_i, Ukrainian_I, Cyrillic_i, Cyrillic_I ]
};
key <AD03> {
type[group1]= "FOUR_LEVEL_ALPHABETIC",
type[group2]= "ALPHABETIC",
type[group3]= "ALPHABETIC",
symbols[Group1]= [ e, E, schwa, U025B ],
symbols[Group2]= [ Cyrillic_u, Cyrillic_U ],
symbols[Group3]= [ Cyrillic_u, Cyrillic_U ]
};
key <AB02> {
type[group1]= "FOUR_LEVEL_SEMIALPHABETIC",
type[group2]= "ALPHABETIC",
type[group3]= "ALPHABETIC",
symbols[Group1]= [ x, X, U2A7D, U2A7E ],
symbols[Group2]= [ Cyrillic_che, Cyrillic_CHE ],
symbols[Group3]= [ Cyrillic_che, Cyrillic_CHE ]
};
key <LSGT> {
type[group1]= "FOUR_LEVEL",
type[group2]= "FOUR_LEVEL",
type[group3]= "FOUR_LEVEL",
# symbols[Group1]= [ U201D, U201C, guillemotright, guillemotleft ],
# symbols[Group2]= [ guillemotright, guillemotleft, U201D, U201C ],
# symbols[Group3]= [ guillemotright, guillemotleft, U201D, U201C ]
symbols[Group1]= [ U2019, U2018, U201D, U201C ],
symbols[Group2]= [ guillemotright, guillemotleft, U201C, U201E ],
symbols[Group3]= [ guillemotright, guillemotleft, U201C, U201E ]
};
key <LEFT> {
type= "FOUR_LEVEL_ALPHABETIC",
[ Left, Left, U2190, U21D0 ]
};
key <RGHT> {
type= "FOUR_LEVEL_ALPHABETIC",
[ Right, Right, U2192, U21D2 ]
};
key <UP> {
type= "FOUR_LEVEL_ALPHABETIC",
[ Up, Up, U2191, U21D1 ]
};
key <DOWN> {
type= "FOUR_LEVEL_ALPHABETIC",
[ Down, Down, U2193, U21D3 ]
};
key <SPCE> {
type[Group1]="FOUR_LEVEL",
symbols[Group1]= [ space, space, Multi_key, nobreakspace ]
};
};
I tried to make it possible to use the Win key as both Super (in combination with other keys) and as a language switcher, but that didn’t work (see the commented out lines).
Here are some more of my experiments from that file which didn’t work are not currently in use:
partial modifier_keys
xkb_symbols "sun" {
key <LALT> {
type[group1]= "FOUR_LEVEL",
symbols[Group1] = [ Super_L ],
actions[Group1] = [ LockGroup(group=1), LockGroup(group=2), LockGroup(group=+1), LockGroup(group=-1) ]
};
modifier_map Mod4 { Super_L };
key <LWIN> { [ NoSymbol, Alt_L ] };
key <RWIN> {
type= "ONE_LEVEL",
symbols[Group1]= [ ISO_Level3_Shift ]
};
key <RALT> { [ Control_R ] };
};
partial modifier_keys
xkb_symbols "win_lang_super" {
key <LWIN> {
type[group1]= "FOUR_LEVEL",
symbols[Group1] = [ Super_L ],
actions[Group1] = [ LockGroup(group=1), LockGroup(group=2), LockGroup(group=+1), LockGroup(group=-1) ]
};
modifier_map Mod4 { Super_L };
};
partial modifier_keys
xkb_symbols "alt_lang_super" {
key <LALT> {
type[group1]= "FOUR_LEVEL",
symbols[Group1] = [ Super_L ],
actions[Group1] = [ LockGroup(group=1), LockGroup(group=2), LockGroup(group=+1), LockGroup(group=-1) ]
};
modifier_map Mod4 { Super_L };
};
I combine all of the above settings using this file, ~/.xkb_keymap
:
xkb_keymap {
xkb_keycodes { include "evdev+aliases(qwerty)" };
xkb_types { include "complete" };
xkb_compat { include "complete+custom(andrewsh)" };
xkb_symbols { include "pc+by(latin)+ru(winkeys):2+by:3+inet(evdev)+custom(andrewsh)" };
xkb_geometry { include "pc(pc105)" };
};
and a script:
xkbcomp -I$HOME/.xkb ~/.xkb_keymap $DISPLAY
xmodmap ~/.xmodmaprc
sun_id=$(xinput list | grep "HID 0430:0005" | grep -o 'id=[0-9]*' | cut -d = -f 2)
if [ -n "$sun_id" ]
then
xkbcomp -I$HOME/.xkb ~/.xkb_keymap_sun $DISPLAY
fi
xmodmap
is not really used anymore, it only has one key mapping for the suspend key. For the Sun keyboard, ~/.xkb_keymap_sun
file is used:
xkb_keymap {
xkb_keycodes { include "evdev+aliases(qwerty)+custom(sun)" };
xkb_types { include "complete" };
xkb_compat { include "complete+ledcompose(compose)+custom(andrewsh)" };
xkb_symbols { include "pc+by(latin)+ru(winkeys):2+by:3+inet(evdev)+custom(andrewsh)+compose(menu)" };
xkb_geometry { include "pc(pc105)" };
};
I’m using inputplug to run those scripts automatically when keyboards are connected or disconnected.
Since I use Unity, I had to tell gnome-settings-daemon to stop interfering with the keyboard layout by setting org.gnome.settings-daemon.plugins.keyboard
active
to false
. That, however, doesn’t work with GNOME anymore: in a GNOME session, the keyboard layout loads, but none of the layout switching hotkeys work.
Trying to make this work in GNOME, I followed what Peter Hutterer described, only to find out it will work in Wayland only. Anyway, here’s what I did.
First, I’ve created these files:
~/.xkb
└── rules
├── evdev
└── evdev.xml
rules/evdev
:
! option = symbols
custom:andrewsh = +custom(andrewsh)
! include %S/evdev
rules/evdev.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xkbConfigRegistry SYSTEM "xkb.dtd">
<xkbConfigRegistry version="1.1">
<optionList>
<group allowMultipleSelection="true">
<configItem>
<name>custom</name>
<description>Custom options</description>
</configItem>
<option>
<configItem>
<name>custom:andrewsh</name>
<description>andrewsh’s custom stuff</description>
</configItem>
</option>
</optionList>
</xkbConfigRegistry>
Then I symlinked ~/.xkb
to ~/.config/xkb
where it is expected to be found by libxkbcommon.