47

I'm using a MacBook Pro with a Swedish physical keyboard but with a U.S. layout defined in System Preferences. However, the tilde (~) and plus-minus (±) keys seem switched. The following key combination should produce a ~:

Screenshot of a Swedish keyboard with U.S. layout

Because the mapping in System Preferences says so:

Screenshot of the Keyboard Preferences pane showing the Input Sources tab

But it results in a ± symbol.

It seems these two keys (± and ~) are switched somehow. How do I make them work as shown in System Preferences?

agarza
  • 2,274
Conor Taylor
  • 601
  • 1
  • 6
  • 9
  • 1
    The 2nd picture appears to be showing some kind of hybrid between US ANSI & UK ISO layouts. US keyboards have no key to the left of Z, UK keyboards do. Tilde is to the left of Z on UK, but left of 1 on US. You might be better using a UK map instead. – Tetsujin Jul 01 '18 at 08:50
  • @Tetsujin you're right. But switching to British makes no difference, the keys are still swapped. – Conor Taylor Jul 01 '18 at 09:19
  • 1
    Ah, ok, darnit. I'm out of ideas, sorry. – Tetsujin Jul 01 '18 at 09:22
  • 1
    The 2nd picture is in fact the US ISO layout, what you are supposed to get when US is used on an ISO keyboard with the extra key. Aren't the keys also swapped when you are NOT holding down SHIFT? – Tom Gewecke Jul 01 '18 at 09:33
  • 2
    Here is a similar question with a couple answers: https://apple.stackexchange.com/questions/239395/and-keys-are-swapped – Tom Gewecke Jul 01 '18 at 09:40
  • @TomGewecke yes they're also swapped when not holding down shift. – Conor Taylor Jul 01 '18 at 14:49
  • I created ~/.tilde-switch as instructed and then added the following to my ~/.zshrc: source ~/.tilde-switch > /dev/null as after a reboot the launch task failed to make the key switch for me – bottlenecked Jan 22 '21 at 07:51
  • Thanks user3575439, that works just fine for me. You made my day ! – shadow_user Nov 28 '22 at 13:56

6 Answers6

85

Here’s a solution that does not use any external tools.

Run this in Terminal.app:

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

Now try your ~ and ± keys: they should be switched around.

Problem is, this fix will only work until the next restart. To make it permanent, you have to automatically run it on system load.

You can do that in three Terminal.app commands:

  1. cat << 'EOF' > ~/.tilde-switch && chmod +x ~/.tilde-switch
    hidutil property --set '{"UserKeyMapping":[{"HIDKeyboardModifierMappingSrc":0x700000035,"HIDKeyboardModifierMappingDst":0x700000064},{"HIDKeyboardModifierMappingSrc":0x700000064,"HIDKeyboardModifierMappingDst":0x700000035}]}'
    EOF
    

    (This is all a single command.) This stores the script from above in an invisible executable file in your home directory, ~/.tilde-switch.

  2. sudo /usr/bin/env bash -c "cat > /Library/LaunchDaemons/org.custom.tilde-switch.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}/.tilde-switch</string>
        <key>RunAtLoad</key>
        <true/>
        <key>KeepAlive</key>
        <false/>
      </dict>
    </plist>
    EOF
    

    (This is all a single command.) This creates a system task: ‘run the file from step 1 on every startup.’

  3. sudo launchctl load -w -- /Library/LaunchDaemons/org.custom.tilde-switch.plist
    

    This loads (activates) the task from step 2.

Steps 2 and 3 will prompt for your password. And now, your ~ and ± keys are permanently switched.

Note 1

This appears to work not only on the MacBook's build-in internal physical keyboard, but on the external keyboards as well.

Note 2

To undo the switching script (not the three steps), here’s the reverse script:

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

Note 3

To undo the three steps:

sudo launchctl unload -w -- /Library/LaunchDaemons/org.custom.tilde-switch.plist; sudo rm -f -- /Library/LaunchDaemons/org.custom.tilde-switch.plist ~/.tilde-switch

Credit

This solution is inspired by this article:

http://homeowmorphism.com/2017/05/27/Remap-CapsLock-Backspace-Sierra

gnebehay
  • 133
