Layer-switching confusion

I ended up checking the value of Layer.isOn(LAYERNAME) in my own layout switching macro, because I wanted the lights to change based on that state. And since I was already using that, I used Layer.on() and Layer.off() instead of LockLayer() anyway.

Presumably you could just have ShiftToLayer be a nonop if the target layer is already on, no?

In my proposed system, that would only be (effectively) a noop if the target layer was the top active layer. If a layer higher than the target layer was on, it would be hidden for the duration of the layer shift.

I think I didn’t quite understand the whole scenario when I made my comment. Now I think I fully understand, but this is still confusing. Is this summary correct?

CURRENT: the topmost active layer (highestLayer) is determined by selecting the layer with the highest index with layerState “on.” Using LockLayer(TARGETLAYER) or ShiftToLayer(TARGETLAYER) changes the value of layerState(TARGETLAYER). This has immediately visible effect if TARGETLAYER == highestLayer, but if TARGETLAYER has a lower index than highestLayer the effect is hidden until layers with higher indexes are made inactive.

PROPOSED: ShiftToLayer() would not change the stored value of layerState(TARGETLAYER) and would take precedence in setting highestLayer for the duration the shift modifier is in effect. When the shift modifier is released, highestLayer would revert to being set based on the highest layer index with layerState “on.”

Do I have that right?

2 Likes

Thanks for the rephrasing; you’ve got it exactly right, with one minor additional detail. In the current version, activating a target layer with an index lower than highestLayer can affect the live keymap, because transparent keys on the higher layers can allow keys from the target layer to “shine through”.

If it weren’t for the resolution of transparent keys I think I’d be 100% on board with this change. Making “shift” higher priority than “lock” seems like a consistent user experience.

I’m not qualified to address how this change might affect performance and response time (nor do I have the wherewithal to test it myself) but the proposed change does increase the complexity of documenting the behavior. Right now the logic is always “active layer with the highest index wins.” If the highest index layer has a transparent key, it just goes down the list in order of index. That’s pretty easy to understand and document.

The proposed change, however, increases the complexity to the extent I start to wonder if it’s worth the additional flexibility. For instance, assume layer 1 has a transparent key that the user expects to pass through to layer 0, but layer 2 is currently active. When the user shifts to layer 1, the pass through here would be to layer 2 (the active layer with the highest index), not to layer 0 (the layer that transparent keys in layer 1 would normally expose). That feels wrong to me, but the thing that feels right (ignoring all active layers with higher index than the shifted layer) also seems a bit counterintuitive. Personally I’d expect my transparent keys to pass through to layer 0 basically 100% of the time, but I’m not really doing the sort of compound stacking it seems like you’re targeting here.

In conclusion: ¯\(ツ)

1 Like

I didn’t describe the behaviour clearly enough, then. In your scenario, starting with layers 0 & 2 active, if the user presses a ShiftToLayer(1) key, any transparent key on layer 1 will look down the stack to layer 0, not layer 2; all mappings on layer 2 will be ignored until the shift is released, so the mapping will be the same as if layers 0 & 1 were active, but not layer 2.

Well then, it does what I expect, my bias is confirmed, and therefore there is no further interpretation that warrants discussion. But seriously, I’d love to hear if there’s anybody who would expect layer 2 to pass through in this usage. I’d expect that to be a non-zero number of people, but I really have no idea. I think you and I are on the same page.

But also to a point made in your original post, I agree that LockLayer and UnlockLayer are poorly named. I think instead of being a boolean toggle, LockLayer should always result in layerState on, and I’d argue that it should also result in the deactivation of higher layers. I think if somebody wants to insert an intermediate layer without unlocking a higher layer (to take advantage of transparency tricks) there should be a distinct command for it. (I mean, I guess you could use Layer.on directly for that usage, because it seems uncommon and a fair price to pay for that level of customization, but maybe an InsertLayer() function might make reasonable sense).

