Idempotent layer switching

Hello,

What LockLayer currently does is more like toggling, i.e. every time it’s ran the meaning of the keypresses changes. Would it be possible to make functions for idempotent switching of layers?

Example: assume we have EnableLayer and DisableLayer and I map left palm key to EnableLayer(FUNCTION) and right palm key to DisableLayer(FUNCTION). Then:

  • Pressing left palm any number of times should result in FUNCTION layer being “enabled”
  • To “disable” it, I would have to press right palm key

Reason: I’d like to implement vim-like modal cursor movement keys. Toggling doesn’t work for me because I don’t always remember which layer is currently active - I need a stateless behavior in which pressing a key will always result in known result.

You can achieve this with a macro:

enum {
 // various other macros...
 MACRO_LAYER_1_ON,
 MACRO_LAYER_2_ON,
 // ...and so on
};

void enableLayerMacro(uint8_t keyState, uint8_t macroIndex) {
  if (keyToggledOn(keyState)) {
    Layer.on(macroIndex - MACRO_LAYER_1_ON + 1);
  }
}

Similar thing for disabling, and in macroActions, you can then do:

case MACRO_LAYER_1_ON ... MACRO_LAYER_X_ON:
  enableLayerMacro(keyState, macroIndex);
  return MACRO_NONE;
case MACRO_LAYER_1_OFF ... MACRO_LAYER_X_OFF:
  disableLayerMacro(keyState, macroIndex);
  return MACRO_NONE;
3 Likes

I too would like layer switching that doesn’t require me to store state in my head.

In my case I have 4 layers, and 2 ‘fun’ keys on my Model 100 and I want the four combinations of the fun keys being pressed/unpressed to correspond to the four layers.

Left fun        Right fun        Layer
-               -                0
pressed         -                1
-               pressed          2
pressed         pressed          3

I can nearly get their with a shift-to-layer on each fun key like so:

Layer        Left fun        Right fun
0            shift-to-1      shift-to-2
1            transparent     shift-to-3
2            shift-to-3      transparent
3            transparent     transparent

However, if I do the following:
Left-fun Down (0 → 1), Right-fun Down (1 → 3), Left-fun Up (3 → 3)
then releasing the left function key at the end makes no difference. I want it to drop back down to layer 2.

Layers seem to be quite tricky for people to get their head around but from what I’ve read I suspect that this isn’t going to be possible through Chrysalis (at the moment at least). Is that right?

Any pointers on how to get this working would be very helpful.

You’ll have to drop down to firmware level to do this. In this sequence of events, Layer 2 was never activated, so Kaleidoscope won’t drop back to it either.

It isn’t possible with Chrysalis, no. The best way I can imagine doing this is dropping down to firmware, completely ignoring the existing layer shift keys, and implement your own layering logic in a plugin. Something along these lines, maybe:

EventHandlerResult afterEachCycle() {
  bool leftPalm = Kaleidoscope.device().isKeyswitchPressed(KeyAddr(3, 6));
  bool rightPalm = Kaleidoscope.device().isKeyswitchPressed(KeyAddr(3, 9));

  if (!leftPalm && !rightPalm) Layer.move(0);
  if (leftPalm && !rightPalm) Layer.move(1);
  if (!leftPalm && rightPalm) Layer.move(2);
  if (leftPalm && rightPalm) Layer.move(3);

  return EventHandlerResult::OK;
}

This checks whether the left or right palm keys are held, by coordinates, so you can map them to whatever you wish (though, it’d make sense to map them to blocked or transparent, to not interfere with the mechanism above), and implements a custom layering logic based on their state. It’s probably easier to do this than trying to fit your desired states into the logic Kaleidoscope normally uses.

If you want transparency on one or more layers, then you’ll need to do some more work, to have the desired layers active.

The above code needs to be in a plugin (an example on how to write a plugin is available here), and is completely untested. Hopefully it can serve as a guideline nevertheless.

1 Like

Here’s an idea: a plugin that implements a layer shift (either using built-in layer shift keys and aborting to stop the built-in layer shifts from happening, or defining its own set of layer shift keys), but it uses the index number as a value to add to the current layer total when the key toggles on (and subtract from when it toggles off). So in @Rachel’s example, left fun becomes “layer shift +1” and right fun becomes “layer shift +2”. This would provide exactly the desired behaviour, and would be fairly simple to write.

I’d like to point out that the thing that makes this difficult with layer shift keys as they are in Kaleidoscope is that, perhaps counter-intuitively, a key doesn’t change it’s value if a layer change happens while the key is active (held). This is a fundamental feature of Kaleidoscope, and avoids all sorts of problems, but it’s not obviously true, and it does mean that a layer shift key does not work the same way as a shift modifier key works.

2 Likes

Come to think of it… we do have Key_KeymapNext_Momentary, aka layer shift + 1. Perhaps that could work, too?

Yeah, I thought of that, too, but it won’t do what the user wants on release.

Yes, that’s exactly what I’d do if I get round to learning how to write a plugin. The function keys being sort of like a bitfield that add up to the desired layer.

Are custom plugins compatible with Chrysalis, or does one end up having to define everything in code at that point?

As long as the sketch uses the Focus and EEPROM plugins in the standard sketch, Chrysalis will still work as normal.

1 Like

Thanks! That’s good to know.

Still getting my head around Keyboardio, Chrysalis, Kaleidescope, Sketches, Plugins, firmware, .ino files, EEPROM, etc. and how they all relate to each other.

Something like Kaleidoscope/glossary.md at master · keyboardio/Kaleidoscope · GitHub but at a slightly higher level might be useful. Though tbh the level of documentation is already pretty good, I’m sure I can find what I need. :clap:

Works a treat! Thank you @algernon !