87

I used to use Karabiner to remap Right Alt to Right Control (as an Emacs user I use Control MUCH more that Alt) but updating to Sierra broke this. Downgrading is not an option.

How do I manually remap keys in Sierra? I specifically would like to remap Right Alt to Right Control.

5 Answers5

83

Apple's Technical Note TN2450 describes how to remap keys. Running the following command will remap Right Alt to be Right Control.

hidutil property --set '{"UserKeyMapping":
    [{"HIDKeyboardModifierMappingSrc":0x7000000e6,
      "HIDKeyboardModifierMappingDst":0x7000000e4}]
}'

Note that the above command is not switching the Right Alt and Right Control. They will both be Right Control. If you have a MacBook, you will not notice this until plugging in an external keyboard. If you want to switch Right Alt and Right Control, you need to add a second switch command, like the following.

hidutil property --set '{"UserKeyMapping":
    [{"HIDKeyboardModifierMappingSrc":0x7000000e4,
      "HIDKeyboardModifierMappingDst":0x7000000e6},
     {"HIDKeyboardModifierMappingSrc":0x7000000e6,
      "HIDKeyboardModifierMappingDst":0x7000000e4}]
}'

The table at the bottom of the Technical Note has a list of hex values for each key. To generalize the above answer to switch any keys, you must or the hex value from that list together with 0x700000000. The following Python code demonstrates one way to do this.

In [1]: def convert(val):
   ...:     int_val = int(val, 16)
   ...:     int_ref = 0x700000000
   ...:
   ...:     return hex(int_ref | int_val)
   ...:

In [2]: r_alt = '0xE6'

In [3]: print(convert(r_alt))
0x7000000e6
Bachsau
  • 167
  • Nice - thanks! Is there any way to restore the mappings to their defaults with this command? – Aᴄʜᴇʀᴏɴғᴀɪʟ Jul 10 '17 at 21:48
  • 2
    @Cᴀʟʟᴏᴅᴀᴄɪᴛʏ to reset any key, you simply run the command again with that key's value in both Src and Dst. – Steven C. Howell Jul 11 '17 at 12:38
  • 1
    This reverts on reboot. I can re-run this command on each boot, but I'd prefer not to do this. Is there a way to make this permanent? – firebush Aug 07 '18 at 14:49
  • 3
    @firebush This post suggests using a login hook to make commands like this persistent: https://stackoverflow.com/a/46460200/629530 – firebush Aug 07 '18 at 21:56
  • 1
    @StevenC.Howell How about combinations of function modifiers? For example, the backward slash \ in my keyboard is Shift+Option+7 which I would like to replace with something simpler such as Option+. which I hardly use. How do I do that? Thanks for your help. – Nanashi No Gombe Oct 08 '18 at 20:12
  • @StevenC.Howell I'm looking for a way to remap a combinatino of keys to a single key as well. In my case (on a Spanish - ISO): ¡ (and ¿) to be fn + Delete Forward. Is that possible? – Xavi Oct 19 '18 at 08:58
  • @NanashiNoGombe I don't think this particular facility works on that level, this is a pretty low-level utility for mapping individual keys on the keyboard. It does not appear to provide any control over key combinations. – tripleee Jan 23 '19 at 11:46
  • I tried this with keycodes I got from https://manytricks.com/keycodes/. Does not work. – Bachsau Jun 23 '19 at 13:30
  • @Bachsau, try the keycodes from the Tichnical Note, TN2450. – Steven C. Howell Jun 24 '19 at 15:00
  • 1
    @StevenC.Howell Unfortunately that list is incomplete. I want to remap the menu key on a windows keyboard to work as a second command key. – Bachsau Jun 24 '19 at 15:04
  • @Bachsau, It may be that the keycode for the menu key is something else from that reference. The Technical notes references the USB HID Usage Tables Specification, Section 10 Keyboard /Keypad Page, with a broken link. I found the referenced pdf here. Perhaps that document has they keycode you are looking for. – Steven C. Howell Jun 24 '19 at 19:51
  • 1
    @Bachsau, I believe you can find the key you're looking for listed as "Keyboard Application - 0x65" – Mandark Oct 18 '19 at 16:24
  • @Mandark That worked! Thanks. I really should have looked somewhat deeper in the HID specifications and their naming of keys. – Bachsau Dec 21 '19 at 09:46
  • In case that you might want to restore the defaults:
    # setting it to 'null' does not work on my MBP High Sierra
    hidutil property --set '{"UserKeyMapping":[]}'
    
    – clarkttfu Aug 19 '19 at 13:48
  • It seems this won't work for me - I want to make the ⏏ button on my keyboard do something useful, but it's not listed in the linked documentation – Ky - Apr 27 '20 at 20:44
  • 1
    I have been mapping my Caps Lock to F10 on all my computers for the past 10 years. In OS X/macOS, I have always used Karabiner to do this. But I have always found it to be an imperfect solution, and I've found more and more issues that Karabiner causes, even though it is a very capable piece of software in its own right. Finally, I've found this solution and I can remap a few keys (all I was using Karabiner for!) without keeping anything running in the background! – Steven Lu Apr 20 '21 at 04:23
36

A more general and user-friendly approach is to use Karabiner-Elements, which is a version of Karabiner that works on Sierra.

mikaraento
  • 571
  • 3
  • 5
  • 2
    Quick update on Karabiner-Elements. It works on macOS High Sierra 10.13.3 (17D102) I successfully swapped my right Command and Option keys. – Steve Clement Mar 20 '18 at 00:16
  • Third party apps are never "more general". Why would anyone clutter their system with apps doings things that are built right into the OS? – Bachsau Jun 23 '19 at 13:14
  • 2
    @Bachsau it is more general in that allows you to do a lot of other stuff, e.g. setting up key combinations, macros and profiles, or using shared and/or split mapping configurations for any internal and external keyboards. – jaynetics Aug 27 '19 at 12:17
  • 1
    @Bachsau I keep different profiles for each of my keyboards, so they function the same. Very nice app – Alex Nov 25 '19 at 22:22
  • Fantastic app. Works on Big Sur like a charm. – Matt Komarnicki Sep 21 '21 at 20:16
19

This is an addendum to Steven C. Howell's answer.

I have a new MacBook Pro with a Scandinavian keyboard. This new model no longer has an Esc key, which is a significant handicap for me.

I discovered that the following will translate the § key (upper left, left of 1 and above tab key) - which I don't think I have ever used for anything before today - to produce Esc.

hidutil property --set '{"UserKeyMapping":[{"HIDKeyboardModifierMappingSrc":0x700000064,"HIDKeyboardModifierMappingDst":0x700000029}]}'

In Apple's documentation (linked from Steven's answer) this is labelled as "Keyboard Non-US \ and |" (0x64).

Several of the "non-US" keys are hard to discover because they typically refer to keys which have a different label on the keyboard you are using. (I also discovered that "Grave accent and tilde" refers to the key between left shift and z, which on my keyboard produces <. I was unable to establish which key corresponds to "Non-US # and ~" and did not experiment further once I found my key.) If you want to experiment, try running the script in the terminal until you find the key you need:

for ((i=1;i<=128;++i)); do
    printf '0x7000000%0x\n' "$i"
    printf '{"UserKeyMapping":[{"HIDKeyboardModifierMappingSrc":0x7000000%0x,"HIDKeyboardModifierMappingDst":0x70000000a}]}' "$i" |
    xargs -0 hidutil property --set >/dev/null
    read -p "Type some stuff: "
    hidutil property --set '{"UserKeyMapping":[{}]}' >/dev/null
done

This loops over the keycodes and changes one at a time, in the hope that you can find through trial and error a key which is not particularly useful for you. Try typing something when it asks you to -- if you get a g instead of what you expected, you have found the right key code. (Change 0x70000000a to something else if g is not a convenient choice for you. Maybe you want to remap the g key?) When you are done typing, just hit Enter to proceed to the next key.

For what it's worth, the last command inside the loop is how you zap all UserKeyMapping settings:

hidutil property --set '{"UserKeyMapping":[{}]}'

When you initially run hidutil property --get UserKeyMapping it produces

(null)

but it seems you cannot feed back this value to zap the setting (or rather, it accepts but ignores this input).

(If you are unfamiliar with the Terminal, just copy/paste the thing from for until done at your bash$ prompt or similar.)

As per this related Stack Overflow question add the command to your launchd configuration to make this change persistent.

In case some readers are not comfortable doing this on their own, here's a quick script which does this for you. Again, just copy/paste this at the Terminal prompt, or perhaps better copy/paste this into a new text file and then run it with sh filename (where obviously filename needs to be changed to the name of the file where you saved the script).

This needs to be run as root.

#!/bin/sh

plistdir=/Library/LaunchDaemons

test -w "$plistdir" || { echo "$0: Need write access to $plistdir" >&2 exit 1 }

test -e "$plistdir"/userkeymapping.plist && { echo "$0: $plistdir/userkeymapping.plist already exists -- aborting" >&2 exit 2 }

cat<<: >"$plistdir"/userkeymapping.plist <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>userkeymapping</string> <key>ProgramArguments</key> <array> <string>hidutil</string> <string>property</string> <string>--set</string> <string>{"UserKeyMapping":[{"HIDKeyboardModifierMappingSrc":0x700000064,"HIDKeyboardModifierMappingDst":0x700000029}]}</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist> : launchctl load "$plistdir"/userkeymapping.plist

Obviously if you ended up with some other key than 0x700000064 you need to change that in the script above, or in the file /Library/LaunchDaemons/userkeymapping.plist it ends up creating if you already ran the above.

The script is just a one-off so if you saved it to a file, it is safe to remove the script after you have executed it.


An earlier version of this answer had a .plist file in ~/Library/LaunchAgents and did not require root access; but this all changed with the MacOS Sonora 14.3 upgrade. The old solution no longer works.

tripleee
  • 1,027
  • 1
    An earlier version of this answer advocated using LoginHook but it wasn't working for me, and based on https://stackoverflow.com/a/22872222/874188 I switched to a launchd approach instead. – tripleee Feb 27 '19 at 06:58
  • 1
    As an ardent vi/vim fan this is precisely what I was looking for. Thank you. – Philip Kearns Apr 14 '19 at 17:57
  • I believe this should work with zsh too, but remain baffled that Apple switched to this wicked shell as the default for new users. – tripleee Apr 16 '21 at 11:04
  • Excellent, spilt some tea on my keyboard and made the Esc key sticky - this is the perfect solution! – Dave Griffiths Jan 05 '24 at 17:59
  • In the last month or two I have found that this still works, but somehow not immediately after I restart my computer. It could be something with how I rigged it to run via launchd too; but just mentioning here in case somebody else runs into the same problem. – tripleee Jan 06 '24 at 11:21
  • After the latest Sonoma update (14.3) the previous launchctl script I had posted here would complain that it needs to be run as root; the precise message is Run as root to remap alphanumerics / special characters with UserKeyMapping. I have now updated this answer to install the script as a root-owned launch daemon instead. – tripleee Jan 24 '24 at 09:09
16

If all you want to do is remap a single key

Example: How to Remap Escape Key on Mac

  1. Go to the  Apple menu and choose “System Preferences” and then go to the “Keyboard” preference panel and choose the “Keyboard” tab

  2. Click on the “Modifier Keys” button in the lower right corner

  3. Choose the key you want to remap and modify to perform the Escape function: Caps Lock (our recommendation), Control, Option, or Command
  4. Select “Escape” from the dropdown list corresponding to the key you want to remap as a hardware Escape key then click “OK” to set the change

reference

1

Remap eject key to backslash was closed and linked here without actually answering the question: The mapping for the eject key is 0xC000000B8 .

Source: https://gist.github.com/vadikgo/0ec180f8aa3324911c1daea305b92ece