How do modifiers work (USB HID)?

Sure thing. Everything flows from kaleidoscope::hid:

https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/hid.cpp#L39

As written in the comments _pressModifierKey is a hack to make sure that when you send a modifier, it gets to the host at least one usb report before the keys it’s modifying. This is a workaround for ChromeOS and not actually a required part of the spec.

Everything eventually flows to pressRawKey,

https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/hid.cpp#L16

which calls KeyboardioHID’s Keyboard.press()

press() pulls out the modifier keys and puts them into _keyReport.modifiers, rather than _keyReport.keys.

Then, sendReport() actually sends the USB event:

What it’s sending is not _keyReport.keys or _keyReport.modifiers, but _keyReport.allKeys, which is the concatenated version of all the bits in those two fields.

(To get the github embeds, click on a line number in a github source listing. You’ll get a URL like https://github.com/keyboardio/KeyboardioHID/blob/master/src/MultiReport/Keyboard.h#L37.
Then just paste it inline in your message.)

When I say ‘macro’, what I mean is “Kaleidoscope Macro”, aka “snippet of code”. To make it readable, one could use a C preprocessor macro to make it look something like

 MyUppper
(SHIFT(Key_a),
       Key_b)

Alas, you’ve told me only the things that I had already figured out myself (I think). Unless I misunderstand (not currently at a desktop machine, so can’t really search through code), _keyReport.modifiers is just the one byte of modifier flags, so the keycodes for the modifiers are not being added to _keyReport.keys anywhere, so there’s no way for the host to tell Left Shift apart from Right Shift? If the modifier keycodes are added to _keyReport.keys, I haven’t seen it anywhere, but as far as I can tell, that’s the only way the host can tell the right and left modifiers (other than alt) apart.

There’s one byte for modifiers. There are 8 modifiers. Each modifier gets its own bit in that report. They’re just encoded differently than non-modifier keys. But you can 100% tell the left and right shift apart. (On MacOS, just open up the Keyboard Viewer to see.)

You can think of key_Report.modifiers as a bit-vector for encoding the states keycodes 0xE0 to 0xE7 into a single byte. The host knows to add 0xE0 to those entries.

The reason it works like this, as I understand it, is that with a traditional HID report, you get six one-byte slots for the codes of key’s you’re sending and one single-byte slot to encode the 8 modifiers. We do something a little different to support nkro, but handle modifiers the traditional way. It may be possible to change that and include modifiers in the bit-vector we use for ‘regular’ keys, but I’ve never tried it, nor have I seen any other keyboard do it that way. If we do try that, we need to qualify it on “all” the operating systems.

1 Like

Okay, I must have been confusing the bitmask that I saw elsewhere (in key_defs.h, maybe?), where only alt got two bits, the shift, control, and gui each got one bit, and the other bits were used for other things.

I know my keyboard is sending unambiguous information to the host — I just have no way of verifying that Kaleidoscope does so, as well.

I think you’re thinking of this code. This defines the bits used in the key state that is passed around in Kaleidoscope. In particular, plugins that use event handler hooks get passed this state, which tells them something about what modifiers are held. However, that’s separate from what the HID part of the code thinks is and isn’t held - it keeps track separately, in the places Jesse has pointed out.

https://github.com/keyboardio/Kaleidoscope/blob/master/src/key_defs.h#L73

Edit: not actually the key state, but the “key flags” that are part of Kaleidoscope’s representation of the key code. You can see that this is how the LCTRL(), LSHIFT(), etc macros are defined. There is no RSHIFT() macro for this reason.

Yes, that’s exactly what I was thinking of. I was following threads from multiple places while trying to sort it out, and confounded that with what I saw elsewhere (possibly the USB HID spec, which I was also looking at).

Does this limit Kaleidoscope’s ability to behave differently based on which shift key (for example) is held? I can imagine someone wanting the same key to have two different functions (prior to sending the USB report) depending on which modifier key(s) are active.

It limits the ability to create a “regular” keymap entry that appears to have both the left and right shift or left and right control keys held.

That table is only about constructing keymap entries with modifiers held down to send to the host.

I’ve never heard of a host-side…anything that behaves differently with “Left Control-X” vs “Right Control-X” or “Left Shift X” vs “Right Shift X”. The same is not true of Alt, which is why it gets two bits.

This doesn’t touch how kaleidoscope handles the shift keys.

…and now I’m no longer confused. Thanks.

I actually wrote a cheesy typing practice program to break me of my bad habit of using same hand modifiers (and causing me pain/problems in my hands). It makes sure I use opposite hand Shift, Ctrl & Alt by detecting which side the modifier used is on. I also started writing a plug-in for my IDE to yell/beep at me anytime I use same side modifiers when using shortcuts.

I also recall some program I had years ago that let you differentiate between left and right cntl keys when mapping shortcuts. Or maybe it was that it a special shortcut that activated by hitting both ctrl keys. I can’t quite remember exactly. But I do remember something of that nature because it was the catalyst for me to first learn that a keyboard did have different scan codes for the left ctrl versus the right ctrl.

So while a corner case, I think there is potential for needing to differentiate left vs right for ctrl and shift.

3 Likes

One of the things I’m currently catching up on in these forums is whether or not it’s going to be possible to make the Model 01 do something I’m currently doing via Karabiner Elements: forcing me to use the “proper” shift key. For example, right now if I type Shift_Left+a, I get nothing. Shift_Right+a produces A.

I’d love it if that could be implemented entirely within the Model 01 firmware – but it sounds like that’s just not possible? (Maybe due to how the USB HID spec works?)

3 Likes

Totally doable. One way is a plugin that blacklists key combos. Another is my crazy idea about turning the shift keys into layer changing keys.

3 Likes

I like the idea of the shift key being a layer-changer :grinning:

I expect to be spending my spare time over the next few weeks getting up to speed on how the firmware works; a blacklist plugin might be a good learning project…

The trouble with shift as a layer switch is that you would need something additional to accomplish shift+click.

1 Like

Or some way to have that layer shift key also send ‘shift’ when no non-modifiers were held

That’s basically what I meant by “something additional”.

In XKB, RCtrl is the default fifth level modifier. Is there a way to specifically select this as available for RAlt?

(This topic was linked to from my recent thread.)