OS dependent keymap?

Is it possible for a key to send one keycode for Windows and another for Mac? Is the firmware able to be aware of which OS it’s connected to? I seem to recall some discussions around this, but that was very early on, before most of us started working with Kaleidoscope.

Alternately, can I program some key on a function layer to swap the mappings of two keys?

My use case is this - when I’m gaming on my Windows box at home, I want to swap Alt and GUI, but when I’m at work on a Mac, I want them in the default positions. Eventually, when I get my second board, this will be a non-issue.

2 Likes

There is the HostOS plugin that can try to guess the OS, but it’s just a guess, and may very well be wrong. We can’t ask the OS, and it doesn’t tell us either. The plugin worked well for me when trying on Linux and Windows, but I’ve heard it doesn’t detect MacOS properly. (It also doesn’t support any of the BSDs)

Yes! If you disable the guesser part of HostOS, the plugin can help here, and you can pair it with a few macros.

static void AltGUI(uint8_t macro_index, uint8_t key_state) {
  if (HostOS.os() == kaleidoscope::hostos::OSX) {
    if (macro_index == M_ALTGUI) {
      handleKeyswitchEvent(Key_LeftAlt, Macros.row, Macros.col,
                           key_state | INJECTED);
    } else if (macro_index == M_GUIALT) {
      handleKeyswitchEvent(Key_LeftGUI, Macros.row, Macros.col, 
                           key_state | INJECTED);
    }
  } else if (HostOS.os() == kaleidoscope::hostos::WINDOWS) {
    if (macro_index == M_ALTGUI) {
      handleKeyswitchEvent(Key_LeftGUI, Macros.row, Macros.col, 
                           key_state | INJECTED);
    } else if (macro_index == M_GUIALT) {
      handleKeyswitchEvent(Key_LeftAlt, Macros.row, Macros.col, 
                           key_state | INJECTED);
    }
  }
}

void osSwitch(uint8_t key_state) {
  if (!keyToggledOn(key_state))
    return;

  if (HostOS.os() == kaleidoscope::hostos::OSX) {
    HostOS.os(kaleidoscope::hostos::WINDOWS);
  } else {
    HostOS.os(kaleidoscope::hostos::OSX);
  }
}

Something along these lines.

4 Likes

In the long run, it would make sense to have a plugin do this for us, and allow one to just specify a map like:

static const Key osMap[][4] PROGMEM = {
  // key, Linux, OSX, Windows
  { Key_LeftAlt, Key_LeftAlt, Key_LeftGUI, Key_LeftAlt },
  { NULL, NULL, NULL }
}

And then the plugin would automatically replace any Key_LeftAlts with either a left alt or a left GUI, depending on the OS. Could perhaps make it simpler by dropping the first item, and requiring the user to set a base OS.

The trouble with that approach is that it would only replace keycodes; it would not allow the user to redefine keys based on the OS. I’d much rather have an array for each alternate OS based on row and column indexing such that I could redefine just one of multiple keys for Key_LeftAlt (for example), without globally affecting every Key_LeftAlt in every layer. Entries could be like this:

{OS_MAC, 2, 4, Key_LeftGui}
{OS_LINUX, 1, 3, Key_RightAlt}

…with the first element an enum for the OS (or have a separate array per OS for more efficient code), then row & column numbers for the keyswitch, then the value of the override keycode (or macro).

1 Like

Would it be possible to have OS specific layers?

Then maybe the OS layers could be half-empty and just override the keys I want to change and the fall through to the default layer (fn / querty / numlock)?

Maybe have different enums for keymaps depending on the OS?

there are a lot of things we could do. What would be most helpful to me is for people to explain their specific use cases and pain points. Once we have a better handle on the problem we are trying to solve (as opposed to some cool technical solutions that may or may not solve various Users issues) will be in a much better place to figure out a design that’s going to work well for everybody.

1 Like

There’s one thing that I would love a solution to, but I expect is unsolvable. It is by no means a problem unique to the Model 01.

I want a way to unify many keyboard shortcuts (particularly browser shortcuts) between my Macbook and my Windows box. Games often allow ways to remap controls, but browsers and other applications rarely do.

As a specific example: to refresh a page in Chrome on my Macbook the shortcut is cmd-R, while on my Windows box that same shortcut is F5 or ctrl-R. If I type cmd-R on Windows the OS brings up a dialog box that steals keyboard focus.

But then there’s the added difficulty that I use (g)vim on both systems, as well as ssh into various other system types, so I need ctrl to always be ctrl.

So I suppose I’m looking at something where on Windows cmd acts like ctrl unless some other modifier (e.g. fn) is pressed. Then all I have to do is start using the “mac” shortcuts with my windows applications and something else entirely for the windows-specific shortcuts (such as cmd-shift-esc).

But that couldn’t be default because it would fail the POLA test for anyone who doesn’t split their time between Windows and macOS (and maybe even most of those).

2 Likes

That’s pretty much exactly my use case, with IntelliJ IDEA thrown into the mix. My plan was to swap GUI & Alt; I have some shortcuts that use both, but many of the ones that only use one use cmd on mac and alt on windows.

1 Like

