Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows and Mac OS X keyboard drivers don't honor layout while the GNU/Linux one does #723

Open
oitofelix opened this issue Jan 23, 2017 · 21 comments

Comments

@oitofelix
Copy link
Contributor

My keyboard has Brazilian ABNT 2 layout. In GNU/Linux Allegro key codes match their enumeration names with their respective symbols on the real keyboard as expected. For instance, ALLEGRO_KEY_OPENBRACE corresponds to the key immediately below equals and backspace keys, as shown in the picture linked above. On Windows and Mac OS X, however, that key corresponds to ALLEGRO_KEY_CLOSEBRACE, as one would expect for the US QWERTY layout. In general, it seems Allegro is interpreting key codes unconditionally based on US layout for both platforms, while in GNU/Linux it honors the current keyboard layout.

@allefant
Copy link
Contributor

This is by design. Basically the KEY_* constants are random and you have to allow the user to assign them (you can use al_keycode_to_name() to help with that). However, in practice, on Windows/OSX it is easy to get the hardware keycode, which is independent of the layout. And so KEY_* constants largely correspond to the physical positions of a US keyboard there.

On X11, there seems to be no obvious way to get those codes, and so keys are mapped to what the layout says. Personally I think the Linux way is better - and we /could/ change Windows/OSX to do the same. People using a US keyboard would not see any change at all. However for everyone else using Windows/OSX and having a game with hardcoded keys it would suddenly break it - so it may not be a good idea to change this at this point.

@oitofelix
Copy link
Contributor Author

Basically the KEY_* constants are random and you have to allow the user to assign them

When you say "user" are you referring to the user of the game which uses Allegro? MININIM uses hard coded keys and changing Windows and Mac OX X drivers to behave like the GNU/Linux one would simply fix it.

If such a change happens to break someone's else game, it's because it broke an already broken game, fixing it in effect, because developers using hard coded keys can't presuppose everyone else has the same keyboard layout.

It's natural to expect that Allegro respects the keyboard layout. I think this would be a change for the better.

@fatcerberus
Copy link
Contributor

I think @allefant means that the game isn't supposed to hardcode the keys at all - you're supposed to allow the user to remap them in the same way you have to allow remapping joystick buttons. As for breaking hardcoded games, that's referring to games that map to, e.g. QWERTY Z and X (or A and S), having Allegro suddenly use the physical keyboard layout might move those to inconvenient locations. Players would almost certainly consider that a regression.

That said, this is inconvenient for games using the keyboard routines for actual text input. So even though it has the potential to break existing games, I ultimately vote for fixing it too.

@SiegeLord
Copy link
Member

In terms of changing this, we'll probably have a config option (want_old_style_keycodes) set to true and people can opt into the new 'fixed behavior'.

That said, I think it's the Linux behavior that's wrong. Conceptually, the mechanics of human hands are international, so if you want to hardcode keys you probably want them to be in the same general area of the keyboard rather than matching some arbitrary key stickers. E.g. WASD should be a cluster of keys on the left of the keyboard no matter the layout. To that end, I think keycodes should be layout independent, if possible.

The intended way to use keycodes is to go through al_keycode_to_name to display them to the user, and allow their configuration. al_keycode_to_name respects the layout on all the platforms, so if used properly, it should be fine as far as communicating defaults to the user. What is broken is that you can't meaningfully come up with useful defaults as on some platforms they'll refer to different portions of the keyboard.

@fatcerberus
Copy link
Contributor

As mentioned above, the Windows/OSX behavior is inconvenient for games using the keyboard routines for actual typing (e.g. "enter character name" type screens), since the game has no way of knowing the physical layout. That can be "fixed" by having the player remap them all to their real mappings, but that'd be a nuisance if every non-US player had to do that before they could even play the game.

Unless al_keycode_to_name() already solves this issue?

@elias-pschernig
Copy link
Member

elias-pschernig commented Jan 24, 2017

For text input you use ALLEGRO_EVENT_KEY_CHAR and ALLEGRO_EVENT_KEY_CHAR only, which always will give you the correct unicode.

@fatcerberus
Copy link
Contributor

For text input you use ALLEGRO_EVENT_KEY_CHAR and ALLEGRO_EVENT_KEY_CHAR only, which always will give you the correct unicode.

