Toggling layers together?

Note: I don’t actually have my keyboard yet. :wink:

I’ve settled on having two different function layers, but they’re going to be extremely similar. To aid in maintenance and clarity, I’m just making “MY_FUNCTION_R” transparent except where it differs from “MY_FUNCTION_L”.

But of course this means I have to be able to shift to both when holding down my right palm key. Would this be as simple as putting two lines of ShiftToLayer() in a macro, like this:

const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) {
  switch (macroIndex) {

  // Some other cases

  case MACRO_L_FUNCTION:
    ShiftToLayer(MY_FUNCTION_L);
    ShiftToLayer(MY_FUNCTION_R);
    break;

  // Some other cases, including default.

  }
  return MACRO_NONE;
}

?

What happens with the interaction between two keys trying to ShiftToLayer() with overlapping states of being pressed & released?

I’ll be honest, I didn’t think of just putting two lines of ShiftToLayer() till I started making this post. I was messing around with keyToggled[on/off]() and Layer.[on/off](). So maybe I’ve already answered my own question, but I’m still curious about edge cases, and whether there’s something better, like a single function that accepts multiple layers for temporary press & release switching.

1 Like

You won’t be able to use ShiftToLayer that way, because that’s just a key code, it runs no code on its own. What you can do, is to return a macro definition that does two ShiftToLayers:

if (keyIsPressed(keyState)) {
  return MACRO(Dr(ShiftToLayer(MY_FUNCTION_L)), Dr(ShiftToLayer(MY_FUNCTION_R)));
} else {
  return MACRO(Ur(ShiftToLayer(MY_FUNCTION_L)), Ur(ShiftToLayer(MY_FUNCTION_R)));
}

Or you can just work with Layer.on() and Layer.off() directly. You will need keyIsPressed() though, to keep the layer keys down while the key is held.

The end result depends on the order in which they appear on the keymap. The latter will win, and override the first. However, the layer state may change between processing the first and the second, which may have surprising results.

To clarify, the higher layer shift doesn’t override the lower one (both activated via ShiftToLayer()), which is especially relevant to the intended use case. Instead, both layers will be turned on, and non-transparent keys on the higher layer will take precedence over keys on the lower one.

Yeah, what I meant was the state of each given layer will be overriden by the second key, but… that’s not entirely correct, either.

The thing to keep in mind is that ShiftToLayer() changes the layer state under the following conditions: if a key is pressed, and the target layer isn’t on, it gets turned on. If a key toggles off, the target layer deactivates. Because keys are processed in a certain order, it may happen that one layer key toggles off, turning the layer off, then another, being held, turns it back on, in the same cycle. But between these two, the layer will be off.

…or the held layer shift key is processed first, in which case the layer won’t turn back on until the next event handler cycle, but will be off during other hooks. Either way, if you’re really, really unlucky with timing, and you press a third key at the same time as the first layer shift key is released, you could get a key mapped from the wrong layer. I hadn’t even thought of this scenario before, and it’s very unlikely to be observed, even if someone intentionally tries to trigger it, but it’s one more scan-order bug to add to the list.

1 Like

I’m thinking of just mapping each alternate palm key with XXX, or something consistent that doesn’t switch layers or that isn’t too disruptive if I do it on accident.

By “if a key is pressed”, you mean currently held down, rather than just toggled on, right? So theoretically, no-oping the not-held palm key should remove all ambiguity. If I’m holding both down, whichever registered first applies. If I release the currently applied palm key, the other will be seen as down, and the other function layer will apply from that moment until it’s released.

If I understand what you’ve got in mind, it won’t work the way you want it to. Keys only get their mapping when they toggle on, so a key that is held doesn’t change its Key value when a layer update happens. In this way, layer shift keys are not like modifier keys, which may seem odd, but it’s a feature, not a bug. If held keys got remapped during a layer update, you could easily get spurious input, and the host’s normal repeat-rate limiting wouldn’t help cover it up for you.

No, I mean, I’ll map the left palm key to XXX in MY_FUNCTION_R, and the right palm key to XXX in MY_FUNCTION_L. Holding one down makes it so the other cannot be activated, which should result in more consistent results. Otherwise, holding both would always result in MY_FUNCTION_R (the one that’s 2 layers) being active.

My point is this:

Suppose you have two keys on the base layer, one ShiftToLayer(1) (SL1), and one ShiftToLayer(2) (SL2), and each of those keys is mapped to Key_NoKey on both layers 1 & 2. Then take the following sequence of events:

  1. Press SL1
  2. Press SL2
  3. Release SL1

With SL2 still held, this will not result in Layer 2 being active; it will just turn on Layer 1, then turn it off again.

Gotcha. What a key is is only ever determined at the moment it is toggled on. This is very good and sane behavior.

Not only is this very acceptable to this specific case, for me, it also takes a huge load off of my mind about all these hypothetical edge cases that have been floating around in my brain (like I said, I don’t have my keyboard yet :slight_smile: )

…assuming I’ve understood correctly.

A quick, related question: if two keys are mapped to the same key code (whether in the same or different layers or whatever), and they’re both pressed, does releasing one release the key from being pressed, or do they both have to be released? What about if they’re both mapped to the same macro, does releasing only one of them trigger the keyToggledOff(keyState) condition?

2 Likes

Depends on the keycode. For normal keys, both have to be released, because the release event is not handled explicitly. Mind you, there may be a short time when the key appears released for the firmware, but before sending the report, the other gets handled, and the key will appear held to the host. Unless something else triggers a report sending in-between…

For any other, special key, there’s no hard rule. Each have their own nuances.

No, both will.

Adding a bit to this — for a plain keycode (e.g. Key_A), if it doesn’t get any special treatment from any plugin, there’s no difference between “toggled on” and “held”; Kaleidoscope only checks the current state of the keyswitch, and if it’s active, the keycode gets added to the keyboard HID report. So when one Key_A is released, there’s no “release event” — Kaleidoscope just doesn’t add its keycode to the report. If another Key_A is still pressed, that keycode will be added to the report when that key is processed.

For layer keys, there is one relevant behaviour that you might not expect. If you tap LockLayer(N), then release a ShiftToLayer(N), Layer N will turn off, regardless of when the layer shift key was pressed relative to the lock layer key. This is unlike more generally familiar keys (caps lock doesn’t normally turn off when a shift key is released).

1 Like