CBlew
  • 971
  • 3
    Finally! You saved my day – phaberest May 07 '19 at 22:04
  • This solution works perfectly for my built-in keyboard, however, it unfortunately also affects my Ergodox-EZ, where the key i've mapped to grave now produces § – Benny Powers Aug 27 '19 at 04:33
  • I tried this script, but it didn't work as expected
    #!/bin/bash
    hidutil property \
      --matching '{"Transport":"SPI"}' \
      --set '
        {
          "UserKeyMapping": [
            {"HIDKeyboardModifierMappingSrc":0x700000035,"HIDKeyboardModifierMappingDst":0x700000064},
            {"HIDKeyboardModifierMappingSrc":0x700000064,"HIDKeyboardModifierMappingDst":0x700000035}
          ]
        }
      '
    
    – Benny Powers Aug 27 '19 at 04:56
  • @BennyPowers Interesting. When I tested this with an external keyboard, it remained unaffected. Sorry to say, I am not familiar with how Ergodox-EZ hooks up into the system, perhaps the reason lies there. – CBlew Aug 27 '19 at 07:25
  • You can restrict it to certain devices. Just use the 'matching' flag to use a specific device and/or manufacturer. For instance, I swap my right ALT and CTRL keys because I don't use ALT on the right side, but I always use CTRL, however on a MacBook's keyboard, there is no right CTRL. However, I only want this to switch on the international keyboard. That's how I do it. – Mark A. Donohoe Feb 10 '20 at 17:05
  • 2
    In case of: Invalid property list error you may need to replace <key>Program</key> with <key>ProgramArguments</key><array><string>${HOME}/.tilde-switch</string></array> – jmarceli May 30 '20 at 15:07
  • 1
    I've tried the fix and it works great! Thanks a lot! Just a small note - it actually affected all my devices, so it works for external keyboards as well (thankfully) and not only for the built-in keyboard as mentioned in the post. – Miroslav Nedyalkov Jun 30 '20 at 14:06
  • 1
    @MiroslavNedyalkov I was probably confused on that part when I first wrote the answer. Edited, thanks. – CBlew Jun 30 '20 at 15:48
  • 2
    Worked for me! Why not putting it however, in .bashrc? – Vladimir Despotovic Jul 24 '20 at 21:46
  • 1
    Much easier to make a .zsh script, and execute it in ~/.zprofile – DanSkeel Sep 16 '20 at 12:04
  • Thanks! Also happened to me because I have an US Layout on the MacBook but got an external Apple Keyboard with CH Layout in my office. Seems to be too complex for macOS to properly apply the right settings for different keyboards. – Marc Jul 07 '21 at 08:25
  • 1
    @CBlew Doesn't work anymore on macOS Ventura. Perhaps the plist syntax has changed. No errors, but the script is not executing at startup. – Inian Mar 21 '23 at 07:14
  • On Ventura it works for the built-in keyboard of a macbook pro (QWERTZ), but when connecting a logitech (ansi), it actually switches the keys of that, on which without the script, it works out of the box. – dimisjim Oct 05 '23 at 11:42
  • 1
    Anyone else have an issue running this with launchctl version 7? I get Load failed: 5: Input/output error. – tdranv Dec 18 '23 at 07:42
  • 4
    The code above stopped working after updating to MacOS 14.2. It executes fine but the buttons are not swapped anymore. Did Apple update the button codes? – IAmVisco Dec 18 '23 at 12:07
  • 1
    Can confirm it is not working anymore after recent MacOS upgrades. – Tarantula Dec 22 '23 at 10:48
  • This does work on Sonoma 14.3 (23D56). The only thing that does not work is step 3, launching it on start. It fails with Load failed: 5: Input/output error. – tdranv Feb 05 '24 at 06:42
  • 1
    after sonoma 14.3, if you run the hidutil command without sudo it will not work and output this: Run as root to remap alphanumerics / special characters with UserKeyMapping. Added sudo and it works for me. – dimisjim Feb 08 '24 at 08:36
28

Using Karabiner Elements, you can add a "Simple Modification" as follows:

"Karabiner Elements Preferences" window, "Simple Modifications" tab, "Target Device" dropdown is set to "Apple Internal Keyboard / Trackpad (No manufacturer name)". Two rows are shown in the configuration table. The first row contains "From key": "non_us_backslash", "To key": "grave_accent_and_tilde (<code>)". The second row contains "From key": "grave_accent_and_tilde (</code>)", "To key": "non_us_backslash".

