On Mac OS X, there's a fantastic tool called Karabiner (previously KeyRemap4MacBook) that lets you control how your keyboard and mouse work — things like keyboard repeat rate, mouse acceleration, arrow keys, function keys, control/escape/shift/option/fn, adding keys to enter unicode characters, and many other things. I use it primarily for remapping the function keys. On my laptop, I want F1,F2 to control brightness, F3,F4,F5,F6,F7,F8,F9 to be function keys, and F10,F11,F12 to control volume. Mac OS X offers in the System Preferences a choice of having them all be function keys or all be special functions. I want five to be special functions and seven to be function keys. Karabiner lets me do that.

Karabiner's standard settings dialog lets me swap just some of these keys. First, I told Mac OS X to make them all function keys. Then in Karabiner, I selected

  • Change F1..F19 Key & Functional Key
    • Change Functional Key
      • Fn+Functional Keys to F1..F12
        • [X] Fn+Brightness Adjust to F1,F2
        • [X] Fn+Speaker Controls to F10,F11,F12
    • Change F1..F19 Key
      • F1..F12 to Functional Keys
        • [X] F1,F2 to Brightness Adjust
        • [X] F10,F11,F12 to Speaker Controls

Great! It now behaves how I want.

Except… I want more! Once I got an external keyboard, I wanted the function keys to behave differently. The external keyboard has its own volume keys, so I don't want them swapped there. I use it with an external display, so the brightness keys aren't useful to swap.

Karabiner offers an XML configuration file when the standard settings dialog isn't enough. Reading the XML file format documentation, I saw that I can restrict certain settings to only run in some apps or some devices. I came up with this equivalent for the above settings (and then later realized all the built-in settings have XML files like this one):

  <item>
    <name>Swap F1,F2 with Brightness keys</name>
    <identifier>amitp.brightness</identifier>
    <autogen>__KeyToKey__ KeyCode::F1, ConsumerKeyCode::BRIGHTNESS_DOWN</autogen>
    <autogen>__KeyToKey__ KeyCode::F2, ConsumerKeyCode::BRIGHTNESS_UP</autogen>
    <autogen>__KeyToKey__ ConsumerKeyCode::BRIGHTNESS_DOWN, KeyCode::F1</autogen>
    <autogen>__KeyToKey__ ConsumerKeyCode::BRIGHTNESS_UP, KeyCode::F2</autogen>
  </item>
  <item>
    <name>Swap F10,F11,F12 with Volume keys</name>
    <identifier>amitp.volume</identifier>
    <autogen>__KeyToKey__ KeyCode::F10, ConsumerKeyCode::VOLUME_MUTE</autogen>
    <autogen>__KeyToKey__ KeyCode::F11, ConsumerKeyCode::VOLUME_DOWN</autogen>
    <autogen>__KeyToKey__ KeyCode::F12, ConsumerKeyCode::VOLUME_UP</autogen>
    <autogen>__KeyToKey__ ConsumerKeyCode::VOLUME_MUTE, KeyCode::F10</autogen>
    <autogen>__KeyToKey__ ConsumerKeyCode::VOLUME_DOWN, KeyCode::F11</autogen>
    <autogen>__KeyToKey__ ConsumerKeyCode::VOLUME_UP, KeyCode::F12</autogen>
  </item>