Thanks for that tip; I'm using ALLEGRO_EVENT_KEY_CHAR to manage the keyboard queue in minisphere so good to know I'm doing the right thing there.

@oitofelix
Copy link
Contributor Author

oitofelix commented Jan 24, 2017

That said, I think it's the Linux behavior that's wrong. Conceptually, the mechanics of human hands are international, so if you want to hardcode keys you probably want them to be in the same general area of the keyboard rather than matching some arbitrary key stickers.

I see. It seems once more I misunderstood an Allegro feature. So the purpose of key codes is actually to have a layout-free reference to keys (a physical/universal layout instead of a label-based/relative one). If that's the case, I agree that the GNU/Linux behavior is wrong.

PS: Key codes having meaningful (enumeration) names certainly adds up to that misunderstanding.

@fatcerberus
Copy link
Contributor

I gather that the Allegro key codes are meant to be an analogue to hardware scan codes, which are also layout-agnostic.

As for the enumeration, the constants had to be given meaningful names one way or another - so US QWERTY names seem like a good reference point particularly since it's probably the layout most familiar to programmers.

@oitofelix
Copy link
Contributor Author

As for the enumeration, the constants had to be given meaningful names one way or another

Not really. KEY0, KEY1, KEY2...

@fatcerberus
Copy link
Contributor

Well at that point one may as well use the scancodes directly, no? :) Even so I much prefer that the enumeration uses the QWERTY-names rather than just numbered codes. Even if QWERTY isn't your native layout you can map them back to a diagram of one. Otherwise you'd have to remember where on the keyboard each numbered code corresponds to.

@fatcerberus
Copy link
Contributor

But anyway, I agree that the behavior should at least be consistent across all platforms, which it currently isn't.

@SteveFosdick
Copy link

The allegro keycodes not varying for the keys in the same physical position is not necessarily a problem but the failure to return a keycode at all for key physically present on non-US keyboard but not on the US keyboard is an issue as without a keycode being returned it impossible for the application using Allegro to map it to anything.

As an example, I have the UK layout and there is key to the left of the 1 key. If I tap this key and log the keyboard events received I get no key down event but I do get a key up event with a code of zero.

@SiegeLord
Copy link
Member

As an example, I have the UK layout and there is key to the left of the 1 key. If I tap this key and log the keyboard events received I get no key down event but I do get a key up event with a code of zero.

What system do you have? This is something we should be able to fix more easily than this bug.

@SteveFosdick
Copy link

SteveFosdick commented Mar 21, 2018

