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.