“Synthetic modifiers” and fast typing (the `}[` → `}{` thing)

I could swear I’ve read about this before somewhere, but now I can’t find it.

Here’s an experiment you can do with the default layout.

Type a } (fn+i) and then a [ (fn+o). The result? }[ as expected.

Now, press and hold fn+i. While holding, press fn+o. The result? }{! Not }[ as one would expect (since holding a and pressing b results in ab).

Why does this happen? Because there is no } key in the en-US QWERTY layout. What the firmware does is first sending shift down directly followed by ] down (which results in a } being typed), and then shift up followed by ] up when releasing the key.

So when you hold fn+i (“the } key”) the OS thinks that the shift key is held. So when you press fn+o (“the [ key”), the OS shifts it into {.

Is this a problem? Not really, I’d say. I don’t think people type a } followed by a [ very often. Because that’s when a problem could arise. Many times when typing two characters quickly after each other, you actually press the second key before releasing the first. In other words, holding the first key while hitting the second.

So why do I bring this up? I’ve been experimenting with ditching my custom XKB (Linux keyboard layout thing) layout in favor of using the awesome Kaleidoscope firmware as much as possible instead.

I type mostly in English, and that works really well. Occasionally, I type in Swedish. This requires three more letters: å, ä and ö. To get those, I use the international en-US QWERTY layout in my OS, which allows typing for example altgr+q to get an ä. But I don’t want to hold altgr when typing those letters. So I’ve made a key that’s bound to RALT(Key_Q). Nice.

The problem arises when I want to write the word “är” which means “is”. A pretty common word, that is. Guess what happens when I write that quickly? I get “äë”, because altgr+r gives an ë.

Finally, on to the actual questions:

  • Is the }[}{ thing considered a bug?
  • Is there anything that could potentially be done about this?
  • If so, would it be crazy hard?
  • Could somebody point me out where I could potentially start poking in the firmware to deal with this? I’m no good at c++ but I can be quite stubborn…
  • Or could this be done in my layout without changes to the core (for now)?
  • Does anyone else want this?
  • Or should I just go back to my custom XKB layout again? :slight_smile:

One way I imagine the firmware could deal with this is instead of just sending right alt up and right alt down along with the actual key for RALT(…), it would also set a flag such as synthetic_right_alt_down_sent. Then, when pressing any other key it would check if synthetic_right_alt_down_sent is set and send a right alt up first. Same thing for LSHIFT(…) and all other modifiers.

Thanks for reading :slight_smile:

(You can find all custom keys I use here: https://github.com/lydell/Model01-Firmware/blob/master/key_defs_custom.h)

2 Likes

I have a solution for this in planning stages, but it involves some very extensive changes to the Kaleidoscope core.

In the meantime, I have another solution that should work, but has two issues: use a macro key. Instead of just having a key for RALT(A) (for example), you could use a macro that sends the appropriate keycode sequence when the key toggles on, and does nothing when the key is held. You’d sacrifice the ability to repeat that key by holding it, but you probably don’t need to hold ä very often. This would make the subsequent key immune to the rollover problem, but you’d still have a problem with the scan order bug that Kaelidoscope-Macros has (#19). Fortunately, there’s a PR that fixes that bug (#20), which works, but hasn’t been merged yet. That problem would likely come up when you’d try to type Ä, because the ä key would likely be scanned before shift.

1 Like

Cool. What do you think about my proposed idea? Is it too naive?

Also thanks for the macro idea. I’ll keep that in mind.

I also occationally type in Swedish. I am using a custom layout in Windows and mapping keycodes International 1-3 to åäö, but have considered using the international US layout, like you. One reason I’m not using it is because of the dead keys of the international US layout. I would find it very annoying to have ', ``, ",^ and ~ dead (having to type an additional space to e.g. get a plain tilde). How do you get around this, or do you live with it? Or maybe use a macro to send the addtional space?

Sorry for hijacking the thread, as your post was not about this. (How do you write in a monospace backtick using markdown?)

2 Likes

On Linux there are actually several en-US QWERTY international layouts. I use the one called “English (international AltGr dead keys)”, where `, ~, ^, etc. are all not dead (but are dead in the AltGr layer). So that’s how I get around it :slight_smile: Not sure if there’s something similar for Windows.

Like this: `` ` ``.

It’s a reasonable idea, but it does have problems. You also need to check if there’s a real modifier key held before you release the “synthetic” one’s keycode. And what counts as a “real” modifier? LSHIFT(LALT(LeftControl)) probably should be considered three “real” modifiers, not one “real” and two “synthetic” ones.

My idea is similar, but would involve masking held keys that have a modifier & printable keycode combination when a non-modified printable key is pressed.

1 Like

That’s a good point! :+1:

I guess one way to get around the “what is a real modifier” problem is to:

  • Consider Key_LeftShift, Key_LeftControl, etc. as “real”.
  • Add LSHIFT_REAL(…) etc. which would allow you to create that shift-alt-ctrl combo key: LSHIFT_REAL(LALT_REAL(Key_LeftControl)). I don’t really like the _REAL name though. Might also be a bit difficult to explain.

Another way of doing would be to just exchange one problem for the other, until a better fix is implemented. Picking the lesser of two evils. Provided we know which one is less evil :wink:

It’s not about the names. LSHIFT() is a macro that sets one bit in the flags byte of the Key structure. Now, it would be possible to use more space there for encoding modifier-only combos, but it’s really not necessary, because we can check for keys with printable keycodes that have modifiers applied — those are the ones that aren’t “real” modifiers. So when a key with a printable keycode toggles on, we can check for pressed keys that meet that criterion, and mask them, without requiring any additions to the keymap markup as it exists now.

Awesome! Hope you’ll get around prototyping that some time when you feel a rush of motivation :slight_smile:

I’ve been running into this exact issue, but it’s thankfully rare. Still, glad that great minds are already working on that problem.

It happens in Javascript occasionally. A literal object ({...} syntax) subscripted ([...]) to pull out a particular property. Something like {en: 'foo', fr: 'le foo'}[LANG].

2 Likes

You might be interested in my layout. It is optimized for programming on windows, with the occasional need for danish characters (via the fn key).

It based on a standard us layout for windows. I get danish characters using WinCompose and macros that rely on wincompose.
WinCompose handles all use cases, where you might want a dead key, very elegantly without actually having any dead keys in your layout.

Btw. I also have a custom bracket key, which gets around the shift issue with [{

1 Like

Should I open an issue on Github for easier tracking?

I had forgotten about this for the moment — yes, opening an issue would be helpful.

1 Like

Here we go: https://github.com/keyboardio/Kaleidoscope/issues/293