Basically, you need to know that the name of the § (±) key in Karabiner's configuration screen is non_us_backslash

In my case, I used this method specifically for the built-in keyboard, leaving my external keyboards unchanged.

  • this should be the accepted answer as it covers the case when you have an ISO built-in but an ANSI external keyboard – dimisjim Feb 09 '24 at 13:20
20

Here is Apple documentation with table containing all the keys and corresponding hexes. https://developer.apple.com/library/archive/technotes/tn2450/_index.html

Initial state is no active key remappings:

hidutil property --get UserKeyMapping
(null)

Remap ~ to §± one way:

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

Swap them both ways:

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

For removing any remappings back to initial just pass an empty array:

hidutil property --set '{"UserKeyMapping":[]}'
  • that's perfect, it worked, and I didn't need to install any apps that require weird permissions!

    It would be amazing if you included how you discovered the key codes too.

    Thank you

    – SudoPlz Jul 14 '21 at 15:22
  • @SudoPlz Good question. So there is a link in my initial comment. Open that page, scroll down to Table 1 List of keyboard usages and their usage IDs., and here you can find Keyboard Grave Accent and Tilde 0x35 and Keyboard Non-US \ and | 0x64. – gordey4doronin Jul 15 '21 at 10:49
  • This is the most brilliant solution. Thank you @gordey4doronin – Fahad Yousuf Sep 10 '22 at 15:44
  • Amazing, thanks for the good solid simple answer and clarification. I read the link and understood both solutions, but didn't go through till the key codes :D Thanks! – Atef Jun 07 '23 at 08:23
  • Works perfectly on macOS Ventura 13.4. Thank you so much. – zhovner Jul 02 '23 at 13:04
4

Building on CBlew's solution, one can do without the extra file (which wouldn't work as-is for a multi-user system). Write a plist file like

<?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.example.setkeyboard</string>
    <key>ProgramArguments</key>
    <array>
      <string>/usr/bin/hidutil</string>
      <string>property</string>
      <string>--matching</string>
      <string>{"VendorID": 5426, "ProductID": 579}</string>
      <string>--set</string>
      <string>{"UserKeyMapping":[{"HIDKeyboardModifierMappingDst":0x7000000e3,"HIDKeyboardModifierMappingSrc":0x7000000e2},{"HIDKeyboardModifierMappingDst":0x7000000e2,"HIDKeyboardModifierMappingSrc":0x7000000e3},{"HIDKeyboardModifierMappingDst":0x7000000e7,"HIDKeyboardModifierMappingSrc":0x7000000e6},{"HIDKeyboardModifierMappingDst":0x7000000e6,"HIDKeyboardModifierMappingSrc":0x700000065}]}</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <false/>
  </dict>
</plist>

then set it with sudo launchctl load -w -- /Library/LaunchDaemons/com.example.setkeyboard.plist.

For generating the hidutil invocation I am using this simple Python script.

heiner
  • 221
2

I had this problem switching to a new macbook (with external keyboard). I fixed it by removing my input source (dvorak) and then clicking on "Change keyboard type..." and getting it to identify my external keyboard again (as ISO european). I then re-added my input source and the ~ and ± keys were swapped back and in the correct place.

  • didn't work for me unfortunately. – SudoPlz Jul 14 '21 at 15:06
  • 1
    @user3575439's solution was what worked for me on Dell hackintosh laptop. On macOS Monterey 12.0.1 goto System Preferences → Keyboard → Change Keyboard Type and change to ISO. – dm5n Nov 23 '21 at 07:39
2

I had the exact same situation (Nordic keyboard, US layout). The problem is that macOS thinks you're using the wrong "type" (ISO vs ANSI) keyboard. To fix it:

  1. Open keyboard settings
  2. Click Change keyboard type... at the very bottom
  3. The Keyboard Setup Assistant will pop up and ask you to press the key to the right of the left shift key
  4. Do not press that >
  5. Instead, press Z

macOS will now determine that you're using a standard US keyboard.

agarza
  • 2,274
SeanW
  • 21
  • Steps 1-2-3 + selecting ANSI rather than ISO fixed it, regardless of what I press in step 4/5. – EliadL Jan 19 '24 at 15:02