Tweak Qukeys with additional keypress to perform sticky LockLayer(n)?

With my new Atreus I have begun using the Qukeys plugin and I think it will be really useful.
(On the Model 01 I implemented all layer switching and modifier application with OSL() and OSM(). To temporarily switch to the mouse, keypad or spcecial symbols layer or apply capslock e. g. I often used the double tap feature of OSM/OSL.)
Currently the Qukey modifiers are on the home row keys of both hands (asdf and mirrored) and the Qukey layer switch keys are one row below (xcv and mirrored).
For ShiftToLayer functionality this is perfect.
What I would like do, is make a modifier or layer switch key sticky by tapping another dedicated (unique) key.
I. e., to lock layer 1 e. g., I would press and hold the corresponding Qukey followed by a tap on the dedicated MakeQukeySticky key. This latter key should turn (all?) the pressed Qukeys into sticky.
To lock layer 2, I would press/hold the corresponding Qukey and again tap the unique MakeQukeySticky key.
Ideally a second tap on the MakeQukeySticky key (without any Qukey held at the same time) would cancel the formerly applied stickiness.
A combination of another held Qukey and the MakeQukeySticky key should add the stickiness to the ones already present.
Is this doable at the moment?

regards, jo

You could make the alternate value of the layer-shift qukeys OSL(layer) instead of ShiftToLayer(layer). Then a long press of the qukey, followed by a tap (with a relatively long OneShot timeout to make it easier) on the same key, it would become sticky.

If PR #905 gets merged, this could become easier, with auto-OneShot keys.

At the moment, neither of these things work with Chrysalis alone.

Alternatively, it’s possible to write a custom plugin to search the keymap for ShiftToLayer() keys and make them sticky using the OneShot plugin. This would also get better with #905. It would work more like what you have in mind.

In fact, this gives me another idea to add to OneShot…

1 Like

Thank you for your input.
Unfortunately the proposed custom plugin is out of my programming capabilities…
Do I get you right that for the first solution a double tap (first one long and second short) should do the trick? I. e., with

kaleidoscope::plugin::Qukey(0, KeyAddr(2, 3), OSL(L1)),



and a tap on qwerty key ‘v’ (held for >500ms) followed by another tap on ‘v’ should result in a locked layer ‘L1’?

I flashed that to the keyboard but cannot reach the sticky state.

What’s that key mapped to on L1? If it’s not transparent, the current version of OneShot won’t work to make it sticky.

1 Like

Ok, that is the problem: asdf, xcv, jkl; and m<> are not transparent in none of my upper layers.
Thank you.

For reference: here is my current Atreus layout
Atreus_jo.ino (12.0 KB)

There’s a PR (#905) for OneShot that would fix this problem, if it gets merged. If you’re able to, you could try it out. If you like it, you could lobby for it to get accepted.

1 Like

I would love to try it out, but haven’t succeeded to incorporate the PR changes into my local git repository. Neither ‘git fetch origin pull/905/head:testing’, nor ‘gh pr checkout 905’ won’t find a PR with that number.
Sorry, but git novice here…

Those commands should work from the Kaleidoscope directory. Are you perhaps in Kaleidoscope-Bundle-Keyboardio instead?

Yes, you were right. After cloning Kaleidoscope the fetch of PR905 worked. Thank you.
I switched to the branch with the PR (git checkout), built the firmware and flashed it.
But how can I assure to really have your newest changes in the firmware? What do I have to do to get the alternate Qukey value (e. g. ‘OSL(L2)’) sticky?

If you set the OneShot timeouts relatively high, then you should be able to press and hold the qukey long enough to trigger its alternate value (the OneShot key), then release it and tap it again, and it should become a sticky layer shift.

Just to assure that I am doing things right:
Have the ‘Kaleidoscope-Bundle-Keyboardio’ here:


As a submodule ‘Kaleidoscope’ resides here:


In the submodule do a ‘git fetch origin pull/905/head:oneshot_redesign’ and ‘git checkout oneshot_redesign’.
Put my layout there:


Do a ‘make; make flash’ in


After flashing keyboard behaviour is a bit strange:
Although the ‘Key_MetaSticky’ is not defined anywhere in the keymaps, when tapping on a key with ‘OSL(L2)’ and afterwards on a second key with ‘M(MACRO_MINUS_DOLLAR)’ as it’s L2 value, the second key permanently changes to it’s L2 value. This is not the case if the second key’s L2 value is e. g. ‘LSHIFT(Key_Period)’.

const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) {
  //bool shifted = Keyboard.isModifierActive(Key_LeftShift.keyCode) ||
  bool shifted = Keyboard.isModifierActive(Key_LeftShift.getKeyCode()) ||
  switch (macroIndex) {
   if (shifted) {
     return MACRODOWN(D(LeftShift), T(4), U(LeftShift));
   else {
     return MACRODOWN(T(Slash)); 
   if (shifted) {
     return MACRODOWN(U(LeftShift), D(RightAlt), T(Q), U(RightAlt));
   else {
     return MACRODOWN(D(RightAlt), T(Backslash), U(RightAlt)); 
  return MACRO_NONE;

We are on layer L0. When tapping on (or holding) a key with ‘OSL(L1)’ and afterwards tapping on a second key with ‘M(MACRO_APOSTROPHE_AT)’ as it’s L0 value and ‘Key_Tab’ as it’s L1 value, the keyboard outputs the second key’s L0 value – as if OSL was without any effect?

Trying to get a Qukey’s alternative value ‘OSL(L1)’ sticky by holding the Qukey for approx. 1s, releasing it and tapping it again immediately afterwards, always yields the L0 value of the Qukey and afterwards the L0 value of any subsequently pressed key – ‘OSL(L1)’ has neither become active for one shot, nor has layer L1 become sticky. It makes no difference if Qukey’s layer L1 value is transparent or not.

OneShot timeout is set:


A Key with ‘Key_Cycle’ on layer L0 with

void cycleAction(Key previous_key, uint8_t cycle_count) {
    bool is_shifted = previous_key.getFlags() & SHIFT_HELD;
    if (previous_key.getKeyCode() == Key_A.getKeyCode())
      cycleThrough (Key_A, Key_Quote, LSHIFT(Key_Quote));
  if (previous_key.getKeyCode() == Key_O.getKeyCode())
      cycleThrough (Key_O, Key_Semicolon, LSHIFT(Key_Semicolon));
  if (previous_key.getKeyCode() == Key_U.getKeyCode())
      cycleThrough (Key_U, Key_LeftBracket, LSHIFT(Key_LeftBracket));
  if (previous_key.getKeyCode() == Key_S.getKeyCode())
      cycleThrough (Key_S, Key_Minus);

seems to prevent to access it’s values on other layers (e. g. L1, L2, L3) with ‘OSL(Ln)’.

‘Key_OneShot_Stickify’ tapped after one or a combination of modifier and layer shift keys (Qukeys as well!) makes them sticky – great! Thank you. :+1:
The Stickiness can be canceled by Escape-OneShot (except for the metasticky keys, cf. I)).


I apologize for taking so long to reply. I’m going to try to address the things that I can first, in no particular order:

I believe that both this and the other Macros problem you reported were due to the Macros key never really getting released in the keymap cache. It was a trivial fix once I figured it out; thanks for finding the problem. I’m updating the PR now, and I’d love to know if that fixes the problem for you.

On another note, I see that you’re using OneShot.isModifierActive() in your macroAction() function. This is now unnecessary; it should work fine without that. Also, in MACRO_MINUS_DOLLAR, you’re redundantly applying LeftShift when it’s already active. Not a big deal, of course.

This should become easier if you set OneShot’s hold timeout to a higher value (the default is 250). For example:


This hold timeout is the time you have to release the qukey after it takes on the layer-shift value. If you wait too long, it simply becomes a normal layer-shift key. I can only reliably get it to work on my Model01 with LED-ActiveModColor helping me out by illuminating the key as soon as the qukey takes on a layer shift (or modifier) value. Some other feedback could work for modifiers, but not for layer-shift keys.

The trouble with the Cycle plugin appears to be the same as with Macros. I’m adding that fix, too, but I haven’t tested it yet.

Thank you for giving it a try, and reporting the bugs you found; that was very helpful!

1 Like

@cerise — I should add that when I updated the PR, I rewrote git history a bit (the bugfixes needed to be inserted in the other PR that #905 is built on, plus I rebased them both onto master), so you may want to do a hard reset on your branch after fetching from the PR repo to pick up the changes. A simple pull is not likely to work properly.

Thanks a lot for your assistance – no need to hurry.

I tried the new PR version.

Hold and tap to get a Qukey’s alternative OSL sticky does work now.
No problems with macros any more.

The ‘Key_Oneshot_Stickify’ has gone lost?

Qukeys timing default values seems to have changed: I have to hold the Qukey relatively long now to get it’s alternate value.

The ‘bool is_shifted = previous_key.getFlags() & SHIFT_HELD;’ in the function cycleAction seems to always be false.

But also some new strange behaviour: After doing some mouse moves by holding OSL(L3) and tapping ‘s’, ‘f’ (mouse left, right) and releasing the OSL key, the keys ‘s’ and ‘f’ do not work any more on layer L0. On layers L3 mouse movement keeps working. On layer L2 the mouse is moved alike, although keys ‘s’ and ‘f’ should do cursor movements (left/right).

Uh-oh. I may have force-pushed that change to oblivion. If I can’t find it in git, I can reconstruct it, though.

[Edit: PR has been updated, and Key_OneShot_Stickify is back, though it’s likely to get a name change before it gets merged.]

Almost certainly MouseKeys consuming a toggle off event that it needs to allow through. I can fix it. There are probably other plugins that will have that problem, too, but I haven’t had time to do a thorough search yet.

[Edit: PR updated, MouseKeys behaviour should be fixed.]

I haven’t observed that problem. What does your current sketch look like? Maybe I can sort out what’s causing it.

My current sketch is here: Atreus.ino (11.9 KB)

I have fetched the current PR #905 into a new branch and changed to that:

git fetch origin pull/905/head:oneshot_redesign3
git checkout oneshot_redesign3

No complaints about unknown ‘Key_Oneshot_Stickify’ when compiling, but no effect of the dedicated key any more (was ok the day before yesterday).

None of my OSL(Ln) keys do work; no layer shifting.

OSL(Ln) as alternate value of Qukeys do work. But the first layer Ln switched to remains sticky and cannot be quit (by Key_Escape) any more.