Dealing with keyboard layouts for input on multiple platforms
-
You could have a set of defaults based on the locale.
-
It's more complicated than that, which is the source of the problem.
For starters, there's hundreds of different locales used worldwide. It isn't practical for me to create a separate set of defaults for each one when all I want is to have the exact same physical layout for all of them.
To make matters worse, many users have more than one keyboard layout installed and switch between them on-the-fly, normally using Alt+Shift (at least on Windows). This means that even if the user sets their own key bindings, if they try to play the game while the wrong layout is active, it wouldn't work or may shuffle around all the keys if the two layouts share letters/keys but at different positions (e.g. QWERTY, QWERTZ, AZERTY, Dvorak).
Qt has an event type called KeyboardLayoutChange, which should be helpful, but what Qt doesn't have is any other info about keyboard layouts whatsoever. This event has no extra info in it either.
QLocale doesn't have any keyboard layout info either.
I am just finding no way to even find the active layout, let alone set defaults based on it.
-
Well, it's not something that's provided by Qt out of the box, so you'll have to check the platforms you want to support for how they provide that information.
You could even consider contributing that to Qt to improve it for everybody.
You might also open a feature request on the bug report system for that.
-
I found the next solution only. I changed the system keyboard layout to English using this command:
PostMessage(GetForegroundWindow(), WM_INPUTLANGCHANGEREQUEST, 1, 0x04090409);
But it is for Windows only, for example:
#ifdef _WIN32 #include <windows.h> #endif OpenGLWindow::OpenGLWindow() { setTitle("OpenGL ES 2.0, Qt6, C++"); resize(350, 350); #ifdef _WIN32 PostMessage(GetForegroundWindow(), WM_INPUTLANGCHANGEREQUEST, 1, 0x04090409); #endif QSurfaceFormat surfaceFormat; surfaceFormat.setSwapInterval(1); connect(this, SIGNAL(frameSwapped()), this, SLOT(update())); setFormat(surfaceFormat); }
-
This is a typical problem in any software (not just games) that is designed without actively thinking about different keyboard layouts.
An example is assigning an application shortcut such as Ctrl+" which works fine in In US keyboard layout since " is a single key press. But in the euro layouts " is not a single key press button but it's a character translated by the keyboard layout system and is typically a combination of a modifier key + some other key. So for myself it's Shift+2.
This means that Ctrl+" shortcut is completely fubar. Would would it be? Ctrl+Shift+2 ? Forget it, wont' work.
There are really 3(+) layers in keyboard event processing.
-
Primitive keyboard / driver specific scan codes that map directly to to the physical buttons without any regard what the button face (engraving says). Think of this simply as an electronic digital signal that indicates which button on the matrix was pressed.
-
Virtual keys coming from the device keyboard layout driver. You can think of this as the "universal set of scan codes that map to known IDs". Most keyboards regardless of the engraving have keys such as A, B, C,D, 1, 2, 3 etc and the drivers and the layouts are capable of mapping these universally (at least on QWERTY layouts) to virtual keys that have then designed identifiers. If you program against the native WIn32 you immediately recognize these, VK_ENTER or 'A' 0x41. On Linux X11 provides XK_Return, XK_a, XK_k etc.
-
Translated character input that can comes through to your application via the layout and IME system. This has nothing to do with key presses per se, even though mostly there were some key presses but the combination of modifier keys and non-modifier keys were mapped to some character. This is how Unicode input works, how IME works and how for example Chinese input works. The user hammers the keyboard and the system translates and maps the key presses to a character
-
Application level processing triggered by some keyboard input. Logical operation such as "fire weapon" in a video game.
Conclusions:
- Your application should never rely on characters for shortcuts etc actions since they're always going to fail for some users.
- Your application can't rely on scan codes (ahead of time) since they're device specific and not portable.
- Your application might have a fighting chance if you rely only on the small subset of virtual keys.
In my experience there are enough quirks and surprising platforms, layouts etc that the perfect solution that never fails for anyone is impossible. But with a few assumptions it's possible to get close enough to an acceptable working solution.
When defining shortcut keys
- Rely on simple virtual keys and modifier key combinations as the default and process these using the OS's VK events.
- Don't rely on characters.
- Let the user to re-map keys.
- For lower latency (such as games) first map the user specified virtual keys to device / platform specific scan codes and then check against the scan codes.
-
-
I have the same question for WebAssembly: https://forum.qt.io/topic/155454/dealing-with-keyboard-layout-for-input-on-qt-webassembly