Defining custom keys

Can someone explain this to me:
constexpr Key Key_LEDEffectNext = Key(0, KEY_FLAGS | SYNTHETIC | IS_INTERNAL | LED_TOGGLE);

More specifically what significance the ‘0’ has.
What ‘SYNTHETIC’ and ‘IS_INTERNAL’ precisely means in relation the Kaleidoscope.

I am asking because I want to define my own ‘abstract’ key like the led one. But I am wondering whether there is a ‘correct’ and a ‘wrong’ way to do this. My first inclination is to just make something like this constexpr Key Key_MyCustomKey1 = Key(0xF0, KEY_FLAGS); where the ‘0xF0’ is in a reserved HID value range.

But it seems a bit ‘off’ for some reason to do it this way. I’d much rather do it the ‘Kaleidoscopic’ way.

Oh, and I also need to account for modifiers. Are those in the KEY_FLAGS part or can I still do Kaleidoscope.hid().keyboard().isModifierKeyActive(Key_LeftShift) and get a reliable result? I am asking because of this part from key_defs.h:

#define KEY_FLAGS  0b00000000
#define CTRL_HELD  0b00000001
#define LALT_HELD  0b00000010
#define RALT_HELD  0b00000100
#define SHIFT_HELD 0b00001000
#define GUI_HELD   0b00010000

#define SYNTHETIC  0b01000000
#define RESERVED   0b10000000

// we assert that synthetic keys can never have keys held, so we reuse the _HELD bits
#define IS_SYSCTL        0b00000001
#define IS_INTERNAL      0b00000010
#define SWITCH_TO_KEYMAP 0b00000100
#define IS_CONSUMER      0b00001000

The line about synthetic keys can never have keys held, I don’t understand. Does this mean that if I press the Led button with shift held, it is impossible to check if shift is in fact held?

Before I explain specific questions, I think a bit of an explanation is necessary about how Key objects are handled by Kaleidoscope. There are basically two ways Kaleidoscope treats keys: either as a pair of keyCode and flags (used in the core parts of the firmware), or as a raw 16-bit integer (used in most plugins that implement custom keycodes). The reason for this is part historical, part practical. Some keys are easier to express with a code, flag pair.

To define custom keys, the recommended way is to use the Ranges plugin - its documentation sheds more light on keycodes too. The way to use it is like this:

enum {
  MY_CUSTOM_KEY = kaleidoscope::ranges::SAFE_START

constexpr Key Key_MyCustomKey = Key(MY_CUSTOM_KEY);

Using SAFE_START guarantees that no plugin bundled with Kaleidoscope will define the same keycode.

In this case, the flags tell the firmware that it’s an internal key (IS_INTERNAL), it is a custom keycode, not something in the HID spec (SYNTHETHIC), and it belongs to the LED toggle group of keys (LED_TOGGLE). The code part (0) selects the functionality: 0 is LEDEffectNext, 1 is previous, and 2 is toggle.

Heh, I should’ve read the whole post first, before responding. The example I gave above is the Kaleidoscopic-way.

.isModifierKeyActive() will give you a reliable result, yes. You do not need to worry about the *_HELD flags, those are for encoding a held modifier and a key into the same keycode. That’s usually not what you want to do with custom keycodes. So you can just go and check active modifiers in your custom handler, it’ll work.

The *_HELD flags are for special keys like LSHIFT(Key_9), where you want a key to be augmented with a modifier, encoded in one keycode.

No, it means that you can’t have things like LSHIFT(Key_LEDEffectNext). It means you can’t encode a modifier and a synthetic key into the same keycode.

You should take a look at the Kaleidoscope-Ranges plugin (in the plugins/ directory) for how to define your own special keys. Many plugins have their own set of keys in that range; you can define your own starting from SAFE_START.

What is it that you want to do with modifier keys? There are several helper functions for determining if one is active, and ways to add or suppress them, but it would help to know the details.

This is not really true any longer. The keyboard HID report doesn’t get cleared and repopulated until after onKeyEvent() handlers are called, so the only time when the HID report is in that incomplete state (from a plugin perspective) is during its onAddToReport() handler, if it has one.

1 Like

I need to stop living in the past. Thanks for clarifying, I’ll edit my post!

Thank you, both of you!

As it happens I actually stumbled upon the range plugin (entirely by chance even) after making the post, and thought “hmmmm, maybe that is what I am looking for”.

Regarding the use case, I need to have a key make two different keys, both with shift, based on whether the key is hit with shift or not… and I thought I had to do that myself… but I also stumbled upon the CharShift plugin which seems to perfectly handle that use case.

So this entire thread can be filed as “good to know” but not really necessary.


The past went on for a long time, and required frequent clarifications. It’s much easier to forget the details of how things work when they’re working well!

1 Like

About “Ranges”, can you point me to an example where it’s used in a plugin?

If I put this in my plugin.h:

enum {
  MY_KEY_1 = kaleidoscope::ranges::SAFE_START

constexpr Key Key_MyKey1 = Key(MY_KEY_1);

and then use Key_MyKey1 in my keymap, I get a bunch of errors including this error: expected initializer before 'Key_MyKey1'

You’re missing a semicolon after the enum’s closing brace.

Incidentally, you could just skip the enum and define the key like this:

constexpr Key Key_MyKey1{kaleidoscope::ranges::SAFE_START};
1 Like