Skip to content

An Operating System agnostic keyboard

Published: at 04:12 PM

One Keyboard - Any OS

In my day job, I maintain the .NET SDK for Sentry, which is an interesting beast because .NET runs on Windows, macOS, Linux, Android and iOS. I have to use multiple different operating systems then… if I’m writing some code for our ASP.NET integration, I can only do that on Windows. Conversely, if I’m working on something related to iOS, that pretty much has to be done on macOS.

OS specific shortcuts, and other terrible ideas

Changing between these systems is painful. There’s simple stuff for editing like:

ActionMacOSWindows
cut⌘ + XCtrl + X
copy⌘ + CCtrl + C
paste⌘ + VCtrl + V
undo⌘ + ZCtrl + Z

If it was just that, I could use something like Windows power tools to remap the key (which gets registered as the Win key on Windows) to Ctrl, and all would be well in the world. It’s not that simple though (of course it’s not). Then there’s navigation:

ActionMacOSWindows
word startAlt + ⬅︎Ctrl ⬅︎
word endAlt + ⮕Ctrl + ⮕
line start⌘ + ⬅︎HOME
line end⌘ + ⮕END
doc start⌘ + ⬆︎Ctrl + HOME
doc end⌘ + ⬇︎Ctrl + END

Here, there’s no “one” key or even multiple keys that we can remap so that our typing experience is the same on both operating systems… and this is just the beginning. Then there the shortcuts for arranging windows/applications on the screen, system shortcuts like locking the computer, switching between applications, showing the desktop etc.

Learning all of those shortcuts once is painful enough. Learning them twice is, well, twice as painful. Having to consciously remember which OS I’m on and press the right shortcut for the situation is frankly cognitive overload that I don’t need. My brain is already pretty loaded trying to deal with the complexity of the Sentry SDK - I don’t need to be playing keyboard shortcut mapping games to get my OS to do what I want.

There must be a better way!

Programmable keyboard to the rescue 🦸‍♀️

I’m using a wireless programmable keyboard, so it’s running on ZMK.

Out of the box, ZMK doesn’t have any way to recognise which OS the keyboard is connected to… it’s just presenting itself as a generic HID device over bluetooth. So that’s problem number one.

Problem number two is that even if ZMK was aware of the OS, it also doesn’t have any way to output a different key depending on what the current OS is.

ZMK does, however, let you extend it by creating your own custom behaviours… so I built a custom behaviour called oskey:

After adding oskey to your keyboard config, you can then do a couple of things.

Switching OS

Firstly, you can setup a trigger to change the “current” OS. This is just a bit of internal state that’s used by the next behaviour. The trigger could be a keypress. So for example you could have a dedicated OS layer with keys on it that set the OS to Windows, macOS and Linux… And I do have such keys in one of my layers, but I doubt I’ll use them much… I figured I’m already switching the bluetooth channel when I change from my macOS to my Windows machine, so instead I’ve configured a macro that sets the current OS at the same time as changing the bluetooth connection:

  macros {
      bt_mac: bt_mac {
          compatible = "zmk,behavior-macro";
          #binding-cells = <0>;
          bindings = <&bt BT_SEL 0>, <&os_sel OS_MAC>;
      };

      bt_win: bt_win {
          compatible = "zmk,behavior-macro";
          #binding-cells = <0>;
          bindings = <&bt BT_SEL 1>, <&os_sel OS_WIN>;
      };
  };

OS Agnostic Keys

Now that we have a way to tell ZMK which OS we’re using, we can tell it to do different things depending on the OS… So for example, I have this behaviour configured in my keymap:

  ok_prev_word: os_key_prev_word {
      compatible = "zmk,behavior-os-key";
      #binding-cells = <0>;
      /*                 Win        Mac         Lin  */
      bindings = <&kp LC(LEFT)>, <&kp LA(LEFT)>, <&kp LA(LEFT)>;
  };

That says, if the current OS is Windows or Linux, output Ctrl + ⬅︎… but on macOS output Alt + ⬅︎… I then assign that behaviour to a key on my navigation layer and now when I change from using my Mac to my Windows machine, I get the same behaviour when pressing that key.

Configuring behaviours for the other keys on the navigation layer is pretty trivial substitution at this point:

  ok_prev_word: os_key_prev_word {
      compatible = "zmk,behavior-os-key";
      #binding-cells = <0>;
      /*                 Win        Mac         Lin  */
      bindings = <&kp LC(LEFT)>, <&kp LA(LEFT)>, <&kp LA(LEFT)>;
  };

  ok_next_word: os_key_next_word {
      compatible = "zmk,behavior-os-key";
      #binding-cells = <0>;
      /*                 Win        Mac         Lin  */
      bindings = <&kp LC(RIGHT)>, <&kp LA(RIGHT)>, <&kp LA(RIGHT)>;
  };

  ok_line_start: os_key_line_start {
      compatible = "zmk,behavior-os-key";
      #binding-cells = <0>;
      /*             Win         Mac            Lin  */
      bindings = <&kp HOME>, <&kp LG(LEFT)>, <&kp HOME>;
  };

  ok_line_end: os_key_line_end {
      compatible = "zmk,behavior-os-key";
      #binding-cells = <0>;
      /*            Win           Mac           Lin  */
      bindings = <&kp END>, <&kp LG(RIGHT)>, <&kp END>;
  };

  ok_doc_start: os_key_doc_start {
      compatible = "zmk,behavior-os-key";
      #binding-cells = <0>;
      /*             Win         Mac            Lin  */
      bindings = <&kp LC(HOME)>, <&kp LG(UP)>, <&kp LC(HOME)>;
  };

  ok_doc_end: os_key_doc_end {
      compatible = "zmk,behavior-os-key";
      #binding-cells = <0>;
      /*            Win           Mac           Lin  */
      bindings = <&kp LC(END)>, <&kp LG(DOWN)>, <&kp LC(END)>;
  };

Actions over Shortcuts

More generally, attempting to configure the keyboard so that I can easily press the weird and wonderful combinations that the OS wants me to press seems to be a dead end… Windows will ask me to do one thing and macOS will ask another contradictory thing.

So instead of trying to use my keyboard to press Ctrl + C, it’s better to configure a Copy key on one of my layers, that emits Ctrl + C on Windows and ⌘ + C on macOS.

Wrapping up

I just built all of this so I haven’t fully explored how I’m going to set my keyboard layout up but for navigation alone, this has been a godsend.

I’m hopeful of a near future where I can comfortably use my Mac and my Windows machines interchangeably without having to think about what my hands are doing. Instead, I can be focused on my work!

ZMK really is awesome… eventually maybe a bit of that awesome rubs off on people who use ZMK 😜