Apple keyboards, Ubuntu and Udev joy

My work setup at my new job includes a tank of a laptop (HP - apparently they're good at sturdy, power-filled laptops with that extra oomph when you need to whack someone over the head with computer) and because I don't want to spend my day typing on a laptop keybard (mind you, it's pretty good quality, but still - too cramped) I got myself an Apple keyboard. One of those shiny aluminium ones. They keys are just awesome - my fingers are very happy about using it.

There's just one drawback: Mac users cannot use the same keyboard layout the rest of us are comfortable with (the hipster factor, or some such) so some keys are switched, others have been replaced. So what do you do when you run Ubuntu 13.04? You hack it, of course.

Step 1 - Fix the braindead reversal of function keys and special keys

Seriously, this one is just stupid. Or, rather, it makes very good sense if your user base is braindead. Only in that scenario would you need the special functions more than function keys (fascinatingly, Apple, king of the context aware os, make keyboards that disregard context in favour of always having the same functionality). Enough whining, make hack now!

From https://help.ubuntu.com/community/AppleKeyboard we learn that it's possible to add a config file to modprobe to set the proper functionality for the keyboard. You'll need to:

echo options hid_apple fnmode=2 | sudo tee -a /etc/modprobe.d/hid_apple.conf
sudo update-initramfs -u -k all
sudo reboot  # consider waiting for step 2 with this

After this the function keys should be working normal.

Step 2 - Fix reversed keys

This follows the same recipe as above - hence the point in waiting. Do:

echo options hid_apple iso_layout=0 | sudo tee -a /etc/modprobe.d/hid_apple.conf
sudo update-initramfs -u -k all
sudo reboot

This should make sure the key to the right of the left shift key is working properly - also possibly the key over the tab key.

Step 3 - The udev happiness

Major annoyances fixed it's on to possibly the biggest: the swapping of cmd and alt. Here, you can actually change the setup of the keyboard using just the Keyboard Layout setting in Ubuntu - open the options dialogue, select Options, then expand Alt/Win key behaviour and select Left alt is switched with left win. This will flip the two.

Now, if don't use the computer without having the Apple keyboard plugged in, congratulations - you can now breathe more easily (you might want to take a look at the other possible changes to keyboard layout you can make, though, as some of them make quite good sense).

If, however, you switch between using just the laptop and using the plugged in keyboard, you'll get really annoyed as the alt and win keys on your laptop suddenly switch - or those on the usb-keyboard do. So what you really want is to be able to switch keyboard layouts automatically when the usb-keyboard is plugged in or removed. This is where udev (linux dynamic device management, according to man) comes in. It allows you to write rules that are executed when devices are plugged in or removed - if the rules match the device. Just what the doctor ordered!

First you need to get a proper match rule-match for your device. This is a bit of a jungle if you don't have experience with udev - but this is how you could find what you need:

# this will export all devices udev knows about
# search this list for apple and mark down the top
# line of the paragraph you find it in - it'll start with
# /devices

udevadm info --export-db | less

# then use this command, to get rule matches

udevadm info --path <device path you copied above --attribute-walk --export | less

Usable entries from the above would be like

# product vendor - alright if you don't have other apple devices attached
ATTRS{idVendor}=="05ac"

or

# possibly better, wouldn't match other apple products
ATTRS{product}=="Apple Keyboard"

With these it's time to create the needed rule. My rule looks like this:

SUBSYSTEM=="hid", ATTRS{product}=="Apple Keyboard", ACTION=="add", RUN+="/usr/local/bin/switch-keyboard.sh apple udev"
SUBSYSTEM=="hid", ATTRS{product}=="Apple Keyboard", ACTION=="remove", RUN+="/usr/local/bin/switch-keyboard.sh laptop udev"

I put the above in a file named apple-keyboard.rules which is put into /etc/udev/rules.d/ - udev uses inotify to check the directory for changes, so the rule should be ready to run soon as you have saved the file.

To quickly go over what the file does:

  • each line is rule that udev will try to match against device changes. The line consists of match pairs: a category and a string match on the other side
  • first, it will match on the hid subsystem - you could also try to match on device or kernel level. For my needs, subsystem is fine
  • then it will try to match on attributes - here is where the info you found with udevadm comes in. I've added the ATTRS{product} category and match on "Apple Keyboard" - this will check parent attributes of the device for that string
  • after this it will match on what is happening - the action. I'm matching on either add or remove, to be able to switch keyboard layouts when the keyboard is plugged in or removed
  • and finally there is a RUN key which specifies a script to run (in fact the entire thing will be run, so you can include parameters) - which is what allows you to do the layout switching

The next step is figuring out how to change the keyboard layout from the command line. This is possible with setxkbmap, as it happens - it'll let you set a lot of different layout details, keyswitches being one. First, you should use setxkbmap to note down the options you use with and without the keyboard plugged in. This is done like this:

# the options should display on the last line of output here
setxkbmap -query

With both sets of options, you can then stick together a script file that will change between the two. My script looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash

if [ "$2" = "udev" ]
then
  nohup $0 $1 &

else
  sleep 1
  DISPLAY=":0.0"
  HOME=/home/peter/
  XAUTHORITY=$HOME/.Xauthority
  export DISPLAY XAUTHORITY HOME

  # clear all options
  /usr/bin/sudo -u peter /usr/bin/setxkbmap -model "pc105" -layout "gb,dk,pl,de" -option ""

  if [ "$1" = "apple" ]
  then
    # set the Apple keyboard
    /usr/bin/sudo -u peter /usr/bin/setxkbmap -rules "evdev" -model "pc105" -layout "gb,dk,pl,de" -option "grp:lalt_lshift_toggle,altwin:swap_lalt_lwin,apple:alupckeys"

  else
    # set the Apple keyboard
    /usr/bin/sudo -u peter /usr/bin/setxkbmap -rules "evdev" -model "pc105" -layout "gb,dk,pl,de" -option "grp:lalt_lshift_toggle"
  fi
fi

There are a couple of things worth noting here:

  • udev runs without access to your normal environment. That means specifying the display to set keyboard layout for, as well as a path to your Xauthority file
  • it also means prefixing all commands with proper paths - /usr/bin/ will not be in the path
  • also, udev might need to finish processing the rules file in order for the script to work properly - hence it is run from udev with a parameter that makes it kick off itself again, using nohup. Some experimenting seems to suggest this is not necessary - but doesn't hurt though
  • lastly, the script first resets the keyboard layout options, before setting the proper options depending upon add or remove from the rules file
  • oh, and importantly: I got information for the script from http://superuser.com/questions/249064/udev-rule-to-auto-load-keyboard-layout-when-usb-keyboard-plugged-in - specifically the parts about display and Xauthority environment vars, without which the script will fail
  • note: don't just copy the script as you'll end up with four keyboard layouts to choose from and my chosen keys for switching - adapt it to your needs then save it

Conclusion

This is probably what I love most about linux: someone has come up with ways to fix the problems you're facing, you can almost certainly find information about how to do it, and it's all freely available. If you're willing to spend a bit of time on it you'll learn a lot, empower yourself, solve your problems, and won't spend a dime.

social