Macros: tapping a key from another layer, in the same physical location

I would like to define a block of keys on a Fn layer translating to a macro that mashes three modifiers and then taps the qwerty layer’s underlying key. Since Macros has .col and .row, and I have the keymap definition, I thought I could do this in my macro handler:

  case MACRO_HS:
    return MACRODOWN(D(LeftControl), D(LeftGui), D(LeftAlt),
                     W(10),
                     Tr(keymaps[QWERTY][Macros.row][Macros.col]),
                     W(10),                         
                     U(LeftControl), U(LeftGui), U(LeftAlt));

(where keymaps is the global constant defined by the KEYMAPS macro in my .ino file). However, whenever I activate the macro, a keyboard event viewer shows the modifiers go down and up again, but the key isn’t getting tapped.

What am I doing wrong? Is it impossible to use .row/.col like this after all?

For more context, I’ve committed & uploaded the entire code and the keymap definition here: https://github.com/antifuchs/Model01-Firmware/blob/f47a364a975f43de2fd8bbfd58ccd35a8cbd52d8/Model01-Firmware.ino#L53-L55

Update! I found Mapping a single keypress to a chord, which is very similar to what I want, and fixed my keymap definition to not use the macro (which is a shame, because it seems like quite a useful primitive!) in https://github.com/keyboardio/Model01-Firmware/commit/c63e1774782afc2adf582e5b04c88f1f7dd4e267

Sadly, it is, because keymaps is in PROGMEM, among other things, so you’d need special code to read from there.

On the bright side, what you want is possible, either via the LSHIFT/LCTRL/etc helpers - as you found - or via a bit of coding:

if (keyToggledOn(keyState)) {
  handleKeyswitchEvent(LCTRL(LGUI(Key_LeftAlt)), UNKNOWN_KEYSWITCH_LOCATION, 
                       IS_PRESSED | INJECTED);
  delay(10);
  Key orig = Layer.getKey(QWERTY, Macros.row, Macros.col);
  handleKeyswitchEvent(orig, UNKNOWN_KEYSWITCH_LOCATION,
                       IS_PRESSED | INJECTED);
  kaleidoscope::hid::sendKeyboardReport();
  delay(10);
  handleKeyswitchEvent(orig, UNKNOWN_KEYSWITCH_LOCATION,
                       WAS_PRESSED | INJECTED);
  handleKeyswitchEvent(LCTRL(LGUI(Key_LeftAlt)), UNKNOWN_KEYSWITCH_LOCATION, 
                       WAS_PRESSED | INJECTED);
  kaleidoscope::hid::sendKeyboardReport();
}
1 Like

Thanks for clarifying! I dind’t know about PROGMEM, but that makes sense. Good to know about the Layer.getKey function too, that’s very interesting (:

I tried your code, and it works exactly as I hoped it would. I thought maybe I could use the macro form for it too, using Layer.getKey (no offense, but that handleKeyswitchEvent thing is very very verbose), and that fails exactly the way it did before - modifiers get pressed, but orig doesn’t get tapped:

Key orig = Layer.getKey(QWERTY, Macros.row, Macros.col);
return MACRODOWN(D(LeftControl), D(LeftGui), D(LeftAlt),
                 W(10),
                 Tr(orig),
                 W(10),
                 U(LeftControl), U(LeftGui), U(LeftAlt));

I’ll stick with the non-macro form (putting the key definitions in the keymap), but curiosity demands I ask why the macro_t way doesn’t work the same way (:

Because macros are stored in PROGMEM (basically, read-only memory), and must be completely known at compile-time. However, orig is only known at runtime, so Tr(orig) will likely end up being Tr(Key_NoKey).

1 Like