Skycoder42 / QHotkey

A global shortcut/hotkey for Desktop Qt-Applications
BSD 3-Clause "New" or "Revised" License
552 stars 162 forks source link

Can't register shortcut for non-English active keyboard on Windows #17

Closed semanser closed 6 years ago

semanser commented 6 years ago

Hi. Looks like current implementation does not support different keyboard languages.

For example if I register shortcut with English keyboard, everything working good (even after language change). But when I register shortcut with non-English active keyboard (Ukrainian for example), shortcut doesn't get registered.

After some debug, I found that the reason of the problem is in the VkKeyScan function:

https://github.com/Skycoder42/QHotkey/blob/080ddd479abdaa559900d4480beaffb41ea3c9db/QHotkey/qhotkey_win.cpp#L41-L45

This function always return -1 if current active keyboard is non-English.

As documentation said:

Translates a character to the corresponding virtual-key code and shift state for the current keyboard.

If the function finds no key that translates to the passed character code, both the low-order and high-order bytes contain –1.

So this function is dependends on the current active keyboard and should be replaced with to something else.

semanser commented 6 years ago

I am experimenting with VkKeyScanEx function which accepts current keyboard layout, but vKey is still -1:

        DWORD dwThreadID = GetCurrentThreadId();
        HKL hCurKeyboard = GetKeyboardLayout(dwThreadID);
        const SHORT vKey = VkKeyScanExW(keycode, hCurKeyboard); // vKey = -1
Skycoder42 commented 6 years ago

So, what are the alternatives? This is the only function I know of that can (theoretically) translate characters to keycodes. Maybe we could load an english keyboard layout independent of the locale via LoadKeyboardLayout, but that would still not work with keyboard layouts other than the english one and will propably map keys wrong for non english keyboards...

semanser commented 6 years ago

@Skycoder42 As I mentioned possible solution is VkKeyScanEx because it's can accept current active keyboard layout as a parameter, but currently I have no idea why my implementation above doesn't work.

Skycoder42 commented 6 years ago

I think VkKeyScan is simply a shortcut for your code. Maybe LoadKeyboardLayout will do the trick? Otherwise, I have no idea how to solve this.

semanser commented 6 years ago

I found the solution that works well for different active languages and it's pretty simple without win api:

if(keycode <= 0xFFFF) {
        return (byte)keycode;
}

keycode is type of Qt::key. Looks like convert keycode to byte returns the expected result for all languages. Values of every keyboard key describe here. Let me know what do you think about it and if ok, I will create a pull request.

Skycoder42 commented 6 years ago

We could use this as the default of the switch case? Because it will propably not work for every single key. So if VkKeyScan finds nothing and the switch case finds nothing, instead of always failing we can fallback to this cast in the default case. That would be fine by me

semanser commented 6 years ago

Yes, this sounds good to me too and works pretty well. But does ok = false in that case? So it should be

default:
    ok = false;
    return (byte)keycode;

or simply:

default:
    return (byte)keycode;

For what ok is responsible?

Skycoder42 commented 6 years ago

The ok is ment to detect errors, as any returned integer can be a valid value, depending on the platform.

I would suggest:

if(keycode <= 0xFFFF)
    return (byte)keycode;
else {
    ok = false;
    return 0;
}