I’m planning to use a combination of macros and global OS setting to deal with this problem. There’s an example of how to do this in the AppSwitcher example in Kaleidoscope.

1 Like

Note: that example isn’t fully-fleshed out; it only contains the code for macOS, but it’s fairly straightforward how to add another OS or two.

On my own Mac PCs the system interprets ctrl as cmd and vice versa; this is an option in system preferences. That works ok for me, but since I tell keyboardio what my hostos is anyway, I wonder if I can make keyboardio switch just the ctrl and cmd keys depending on the os. I can see how I might do this using alternative keymaps, but it would be bizarre to switch an entire keymap to just change three keys.

Can I define my own alias key codes and change the definitions of those key codes on the fly with a macro?

It’s definitely doable! Something along these lines:

namespace kaleidoscope {
class MacOSControl : public kaleidoscope::Plugin {
public:
  MacOSControl () {}

  EventHandlerResult onKeyswitchEvent(Key &mappedKey, byte row, byte col, 
                                      uint8_t keyState) {
    if (::HostOS.os() != kaleidoscope::hostos::OSX)
      return EventHandlerResult::OK;

    switch (mappedKey.raw) {
    case Key_LeftControl.raw:
      mappedKey = Key_LeftGui;
      break;
    case Key_LeftGui.raw:
      mappedKey = Key_LeftControl;
      break;
    case Key_RightControl.raw:
      mappedKey = Key_RightGui;
      break;
    case Key_LeftGui.raw:
      mappedKey = Key_RightControl;
      break;
    }
    return EventHandlerResult::OK;
  }
};
}

kaleidoscope::MacOSControl MacOSControl;

KALEIDOSCOPE_INIT_PLUGINS(MacOSControl, ....);

Something along these lines. The idea is that we create a tiny plugin that watches which keys are pressed, and if HostOS is set to OSX, does the appropriate swapping, otherwise leaves them alone. So on your keymap, you’d use the non-OSX keys. It’s also possible to do it the other way around with little change.

Do note that I still have trouble following which key is what on OSX, so the above example may very well require some tuning.

The same thing could be accomplished with a macro too, I think, but this approach is lighter, and I think more reliable. If it works, that is. I haven’t tested it yet. O:)

1 Like

I understand that, I think. I’m going to try it. Thanks!

Looking to do something very similar for CMD/ALT for OSX/Windows.

When I tried the example code I get an error about being able to reference the .raw,

Do I need to #include something else to enable this functionality?

error: accessing 'kaleidoscope::Key::raw' member instead of initialized 'kaleidoscope::Key::<anonymous>' member in constant expression                                                                                                                            
  case Key_LeftControl.raw:

Oops. It seems we need to do something about Key_* so they work like this again. There’s an existing PR which I will need to revisit… until then, you can use if/else instead:

    if (mappedKey == Key_LeftControl)
        mappedKey = Key_LeftGui;
    else if (mappedKey == Key_LeftGui)
        mappedKey = Key_LeftControl;
    else if (mappedKey == Key_RightControl)
        mappedKey = Key_RightGui;
    else if (mappedKey == Key_LeftGui)
        mappedKey = Key_RightControl;

Replace the switch statement in the original with this, and that should work.

1 Like

Thanks again, I have got it building at least and I have adapted for my use case, I will apply it tomorrow when I am near my Ki/oM01 again!

1 Like

Edit: Given up attempting to use HostOS, using separate layers for different OS with a key to flip between them.

I am having some trouble with the basic logic of HostOS detection, I am able to effect the change I want but not automatically. My code fragment looks like this:

namespace kaleidoscope {
class GuiAlt : public kaleidoscope::Plugin {
public:
     GuiAlt () {}
     EventHandlerResult onKeyswitchEvent(Key &amp;mappedKey, byte row, byte 
col, uint8_t keyState) {
         if (HostOS.os() == kaleidoscope::hostos::OSX) {
             return EventHandlerResult::OK;
         }
         else {
             if (mappedKey == Key_LeftGui)
                 mappedKey = Key_LeftAlt;
             else if (mappedKey == Key_RightGui)
                 mappedKey = Key_RightAlt;
             else if (mappedKey == Key_LeftAlt)
                 mappedKey = Key_LeftGui;
             else if (mappedKey == Key_RightAlt)
                 mappedKey = Key_RightGui;
             return EventHandlerResult::OK;
         }
     }
};
}

I have tried it exactly as you posted and many variations in between. The only way I can make it work is when I am at work I switch the == to a != and that does flip the behaviour. Not sure if there is something functionally wrong with the very basic if/else or I have not gotten hostos setup correctly.

This is what I have in my sketch to facilitate the use of HostOS:

#include "Kaleidoscope-HostOS.h"
#include "Kaleidoscope/HostOS-select.h"
#define KALEIDOSCOPE_HOSTOS_GUESSER 1

KALEIDOSCOPE_INIT_PLUGINS(
  BootGreetingEffect,
  LEDControl,
  LEDOff,
  HostOS,
  Sand,
  Macros,
  MouseKeys,
  HostPowerManagement,
  Qukeys,
  TapDance,
  OneShot,
  EscapeOneShot,
  ActiveModColorEffect,
  GuiAlt
);