Updating modifier key mappings through defaults command tool

I am trying to automate the inital setup of my macbook, that includes installing software and changing OS X configuration to my needs.

I have tried to update the modifier keys with defaults, the code is as follow:

# The apple keyboard id (1452-567-0) should probably be modified in case you use other different model 
COMPUTER_UUID=`ioreg -rd1 -c IOPlatformExpertDevice | grep -E '(UUID)' | awk '{print $3}' | tr -d \"`
defaults write ~/Library/Preferences/ByHost/.GlobalPreferences.$COMPUTER_UUID com.apple.keyboard.modifiermapping.1452-567-0 '( { HIDKeyboardModifierMappingDst = 2;   HIDKeyboardModifierMappingSrc = 0; } )'

The problem seems that even if the pattern is changed the System Preferences does not catch the update, not even restarting the computer.

Is there any way to perform this operation in Snow Leopard?

Thanks.


It's important for the defaults command to use the correct "keyboard ID" in the key, it seems to be: com.apple.keyboard.modifiermapping.$VendorID-$ProductID-0

For example the internal keyboard for my MacBook Air uses: com.apple.keyboard.modifiermapping.1452-579-0, while the external keyboard on my iMac uses com.apple.keyboard.modifiermapping.1118-219-0

How to get the correct "keyboard ID"? On the command line you can use:

ioreg -p IOUSB -c IOUSBDevice | grep -e class -e idVendor -e idProduct

to get a list of your USB devices with the relevant parameters:

  [...]
  +-o Natural® Ergonomic Keyboard 4000@fa140000  <class IOUSBDevice, id 0x100000452, registered, matched, active, busy 0 (115 ms), retain 12>
        "idProduct" = 219
        "idVendor" = 1118

My guess is that the third parameter (the "-0" part) is a "counter", in case you have more than one keyboard of the same type.

So, to switch off the CapsLock key on my external keyboard I can now use:

defaults -currentHost write -g com.apple.keyboard.modifiermapping.1118-219-0 -array-add '<dict><key>HIDKeyboardModifierMappingDst</key><integer>-1</integer><key>HIDKeyboardModifierMappingSrc</key><integer>0</integer></dict>'

And, for completeness' sake, here's a list of possible key codes to use (from Mac OS X Hints):

  • None — –1
  • Caps Lock — 0
  • Shift (Left) — 1
  • Control (Left) — 2
  • Option (Left) — 3
  • Command (Left) — 4
  • Keypad 0 — 5
  • Help — 6
  • Shift (Right) — 9
  • Control (Right) — 10
  • Option (Right) — 11
  • Command (Right) — 12

Update: thanks to Lauri Ranta here's a command that works for Bluetooth & USB keyboards:

ioreg -n IOHIDKeyboard -r | grep -e 'class IOHIDKeyboard' -e VendorID\" -e Product

which gives you slightly different output:

+-o IOHIDKeyboard  <class IOHIDKeyboard, id 0x100000489, registered, matched, active, busy 0 (0 ms), retain 8>
  |   "Product" = "Apple Wireless Keyboard"
  |   "VendorID" = 1452
  |   "ProductID" = 570

The problem I'm having is that every time I plug my keyboard in, it gets a different ID in ioreg. All I need to do is swap the command and the apple keys.

I decided to solve this in my case the "wrong way" using AppleScript and the System Preferences GUI. It's an ugly hack, but it works! Your mileage may vary.

--The beginning of the name of the target keyboard (to type into the drop-down selection list)
set keyboardName to "natural"

--reboot system preferences to make GUI state more predictable
tell application "System Preferences"
    quit
    delay 1
    activate
    delay 1
    activate
end tell

tell application "System Events"

    --Bring up keyboard prefs
    key code 53 --escape
    keystroke "f" using command down
    delay 0.5
    key code 53 --escape
    keystroke "keyboard"
    delay 0.5
    key code 36 --return
    delay 1

    --Open modifier keys submenu
    repeat 4 times
        keystroke tab using shift down
        --delay 0.1
    end repeat
    --delay 0.1
    keystroke space
    delay 0.1

    --Select keyboard
    keystroke space
    keystroke keyboardName
    keystroke return
    delay 0.1

    --Select "option key" drop-down
    repeat 3 times
        keystroke tab
    end repeat

    delay 0.5

    --Open drop-down and go to top
    keystroke space
    delay 0.1
    repeat 4 times
        key code 126 --up arrow
    end repeat

    --Select "command" option
    repeat 3 times
        key code 125 --down arrow
    end repeat
    delay 0.1

    keystroke return

    -- Select "command key" drop-down
    keystroke tab
    delay 0.1

    --Open drop-down and go to top
    keystroke space
    delay 0.1
    repeat 4 times
        key code 126 --up arrow
    end repeat

    --Select "command" option
    repeat 2 times
        key code 125 --down arrow
    end repeat
    delay 0.1

    keystroke return
    delay 0.1

    --Commit changes! phew.
    keystroke return
end tell

I, too, have been unable to get defaults write (or updating .plist files) to affect my HIDKeyboardModifierMappingSrc and HIDKeyboardModifierMappingDst settings under Snow Leopard.

I'm trying to "pre-populate" my Guest account to support my PS2-to-USB keyboard (which apparently has Option and Command swapped.) Other pages' suggestions to "log out and log back in" don't help; "logging out" of the Guest account deletes all my changes.

(I have successfully updated my "com.apple.dock" and "com.apple.menuextra.clock" plist files to customize the dock and the 24-hour clock, respectively, which makes it even more frustrating that the Keyboard Modifiers won't work...)

I've tried using "dtruss -asf" to compare what "System Preferences.app" is doing (which apparently works) to what "defaults" is doing. Nearest I can figure, I see messages along the lines of:

   Foundation`+[__NSOperationInternal _observeValueForKeyPath:ofObject:changeKind:oldValue:newValue:indexes:context:]+0x151
   Foundation`NSKeyValueNotifyObserver+0x81
   Foundation`NSKeyValueDidChange+0x1ca
   Foundation`-[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:usingBlock:]+0x176

If anybody's figured out a solution that works under Snow Leopard, I'd be happy to hear it...