SDL icon indicating copy to clipboard operation
SDL copied to clipboard

Improve handling of CTRL key

Open schauveau opened this issue 3 years ago • 6 comments

When CTRL is pressed, SDL2 does not generate any SDL_TEXTINPUT event which mean that the code can only use the information provided in SDL_KEYPRESS ; that is the 'keycode' and the 'modifiers' bits.

This is problematic because because there is no practical way to figure out which character should be used when Shift or AltGr is enabled. For example, if an application want to handle ctrl-@ then it should detect Shift-2 with the US qwerty layout, Shift-' with the UK qwerty layout and AltGr-à with the French azerty layout.

This is not feasible with the current API because the layout is not (and should probably not) be exposed to the user.

That limitation is related to https://github.com/libsdl-org/SDL/issues/1767#issuecomment-929977899 and https://github.com/libsdl-org/SDL/issues/5685

My proposed solution is to produce a new event of kind SDL_TEXTINPUTCTRL that will be emitted when CTRL prevents the production of SDL_TEXTINPUT. That should be a non-breaking change since old applications can ignore the new events.

I have a working proof of concept for X11 and Wayland in https://github.com/schauveau/SDL-ctrl

See the README.md for more more details.

schauveau avatar Jul 29 '22 12:07 schauveau

I made the incorrect assumption that SDL 2 was never producing a SDL_TEXTINPUT event when CTRL is set That seems to the case for wayland because SDL_SendKeyboardText() is never called when KMOD_CTRL is set.

However, this is not always true for x11. Some key combination with CTRL are producing a SDL_TEXTINPUTwhile other are not. For example, CTRL-1 produces 1 while CTRL-2 produces nothing.

This is probably because SDL_SendKeyboardText is explicitly ignoring any text starting with a character that is not printable (lower than 32 or equal to 127).

That does not necessarily make SDL_TEXTINPUTCTRL a bad idea but the expected behavior should be standardized.

schauveau avatar Jul 29 '22 16:07 schauveau

Yes, that's more the problem, that we haven't defined standard behavior. Usually this ends up being what Windows does, to conform with the expectations of the majority of game developers.

slouken avatar Jul 29 '22 18:07 slouken

I do not have any Windows machine to check the behavior. Can someone provide a sample output of checkkeys on Windows showing how various keys are reported with and without CTRL and SHIFT?

schauveau avatar Jul 30 '22 05:07 schauveau

I am especially interested by the CTRL behavior with '@', 'a' to 'z', [ , \ , ], ^ and _. The reason being that on Unix/Linux they are traditionally mapped to the control characters 0 to 31. The x11 SDL backend does not produce any inputtext because SDL_SendKeyboardText is explicitly discarding those characters.
Does Window have similar rules? no idea!

schauveau avatar Jul 30 '22 07:07 schauveau

After a bit of digging, I figured out how CTRL is handled by X11, Wayland and any Linux application based on the libxkbcommon library. Basically, the key is decoded to a unicode codepoint which is then modified by the following function:

static char
XkbToControl(char ch)
{
    char c = ch;

    if ((c >= '@' && c < '\177') || c == ' ')
        c &= 0x1F;
    else if (c == '2')
        c = '\000';
    else if (c >= '3' && c <= '7')
        c -= ('3' - '\033');
    else if (c == '8')
        c = '\177';
    else if (c == '/')
        c = '_' & 0x1F;
    return c;
}

https://github.com/xkbcommon/libxkbcommon/blob/master/src/state.c#L899

That explains a lot about the seemingly odd behavior of CTRL. Never too late to learn even after 25 years of Linux/Unix.

Of course, since this is performed on the application side that means it is trivial to obtain the text of the key by temporarily clearing out the ctrl state during decoding as done in my proof of concept code.

Unfortunately, that may be more tricky to do on Windows since the text seems to be provided by the OS via WM_UNICHAR or WM_CHAR messages.

schauveau avatar Jul 30 '22 08:07 schauveau