Linux Keyboard Layout Customization

Setting the keyboard layout on Linux/X11 has always been a bit of a mystery to me. To make matters worse, I’m using Vagrant to frequently spin up a new VM; the extra step of system restart or log out so that changes can take effect is very unwelcome.

When troubleshooting initialization and configuration issues, it’s helpful to know what kind of shell you are in. A login shell normally only runs once at login and executes .bash_profile. An interactive shell will usually run every time you open a system console and executes .bashrc. The ‘i’ in the ‘-‘ variable indicates an interactive shell.

> echo $-
himBH

> shopt login_shell
login_shell     off

You can set the keyboard layout globally by setting the system locale. The only problem with this is that it doesn’t take effect until the next login. Normally this wouldn’t be a problem, but if you are frequently starting with a new VM, logging out just to fix the keyboard layout is annoying.

> localectl set-x11-keymap us,us "" dvorak,

Since I’m using a VM and the host machine is already password protected, there is really no reason to require logging in at all. My KDE install is using GDM by default.

> grep 'ExecStart=' /etc/systemd/system/display-manager.service
ExecStart=/usr/sbin/gdm

You can modify /etc/gdm/custom.conf to log in automatically. Unfortunately, this does not take effect until the next boot, so I added it to my base Vagrant box. It may also be possible to restart the display manager.

[daemon] 
AutomaticLoginEnable=True 
AutomaticLogin=username

This was causing some problems with my KDE autostart script, but it’s easy enough to fix. However, I had similar problems with KDE configurations (e.g. kwinrc) that I changed not taking effect immediately when Vagrant first boots the system. You can fix this by looking to see if any new scripts have been added to the Autostart directory in the past few minutes and then restarting the KDE session. If you are modifying .bash_profile or .bashrc at first boot, you may still have a problem with these files running before your changes are made. The fix mentioned above should also fix this issue. Alternatively, if you are only modifying .bashrc, you could just sleep for a bit before starting any interactive shell. You could modify the configuration files in the base box; the only problem is that KDE doesn’t create the directories and files until your first login.

if [ ! -z  "$(find ~/.kde/Autostart -mtime -.002)" ]; then
    kwin --replace >& /dev/null &
fi

Using auto-login obviates the problem of the login screen still using the old keyboard layout, but once you are logged in, the keyboard layout is still the old one. You can also set the keyboard layout for only the current X11 session, which takes effect immediately.

> setxkbmap dvorak
> setxkbmap -query
rules:      evdev
model:      evdev
layout:     dvorak

It probably won’t hurt to run this every time, but there is no need on subsequent logins. First, notice that once set-x11-keymap takes effect, the variant field of a setxkbmap query has been set.

> more /etc/X11/xorg.conf.d/00-keyboard.conf
# Read and parsed by systemd-localed. It's probably wise not to edit this file
# manually too freely.
Section "InputClass"
        Identifier "system-keyboard"
        MatchIsKeyboard "on"
        Option "XkbLayout" "us,us"
        Option "XkbVariant" "dvorak,"
EndSection

> setxkbmap -query
rules:      evdev
model:      evdev
layout:     us,us
variant:    dvorak,

So we can add the following to .bash_profile or .bashrc, and it will only run before set-x11-keymap takes effect.

if [[ ! $(setxkbmap -query) =~ variant:[[:space:]]*dvorak ]]; then
    setxkbmap dvorak
fi

There is still a problem though. If you ssh in, you don’t want to change the keyboard layout because you will already be using the altered keyboard layout of the host. The xhost command will return false if it can’t connect to an X11 display.

> xhost  # from GUI
access control enabled, only authorized clients can connect
SI:localuser:root
SI:localuser:myuser

> xhost  # from ssh
xhost:  unable to open display ""

So, with this extra condition, we now only run setxkbmap before setx-x11-keymap takes effect and if we are in the GUI.

if xhost >& /dev/null && [[ ! $(setxkbmap -query) =~ variant:[[:space:]]*dvorak ]]; then
    setxkbmap dvorak
fi

Another possibility is to run setxkbmap in the auto-start script only the first time the auto-start script runs.

Update

There is an easier way to deal with the problem of setting the keyboard layout and KDE configuration files on first boot. If you have created a base box using Vagrant, simply leave the run level as the default. This is multi-user.target on CentOS with Systemd. Upon booting with X11, make any changes to the KDE configuration files, change to auto-login if you haven’t already, set the locale, and then change the run-level to graphical.target.

systemctl isolate graphical.target
systemctl set-default graphical.target

The first line changes to the graphical run-level immediately. The second makes it permanent.

There’s no longer any need to also change the keyboard layout using setxkbmap on the first login. And if you do it this way, you won’t run into any race conditions between updating configuration files and KDE loading those same configuration files.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top