It's Windows 10 running within VirtualBox.
Window reports: Microsoft Windows [Version 10.0.16299.309]
I am using Allegro 5.2.4.1
I have checked that the key concerned produces a character (`) in the Windows command prompt console.
Running this test program:

#include <allegro5/allegro.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    FILE *log;
    ALLEGRO_DISPLAY *display;
    ALLEGRO_EVENT_QUEUE *queue;
    ALLEGRO_EVENT event;
    bool quit = false;

    if (al_init()) {
        if ((log = fopen("keyb.log", "a"))) {
            if (al_install_keyboard()) {
                if ((queue = al_create_event_queue())) {
                    if ((display = al_create_display(200, 100))) {
                        al_register_event_source(queue, al_get_display_event_source(display));
                        al_register_event_source(queue, al_get_keyboard_event_source());
                        while (!quit) {
                            al_wait_for_event(queue, &event);
                            switch(event.type) {
                            case ALLEGRO_EVENT_KEY_DOWN:
                                fprintf(log, "key down, code=%d\n", event.keyboard.keycode);
                                break;
                            case ALLEGRO_EVENT_KEY_UP:
                                fprintf(log, "key up, code=%d\n", event.keyboard.keycode);
                                break;
                            case ALLEGRO_EVENT_KEY_CHAR:
                                fprintf(log, "key char, code=%d, unichar=%d\n", event.keyboard.keycode, event.keyboard.unichar);
                                break;
                            case ALLEGRO_EVENT_DISPLAY_CLOSE:
                                quit = true;
                                break;
                            default:
                                fprintf(log, "unexpected event type %d received\n", event.type);
                            }
                        }
                        al_destroy_display(display);
                    }
                    else
                        fputs("keyb: unable to create display\n", stderr);
                    al_destroy_event_queue(queue);
                }
                else
                    fputs("keyb: unable to create queue\n", stderr);
            }
            else
                fputs("keyb: unable to install keyboard\n", stderr);
            fclose(log);
        }
        else
            fprintf(stderr, "keyb: unable to open log file: %s\n", strerror(errno));
    }
    else
        fputs("keyb: unable to initialise allegro\n", stderr);
    return 0;
}

and tapping that key gives:

key char, code=0, unichar=96
key up, code=0

running the same test program on GNU/Linux (Arch), Allegro 5.2.3.1 gives:

key down, code=60
key char, code=60, unichar=96
key up, code=60

In keycodes.h code 60 is ALLEGRO_KEY_TILDE which makes sense as this key on the UK keyboard is in physically the same place as the ~ key on a US keyboard.

@elias-pschernig
Copy link
Member

Under windows this table is used, as you can see several entries have a 0: https://github.com/liballeg/allegro5/blob/master/src/win/wkeyboard.c#L39

All we need is figure out which entry your key produces and put in some unused ALLEGRO_* code (or a new one).

@elias-pschernig
Copy link
Member

ALLEGRO_KEY_TILDE would be produced for the Windows keycode 192 (0xC0). Somehow your key must produce something else.

@elias-pschernig
Copy link
Member

One last thing, what is your host operating system? Google finds a lot of people who cannot use the tilde key when running a Windows guest on an OSX host.

@SteveFosdick
Copy link

SteveFosdick commented Mar 23, 2018

The host is Linux, the same one that I have comparing with above. I was going to try compiling Allegro myself and and adding some debug to get the VK code but cmake just locks up when I try. Instead I found an example program on the net and hacked that so it would print the VK code:

//#include "stdafx.h"  
#include <windows.h>
#include <stdio.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);  
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)  
{  
    TCHAR appname[] = TEXT("Move Window");  
    MSG msg;  
    HWND hwnd;  
    WNDCLASS wndclass;  
    wndclass.cbClsExtra = 0;  
    wndclass.cbWndExtra = 0;  
    wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);  
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);  
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);  
    wndclass.hInstance = hInstance;  
    wndclass.lpfnWndProc = WndProc;  
    wndclass.lpszClassName = appname;  
    wndclass.lpszMenuName = NULL;  
    wndclass.style = CS_HREDRAW | CS_VREDRAW;  
    if (!RegisterClass( & wndclass))  
    {  
        MessageBox(NULL, TEXT("Class not registered"), TEXT("Error...."), MB_OK);  
    }  
    hwnd = CreateWindow(appname,  
        appname,  
        WS_OVERLAPPEDWINDOW,  
        300,  
        200,  
        400,  
        300,  
        NULL,  
        NULL,  
        hInstance,  
        NULL);  
    ShowWindow(hwnd, iCmdShow);  
    UpdateWindow(hwnd);  
    while (GetMessage( & msg, NULL, 0, 0))  
    {  
        TranslateMessage( & msg);  
        DispatchMessage( & msg);  
    }  
    return msg.wParam;  
}  
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)  
{  
    HDC hdc;  
    RECT rc;  
    static int X, Y;  
    PAINTSTRUCT ps;  
    HWND hwndSmaller;  
    switch (message)  
    {  
        case WM_CREATE:  
            return 0;  
            break;  
        case WM_PAINT:  
            return 0;  
            break;  
        case WM_SIZE:  
            X = LOWORD(lParam);  
            Y = HIWORD(lParam);  
            return 0;  
            break;  
            // key down    
        case WM_KEYDOWN:
            printf("key down wk=%02x\n", wParam);
            return 0;
            break;  
        case WM_DESTROY:  
            PostQuitMessage(EXIT_SUCCESS);  
            return 0;  
    }  
    return DefWindowProc(hwnd, message, wParam, lParam);  
}

That gives me

key down wk=df

So that's DF (hex) for the problematic key which indeed maps to zero in that table.

@SteveFosdick
Copy link

Having finally got cmake to work, putting ALLEGRO_KEY_UNKNOWN+39 in position DF in that table does the trick.

@dos1
Copy link
Contributor

dos1 commented Mar 24, 2018

In the past I've had some issues with tilde on macOS and experimental SDL backend on GNU/Linux. I think I have some workaround somewhere inside my engine. Let me check if those are still relevant! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants