For the last few years I’ve been using an early 2015 MacBook Pro for work and have sometimes suffered from RSI due in part to the terrible ergonomics of the Magic Keyboard. I solved this by buying a Mac-compatible mechanical keyboard from Varmilo.

Unfortunately, MacOS has never correctly detected the layout of this keyboard. It thinks it’s ANSI layout (no extra key left of ‘z’) but it’s actually ISO layout (backtick/tidle left of z). The Mac’s default behaviour is to swap the section mark and plus-minus (§, ±) keys with the backtick/tilde key. It’s usable but annoying. Until recently I solved this by remapping the offending keys using Karabiner Elements.

Fast-forward a few years and I’ve got a newer MacBook Pro, circa 2019. No matter how many times I go through the setup, Karabiner Elements always fails to install its driver on this machine. I needed a new solution. Forunately, there’s a CLI for changing user keymappings on MacOS, hidutil. I found some scripts on the Apple Stack Exchange and rewrote them into a shell utility to make it easier to use.

#!/bin/sh
# Adapted from https://apple.stackexchange.com/a/353941 under CC BY 4.0
set -e

PLIST="/Library/LaunchDaemons/org.custom.tilde-switch.plist"

case $1 in
on)
  hidutil property --set '{"UserKeyMapping":[
    {
      "HIDKeyboardModifierMappingSrc": 0x700000035,
      "HIDKeyboardModifierMappingDst": 0x700000064
    },
    {
      "HIDKeyboardModifierMappingSrc": 0x700000064,
      "HIDKeyboardModifierMappingDst": 0x700000035
    }
  ]}'
  ;;
off)
  hidutil property --set '{"UserKeyMapping":[]}'
  ;;
install)
  sudo sh -c "cat > ${PLIST}" << EOF
<?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>org.custom.tilde-switch</string>
    <key>Program</key>
    <string>${HOME}/bin/switch-tilde-keys</string>
    <key>ProgramArguments</key>
      <array>
        <string>on</string>
      </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <false/>
  </dict>
</plist>
EOF
  sudo launchctl load -w -- ${PLIST}
  ;;
uninstall)
  sudo launchctl unload -w -- ${PLIST} && sudo rm -f -- ${PLIST}
  ;;
*)
  echo "Usage: $0 on|off|install|uninstall" >&2
  exit 1
  ;;
esac

The way this swaps keymaps, it fixes my external keyboard but also makes the same change to the MacBook’s internal keyboard, making that one wrong. For that reason it’s convenient to have the commands to disable the change or uninstall the PList file in case I’m traveling or my hardware changes. Thanks to the Covid situation, I haven’t been in the office for a year and a half but that may eventually change.