I decided to pull my old P4 box out of retirement to give FreeBSD 9 a try. For the most part, things are as I remember, however FreeBSD 9’s default Xorg uses HAL to manage all the devices, including the keyboard and mouse. What this means, as far as making X feel like “home”, is that I can no longer make my usual xorg.conf edits to replace Caps Lock (which is useless) with another Ctrl. I have been using my keyboard with the Caps Lock key as a Ctrl for so long that it causes so much frustration even after a few minutes, so it’s usually one of the first things that changes. When I start using a new desktop.
My main computer is a Mac, and I was an early Lion adopter. At first, I didn’t like the “natural scrolling”, so I disabled it. I hated the idea of having to reset all the defaults every time I used a new Mac though, so I decided to give it another try. Eventually it really grew on me! So much so, that sitting at my FreeBSD desktop I would try to scroll up or down and get frustrated that everything felt backwards. I decided that in addition to my additional Ctrl key, I’d have to get the scrolling to reverse also. Luckily, both of these changes are trivial, once you know what information to look for and what file to create and/or edit.
I won’t get into the entire story of how I ended up compiling every application on my system from ports – the short version of the story is that I installed vim and basically it installed X along with a million other things (that I knew I was going to use anyway). If you don’t want that to happen you can avoid it by setting the WITHOUT_X11 variable. That’s no different from earlier FreeBSD versions though, whereas this hald configuration stuff did throw me for a loop at first. In any event, once I got Xorg and HAL (and dbus, etc) installed, I had to create an fdi file in /usr/local/etc/hal/fdi/policy/ which holds the configuration details. I don’t think the name of the file matters too much, I honestly stopped researching once I got the configuration working the way I wanted. Here’s my x11-input.fdi:
File Named: /usr/local/etc/hal/fdi/policy/x11-input.fdi
--
<?xml version="1.0" encoding="UTF-8"?>
<deviceinfo version="0.2">
<device>
<match key="info.capabilities" contains="input.keyboard">
<merge key="input.xkb.Options" type="string">ctrl:nocaps</merge>
</match>
<match key="info.product" string="USB Receiver">
<merge key="input.x11_options.ZAxisMapping" type="string">5 4</merge>
</match>
</device>
</deviceinfo>
If you’re like me, you’ll want to just copy and paste this file, save it, and move on with life. Don’t do that (yet!). There are a few things you’ll need to know before you can roll with this as is. First – you’ll notice that my encoding is UTF-8. This is extremely important to take note of. If your system is not using UTF-8, this will not work. It might seem obvious, but when I first found an fdi file and tried to do the old copypasta in/out in/out I got nothing for my trouble! That’s because the encoding was ISO-8859-1 and on my system it needed to be UTF-8. Get to know your system’s encoding.
Again, in the interest of full disclosure – there is more research to be done with regards to what exactly every piece of info in this file means – I didn’t look it all up because it started working. I say this now because you’ll see the deviceinfo version atrribute is set to “0.2” and you might be wondering “why?” Well, I don’t know why. I’ve seen some 0.1 and some 0.2 – 0.2 sounded more recent, and so I am using it, and it works. I’ll look it up later 😉
Moving on in the file, you see one pair of device tags which have 2 children “match” tags. This confused me at first – I thought I needed a set of device tags for each device. As it turns out, one set is enough – the match tags inside actually are the most specific you need to get as far as “per device” configuration. The match tags essentially allow you to specify a key and value to search for, such that the merge tags within will apply the actual configuration to the device that’s “matched”. Makes sense right? You can apply the configuration to multiple devices my having a more generic match definition, or apply the config to one device only by matching for a more specific value for a given key. At this point you’re probably wondering what exactly is being matched. Enter hal-device.
HAL gives you a command line tool named hal-device, which will tell you all sorts of information about the devices on your system. I recommend that when you run it you try piping it into a pager like less, or filtering the output with grep, because it’s going to be a lot of info. The hal-device command spits out the device info in a key/value format. If you run the following, you should see some information about your keyboard:
hal-device |grep -7 keyboard
The -7 gives you 7 lines of context surrounding each instance of the text “keyboard”. On my system the output look like this:
usb.interface.description = '' (string)
usb.freebsd.devname = 'ukbd0' (string)
freebsd.driver = 'ukbd' (string)
freebsd.unit = 0 (0x0) (int)
input.device = '' (string)
info.subsystem = 'usb' (string)
info.product = 'Logitech USB Keyboard' (string)
info.capabilities = { 'input', 'input.keyboard', 'input.keys', 'button' } (string list)
info.addons.singleton = { 'hald-addon-input' } (string list)
input.x11_driver = 'kbd' (string)
freebsd.device_file = '/dev/ukbd0' (string)
info.category = 'input' (string)
info.udi = '/org/freedesktop/Hal/devices/usb_device_46d_c315_noserial_if0' (string)
input.xkb.Options = 'ctrl:nocaps' (string)
info.bus = 'usb' (string)
--
46: udi = '/org/freedesktop/Hal/devices/atkbd_0'
freebsd.driver = 'atkbd' (string)
freebsd.unit = 0 (0x0) (int)
platform.id = 'atkbd.0' (string)
freebsd.device_file = '/dev/atkbd0' (string)
info.category = 'input' (string)
info.capabilities = { 'input', 'input.keyboard', 'input.keys', 'button' } (string list)
input.device = '' (string)
info.addons.singleton = { 'hald-addon-input' } (string list)
input.x11_driver = 'kbd' (string)
input.xkb.Options = 'ctrl:nocaps' (string)
info.udi = '/org/freedesktop/Hal/devices/atkbd_0' (string)
info.parent = '/org/freedesktop/Hal/devices/atkbdc_0' (string)
info.product = 'AT Keyboard' (string)
Now that you see that, look above at the fdi file again. You’ll see how the XML format lends itself to this rather well – you can read it almost as English:
"match [the] key [named] info.capabilities [if it] contains [the value] input.keyboard".
Got it? Great. The merge tag then becomes a sort of command that says “set the value of the key named input.xkb.Options, which happens to be a type of string, to the value ctrl:nocaps”. Not so complicated right?
It gets a little trickier with the mouse, mostly because you may not realize that the mouse wheel scrolls along the Z axis. To me, the Z axis represents layers on a web page, not mouse scrolling – but this is the way it’s referred to in X, and therefore in the HAL config files that deal with the X input devices. You’ll also notice that the key I’m matching on is “info.product” – and I’m looking for “USB Receiver” – this is because for the particular wireless mouse I use (which happens to be USB, as you may have guessed), HAL lists the name as “USB Receiver”. Yours might not, in fact – it probably will not, be the same. Use a command like:
% hal-device |grep -10 mouse
--
usb.level_number = 1 (0x1) (int)
usb.device_subclass = 0 (0x0) (int)
usb.device_protocol = 0 (0x0) (int)
usb.interface.class = 3 (0x3) (int)
usb.interface.subclass = 1 (0x1) (int)
usb.interface.protocol = 2 (0x2) (int)
usb.interface.description = '' (string)
usb.freebsd.devname = 'ums0' (string)
freebsd.driver = 'ums' (string)
freebsd.unit = 0 (0x0) (int)
input.device = '/dev/sysmouse' (string)
info.subsystem = 'usb' (string)
info.product = 'USB Receiver' (string)
info.capabilities = { 'input', 'input.mouse' } (string list)
info.addons = { 'hald-addon-mouse-sysmouse' } (string list)
input.x11_driver = 'mouse' (string)
freebsd.device_file = '/dev/ums0' (string)
info.category = 'input' (string)
info.udi = '/org/freedesktop/Hal/devices/usb_device_46d_c51b_noserial_if0' (string)
input.x11_options.ZAxisMapping = '5 4' (string)
info.bus = 'usb' (string)
usb.interface.number = 0 (0x0) (int)
usb.is_self_powered = false (bool)
usb.can_wake_up = true (bool)
usb.max_power = 98 (0x62) (int)
info.parent = '/org/freedesktop/Hal/devices/usb_device_46d_c51b_noserial' (string)
To find the details about your mouse. As you can see, my configuration has been applied to that the “button” that is triggered when this mouse wheel is scrolled down (5) actually scrolls up, because it comes first, hence 4, the “up scroll button”, scrolls down. Also note – that if I were to plug in some other mouse, the configuration might not apply! This mouse configuration change only applies where info.product = “USB Receiver”. I could just as easily use info.capabilities and look for contains “input.mouse”, similar to how I matched the keyboard, thereby applying this switch to all mice on my system. This is just an example to show you that you can apply configurations my matching different keys. One benefit of doing it this way is that if my wife were to sit at this workstation, she’d be confused by the mouse – but all she’d need to do is plug her own mouse in and it would have the default scroll directions.
And there you have it! I spent a few hours hunting down this info, hopefully it took you less time to find this blog post! Enjoy! 🙂