How do modifiers work (USB HID)?

Once again, I find myself slogging through unfamiliar and (to me) obtuse code, trying to figure out how it all works in order to assess feasibility of some ideas I have, and it’s clear that this is going to take a long time, and probably get me nowhere unless I have hardware, which won’t happen until January.

My main thing is trying to figure out how to de-couple shifted symbols from their rather arbitrarily assigned unshifted counterparts, so I can have (among other things) a set of four keys, each of which has two brackets on it (one shifted, one not). I think I understand the structure of the USB report sent from the keyboard to the host, with one byte of modifier bits, and others for the keycodes of active switches. But there are still questions that I have. First, there are keycodes for the eight modifier keys (0xE00xE7), but it’s not clear if these actually get included in the report, or if the keyboard simply changes the flags byte, and leaves the modifier keycodes out of the report itself.

This seems like it might matter when constructing a macro to “unshift” a symbol that’s in the shift “layer” on a standard keyboard, but which I want accessible when “shift” is held on a Kaleidoscope layout (and TopsyTurvy is inadequate here – in general the two symbols might be found on different keys). If the report was altered to clear the shift bit from the flags, but the keycode for one of the shift keys was still included in that report, I don’t know what to expect.

Do the modifier keycodes get included in the report? If so, why bother with the flags byte? If not, why do those keys have keycodes at all? I see that only alt has two bits in the flags byte (one for left, one for right), but the other three modifiers appear to be indistinguishable there – but the machine I’m typing on can clearly distinguish between the two shift keys…

I’m not sure of all the details of how HID things work - my impression is the flag bits indicate modifiers, but the modifier keys themselves also have keycodes (hence why there’s only one shift bit, but shift keys can be distinguished (alt has two different flag bits, because right-alt is usually a compose key, I think)).

In terms of doing different things when keys are pressed with modifiers though, I’ve done something like the following to make a macro key do different things when shift keys are held down:
https://bitbucket.org/jamesnvc/keyboardiolayout/src/92ffe67008df08c8afb70424c252f3318198541c/james_layout.ino?at=master&fileviewer=file-view-default#james_layout.ino-76

1 Like

My immediate question is how to properly write a macro that (virtually) releases any held shift key and types a key, but doesn’t do that if any other non-modifier key is also held. It seems fairly straightforward, but I want to understand it better, because it seems like I could just clear the shift flag from the report, but might need to formally call the functions to release those keys. And because I just want to understand it better.

I’m also considering using shiftToLayer() on my shift keys and having a whole separate layer for shifted symbols, for the sake of clarity, but it’s not obvious how to do that and also have it work as a modifier key.

I’ve long considered a version of the layout without any Key_LeftShift or Key_RightShift and instead just another layer, where many of the keys are LSHIFT(Key_Blah).

I’m not 100% sure it will work, but I’m not sure it won’t work.

It’d probably be easier to answer your questions if you were up for sharing concrete examples of what your end goal is, but I’ll try to answer what I can from your existing posts. (Do note that I’m not through my first coffee yet.)

They are part of the report, but they’re handled specially. You can see the code here:

Then shift would be sent, and you’d get shifted keys. It is a limit of the USB HID protocol (and the PS/2 keyboard protocol before it), that you can’t have say both, ‘w’ and ‘A’ in the same key report.

1 Like

For a lot of cases, it will work (I used to have such a layer for months on my ErgoDox EZ). This scenario fails when you want to have the shift augment another device, such as a mouse. Things get a bit complicated then.

1 Like

Ah, but what about shift-click, or just holding shift for some other purpose?

@algernon wins the race!

Um. For those cases, would you be willing to map some other key to a raw shift key, with the understanding that it’s going to make the rest of your layout kinda nutty?

I suppose you could map that other special raw shift key to a macro that sends shift events ONLY if the key report is otherwise blank…

No, because the whole idea would be to decrease the nuttiness that’s already inherent in “normal” keyboard design.

I doubt that I can be completely satisfied without substantial changes to Kaleidoscope itself, which I doubt anyone (including me) is willing to do. I’ll have to admit defeat and make the awkward macros that get the job done.

Based on what you’ve written so far, I think the nuttiness you’re running against is the USB HID protocol itself, which is much more intended as an event stream for reporting the state of physical buttons to a host computer than to report characters themselves.

If you can explain what changes to Kaleidoscope might solve these problems, I’d be thrilled to listen. Even if we can’t deal with them today, I’d really like us to be able to do what you want.

@algernon is involved in the hid-io project, which would basically do an end-run around the USB HID Keyboard spec to announce character events on the computer-side of things, but that’ll require custom drivers.

2 Likes

From that code snippet, it looks like the keycodes for the modifier keys don’t get included in the report, but I’m still not sure. Can the host tell the difference between left and right shift when you use that firmware?

The nuttiness is that keyboards send keycodes instead of characters to the host. That made sense for computers 40 years ago, but it’s really dumb today. HID-IO sounds great, and in another 20 years when it’s included in all major operating systems, and it doesn’t mean installing or configuring anything on every computer I plug my keyboard into, that sure will be nice! Until then, ugly hacks it shall be!

They do get included, but as their own bit of the data structure. Left and Right versions of each mod get their own slot.

These are the raw codes for those modifiers:

https://github.com/keyboardio/KeyboardioHID/blob/master/src/MultiReport/Keyboard.h#L37 is the data structure.

1 Like

Heck, I’d be willing to say that it made sense 15 years ago :wink:

As we get a better feel for things, hopefully, we can figure out the least-ugly hacks possible :slight_smile:

I’d want keymaps to be optionally defined with pairs (unshifted, shifted) instead of indivisible keys. This is surely more trouble than it’s worth. Although it would be nice if it was easy to decouple shifted symbols, because that might encourage people to try novel keymaps that might work a lot better.

2 Likes

Indeed. Let’s say it makes sense for pre-USB keyboards, regardless of when they were made or used (I often still type on one, myself).

Can you show me the line where those codes are added to the report? I think I see where it is for “printable” keys, but not the modifiers.

Also, how do you add those github code snippet links? I can’t figure it out.

To me, that sounds like a layer that’s actually two layers. Right now, adding the additional 128 optional bytes to each layer would burn way too much memory. Both of those things together are why I proposed a ‘shifted’ layer. Of course, the proof is in actually trying it, which I have not done.

The thing I kind of want that might get you what you also want is some kind of keymap that lets you define macros for a key “inline” in the map.

That sounds good, but I don’t think the macro would be simple enough to be readable there.