MacOS – swap mapping of left and right shift keys

keyboardmacos

I have an odd, highly specific problem when using my third-party USB keyboard with my MacBook Pro: option + left-shift + right-arrow (which as Mac programmers will know means "highlight the word to the right of the cursor") does not work. The reasons behind this are unclear, but I know it's not to do with user-level software preferences, nor directly to with a low-level hardware problem (see my original question on superuser for the full troubleshooting logic).

I should note that it's a made-for-Windows keyboard: a Thinkpad USB Keyboard with Trackpoint, in fact—the trackpoint being the reason I like it so much and stick with it despite its problems. So the key I'm that I'm mapping to option in the System Preferences is in fact the "Windows Logo" key.

It does work if I use option + RIGHT-shift + right-arrow but this is a very clumsy hand position for moving quickly around my documents. So I'd like to try swapping the Mac's interpretation of the left and right shift keys. Is this possible, and if so, how?


Update:

At the suggestion of Tetsujin
I tried Karabiner but it seems like the problem is happening at a lower level. The keyboard event for right-arrow never even reaches Karabiner if the Windows-logo and (physical) left-shift keys are held down (and it doesn't matter whether Windows logo is mapped to another modifier: the problem is stuck to that specific physical key combo).

In response to the comment by Insomniac Software
here's what the Karabiner EventViewer says when I press:

Windows-logo + left-shift + left-arrow:

eventType:keyMod          code:0x3a       name:Option_L        flags:Opt                                misc:KeyCode::OPTION_L  characters:    
eventType:keyMod          code:0x38       name:Shift_L         flags:Shift Opt                          misc:KeyCode::SHIFT_L   characters:    
eventType:keyDown         code:0x7b       name:Left            flags:Shift Opt NumPad Fn                misc:KeyCode::CURSOR_LEFT   characters:    
eventType:keyUp           code:0x7b       name:Left            flags:Shift Opt NumPad Fn                misc:KeyCode::CURSOR_LEFT   characters:    
eventType:keyMod          code:0x38       name:Shift_L         flags:Opt                                misc:KeyCode::SHIFT_L   characters:    
eventType:keyMod          code:0x3a       name:Option_L        flags:                                   misc:KeyCode::OPTION_L  characters:    

Windows-logo + left-shift + right-arrow:

eventType:keyMod          code:0x3a       name:Option_L        flags:Opt                                misc:KeyCode::OPTION_L  characters:    
eventType:keyMod          code:0x38       name:Shift_L         flags:Shift Opt                          misc:KeyCode::SHIFT_L   characters:    
eventType:keyMod          code:0x38       name:Shift_L         flags:Opt                                misc:KeyCode::SHIFT_L   characters:    
eventType:keyMod          code:0x3a       name:Option_L        flags:                                   misc:KeyCode::OPTION_L  characters:    

Windows-logo + right-shift + right-arrow:

eventType:keyMod          code:0x3a       name:Option_L        flags:Opt                                misc:KeyCode::OPTION_L  characters:    
eventType:keyMod          code:0x3c       name:Shift_R         flags:Shift Opt                          misc:KeyCode::SHIFT_R   characters:    
eventType:keyDown         code:0x7c       name:Right           flags:Shift Opt NumPad Fn                misc:KeyCode::CURSOR_RIGHT  characters:    
eventType:keyUp           code:0x7c       name:Right           flags:Shift Opt NumPad Fn                misc:KeyCode::CURSOR_RIGHT  characters:    
eventType:keyMod          code:0x3c       name:Shift_R         flags:Opt                                misc:KeyCode::SHIFT_R   characters:    
eventType:keyMod          code:0x3a       name:Option_L        flags:                                   misc:KeyCode::OPTION_L  characters:    

(NB: windows-logo + right-shift + left-arrow also works, in case you were wondering).

Note that in the specific case of windows-logo + left-shift + right-arrow Karabiner sees fewer events in the first place. I see the same story (at least, from what I can tell) from Karabiner's debug log: fewer events generated by that specific problematic combo. I've trimmed away everything up to --Debug-- on each line, for the same three cases:

# Windows-logo + left-shift + left-arrow
KeyboardEventCallback [ caught]: eventType 12, flags 0x80080020, key 0x003a, kbdType  40, repeat = 0
KeyboardEventCallback [sending]: eventType 12, flags 0x00080020, key 0x003a, kbdType  40, repeat = 0
KeyboardEventCallback [ caught]: eventType 12, flags 0x800a0022, key 0x0038, kbdType  40, repeat = 0
KeyboardEventCallback [sending]: eventType 12, flags 0x000a0022, key 0x0038, kbdType  40, repeat = 0
UpdateEventFlagsCallback [ caught]: flags 0x002a0022
KeyboardEventCallback [ caught]: eventType 10, flags 0x802a0022, key 0x007b, kbdType  40, repeat = 0
UpdateEventFlagsCallback [sending]: flags 0x002a0022
KeyboardEventCallback [sending]: eventType 10, flags 0x002a0022, key 0x007b, kbdType  40, repeat = 0
KeyboardEventCallback [ caught]: eventType 11, flags 0x802a0022, key 0x007b, kbdType  40, repeat = 0
UpdateEventFlagsCallback [ caught]: flags 0x000a0022
KeyboardEventCallback [sending]: eventType 11, flags 0x002a0022, key 0x007b, kbdType  40, repeat = 0
UpdateEventFlagsCallback [sending]: flags 0x000a0022
KeyboardEventCallback [ caught]: eventType 12, flags 0x80080020, key 0x0038, kbdType  40, repeat = 0
KeyboardEventCallback [sending]: eventType 12, flags 0x00080020, key 0x0038, kbdType  40, repeat = 0
KeyboardEventCallback [ caught]: eventType 12, flags 0x80000000, key 0x003a, kbdType  40, repeat = 0
KeyboardEventCallback [sending]: eventType 12, flags 0x00000000, key 0x003a, kbdType  40, repeat = 0


# Windows-logo + left-shift + right-arrow
KeyboardEventCallback [ caught]: eventType 12, flags 0x80080020, key 0x003a, kbdType  40, repeat = 0
KeyboardEventCallback [sending]: eventType 12, flags 0x00080020, key 0x003a, kbdType  40, repeat = 0
KeyboardEventCallback [ caught]: eventType 12, flags 0x800a0022, key 0x0038, kbdType  40, repeat = 0
KeyboardEventCallback [sending]: eventType 12, flags 0x000a0022, key 0x0038, kbdType  40, repeat = 0
KeyboardEventCallback [ caught]: eventType 12, flags 0x80080020, key 0x0038, kbdType  40, repeat = 0
KeyboardEventCallback [sending]: eventType 12, flags 0x00080020, key 0x0038, kbdType  40, repeat = 0
KeyboardEventCallback [ caught]: eventType 12, flags 0x80000000, key 0x003a, kbdType  40, repeat = 0
KeyboardEventCallback [sending]: eventType 12, flags 0x00000000, key 0x003a, kbdType  40, repeat = 0


# Windows-logo + right-shift + right-arrow
KeyboardEventCallback [ caught]: eventType 12, flags 0x80080020, key 0x003a, kbdType  40, repeat = 0
KeyboardEventCallback [sending]: eventType 12, flags 0x00080020, key 0x003a, kbdType  40, repeat = 0
KeyboardEventCallback [ caught]: eventType 12, flags 0x800a0024, key 0x003c, kbdType  40, repeat = 0
KeyboardEventCallback [sending]: eventType 12, flags 0x000a0024, key 0x003c, kbdType  40, repeat = 0
UpdateEventFlagsCallback [ caught]: flags 0x002a0024
KeyboardEventCallback [ caught]: eventType 10, flags 0x802a0024, key 0x007c, kbdType  40, repeat = 0
UpdateEventFlagsCallback [sending]: flags 0x002a0024
KeyboardEventCallback [sending]: eventType 10, flags 0x002a0024, key 0x007c, kbdType  40, repeat = 0
KeyboardEventCallback [ caught]: eventType 11, flags 0x802a0024, key 0x007c, kbdType  40, repeat = 0
UpdateEventFlagsCallback [ caught]: flags 0x000a0024
KeyboardEventCallback [sending]: eventType 11, flags 0x002a0024, key 0x007c, kbdType  40, repeat = 0
UpdateEventFlagsCallback [sending]: flags 0x000a0024
KeyboardEventCallback [ caught]: eventType 12, flags 0x80080020, key 0x003c, kbdType  40, repeat = 0
KeyboardEventCallback [sending]: eventType 12, flags 0x00080020, key 0x003c, kbdType  40, repeat = 0
KeyboardEventCallback [ caught]: eventType 12, flags 0x80000000, key 0x003a, kbdType  40, repeat = 0
KeyboardEventCallback [sending]: eventType 12, flags 0x00000000, key 0x003a, kbdType  40, repeat = 0

Best Answer

From your edits, it seems like the specific keyboard you're using is causing the problem. I don't know any way to fix that, unfortunately, but I thought I'd answer the original question anyway.

You can remap modifier keys in OS X from at least 10.4 through to 10.10.4 by changing a Property List (.plist) file. The file is located within ~/Library/Preferences/ByHost, and is named .GlobalPreferences.[a long identifier].plist, where each user's long identifier varies. (In the Terminal, you can just use Tab-completion to pick up the specific name of your file.)

Here are the steps to copy, convert, edit, and replace the .plist:

  1. In System Preferences > Keyboard, click "Modifier Keys" and remap at least one of the buttons (e.g. Caps Lock > Control). This will ensure that the proper section exists in the .plist, and make it easier to find.
  2. In Terminal, go to or create a directory where you'll do the work, then:

    1. cp ~/Library/Preferences/ByHost/.GlobalPreferences (then press Tab to autocomplete the unique filename)
    2. plutil -convert xml1 -o ./keys-xml.plist ./keys-binary.plist
  3. Open the converted XML file. (You can use open /Applications/TextEdit.app keys-xml.plist to launch TextEdit from the Terminal.)

  4. Locate the section in the file called "com.apple.keyboard.modifiermapping...". You should see at least one "dict" group within that section. It will actually be one entry per specific key that's been remapped, so if you remapped Control to Shift, you'd see entries remapping LeftControl to LeftShift and RightControl to RightShift.
  5. Modify the existing entry using the table below, changing the HIDKeyboardModifierMappingSrc key value to the Source key (the physical button you'll press, and the HIDKeyboardModifierMappingDst key value to the Destination key you want (the key you want to take effect).
  6. Copy the entire dict section (from <dict> to </dict>, including the key mappings, and paste one per key you want to remap, changing the Src and Dst for each.
  7. Save and close the file. Then, back in Terminal:
    1. plutil -convert binary1 -o ./keys-binary_new.plist keys-xml.plist
    2. cp ./keys-binary_new.plist ~/Library/Preferences/ByHost/.GlobalPreferences (then Tab to autocomplete)
  8. Log out and back in for the changes to take effect.

Here are the key values to use for each modifier key in the Src/Dst mapping:

+-----------------+-----------+
|    Key Name     | Key Value |
+-----------------+-----------+
| 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 |
| ~~Kernel Panic~~|      ~~16~|
+-----------------+-----------+

NB: Key Code 16 should typically not be used.

Source: Rewritten from http://hints.macworld.com/article.php?story=20060825072451882