Using Norman keyboard layout with IME inputs
[Update Sept 3, 2024]: After using this for a few months, I’ve found that this solution has a strange N-key like bug. If I hit certain keys quickly back-to-back, the first key ends up getting repeated and the second key gets lost. For example, typing “t” (which is “k” in Norman) followed by “k” (which is “i” in Norman), results in “kk”. This particular combination is annoying, because it appears in words like “kick”. I haven’t found anything about this bug online.
I’ve been using the Norman keyboard layout for many years. It works great for me, and I love how it noticably reduces finger movement. Usually I use the official keyboard layout package for MacOS and Windows. However, in Sonoma (or thereabouts), MacOS changed the way that the keyboard layouts work with IME input types. Previously, as long as I selected the Norman layout first before switching to an IME layout (like Hindi or Chinese), the keyboard layout would continue to be Norman under-the-hood. That was exactly what I wanted, because I wanted to use the Norman layout in conjunction with the IME input. Since these changes, that’s no longer possible. Instead, it seems that the IME layout is hard-coded to a QWERTY layout inside as implied by the layout settings:
I finally found a solution for this problem in an unlikely place. Instead of using the official layout package,
I’m using hidutil
and Hammerspoon to remap the keys into the Norman layout. Now
my keyboard works perfectly with IME layouts. Even better, hidutil
supports remapping keys for specific devices,
so I’m only remapping keys for the internal laptop keyboard. My external keyboard, a Moonlander
mechanical keyboard, continues to work using my custom firmware layout
tuned for Norman. This means that I can type on my external keyboard or on the internal keyboard without needing
to change the keyboard layout. Previously, the MacOS keyboard layout would need to be set to QWERTY while using my
Moonlander to avoid double conversions: tapping “t” on my Moonlander would send a “t” to MacOS which would get remapped
to “k” for Norman. I solved this problem before
by using Hammerspoon to automatically change the keyboard layout when the Moonlander was attached/detached, but that
meant that the internal keyboard was stuck on QWERTY as long as the Moonlander was attached. Not ideal.
Here are the steps I used:
- Allow
hidutil
to run withsudo
without prompting for a password. This is required since Sonoma. Created/etc/sudoers.d/01-jdve-hidutil
with the following contents:
jdve ALL = (ALL) NOPASSWD: /usr/bin/hidutil
This gives only my user the ability to run hidutil
using sudo
without prompting for a password.
-
Forked foundation_remapping into my .hammerspoon configs to run
hidutil
withsudo
. Also updated it to support additional filtering parameters that it didn’t support before. -
Remapped the keyboard according to the Norman layout using foundation_remapping. The most important part here is the device filtering rules to match the internal keyboard on my MacBook Pro:
local FRemap = require('foundation_remapping')
local remapper = FRemap.new({vendorID=0, productID=0, locationID=0x31, primaryUsagePage=1})
remapper:remap('e', 'd')
remapper:remap('r', 'f')
remapper:remap('t', 'k')
remapper:remap('y', 'j')
remapper:remap('i', 'r')
remapper:remap('o', 'l')
remapper:remap('p', ';')
remapper:remap('d', 'e')
remapper:remap('f', 't')
remapper:remap('h', 'y')
remapper:remap('j', 'n')
remapper:remap('k', 'i')
remapper:remap('l', 'o')
remapper:remap(';', 'h')
remapper:remap('n', 'p')
remapper:register()
Done!