One common ask from M1 (Apple Silicon / ARM) MacBook Pro users is to be able to re-define what the top row of physical function keys do, for example, to change the Dictation (F5) key to decrease the keyboard backlight brightness and DND (F6) to increase it, similar to other Macs. Here’s how to do that... and more!

Background

Alas, the new MacBook Pros no longer have a touchbar (I’ll miss my VS Code customizations). Instead, the keyboard has physical, full-height function keys that trigger specific hard-coded... er... functions, as seen in the screenshot below:

Current Macbook Pro’s with_function keys

Source: Apple support Magic Keyboard for 14-inch and 16-inch MacBook Pro

What if we want to use these keys to do something different? I got the basic idea from a Reddit post (in Option 1 below) to simply re-map a key’s HID key code with some other key code. This is strictly on a 1:1 basis, so no key combinations are possible, e.g. one cannot map F5 to Ctrl+F5.

The best explanation I found of HID key codes is by Adam Strzelecki at nanoant.com, macOS function key remapping with hidutil.

To summarize, every function key maps to two HID key codes, which can be explored with ioreg -l | grep FnFunctionUsageMap. For me, this generates something like this:

    | |   |     |   |       |   "FnFunctionUsageMap" = 
"0x0007003a,0x00ff0005,0x0007003b,0x00ff0004,
 0x0007003c,0xff010010,0x0007003d,0x000c0221,
 0x0007003e,0x000c00cf,0x0007003f,0x0001009b,
 0x00070040,0x000c00b4,0x00070041,0x000c00cd,
 0x00070042,0x000c00b3,0x00070043,0x000c00e2,
 0x00070044,0x000c00ea,0x00070045,0x000c00e9"

The output will differ by MacBook model! For example, in older MacBook Pros F4 opens launchpad, so the fourth pair of digits will be 0x0007003d,0xff010004 instead. And surprise, surprise, you’ll get no output on MacBooks with touchbars.

The first pair 0x0007003a,0x00ff0005 is for the first function key Decrease Brightness (F1) - when pressed alone it generates 0x00ff0005, but generates 0x0007003a when pressed with the Globe fn modifier. Therefore, Dictation (F5) is the pair 0x0007003e,0x000c00cf, meaning that the Dictation function is 0x000c00cf.

Feel free to cross check with UIKeyboardHIDUsage constants, e.g. keyboardF1 is 58 which is 0x3a in hex. Or use this hidutil key remapping generator for MacOS.

The tool to change the function mapping is hidutil, as described in Apple’s TechNote TN2450. Note that FnFunctionUsageMap above returns 32-bit numbers, whereas hidutil uses 64-bit numbers, which means we pad up each 16-bit pair, e.g. 0x000c 0x00cf becomes 0x0000 0x000c 0x0000 0x00cf or just 0xc000000cf.

Anyway, now we have all the codes we need...

Permanent re-mapping via a LaunchAgent

As mentioned, this is where it all started for me - you can follow the instructions in this Reddit post by bcock92 to re-map the Dictation (F5) and DND (F6) keys to decrease and increase they keyboard backlight. But I don’t do this!

Create a file ~/Library/LaunchAgents/com.local.KeyRemapping.plist (user agent for currently logged in user) with:

<?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>com.local.KeyRemapping</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/hidutil</string>
        <string>property</string>
        <string>--set</string>
        <string>{"UserKeyMapping":[
            {
              "HIDKeyboardModifierMappingSrc": 0xC000000CF,
              "HIDKeyboardModifierMappingDst": 0xFF00000009
            },
            {
              "HIDKeyboardModifierMappingSrc": 0x10000009B,
              "HIDKeyboardModifierMappingDst": 0xFF00000008
            }
        ]}</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

You don’t really need to wait to reboot, just launchctl load ~/Library/LaunchAgents/com.local.KeyRemapping.plist instead.

My preferred method: Via hidutil

How about mapping the keys to do something else? Of course Shortcuts come to mind!

First though, I prefer to use hidutil command directly in Terminal to change the key code mapping. Great for experimenting!

To get the same effect as in Option 1, run this (all can be on one line):

hidutil property -s '{"UserKeyMapping":[
  { "HIDKeyboardModifierMappingSrc":0xC000000CF,
    "HIDKeyboardModifierMappingDst":0xFF00000009
   },{
    "HIDKeyboardModifierMappingSrc":0x10000009B,
    "HIDKeyboardModifierMappingDst":0xFF00000008
  }
]}'

Pros:

  • If you don’t run the command, you’ll have vanilla macOS behaviour.
  • You have run-time control and can script keyboard mappings conditionally e.g. for when you are doing video editing vs. when programming.
  • You can check current mappings hidutil property -g UserKeyMapping.
  • And, un-doing the mapping or fixing mistakes is easy, simply run it again to re-map, or pass an empty array hidutil property -s '{"UserKeyMapping":[]}' and it’s back to vanilla.

Cons:

  • Changes are lost upon reboot... but you know how to workaround that!

So with this in mind, I wanted to remap the Dictation (F5) and DND (F6) keys to F13 and F14 respectively, and then configure those keys to do something else more useful?

hidutil property -s '{"UserKeyMapping":[
{   "HIDKeyboardModifierMappingSrc":0xC000000CF,
    "HIDKeyboardModifierMappingDst":0x700000068
},{ "HIDKeyboardModifierMappingSrc":0x10000009B,     
    "HIDKeyboardModifierMappingDst":0x700000069
}]}'

And now to trigger an action with those keys...

Built-in Shortcuts, F5 => F13 = Screen capture

In the Keyboard Preferences > Shortcuts, I used F13 as the shortcut for Screenshot and recording options i.e. to take a screenshot:

Keyboard shortcuts to take a screenshot, mapped to_F13

I dislike screenshot shadows, I disable them with defaults write com.apple.screencapture disable-shadow -bool true.

Also, if you can use screencapture -coW via a Shortcut, something like what I did for my Touch Bar Quick Action Scripts. This avoids the screen capture sound and gives you more flexibility to script follow-on actions.

Custom Shortcuts, F6 => F14 = Launch a Shortcuts.App Shortcut to toggle Dark Mode

(Jeez, that’s an awkward title)

Let’s map F14 to a Shortcuts Quick Action that lives in the Services Menu.

  • The shortcut uses Set Appearance to toggle between light and dark mode.
  • Make sure to enable Services Menu in the the Details tab as well as specify the Run with shortcut:

Shortcut to toggle appearance of dark / light mode, mapped to F14

Honestly, this does not work 100% of the time for me. It triggers fine when I do it in Terminal, System Preferences, Outlook, Word, Safari, Chrome, etc. but not in Excel, Firefox, VS Code, etc. Anyone know why?

Conclusion

Hope that gives ideas to those missing the touchbar. Feel free experiment e.g.

  • keep going and re-map the next three media keys too,
  • or create alternative sets to map to e.g. F15 or F16,
  • and tigger different key-maps via scripts with different hidutil configurations.

Don’t get too many ideas though! This method can’t support modifier keys Control, Option or Command, and as demonstrated above, can fail to trigger. An alternative is to use Karabiner-Elements, which I used in 2019, but I no longer use this due to kexts issues.

Updated 19 Jun 22: Onwards to the next thrilling chapter - a Script to re-map MacBook Pro function keys!