To wit, that would mean:

  • ShiftToLayer sets the active layer and temporarily ignores layers with higher indexes when resolving transparent keys;
  • LockLayer activates the selected layer (if it is inactive), and deactivates layers with higher indexes;
  • LockLayer on an active layer stops deactivating that layer;
  • UnlockLayer unlocks the selected layer and ignores higher layers;
  • All of the above instructions leave the activation status of layers with lower indexes untouched.

Then we could talk about things like ToggleLayer (to do what LockLayer actually does now) and maybe an InsertLayer (for transparency tricks). Does LockLayer() currently return the state of the target layer, or is it necessary to check Layer.isOn after use?

1 Like

Here’s another thing that’s a bit confusing: LockLayer() and ShiftToLayer() look like function calls, but they’re not. Those two preprocessor macros define two-byte Key objects with the correct flags set so that Kaleidoscope recognizes them as keys that should result in layer changes. Luckily, there is enough space available to add more layer-switching key types.

I would suggest just three:

  • ShiftToLayer() (as previously discussed)
  • ToggleLayer() (a new name for LockLayer())
  • MoveToLayer() (like ShifToLayer(), but permanently activates the target layer, and deactivates layers above)
2 Likes

I’m doing just that: my palm keys are one-shot layer keys, and both activated gets me to an empty layer, which I use to stop magic combos emitting random garbage. In other words, I have a number of magic combos, which all involve both palm keys, and a number of others. To stop the combo from inputting random keys while I’m setting up the hold of all the keys needed for it, the palm keys were set up to get me to an empty layer instead.

It is a bit of a hack, and an edge case, mind you.

I like that name too.

:+1:

What would be best, in my opinion, is to lift out layer handling into a plugin. I quite like the way it works now, but I also understand that it is sometimes - and perhaps too often - incredibly confusing for those who aren’t used to it. It also has a few perhaps surprising limitations (like not being able to shift to a lower layer). Having an alternative - which can even be the one shipping with the factory firmware - would be great. But I’d love to have the current one around too.

3 Likes

With my proposed changes, you’d still be able to do it, but with a slightly modified gesture: tap one palm key, hold the other, then hold the first one, and you’ll be on that empty layer, with both palm keys held. Not as fast as your usual MagicCombo-activation hack.

Or, you could use a MagicCombo of the two palm keys together to get you to the MagicCombo layer (I’m guessing, since I’ve never used MagicCombo myself).

…or did you mean that your MagicCombos don’t use the palm keys? I’ll have to look at your sketch again…

Hmmm. That’s a good idea, would likely work too. Will try it later, thanks!

They do. All my magic combos are PALMS + stuff, where PALMS get me to the empty layer.

Do masked keys interfere with detection for MagicCombos? It’s been a while since I looked at the code, and I’ve never used it, but I seem to think it’s detection mechanism might side-step the key masking.

No, masking does not interfere, because MagicCombo looks at the scanner state, and does so in the loop hook, and as such is unaffected by masking. You remember correctly, it side-steps key masking (intentionally, I’ll add).

2 Likes

In that case, I think these changes wouldn’t interfere with your hack at all.

1 Like

That sounds great, will try it at home as soon as I can!

I do agree, by the way, that it would be good to move layer-switching to a plugin (in nearly is already, in a sense).

The stack would be ‘stackier’ if we could drop layers into it in any order. But the ability to pull a layer out by name from any position (not just the top) is worth keeping, to avoid accumulations of leftovers from poking out around the edges of those topmost layers. Both of these operations could be handled with some complications to the cache-builder.

I’ve tried and tried to think of something else we operate the way Kaleidoscope layering currently works: layers can be in or out of the stack, but only at a fixed position. The only one I can think of is multiplane animation cameras (the technology that made Disney’s Snow White and the Seven Dwarfs revolutionary). Art is drawn for a certain distance from the lens, so that the angular size of the image on it will be coherent with the other depth cues (focal plane and depth of field, overlapping, parallax, vertical position, haze) and the size of the object it portrays. But compositing a cartoon image is not very much like compositing a board of input controls.

(And actually, for the camera you can draw a series of frames that move an object from one depth to another; you just have to cancel out each change in real distance-to-lens with a careful jump in image size, and eventually you can take it from being overlapped to overlapping or vice versa. So Kaleidoscope is more strictly ordered even than multiplane cameras, and with less justification for it from the nature of its task.)

With n layers in fixed order, technically there are 2^n possible composites, but to a human user it hardly matters which small fragment of which unused group of keys peeks out from some deeply buried layer.

I suspect it’s very hard to design a layer that usefully uses transparency on about half the keys. Exposing most of a thematically coherent key cluster (“theme”) from a lower layer is usually much less useful than exposing all of it, designing it to retain utility relative to every higher layer that may nip off one portion or another of it is mind-boggling, and ensuring that the higher layer does expose it entirely is just another packing problem, little different from just putting it in that higher layer in the first place. It’s more flexible only in that several themes on lower layers could share a footprint, if they usefully go together with the upper one and not much with each other. So I believe there’s ecological pressure for layers to be either mostly transparent or mostly opaque.

And that means the highest active mostly-opaque layer dominates the composite (unless numerous higher mostly-transparent layers are very, very well coordinated – but again, that isn’t much different from coalescing them into an opaque layer of their own). Even if we are quite optimistic about the possibility of a second or occasionally even third mostly-transparent layer making a meaningful contribution to the composite, fixed order still means there are about (n choose 2) meaningfully different places to go.

Free order means you don’t have to get multiple themes exposed at the same time, as long as you can bring one to top easily. So the pressure toward mostly-opaque layers is reduced, and the design of mostly-transparent layers requires solving fewer simultaneous packing problems too.

1 Like

@eritain This is 100% a deficiency in the current implementation. https://github.com/keyboardio/Kaleidoscope/pull/867 aims to address it. Right now, the only blocker to it getting merged is testing. I’d really like to see some automated tests, but user reports that the new code works well would probably be enough to tip me over the edge into merging it

2 Likes

On the M01, I didn’t really need multiple layers, simply because there are more keys on the M01 and at most I mainly used the palm keys to temporarily shift to another layer for nav, media controls, misc keys, etc.

On the atreus, you have to use layer if you want to access numbers, punctuation, spacing, nav, modifiers, etc.

I’m trying to understand the difference between lock to layer vs move to layer. The shift to layer requires me to hold down the key to access layer-x. Where move to layer allows me to access that layer without holding down the move to layer key.

On a different note, I didn’t think I’d ever like any other kb as much as the M01, and I may not but I’m really liking the atreus. I backed one on faith that J and K put out marvelous things and ended up immediately getting a second one once I got to touch a real one.

Can we get an atreus 2 that has backlit keys?

Wasn’t sure where to put this or start a new thread but this thread seemed closest.

Oh, I just had a thought. atreus 3 can have morphing keys, the keycap itself is blank but the led below it will project the key corresponding to whatever layer you’ve activated… I know magic, may be too sci fi for some people. Dream big.

Mike R answered this question here:

@rloic already provided a link to the sketch, but I’ll answer your other question.

No, LockLayer() and MoveToLayer() are not equivalent. LockLayer(N) toggles the state of layer N when pressed, activating or deactivating that layer, but leaving all other layers unaffected. MoveToLayer(N) unconditionally activates layer N , and also deactivates all other layers (except the base layer).

The distinction matters because the layer stack is index-ordered. Layer N+1 is always above (and when active, its non-transparent keys will be masking) Layer N , regardless of the order in which they were activated. So, if both layers 2 and 3 are active, pressing LockLayer(2) will deactivate layer 2 , but would only affect the mapping of keys that are transparent on layer 3 . Pressing LockLayer(2) again would re-activate layer 2 , but layer 3 will still be the top layer. Pressing MoveToLayer(2) , however, will also deactivate layer 3 , and will result in layer 2 being active regardless of its previous status.

3 Likes