A US keyboard layout increases my productivity as a developer, because characters like square brackets, pipe and backslash are easier to type than on a German keyboard. For the occasional German special character I like to use the AltGr key, preferably with the keys where they are already printed on my physical keyboard.
From time to time I need to write longer German texts that don’t need a lot of special characters and need Umlauts instead. In those cases use a keyboard shortcut to switch between German and US keyboard.
I know that there are alternative setups like the compose key or the European Keyboard Layout, but I’ve never warmed up to them.
Choosing a layout and variant
On Linux, the software for managing keyboard layouts both for the X server and for Sway is XKB. It defines keyboard layouts that more or less represent the physical keys on he layout and variants that can redefine characters, composition and AltGr behavior.
In my previous installation with xmonad, I had a US keyboard layout with a variant called cz_sk_de
that allowed to type the umlauts with an AltGr combination. I don’t remember if I installed it manually or if it came with my OS. On my new OS, Ubuntu 22.04, the cz_sk_de
variant was not present, so I used the following commands to look which other variants are available:
localectl list-x11-keymap-variants us
localectl list-x11-keymap-variants de
The US layout has an altgr-intl
variant that would allow me to type umlauts and accented characters, but they are in unexpected places.
The German (de
) layouts have a variant called US
that does exactly what I need: US characters by default, Umlauts in familiar places with AltGr.
Configuring my keyboard in Sway
You can list all the inputs available in Sway with the command
swaymsg -t get_inputs
In my case, with a ThinkPad T14, two keyboard inputs were showing up. I guess one is for special keys. Not wanting to mess with it, I decided to use the unique ID instead of type:keyboard
when configuring the input. This is what my configuration looks like:
input "1:1:AT_Translated_Set_2_keyboard" {
xkb_layout de,de
xkb_variant us,
}
bindsym $mod+BackSpace input "1:1:AT_Translated_Set_2_keyboard" xkb_switch_layout next
The xkb_layout
configuration defines the same layout twice, while the xkb_variant
configuration defines one variant and one “pure” layout - no variant, hence the empty space after the comma.
My preferred hotkey for switching layouts is Super+Backspace, since for me it’s the perfect balance between easy to reach and without danger of toggling accidentally.
Making the keyboard configuration host-specific
In case I want to reuse my Sway configuration on a different machine that has a different ID for the keyboard, I’d like to put the keyboard configuration in a host-specific file. This is possible with the following line in the main sway configuration:
include ~/.config/sway/$(hostname)/*
Thanks to the example Sway configuration that uses $hostname variable
Waybar integration
Waybar has an open issue about adding a keyboard layout module, so I wrote my own custom module in Python:
#!/usr/bin/python
import subprocess
import sys
import json
from argparse import ArgumentParser
KEYBOARD_TABLE = {
"German": "DE",
"German (US)": "EN"
}
parser = ArgumentParser()
parser.add_argument("identifier")
args = parser.parse_args()
input_identifier = args.identifier
# We could use the i3ipc library instead, but that would create a
# dependency we'd have to install before using this module
result = subprocess.run(["swaymsg", "-t", "get_inputs", "-r"],
encoding="UTF-8", capture_output=True)
inputs = json.loads(result.stdout)
layout_name = ""
for input in inputs:
if input["identifier"] == input_identifier:
layout_name = input["xkb_active_layout_name"]
if layout_name == "":
print("No layout found. check your identifier", file=sys.stderr)
sys.exit(1)
if layout_name in KEYBOARD_TABLE:
short_name = KEYBOARD_TABLE[layout_name]
print(json.dumps({"text": short_name, "tooltip": layout_name}))
else:
print(json.dumps({"text": layout_name}))
The module also “translates” the raw layout name into shorter names (when configured).
This is the configuration for Waybar:
{
"modules-right": [ "custom/keyboard", "clock"],
"custom/keyboard": {
"exec": "~/.config/waybar/modules/keyboard.py '1:1:AT_Translated_Set_2_keyboard'",
"interval": "once",
"format": " {} ",
"return-type": "json",
"signal": 2,
"on-click": "~/.config/sway/scripts/switch-keyboard '1:1:AT_Translated_Set_2_keyboard'"
},
"clock": {
"format": "{:%Y-%m-%d %H:%M}"
}
}
}
I have set up the module to avoid polling and instead opted for a small
script that toggles the next layout with swaymsg
and then sends a
SIGTERM to waybar to trigger an update. In my Sway configuration I have
changed my Super+Backspace hotkey to trigger this script as well:
#!/bin/bash
if [ -z "$1" ]; then
echo "You need to provide a sway input identifier. Run 'swaymsg -t get_inputs'"
exit 1
fi
swaymsg input "$1" xkb_switch_layout next
pkill -SIGRTMIN+2 waybar