Multiple keyboard layouts on OpenBSD

       930 words, 5 minutes

My ThinkPad P14s Gen 5 AMD has a French (ISO AZERTY). But it is connected to a dock and I mainly use an external (USB) keyboard with an ANSI US layout.

Because I’d rather not type with an US layout on an FR keyboard, and vice-versa, I have configured my OpenBSD OS to deal with both, nearly automagically.

The BIOS

It is possible to set a keyboard layout in the BIOS. This allows typing the FDE passphrase with a proper keyboard layout. Unfortunately, AFAIK, it is not possible to switch keyboard layouts from there.

So I’m stucked with typing the passphrase with whatever layout was configured in the BIOS.

The OpenBSD console

The OpenBSD console can be managed using wsconsctl(8) and its wsconsctl.conf(5) configuration file. As far as I understood, it can only be managed using high privileges. But the configuration is not modified that often when the proper setup is achieved.

I’m not sure why I have that many keyboard references. But looking at dmesg(8), I can identify the ones I need to modify.

# dmesg | grep -B 5 wskbd | more
(...)
pckbd0 at pckbc1 (kbd slot)
wskbd0 at pckbd0: console keyboard
(...)
uhidev0 at uhub6 port 4 configuration 1 interface 5 "DELL DELL Slim Soundbar SB522A" rev 2.00/0.00 addr 4
uhidev0: iclass 3/0, 185 report ids
ucc0 at uhidev0 reportid 1: 3 usages, 3 keys, enum    
wskbd1 at ucc0 mux 1
(...)
uhidev1 at uhub5 port 2 configuration 1 interface 0 "NuPhy NuPhy Field75" rev 2.00/2.02 addr 5
uhidev1: iclass 3/1
ukbd0 at uhidev1: 8 variable keys, 5 key codes
wskbd2 at ukbd0 mux
(...)
uhidev5 at uhub5 port 4 configuration 1 interface 0 "Logitech USB Receiver" rev 2.00/24.11 addr 7
uhidev5: iclass 3/1
ukbd2 at uhidev5: 8 variable keys, 6 key codes
wskbd5 at ukbd2 mux 
(...)
uhidev6 at uhub5 port 4 configuration 1 interface 1 "Logitech USB Receiver" rev 2.00/24.11 addr 7
uhidev6: iclass 3/1, 8 report ids
ums2 at uhidev6 reportid 2: 16 buttons, Z and W dir
wsmouse5 at ums2 mux 0
ucc2 at uhidev6 reportid 3: 767 usages, 20 keys, array
wskbd6 at ucc2 mux 1
(...)
wsdisplay0 at amdgpu0 mux 1: console (std, vt100 emulation), using wskbd0
wskbd1: connecting to wsdisplay0
wskbd2: connecting to wsdisplay0
wskbd3: connecting to wsdisplay0
wskbd4: connecting to wsdisplay0
wskbd5: connecting to wsdisplay0
wskbd6: connecting to wsdisplay0

The Soundbar has no keyboard; that’s probably the wheel switches that advertise themselves as keyboards. I also only have one single Logitech USB dongle and only a mouse is connected to it. I used to have a keyboard connected there but it is turned off for years now.

Anyway, the keyboards I’m really interested in are wskbd0 and wskbd2.

# doas wsconsctl keyboard keyboard2           
keyboard.type=pc-xt
keyboard.bell.pitch=400
keyboard.bell.period=100
keyboard.bell.volume=50
keyboard.bell.pitch.default=400
keyboard.bell.period.default=100
keyboard.bell.volume.default=50
wsconsctl: Use explicit arg to view keyboard.map.
keyboard.repeat.del1=400
keyboard.repeat.deln=100
keyboard.repeat.del1.default=400
keyboard.repeat.deln.default=100
keyboard.ledstate=0
keyboard.encoding=us
keyboard.backlight=0.00%
keyboard2.type=usb
keyboard2.bell.pitch=400
keyboard2.bell.period=100
keyboard2.bell.volume=50
keyboard2.bell.pitch.default=400
keyboard2.bell.period.default=100
keyboard2.bell.volume.default=50
wsconsctl: Use explicit arg to view keyboard2.map.
keyboard2.repeat.del1=400
keyboard2.repeat.deln=100
keyboard2.repeat.del1.default=400
keyboard2.repeat.deln.default=100
keyboard2.ledstate=0
keyboard2.encoding=us
keyboard2.backlight=0.00%

Issuing the relevant wsconsctl command, I can tell OpenBSD that the laptop’s keyboard should use FR and the USB one should use US.

# doas wsconsctl keyboard.encoding=fr keyboard2.encoding=us
keyboard.encoding -> fr
keyboard2.encoding -> us

After making sure I didn’t wreck anything, I modified the configuration file so that the changes apply at the next reboot too.

# doas vi /etc/wsconsctl.conf
keyboard.encoding=fr    # ThinkPad keyboard is FR
keyboard2.encoding=us   # NuPhy keyboard is US

It should be noted that this only work in the console. From xenodm(1) and Xorg(1), both keyboard use the default encoding.

The OpenBSD Xorg session

With the above configuration applied, every new connection to X11 is made using the FR keyboard layout. Great with the ThinkPad keyboard; not with the external USB one.

Once connected to an X11 session, from an X terminal, issuing setxkbmap us allows switching to the US layout. But that doesn’t solve the login phase issue.

Informations are detailed in xorg.conf(5) and XKB-Config(7). I thought I could create a configuration that matches every keyboards and sets a relevant layout. But as far as I understood the documentation, OpenBSD exposes only one keyboard device to Xenocara. So things like MatchDevicePath didn’t work.

So I went for an all-in-one multi layout X11 configuration. Every keyboards are set the same way. And I can switch from layout to layout using the "<Win> + <space>" key binding.

# vi /usr/X11R6/share/X11/xorg.conf.d/99-keyboards.conf
Section "InputClass"
  Identifier          "Multi Layout Keyboard(s)"
  MatchIsKeyboard     "on"
  Option "XkbLayout"  "us,us,fr"
  Option "XkbVariant" "euro,alt-intl,azerty"
  Option "XkbOptions" "grp:win_space_toggle"
EndSection

The “us/euro” layout provides the “€” sign using "<AltGr> + <5>".
The “us/alt-intl” layout provides accented characters in ways that feels natural to me ("<'> + <e> = <é>"). And, TBH, in the same way MS Windows provides accented characters for US layouts; which is memory-muscle-proof when I have to use such $WORK laptops with my external keyboard.
The “fr/azerty” layout is just that.

The nice thing is that the layout switch works inside xenodm and is compatible with Xfce xkb layout switcher panel plug-in.

Final words

This configuration polishes previous experimentation that were described in 2020 and in 2021 .

In case I ever need it, the configuration can be displayed in the terminal:

# setxkbmap -query
rules:      base
model:      pc105
layout:     us,us,fr
variant:    euro,alt-intl,azerty
options:    grp:win_space_toggle

Should creating a configuration file is not wanted, it can still be applied using the command line:

# setxkbmap -layout us,us,fr      \
  -variant euro,alt-intl,azerty   \
  -option terminate:ctrl_alt_bksp \
  -option grp:win_space_toggle

Note that such configuration was also implemented as-is on FreeBSD and Slackware Linux. So that’s a great plus for me.

And… that’s all for now.