However, I want to swap these keys only on the laptop keyboard. I used Karabiner's Event Viewer helper application to figure out that my laptop keyboard's device id was 0x0259. I could then modify the settings to only apply to my laptop keyboard:

  <deviceproductdef>
    <productname>Apple_laptop</productname>
    <productid>0x0259</productid>
  </deviceproductdef>
  <item>
    <name>Swap F1,F2 with Brightness keys</name>
    <appendix>For laptop keyboard only</appendix>
    <identifier>amitp.brightness</identifier>
    <device_only>DeviceVendor::APPLE_COMPUTER, DeviceProduct::Apple_laptop</device_only>
    <autogen>__KeyToKey__ KeyCode::F1, ConsumerKeyCode::BRIGHTNESS_DOWN</autogen>
    <autogen>__KeyToKey__ KeyCode::F2, ConsumerKeyCode::BRIGHTNESS_UP</autogen>
    <autogen>__KeyToKey__ ConsumerKeyCode::BRIGHTNESS_DOWN, KeyCode::F1</autogen>
    <autogen>__KeyToKey__ ConsumerKeyCode::BRIGHTNESS_UP, KeyCode::F2</autogen>
  </item>
  <item>
    <name>Swap F10,F11,F12 with Volume keys</name>
    <identifier>amitp.volume</identifier>
    <appendix>For laptop keyboard only</appendix>
    <device_only>DeviceVendor::APPLE_COMPUTER, DeviceProduct::Apple_laptop</device_only>
    <autogen>__KeyToKey__ KeyCode::F10, ConsumerKeyCode::VOLUME_MUTE</autogen>
    <autogen>__KeyToKey__ KeyCode::F11, ConsumerKeyCode::VOLUME_DOWN</autogen>
    <autogen>__KeyToKey__ KeyCode::F12, ConsumerKeyCode::VOLUME_UP</autogen>
    <autogen>__KeyToKey__ ConsumerKeyCode::VOLUME_MUTE, KeyCode::F10</autogen>
    <autogen>__KeyToKey__ ConsumerKeyCode::VOLUME_DOWN, KeyCode::F11</autogen>
    <autogen>__KeyToKey__ ConsumerKeyCode::VOLUME_UP, KeyCode::F12</autogen>
  </item>

It works great!

I also solved another annoyance. My external keyboard always sends number keys from the number pad, and I'd rather send arrow keys. Karabiner can do that too. I had to find the device vendor and product id from the event viewer, and then I used these settings:

  <devicevendordef>
    <vendorname>KBTalking</vendorname>
    <vendorid>0x099a</vendorid>
  </devicevendordef>

  <deviceproductdef>
    <productname>Bluetooth_keyboard</productname>
    <productid>0x0100</productid>
  </deviceproductdef>
  
  <item>
    <name>Amit's Keypad</name>
    <appendix>Amit's keypad settings</appendix>
    <identifier>amitp.keypad</identifier>
    <device_only>DeviceVendor::KBTalking, DeviceProduct::Bluetooth_keyboard</device_only>
    <autogen>__KeyToKey__ KeyCode::KEYPAD_5, KeyCode::F19</autogen>
    <autogen>__KeyToKey__ KeyCode::KEYPAD_2, KeyCode::CURSOR_DOWN</autogen>
    <autogen>__KeyToKey__ KeyCode::KEYPAD_4, KeyCode::CURSOR_LEFT</autogen>
    <autogen>__KeyToKey__ KeyCode::KEYPAD_6, KeyCode::CURSOR_RIGHT</autogen>
    <autogen>__KeyToKey__ KeyCode::KEYPAD_8, KeyCode::CURSOR_UP</autogen>
    <autogen>__KeyToKey__ KeyCode::KEYPAD_1, KeyCode::END</autogen>
    <autogen>__KeyToKey__ KeyCode::KEYPAD_3, KeyCode::PAGEDOWN</autogen>
    <autogen>__KeyToKey__ KeyCode::KEYPAD_7, KeyCode::HOME</autogen>
    <autogen>__KeyToKey__ KeyCode::KEYPAD_9, KeyCode::PAGEUP</autogen>
    <autogen>__KeyToKey__ KeyCode::KEYPAD_DOT, KeyCode::FORWARD_DELETE</autogen>
  </item>

These small things have made my computing more pleasant. There's so much more that Karabiner can do, but I haven't explored most of its capabilities.

P.S. If you find Karabiner useful, consider donating.

Labels: ,

0 comments: