Are there docs on key states, keyToggledOn, key_is_pressed, key_toggled_off anywhere?

My code is very buggy and doing all sorts of strange things.

I am trying to debug but I am having trouble understanding how the key states work. Are there docs anywhere or can anyone help?

What are keyToggledOn, key_is_pressed, key_toggled_off?
When are they checked? How often?

Is it necessary for a macro to tell the keyboard that a key should still be pressed down, or is it sufficient to just do D(key) once and the key will stay pressed?

They are documented in the source, but to summarize it here, this is how key states work:

Every scan cycle (every iteration of the loop function in your sketch), the key states are scanned. The firmware goes through every key on the keymap, compares its state to the previous one, and calls the event handlers. The key state will then describe the relation of the previous and the current state. If the key was not pressed, and still isn’t, then keyToggledOn, keyIsPressed, and keyToggledOff will all return false. If the key was not pressed before, but is now, then the first two will return true, keyToggledOff will return false. If the key was pressed before, and still is now, then only keyIsPressed will return true. If the key was pressed before, but isn’t now, then only keyToggledOff will return true.

The key states are checked each cycle, the previous and current states are stored. The various event handlers can use the helpers mentioned above to see what happened to keys. Which one, when, and how they use, is up to them.

The key report is cleared each cycle, so unless you re-add a key, it will release. Mind you, if your macro returns a sequence regardless of key state, then D(key) will, indeed, keep the key pressed, until you stop sending it.

Does this help? Or should I try to explain differently? (This whole thing can be very confusing, and I have trouble explaining it to this day…)

1 Like

Thanks, that helps. I am still confused about the last part, though.

Suppose a macro returns D(key) on keyToggledOn only. When does the key get released?
Suppose a macro returns D(key) on keyIsPressed only. When does the key get released?

From the testing I’m doing it seems like the answer is “never” and I have to manually U(key) it.

It should be cleared on the next scan cycle.

This should release the next cycle after you release the key.

Can you show me your code? (Putting it on GitHub preferred, so I can clone it easily :slight_smile:)

I’d have a look to see where things go wrong.

1 Like

Hey, @algernon – shouldn’t you be sleeping? :wink:

2 Likes

Ok, figured it out - you are right and the key releases the next cycle.

My testing indicates that this is true if the macro is a single key, but not if the macro contains multiple keys.

For demonstration purposes, I’ve taken the default firmware (at commit 11676727) and made just a single change:

diff --git a/Model01-Firmware.ino b/Model01-Firmware.ino
index bc45ee3..6a182e0 100644
--- a/Model01-Firmware.ino
+++ b/Model01-Firmware.ino
@@ -236,7 +236,8 @@ const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) {
     break;

   case MACRO_ANY:
-    anyKeyMacro(keyState);
+    if (keyIsPressed(keyState))
+      return MACRO(D(A));
     break;
   }
   return MACRO_NONE;

This works as expected. I’m on a Mac running Key Codes to inspect keyboard inputs, and pressing the any key works identically to pressing the A key: I see a Key Down event, then a brief pause, then Key Down (Repeat) events, and finally (when I release the key) a Key Up event. I would also add that the Key Down (Repeat) events are on the order of magnitude of 10 per second.

Let’s instead change this macro to: return MACRO(D(A), D(B));. Now I get something completely different: A Key Down event for A when I first press any, and a Key Up event for A when I release any, but in the interim, I’m getting alternating Key Down and Key Up events for B on the order of magnitude of 100 per second or more – likely, one per scan cycle.

Now admittedly this is a contrived example, but modifier + key is significantly less contrived. If I repeat this experiment with: return MACRO(D(LeftGui), D(A)); then I get a Modifier Change event when I press and release the any button, and then Key Down/Up events for A every cycle. And likewise, if I try return MACRO(D(A), D(LeftGui)); then I get Key Down/Up for A when I press and release the any button, and two Modifier Change events every cycle.

What I’m ultimately trying to build at the moment is a macro that maps Ctrl+Tab to a certain combination of keys. (And then next I’m going to work on Cmd+Tab, but that’s significantly more complicated for several reasons, so I’m starting with Ctrl+Tab. And yes, I’ve already read the code examples for Cmd/Alt+Tab both here and here.)

Let’s use Chrome on my Mac as an example. When I press and hold Ctrl+Tab using the native keyboard, Chrome will switch to the next tab, pause momentarily, and then start to rapidly advance tabs repeatedly. When I look at the Key Codes app, this first tab switch corresponds to the initial Key Down event, and then the later (rapid) tab switches correspond to the approximately ~10 per second Key Down (Repeat) events. I’m not sure what layer (eg, I’m not even sure if it’s hardware or software) is responsible for generating these Key Down (Repeat) events – but whatever it is, I can’t seem to get that to happen when using Kaleidoscope macros with multiple keys.

Can you report this as an issue against Kaleidoscope-Macros, please? A GitHub issue is much easier to remember and keep track of.

Thanks!

Done. For future viewers who end up on this discussion thread and want to learn the outcome: https://github.com/keyboardio/Kaleidoscope-Macros/issues/23

2 Likes

This works as expected. I’m on a Mac running Key Codes to inspect keyboard inputs, and pressing the any key works identically to pressing the A key: I see a Key Down event, then a brief pause, then Key Down (Repeat) events, and finally (when I release the key) a Key Up event. I would also add that the Key Down (Repeat) events are on the order of magnitude of 10 per second.

Came back to this 2.5 years later, and something has changed. Now, my example (from above) of MACRO(D(A)) results in a key down and up every cycle, instead of a single down (which is then interpreted by my OS as key repeat events).

Done. For future viewers who end up on this discussion thread and want to learn the outcome: https://github.com/keyboardio/Kaleidoscope-Macros/issues/23

Looks like that repo has been deleted (not archived) so that particular issue is gone, but it was migrated to Macros: Key repeat broken for multi-key macros · Issue #368 · keyboardio/Kaleidoscope · GitHub, which led to WITH_IMPLICIT_REPORT/WITH_EXPLICIT_REPORT being added.

Interestingly, the behavior I just described above (of there being a key down and up every cycle) remains the same no matter whether I use explicit or implicit reporting.

I’ve already spent a while chasing down this rabbit hole, so figured I’d pause to ask for help. Do you know what’s changed, is this a bug or intentional, and why are explicit and implicit reporting resulting in the same outcome?