After fiddling around a lot, this is what I have running for now. Maybe I’ll write a complete article of sorts and publish the code within a repository, if that would be of interest.
Set up a new rule set for udev like that:
sudo gedit /etc/udev/rules.d/80-external-keyboard.rules
The rule is supposed to call a shell script whenever some action is triggered by a device with the given combination of vendor and product ID.
ATTRS{idVendor}=="04b4", ATTRS{idProduct}=="4042", RUN+="/home/phil/.bin/switch-kb-layout-wrapper.sh"
After adding the new rule set, restart the udev service:
sudo service udev restart
Note: I was not able to achieve reliable results by providing more specific matching rules in that file. Most significantly, adding a ACTION
matching rule did not work. As far as I can tell, the script was triggered anyway. When adding ACTION=="add"
, the script would still be called upon removing the device. Very strange and confusing.
However the action that triggered the udev rule will be available to the called script as shown below.
Next, the script itself. Well, not quite. Note the wrapper
suffix in the file name. This indicates that this is not the actual script but a wrapper that calls the script and executes it in the background so that udev can finish its process.
~/.bin/switch-kb-layout-wrapper.sh
:
#!/bin/sh
/home/phil/.bin/switch-kb-layout.sh "${ACTION}" &
The variable ACTION
contains the udev action that was triggered by the device. It yields values like add
(device was plugged in) and remove
(device was removed). We’ll use these later on.
~/.bin/switch-kb-layout.sh
:
#!/bin/sh
sleep 1
# Some environment variables that need to be set in order to run `setxkbmap`
DISPLAY=":0.0"
HOME=/home/phil
XAUTHORITY=$HOME/.Xauthority
export DISPLAY XAUTHORITY HOME
udev_action=$1
log_file="$HOME/switch-kb-layout.log"
if [ "${udev_action}" != "add" ] && [ "${udev_action}" != "remove" ]; then
echo "Other action. Aborting." >> $log_file
exit 1
fi
internal_kb_layout="de"
internal_kb_variant=""
external_kb_layout="us"
external_kb_variant="altgr-intl"
kb_layout=""
kb_variant=""
if [ "${udev_action}" = "add" ]; then
kb_layout=$external_kb_layout
kb_variant=$external_kb_variant
elif [ "${udev_action}" = "remove" ]; then
kb_layout=$internal_kb_layout
kb_variant=$internal_kb_variant
fi
setxkbmap -layout "${kb_layout}"
echo "set layout:" "$kb_layout" >> $log_file
if [ ! -z "${kb_variant}" ]; then
setxkbmap -variant "${kb_variant}"
echo "set variant:" "$kb_variant" >> $log_file
fi
Replace my username with yours when setting the HOME
variable ($(whoami)
won’t work here, since this will not be called by your user but by root
).
sed -i "s/phil/YOUR_USERNAME/g" ~/.bin/switch-kb-layout.sh
For testing purpose, I added some lines that log certain events to a file in my home directory to see whether everything works. You can savely remove these.
Finally, these scripts need to have execution permissions. Also it might be important to note that these scripts will be called by the root
user, so be careful what you do in there.
chmod +x ~/.bin/switch-kb-layout-wrapper.sh ~/.bin/switch-kb-layout.sh