2

I have an extensive DefaultKeyBinding.Dict file that mostly works very well for me. I use Karabiner Elements to change the behavior of the right-alt so that it sends §, which signals the beginning of a compose sequence, and then the keybinding table takes over. My DefaultKeyBinding.Dict starts like this:

{"§" = {
  "g" = {
    "h" = ("insertText:", "ȝ");
    "," = ("insertText:", "ģ");
    "." = ("insertText:", "ġ");
    "(" = ("insertText:", "ğ");
    "U" = ("insertText:", "ğ");
    …

These bindings all work as expected. For example, right-alt g h produces ȝ and right-alt g . produces ġ.

But none of my sequences that involves ^ or ~ is working. I know these symbols have a special meaning in the DefaultKeyBinding.Dict syntax, but none of the escape sequences for them is working either. For example, I am supposed to be able to use \136 (octal) or \U005E to represent the caret ^, but keybindings involving this character never work. I've tried inserting each of the following in the same block as the bindings above, then typing right-alt g ^:

    "\U005e" = ("insertText:", "yobgorgle");  // ^
    "\U005E" = ("insertText:", "yobgorgle");  // ^
    "\^" = ("insertText:", "yobgorgle");      // ^
    "\136" = ("insertText:", "yobgorgle");    // ^

    // These are not supposed to work but I tried them anyway
    "\x5e" = ("insertText:", "yobgorgle");    // §g^
    "\0136" = ("insertText:", "yobgorgle");   // §g^

    // Different strategy
    "$6" = ("insertText:", "yobgorgle");      // §g^

None of these has produced yobgorgle; all the advertised syntax has produced a plain ^ symbol. If they were being completely ignored, they would produce §g^ like the bottom three, so something is happening, but I don't understand what.

I don't understand why "$6" didn't work either, that is supposed to mean shift-6, which is where my caret symbol is.

The sequence \033 for the "esc" key does work as advertised.

I am using iTerm2 to test the binding changes. I have not forgotten that I need to kill and restart the app to get it to notice the new keybindings.

What might I be missing?

1 Answer 1

2

(The following was tested on macOS 13.5.)

The caret (^) can be escaped with \\^.

The tilde (~) can be escaped with \\~.

For example, with the following contents of DefaultKeyBinding.dict:

{"§" = {
  "i" = {
    "\\^" = ("insertText:", "î");
  };
};}

Typing the sequence § i ^ will produce the character î.

Why other escape sequences don't work

In my testing, escape sequences such as \136 and \U005E didn't work because they were first translated to ^ before being parsed as a key binding, resulting in identical behaviour to just specifying ^.

Your alternate method $6 didn't work because key bindings are case-sensitive (Shift-sensitive?), and it is not possible to type "6" while the Shift key is held down. You can verify this behaviour by attempting to create a key binding with $b, which doesn't work (because lowercase b cannot be typed while holding Shift), versus $B, which does.

What is the unescaped behaviour?

The unescaped character ^ means that the Control key is held down. If you don't specify anything after the ^, then the text system will accept any input while the control key is held down as a match.

For example, with the following contents of DefaultKeyBinding.dict:

{"§" = {
  "i" = {
    "^" = ("insertText:", "î");
  };
};}

Typing the sequence § i Ctrl+B will produce the character î. But so will § i Ctrl+Option+W, § i Ctrl+Shift+LeftArrow, or any other key combination as long as Ctrl is held down.

The same goes for when the unescaped character ~ is specified in a key binding by itself, but in that case it's any key combination with the Option key held down.

macOS built-in utility for key remapping

This wasn't part of your question, but you may be interested in hidutil, described at https://developer.apple.com/library/archive/technotes/tn2450/_index.html.

You can use the following command to remap the right-Option key to § without using any third-party software:

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

The above does not persist across reboots, so you'll need a way to automatically re-run it at startup. You can do so using a launchd agent. Save the following as ~/Library/LaunchAgents/local.RemapRightOptionToNonUsBackslash.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>local.RemapRightOptionToNonUsBackslash</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/hidutil</string>
        <string>property</string>
        <string>--set</string>
        <string>{"UserKeyMapping":[{"HIDKeyboardModifierMappingSrc":0x7000000E6,"HIDKeyboardModifierMappingDst":0x700000064}]}</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .