As a default implementation for en_US keyboard layout, calling the driver implementation next so that it can override the result.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
v2: Move code from driver to user32 common code instead of nulldrv, graphics drivers callbacks are then called to have a chance to override the default values.
The last patch completely removes LoadKeyboardLayout driver call, it was unimplemented anyway. It can just be ignored if that's not the right way forward.
IMHO it'd make things easier to have an internal keyboard layout management, with key translations being done according to the thread and process active layouts, but I can also see why we would like to make it more seamlessly integrated with the system (although I'm not sure if it's possible to map all Windows specificities).
Supersdes: 204099-204104
dlls/user32/driver.c | 8 +- dlls/user32/input.c | 79 ++++++++++++++++++- dlls/user32/user_private.h | 2 +- dlls/wineandroid.drv/keyboard.c | 94 ----------------------- dlls/wineandroid.drv/wineandroid.drv.spec | 1 - dlls/winemac.drv/keyboard.c | 2 +- dlls/winemac.drv/winemac.drv.spec | 2 +- dlls/winex11.drv/keyboard.c | 2 +- dlls/winex11.drv/winex11.drv.spec | 2 +- 9 files changed, 84 insertions(+), 108 deletions(-)
diff --git a/dlls/user32/driver.c b/dlls/user32/driver.c index 7ac77141696..7f26a48657f 100644 --- a/dlls/user32/driver.c +++ b/dlls/user32/driver.c @@ -284,9 +284,9 @@ static BOOL CDECL nulldrv_RegisterHotKey( HWND hwnd, UINT modifiers, UINT vk ) }
static INT CDECL nulldrv_ToUnicodeEx( UINT virt, UINT scan, const BYTE *state, LPWSTR str, - int size, UINT flags, HKL layout ) + int size, UINT flags, HKL layout, INT default_ret ) { - return 0; + return default_ret; }
static BOOL CDECL nulldrv_UnloadKeyboardLayout( HKL layout ) @@ -592,9 +592,9 @@ static BOOL CDECL loaderdrv_RegisterHotKey( HWND hwnd, UINT modifiers, UINT vk ) }
static INT CDECL loaderdrv_ToUnicodeEx( UINT virt, UINT scan, const BYTE *state, LPWSTR str, - int size, UINT flags, HKL layout ) + int size, UINT flags, HKL layout, INT default_ret ) { - return load_driver()->pToUnicodeEx( virt, scan, state, str, size, flags, layout ); + return load_driver()->pToUnicodeEx( virt, scan, state, str, size, flags, layout, default_ret ); }
static BOOL CDECL loaderdrv_UnloadKeyboardLayout( HKL layout ) diff --git a/dlls/user32/input.c b/dlls/user32/input.c index 805bfe3e9de..4f0be6be3f2 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -877,11 +877,82 @@ INT WINAPI ToUnicode(UINT virtKey, UINT scanCode, const BYTE *lpKeyState, /**************************************************************************** * ToUnicodeEx (USER32.@) */ -INT WINAPI ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState, - LPWSTR lpwStr, int size, UINT flags, HKL hkl) +INT WINAPI ToUnicodeEx( UINT virt, UINT scan, const BYTE *state, + WCHAR *str, int size, UINT flags, HKL layout ) { - if (!lpKeyState) return 0; - return USER_Driver->pToUnicodeEx(virtKey, scanCode, lpKeyState, lpwStr, size, flags, hkl); + static HKL us_layout = (HKL)MAKELONG(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT)); + BOOL shift, ctrl, numlock; + WCHAR buffer[2]; + INT len; + + TRACE_(keyboard)( "virt %u, scan %u, state %p, str %p, size %d, flags %x, layout %p.\n", + virt, scan, state, str, size, flags, layout ); + + if (!state) return 0; + + shift = state[VK_SHIFT] & 0x80; + ctrl = state[VK_CONTROL] & 0x80; + numlock = state[VK_NUMLOCK] & 0x01; + + if (layout != us_layout) buffer[0] = 0; /* FIXME: non-us layout */ + else if (scan & 0x8000) buffer[0] = 0; /* key up */ + else if (!ctrl) + { + switch (virt) + { + case VK_BACK: buffer[0] = '\b'; break; + case VK_OEM_1: buffer[0] = shift ? ':' : ';'; break; + case VK_OEM_2: buffer[0] = shift ? '?' : '/'; break; + case VK_OEM_3: buffer[0] = shift ? '~' : '`'; break; + case VK_OEM_4: buffer[0] = shift ? '{' : '['; break; + case VK_OEM_5: buffer[0] = shift ? '|' : '\'; break; + case VK_OEM_6: buffer[0] = shift ? '}' : ']'; break; + case VK_OEM_7: buffer[0] = shift ? '"' : '''; break; + case VK_OEM_COMMA: buffer[0] = shift ? '<' : ','; break; + case VK_OEM_MINUS: buffer[0] = shift ? '_' : '-'; break; + case VK_OEM_PERIOD: buffer[0] = shift ? '>' : '.'; break; + case VK_OEM_PLUS: buffer[0] = shift ? '+' : '='; break; + case VK_RETURN: buffer[0] = '\r'; break; + case VK_SPACE: buffer[0] = ' '; break; + case VK_TAB: buffer[0] = '\t'; break; + case VK_MULTIPLY: buffer[0] = '*'; break; + case VK_ADD: buffer[0] = '+'; break; + case VK_SUBTRACT: buffer[0] = '-'; break; + case VK_DIVIDE: buffer[0] = '/'; break; + default: + if (virt >= '0' && virt <= '9') + buffer[0] = shift ? ")!@#$%^&*("[virt - '0'] : virt; + else if (virt >= 'A' && virt <= 'Z') + buffer[0] = shift || (state[VK_CAPITAL] & 0x01) ? virt : virt + 'a' - 'A'; + else if (virt >= VK_NUMPAD0 && virt <= VK_NUMPAD9 && numlock && !shift) + buffer[0] = '0' + virt - VK_NUMPAD0; + else if (virt == VK_DECIMAL && numlock && !shift) + buffer[0] = '.'; + else + buffer[0] = 0; + break; + } + } + else /* Control codes */ + { + switch (virt) + { + case VK_OEM_4: buffer[0] = 0x1b; break; + case VK_OEM_5: buffer[0] = 0x1c; break; + case VK_OEM_6: buffer[0] = 0x1d; break; + case VK_SUBTRACT: buffer[0] = 0x1e; break; + default: + if (virt >= 'A' && virt <= 'Z') buffer[0] = virt - 'A' + 1; + else buffer[0] = 0; + break; + } + } + buffer[1] = 0; + + wcsncpy( str, buffer, size ); + len = USER_Driver->pToUnicodeEx( virt, scan, state, str, size, flags, layout, wcslen( buffer ) ); + TRACE_(keyboard)( "ret %d, str %s.\n", len, debugstr_wn(str, len) ); + return len; }
/**************************************************************************** diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h index 5f8059a12a0..98eec94da57 100644 --- a/dlls/user32/user_private.h +++ b/dlls/user32/user_private.h @@ -71,7 +71,7 @@ typedef struct tagUSER_DRIVER { HKL (CDECL *pLoadKeyboardLayout)(LPCWSTR, UINT); UINT (CDECL *pMapVirtualKeyEx)(UINT, UINT, HKL); BOOL (CDECL *pRegisterHotKey)(HWND, UINT, UINT); - INT (CDECL *pToUnicodeEx)(UINT, UINT, const BYTE *, LPWSTR, int, UINT, HKL); + INT (CDECL *pToUnicodeEx)(UINT, UINT, const BYTE *, LPWSTR, int, UINT, HKL, INT); BOOL (CDECL *pUnloadKeyboardLayout)(HKL); void (CDECL *pUnregisterHotKey)(HWND, UINT, UINT); SHORT (CDECL *pVkKeyScanEx)(WCHAR, HKL); diff --git a/dlls/wineandroid.drv/keyboard.c b/dlls/wineandroid.drv/keyboard.c index 0a6ede0ec5f..04565c3fad8 100644 --- a/dlls/wineandroid.drv/keyboard.c +++ b/dlls/wineandroid.drv/keyboard.c @@ -750,100 +750,6 @@ jboolean keyboard_event( JNIEnv *env, jobject obj, jint win, jint action, jint k }
-/*********************************************************************** - * ANDROID_ToUnicodeEx - */ -INT CDECL ANDROID_ToUnicodeEx( UINT virt, UINT scan, const BYTE *state, - LPWSTR buf, int size, UINT flags, HKL hkl ) -{ - WCHAR buffer[2]; - BOOL shift = state[VK_SHIFT] & 0x80; - BOOL ctrl = state[VK_CONTROL] & 0x80; - BOOL numlock = state[VK_NUMLOCK] & 0x01; - - buffer[0] = buffer[1] = 0; - - if (scan & 0x8000) return 0; /* key up */ - - /* FIXME: hardcoded layout */ - - if (!ctrl) - { - switch (virt) - { - case VK_BACK: buffer[0] = '\b'; break; - case VK_OEM_1: buffer[0] = shift ? ':' : ';'; break; - case VK_OEM_2: buffer[0] = shift ? '?' : '/'; break; - case VK_OEM_3: buffer[0] = shift ? '~' : '`'; break; - case VK_OEM_4: buffer[0] = shift ? '{' : '['; break; - case VK_OEM_5: buffer[0] = shift ? '|' : '\'; break; - case VK_OEM_6: buffer[0] = shift ? '}' : ']'; break; - case VK_OEM_7: buffer[0] = shift ? '"' : '''; break; - case VK_OEM_COMMA: buffer[0] = shift ? '<' : ','; break; - case VK_OEM_MINUS: buffer[0] = shift ? '_' : '-'; break; - case VK_OEM_PERIOD: buffer[0] = shift ? '>' : '.'; break; - case VK_OEM_PLUS: buffer[0] = shift ? '+' : '='; break; - case VK_RETURN: buffer[0] = '\r'; break; - case VK_SPACE: buffer[0] = ' '; break; - case VK_TAB: buffer[0] = '\t'; break; - case VK_MULTIPLY: buffer[0] = '*'; break; - case VK_ADD: buffer[0] = '+'; break; - case VK_SUBTRACT: buffer[0] = '-'; break; - case VK_DIVIDE: buffer[0] = '/'; break; - default: - if (virt >= '0' && virt <= '9') - { - buffer[0] = shift ? ")!@#$%^&*("[virt - '0'] : virt; - break; - } - if (virt >= 'A' && virt <= 'Z') - { - buffer[0] = shift || (state[VK_CAPITAL] & 0x01) ? virt : virt + 'a' - 'A'; - break; - } - if (virt >= VK_NUMPAD0 && virt <= VK_NUMPAD9 && numlock && !shift) - { - buffer[0] = '0' + virt - VK_NUMPAD0; - break; - } - if (virt == VK_DECIMAL && numlock && !shift) - { - buffer[0] = '.'; - break; - } - break; - } - } - else /* Control codes */ - { - if (virt >= 'A' && virt <= 'Z') - buffer[0] = virt - 'A' + 1; - else - { - switch (virt) - { - case VK_OEM_4: - buffer[0] = 0x1b; - break; - case VK_OEM_5: - buffer[0] = 0x1c; - break; - case VK_OEM_6: - buffer[0] = 0x1d; - break; - case VK_SUBTRACT: - buffer[0] = 0x1e; - break; - } - } - } - - lstrcpynW( buf, buffer, size ); - TRACE( "returning %d / %s\n", strlenW( buffer ), debugstr_wn(buf, strlenW( buffer ))); - return strlenW( buffer ); -} - - /*********************************************************************** * ANDROID_GetKeyNameText */ diff --git a/dlls/wineandroid.drv/wineandroid.drv.spec b/dlls/wineandroid.drv/wineandroid.drv.spec index 00de23de27e..6f27c1586b3 100644 --- a/dlls/wineandroid.drv/wineandroid.drv.spec +++ b/dlls/wineandroid.drv/wineandroid.drv.spec @@ -7,7 +7,6 @@ @ cdecl GetKeyNameText(long ptr long) ANDROID_GetKeyNameText @ cdecl GetKeyboardLayout(long) ANDROID_GetKeyboardLayout @ cdecl MapVirtualKeyEx(long long long) ANDROID_MapVirtualKeyEx -@ cdecl ToUnicodeEx(long long ptr ptr long long long) ANDROID_ToUnicodeEx @ cdecl VkKeyScanEx(long long) ANDROID_VkKeyScanEx @ cdecl SetCursor(long) ANDROID_SetCursor @ cdecl ChangeDisplaySettingsEx(ptr ptr long long long) ANDROID_ChangeDisplaySettingsEx diff --git a/dlls/winemac.drv/keyboard.c b/dlls/winemac.drv/keyboard.c index 1ea15f59341..5e4b5882c77 100644 --- a/dlls/winemac.drv/keyboard.c +++ b/dlls/winemac.drv/keyboard.c @@ -1553,7 +1553,7 @@ BOOL CDECL macdrv_RegisterHotKey(HWND hwnd, UINT mod_flags, UINT vkey) * */ INT CDECL macdrv_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState, - LPWSTR bufW, int bufW_size, UINT flags, HKL hkl) + LPWSTR bufW, int bufW_size, UINT flags, HKL hkl, INT default_ret) { struct macdrv_thread_data *thread_data = macdrv_init_thread_data(); INT ret = 0; diff --git a/dlls/winemac.drv/winemac.drv.spec b/dlls/winemac.drv/winemac.drv.spec index 39cf33370b4..3baa730eabc 100644 --- a/dlls/winemac.drv/winemac.drv.spec +++ b/dlls/winemac.drv/winemac.drv.spec @@ -34,7 +34,7 @@ @ cdecl SysCommand(long long long) macdrv_SysCommand @ cdecl SystemParametersInfo(long long ptr long) macdrv_SystemParametersInfo @ cdecl ThreadDetach() macdrv_ThreadDetach -@ cdecl ToUnicodeEx(long long ptr ptr long long long) macdrv_ToUnicodeEx +@ cdecl ToUnicodeEx(long long ptr ptr long long long long) macdrv_ToUnicodeEx @ cdecl UnregisterHotKey(long long long) macdrv_UnregisterHotKey @ cdecl UpdateClipboard() macdrv_UpdateClipboard @ cdecl UpdateLayeredWindow(long ptr ptr) macdrv_UpdateLayeredWindow diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 01620c5e4a4..82dd67d9373 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -2432,7 +2432,7 @@ static char KEYBOARD_MapDeadKeysym(KeySym keysym) * */ INT CDECL X11DRV_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState, - LPWSTR bufW, int bufW_size, UINT flags, HKL hkl) + LPWSTR bufW, int bufW_size, UINT flags, HKL hkl, INT default_ret) { Display *display = thread_init_display(); XKeyEvent e; diff --git a/dlls/winex11.drv/winex11.drv.spec b/dlls/winex11.drv/winex11.drv.spec index c0e24d8fe82..7aecac2c016 100644 --- a/dlls/winex11.drv/winex11.drv.spec +++ b/dlls/winex11.drv/winex11.drv.spec @@ -11,7 +11,7 @@ @ cdecl GetKeyboardLayoutName(ptr) X11DRV_GetKeyboardLayoutName @ cdecl LoadKeyboardLayout(wstr long) X11DRV_LoadKeyboardLayout @ cdecl MapVirtualKeyEx(long long long) X11DRV_MapVirtualKeyEx -@ cdecl ToUnicodeEx(long long ptr ptr long long long) X11DRV_ToUnicodeEx +@ cdecl ToUnicodeEx(long long ptr ptr long long long long) X11DRV_ToUnicodeEx @ cdecl UnloadKeyboardLayout(long) X11DRV_UnloadKeyboardLayout @ cdecl VkKeyScanEx(long long) X11DRV_VkKeyScanEx @ cdecl DestroyCursorIcon(long) X11DRV_DestroyCursorIcon
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/user32/input.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/dlls/user32/input.c b/dlls/user32/input.c index 4f0be6be3f2..13506cf6fc7 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -881,7 +881,7 @@ INT WINAPI ToUnicodeEx( UINT virt, UINT scan, const BYTE *state, WCHAR *str, int size, UINT flags, HKL layout ) { static HKL us_layout = (HKL)MAKELONG(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT)); - BOOL shift, ctrl, numlock; + BOOL shift, ctrl, alt, numlock; WCHAR buffer[2]; INT len;
@@ -890,6 +890,7 @@ INT WINAPI ToUnicodeEx( UINT virt, UINT scan, const BYTE *state,
if (!state) return 0;
+ alt = state[VK_MENU] & 0x80; shift = state[VK_SHIFT] & 0x80; ctrl = state[VK_CONTROL] & 0x80; numlock = state[VK_NUMLOCK] & 0x01; @@ -933,7 +934,7 @@ INT WINAPI ToUnicodeEx( UINT virt, UINT scan, const BYTE *state, break; } } - else /* Control codes */ + else if (!alt) /* Control codes */ { switch (virt) { @@ -947,6 +948,7 @@ INT WINAPI ToUnicodeEx( UINT virt, UINT scan, const BYTE *state, break; } } + else buffer[0] = 0; buffer[1] = 0;
wcsncpy( str, buffer, size );
As shown by user32 input and msg tests.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/user32/input.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/dlls/user32/input.c b/dlls/user32/input.c index 13506cf6fc7..dbe00ee88cc 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -897,6 +897,7 @@ INT WINAPI ToUnicodeEx( UINT virt, UINT scan, const BYTE *state,
if (layout != us_layout) buffer[0] = 0; /* FIXME: non-us layout */ else if (scan & 0x8000) buffer[0] = 0; /* key up */ + else if (virt == VK_ESCAPE) buffer[0] = VK_ESCAPE; else if (!ctrl) { switch (virt) @@ -942,6 +943,8 @@ INT WINAPI ToUnicodeEx( UINT virt, UINT scan, const BYTE *state, case VK_OEM_5: buffer[0] = 0x1c; break; case VK_OEM_6: buffer[0] = 0x1d; break; case VK_SUBTRACT: buffer[0] = 0x1e; break; + case VK_RETURN: buffer[0] = shift ? 0 : '\n'; break; + case VK_SPACE: buffer[0] = ' '; break; default: if (virt >= 'A' && virt <= 'Z') buffer[0] = virt - 'A' + 1; else buffer[0] = 0;
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/user32/driver.c | 26 --------------- dlls/user32/input.c | 53 ++++++++++++++++++++++++++----- dlls/user32/user_private.h | 2 -- dlls/winex11.drv/keyboard.c | 21 ------------ dlls/winex11.drv/winex11.drv.spec | 2 -- 5 files changed, 45 insertions(+), 59 deletions(-)
diff --git a/dlls/user32/driver.c b/dlls/user32/driver.c index 7f26a48657f..ac045148abf 100644 --- a/dlls/user32/driver.c +++ b/dlls/user32/driver.c @@ -111,11 +111,9 @@ static const USER_DRIVER *load_driver(void) GET_USER_FUNC(GetKeyboardLayout); GET_USER_FUNC(GetKeyboardLayoutList); GET_USER_FUNC(GetKeyboardLayoutName); - GET_USER_FUNC(LoadKeyboardLayout); GET_USER_FUNC(MapVirtualKeyEx); GET_USER_FUNC(RegisterHotKey); GET_USER_FUNC(ToUnicodeEx); - GET_USER_FUNC(UnloadKeyboardLayout); GET_USER_FUNC(UnregisterHotKey); GET_USER_FUNC(VkKeyScanEx); GET_USER_FUNC(DestroyCursorIcon); @@ -268,11 +266,6 @@ static BOOL CDECL nulldrv_GetKeyboardLayoutName( LPWSTR name ) return FALSE; }
-static HKL CDECL nulldrv_LoadKeyboardLayout( LPCWSTR name, UINT flags ) -{ - return 0; -} - static UINT CDECL nulldrv_MapVirtualKeyEx( UINT code, UINT type, HKL layout ) { return 0; @@ -289,11 +282,6 @@ static INT CDECL nulldrv_ToUnicodeEx( UINT virt, UINT scan, const BYTE *state, L return default_ret; }
-static BOOL CDECL nulldrv_UnloadKeyboardLayout( HKL layout ) -{ - return 0; -} - static void CDECL nulldrv_UnregisterHotKey( HWND hwnd, UINT modifiers, UINT vk ) { } @@ -489,11 +477,9 @@ static USER_DRIVER null_driver = nulldrv_GetKeyboardLayout, nulldrv_GetKeyboardLayoutList, nulldrv_GetKeyboardLayoutName, - nulldrv_LoadKeyboardLayout, nulldrv_MapVirtualKeyEx, nulldrv_RegisterHotKey, nulldrv_ToUnicodeEx, - nulldrv_UnloadKeyboardLayout, nulldrv_UnregisterHotKey, nulldrv_VkKeyScanEx, /* cursor/icon functions */ @@ -576,11 +562,6 @@ static BOOL CDECL loaderdrv_GetKeyboardLayoutName( LPWSTR name ) return load_driver()->pGetKeyboardLayoutName( name ); }
-static HKL CDECL loaderdrv_LoadKeyboardLayout( LPCWSTR name, UINT flags ) -{ - return load_driver()->pLoadKeyboardLayout( name, flags ); -} - static UINT CDECL loaderdrv_MapVirtualKeyEx( UINT code, UINT type, HKL layout ) { return load_driver()->pMapVirtualKeyEx( code, type, layout ); @@ -597,11 +578,6 @@ static INT CDECL loaderdrv_ToUnicodeEx( UINT virt, UINT scan, const BYTE *state, return load_driver()->pToUnicodeEx( virt, scan, state, str, size, flags, layout, default_ret ); }
-static BOOL CDECL loaderdrv_UnloadKeyboardLayout( HKL layout ) -{ - return load_driver()->pUnloadKeyboardLayout( layout ); -} - static void CDECL loaderdrv_UnregisterHotKey( HWND hwnd, UINT modifiers, UINT vk ) { load_driver()->pUnregisterHotKey( hwnd, modifiers, vk ); @@ -704,11 +680,9 @@ static USER_DRIVER lazy_load_driver = loaderdrv_GetKeyboardLayout, loaderdrv_GetKeyboardLayoutList, loaderdrv_GetKeyboardLayoutName, - loaderdrv_LoadKeyboardLayout, loaderdrv_MapVirtualKeyEx, loaderdrv_RegisterHotKey, loaderdrv_ToUnicodeEx, - loaderdrv_UnloadKeyboardLayout, loaderdrv_UnregisterHotKey, loaderdrv_VkKeyScanEx, /* cursor/icon functions */ diff --git a/dlls/user32/input.c b/dlls/user32/input.c index dbe00ee88cc..c4d89a8954e 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -77,6 +77,43 @@ static WORD get_key_state(void) }
+/*********************************************************************** + * get_locale_kbd_layout + */ +static HKL get_locale_kbd_layout(void) +{ + ULONG_PTR layout; + LANGID langid; + + /* FIXME: + * + * layout = main_key_tab[kbd_layout].lcid; + * + * Winword uses return value of GetKeyboardLayout as a codepage + * to translate ANSI keyboard messages to unicode. But we have + * a problem with it: for instance Polish keyboard layout is + * identical to the US one, and therefore instead of the Polish + * locale id we return the US one. + */ + + layout = GetUserDefaultLCID(); + + /* + * Microsoft Office expects this value to be something specific + * for Japanese and Korean Windows with an IME the value is 0xe001 + * We should probably check to see if an IME exists and if so then + * set this word properly. + */ + langid = PRIMARYLANGID(LANGIDFROMLCID(layout)); + if (langid == LANG_CHINESE || langid == LANG_JAPANESE || langid == LANG_KOREAN) + layout = MAKELONG( layout, 0xe001 ); /* IME */ + else + layout |= layout << 16; + + return (HKL)layout; +} + + /********************************************************************** * set_capture_window */ @@ -1086,11 +1123,11 @@ BOOL WINAPI UnregisterHotKey(HWND hwnd,INT id) /*********************************************************************** * LoadKeyboardLayoutW (USER32.@) */ -HKL WINAPI LoadKeyboardLayoutW(LPCWSTR pwszKLID, UINT Flags) +HKL WINAPI LoadKeyboardLayoutW( const WCHAR *name, UINT flags ) { - TRACE_(keyboard)("(%s, %d)\n", debugstr_w(pwszKLID), Flags); - - return USER_Driver->pLoadKeyboardLayout(pwszKLID, Flags); + FIXME_(keyboard)( "name %s, flags %x, semi-stub!\n", debugstr_w( name ), flags ); + /* FIXME: semi-stub: returning default layout */ + return get_locale_kbd_layout(); }
/*********************************************************************** @@ -1113,11 +1150,11 @@ HKL WINAPI LoadKeyboardLayoutA(LPCSTR pwszKLID, UINT Flags) /*********************************************************************** * UnloadKeyboardLayout (USER32.@) */ -BOOL WINAPI UnloadKeyboardLayout(HKL hkl) +BOOL WINAPI UnloadKeyboardLayout( HKL layout ) { - TRACE_(keyboard)("(%p)\n", hkl); - - return USER_Driver->pUnloadKeyboardLayout(hkl); + FIXME_(keyboard)( "layout %p, stub!\n", layout ); + SetLastError( ERROR_CALL_NOT_IMPLEMENTED ); + return FALSE; }
typedef struct __TRACKINGLIST { diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h index 98eec94da57..49ddc6f67b4 100644 --- a/dlls/user32/user_private.h +++ b/dlls/user32/user_private.h @@ -68,11 +68,9 @@ typedef struct tagUSER_DRIVER { HKL (CDECL *pGetKeyboardLayout)(DWORD); UINT (CDECL *pGetKeyboardLayoutList)(INT, HKL *); BOOL (CDECL *pGetKeyboardLayoutName)(LPWSTR); - HKL (CDECL *pLoadKeyboardLayout)(LPCWSTR, UINT); UINT (CDECL *pMapVirtualKeyEx)(UINT, UINT, HKL); BOOL (CDECL *pRegisterHotKey)(HWND, UINT, UINT); INT (CDECL *pToUnicodeEx)(UINT, UINT, const BYTE *, LPWSTR, int, UINT, HKL, INT); - BOOL (CDECL *pUnloadKeyboardLayout)(HKL); void (CDECL *pUnregisterHotKey)(HWND, UINT, UINT); SHORT (CDECL *pVkKeyScanEx)(WCHAR, HKL); /* cursor/icon functions */ diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c index 82dd67d9373..ae6ff0e3d54 100644 --- a/dlls/winex11.drv/keyboard.c +++ b/dlls/winex11.drv/keyboard.c @@ -1906,27 +1906,6 @@ HKL CDECL X11DRV_GetKeyboardLayout(DWORD dwThreadid) }
-/*********************************************************************** - * LoadKeyboardLayout (X11DRV.@) - */ -HKL CDECL X11DRV_LoadKeyboardLayout(LPCWSTR name, UINT flags) -{ - FIXME("%s, %04x: semi-stub! Returning default layout.\n", debugstr_w(name), flags); - return get_locale_kbd_layout(); -} - - -/*********************************************************************** - * UnloadKeyboardLayout (X11DRV.@) - */ -BOOL CDECL X11DRV_UnloadKeyboardLayout(HKL hkl) -{ - FIXME("%p: stub!\n", hkl); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; -} - - /*********************************************************************** * ActivateKeyboardLayout (X11DRV.@) */ diff --git a/dlls/winex11.drv/winex11.drv.spec b/dlls/winex11.drv/winex11.drv.spec index 7aecac2c016..310186791f0 100644 --- a/dlls/winex11.drv/winex11.drv.spec +++ b/dlls/winex11.drv/winex11.drv.spec @@ -9,10 +9,8 @@ @ cdecl GetKeyNameText(long ptr long) X11DRV_GetKeyNameText @ cdecl GetKeyboardLayout(long) X11DRV_GetKeyboardLayout @ cdecl GetKeyboardLayoutName(ptr) X11DRV_GetKeyboardLayoutName -@ cdecl LoadKeyboardLayout(wstr long) X11DRV_LoadKeyboardLayout @ cdecl MapVirtualKeyEx(long long long) X11DRV_MapVirtualKeyEx @ cdecl ToUnicodeEx(long long ptr ptr long long long long) X11DRV_ToUnicodeEx -@ cdecl UnloadKeyboardLayout(long) X11DRV_UnloadKeyboardLayout @ cdecl VkKeyScanEx(long long) X11DRV_VkKeyScanEx @ cdecl DestroyCursorIcon(long) X11DRV_DestroyCursorIcon @ cdecl SetCursor(long) X11DRV_SetCursor
Rémi Bernon rbernon@codeweavers.com wrote:
Signed-off-by: Rémi Bernon rbernon@codeweavers.com
dlls/user32/driver.c | 26 --------------- dlls/user32/input.c | 53 ++++++++++++++++++++++++++----- dlls/user32/user_private.h | 2 -- dlls/winex11.drv/keyboard.c | 21 ------------ dlls/winex11.drv/winex11.drv.spec | 2 -- 5 files changed, 45 insertions(+), 59 deletions(-)
This patch is going in the wrong direction IMO, it won't allow implementing proper support for keyboard layouts and WM_INPUTLANGCHANGE using XKB keyboard groups. I sent a patch that implements ActivateKeyboardLayout() using XKB extension, and top of it WM_INPUTLANGCHANGE could be added: https://www.winehq.org/pipermail/wine-devel/2020-April/164161.html Unfortunately it was ignored.
How are you planning to implement support for keyboard layouts without support on the driver side?
On 4/23/21 8:25 PM, Dmitry Timoshkov wrote:
Rémi Bernon rbernon@codeweavers.com wrote:
Signed-off-by: Rémi Bernon rbernon@codeweavers.com
dlls/user32/driver.c | 26 --------------- dlls/user32/input.c | 53 ++++++++++++++++++++++++++----- dlls/user32/user_private.h | 2 -- dlls/winex11.drv/keyboard.c | 21 ------------ dlls/winex11.drv/winex11.drv.spec | 2 -- 5 files changed, 45 insertions(+), 59 deletions(-)
This patch is going in the wrong direction IMO, it won't allow implementing proper support for keyboard layouts and WM_INPUTLANGCHANGE using XKB keyboard groups. I sent a patch that implements ActivateKeyboardLayout() using XKB extension, and top of it WM_INPUTLANGCHANGE could be added: https://www.winehq.org/pipermail/wine-devel/2020-April/164161.html Unfortunately it was ignored.
How are you planning to implement support for keyboard layouts without support on the driver side?
I'm not sure to be honest, and as I indicated in my comment it's very possible indeed that this is not the right thing to do. Also, I don't yet see how this last patch is doing anything wrt the patch you sent. This is only removing LoadKeyboardLayout which doesn't do anything, currently or in your patch.
My general feeling is that although it may be nice for the integration, it's going to be hard to implement keyboard layouts by bridging it directly with the host. Also I'm not sure having Windows applications messing with the keyboard layout is a great user improvement. (In the same way Windows apps messing with the display resolution isn't, in my opinion).
I also don't know how the graphics drivers are going to be treated with the 32on64 transition, but my understanding is that the less we do on the unix side, the better we are.
So overall, I'm tempted to think that if we want that kind of feature, implementing everything on the Win32 side would be better ultimately (but a lot more work, true). We would be able to properly implement thread-local or process-wide layout changes without being limited by what the host can provide.
Rémi Bernon rbernon@codeweavers.com wrote:
This patch is going in the wrong direction IMO, it won't allow implementing proper support for keyboard layouts and WM_INPUTLANGCHANGE using XKB keyboard groups. I sent a patch that implements ActivateKeyboardLayout() using XKB extension, and top of it WM_INPUTLANGCHANGE could be added: https://www.winehq.org/pipermail/wine-devel/2020-April/164161.html Unfortunately it was ignored.
How are you planning to implement support for keyboard layouts without support on the driver side?
I'm not sure to be honest, and as I indicated in my comment it's very possible indeed that this is not the right thing to do. Also, I don't yet see how this last patch is doing anything wrt the patch you sent. This is only removing LoadKeyboardLayout which doesn't do anything, currently or in your patch.
That patch doesn't implement LoadKeyboardLayout(), however it makes it possible.
My general feeling is that although it may be nice for the integration, it's going to be hard to implement keyboard layouts by bridging it directly with the host. Also I'm not sure having Windows applications messing with the keyboard layout is a great user improvement. (In the same way Windows apps messing with the display resolution isn't, in my opinion).
I have an application that on startup changes keyboard layout to Russian, so that an operator can readily start typing in the input fields. That's very helpful.
Also, it's not only about messing with layouts, it's about making applications detect available input locales and correctly handle input locale changes. For instance Winword highly depends on this.
On 4/23/21 8:54 PM, Dmitry Timoshkov wrote:
Rémi Bernon rbernon@codeweavers.com wrote:
This patch is going in the wrong direction IMO, it won't allow implementing proper support for keyboard layouts and WM_INPUTLANGCHANGE using XKB keyboard groups. I sent a patch that implements ActivateKeyboardLayout() using XKB extension, and top of it WM_INPUTLANGCHANGE could be added: https://www.winehq.org/pipermail/wine-devel/2020-April/164161.html Unfortunately it was ignored.
How are you planning to implement support for keyboard layouts without support on the driver side?
I'm not sure to be honest, and as I indicated in my comment it's very possible indeed that this is not the right thing to do. Also, I don't yet see how this last patch is doing anything wrt the patch you sent. This is only removing LoadKeyboardLayout which doesn't do anything, currently or in your patch.
That patch doesn't implement LoadKeyboardLayout(), however it makes it possible.
My general feeling is that although it may be nice for the integration, it's going to be hard to implement keyboard layouts by bridging it directly with the host. Also I'm not sure having Windows applications messing with the keyboard layout is a great user improvement. (In the same way Windows apps messing with the display resolution isn't, in my opinion).
I have an application that on startup changes keyboard layout to Russian, so that an operator can readily start typing in the input fields. That's very helpful.
Also, it's not only about messing with layouts, it's about making applications detect available input locales and correctly handle input locale changes. For instance Winword highly depends on this.
I'm not saying we shouldn't support switching input locales, but that /correctly/ implementing the Windows input locales probably requires to do it in Wine.
(To be clear, I'm replying here for the sake of the argument but I've got no plan to do anything about it. I was only factoring driver code, and this last patch could very well be ignored like I said).
1) Assuming each thread can have a different input locale, which is a legacy behavior according to MSDN, we could imagine the following scenario:
An application with a child window running in a separate thread from its parent. If the child window thread calls ActivateKeyboardLayout, the vkey and char events it'll receive should use the new layout, while the vkey and char events the parent receive should still use the default layout.
The way winex11 is currently structured (and I don't see this changing any time soon), the toplevel window would translate X11 events to vkeys messages according its locale, which would be wrong if the child window has input focus.
2) MSDN now mentions that ActivateKeyboardLayout / LoadKeyboardLayout do a system-wide change, as long as the calling application is foreground.
This makes things simpler in some sense, and that would solve the issue from 1), but then implementing it (somehow) and letting Wine change system-wide input locale is in my opinion risky. I would rather see Wine input locale management not leaking out.
There's several (a lot of?) ways to change input locale on Linux, I didn't even play with many of them but as far as I could see already they don't work together very well.
For instance, I can change input locale through setxkbmap (which Wine sees when detecting keyboard or xkb groups btw), or using GNOME language switcher (which Wine doesn't see). Both aren't synced at all, and I don't think adding Wine to the party would help.
FWIW I also tried your patch and it wasn't able to change anything as far as I could tell (I used setxkbmap fr,us to setup two groups and tried switching between them, but the fr translation was always active).
I don't know what the best solution here is, but I think maybe we should have some input locale support implemented inside Wine, eventually monitor host input locale changes to select the corresponding locale internally too.
This would also let Wine support input locales on systems without any native input locale support or ways to change it system-wide (if there are any and if we consider portability beyond X11 / macOS / Android).
Then I agree that being able to use the host input locales directly is also something that is nice to have, especially for user with customized layouts.
Rémi Bernon rbernon@codeweavers.com wrote:
- Assuming each thread can have a different input locale, which is a
legacy behavior according to MSDN, we could imagine the following scenario:
An application with a child window running in a separate thread from its parent. If the child window thread calls ActivateKeyboardLayout, the vkey and char events it'll receive should use the new layout, while the vkey and char events the parent receive should still use the default layout.
The way winex11 is currently structured (and I don't see this changing any time soon), the toplevel window would translate X11 events to vkeys messages according its locale, which would be wrong if the child window has input focus.
Every thread has its own x11 connection (aka Display), so all events including keyboard ones are bound to a thread. On the other hand input locale depends on a WM configuration, and might be window or system bound.
- MSDN now mentions that ActivateKeyboardLayout / LoadKeyboardLayout do
a system-wide change, as long as the calling application is foreground.
This makes things simpler in some sense, and that would solve the issue from 1), but then implementing it (somehow) and letting Wine change system-wide input locale is in my opinion risky. I would rather see Wine input locale management not leaking out.
There's several (a lot of?) ways to change input locale on Linux, I didn't even play with many of them but as far as I could see already they don't work together very well.
For instance, I can change input locale through setxkbmap (which Wine sees when detecting keyboard or xkb groups btw), or using GNOME language switcher (which Wine doesn't see). Both aren't synced at all, and I don't think adding Wine to the party would help.
Everything under X11 goes via XKB these days, so if Wine doesn't see a layout switch from the GNOME switcher that's most likely a bug in Wine.
FWIW I also tried your patch and it wasn't able to change anything as far as I could tell (I used setxkbmap fr,us to setup two groups and tried switching between them, but the fr translation was always active).
How are you testing layout switching? With a custom Windows application or some other way?
On 4/24/21 8:43 PM, Dmitry Timoshkov wrote:
Rémi Bernon rbernon@codeweavers.com wrote:
- Assuming each thread can have a different input locale, which is a
legacy behavior according to MSDN, we could imagine the following scenario:
An application with a child window running in a separate thread from its parent. If the child window thread calls ActivateKeyboardLayout, the vkey and char events it'll receive should use the new layout, while the vkey and char events the parent receive should still use the default layout.
The way winex11 is currently structured (and I don't see this changing any time soon), the toplevel window would translate X11 events to vkeys messages according its locale, which would be wrong if the child window has input focus.
Every thread has its own x11 connection (aka Display), so all events including keyboard ones are bound to a thread. On the other hand input locale depends on a WM configuration, and might be window or system bound.
Yes.
I don't know how XkbLockGroup acts, but if we want to support the legacy per-thread input locales and it acts system-wide then it will not be possible to have such granularity.
If it works per-window / per-client and we want to implement the new system-wide behavior, it'll be difficult too.
- MSDN now mentions that ActivateKeyboardLayout / LoadKeyboardLayout do
a system-wide change, as long as the calling application is foreground.
This makes things simpler in some sense, and that would solve the issue from 1), but then implementing it (somehow) and letting Wine change system-wide input locale is in my opinion risky. I would rather see Wine input locale management not leaking out.
There's several (a lot of?) ways to change input locale on Linux, I didn't even play with many of them but as far as I could see already they don't work together very well.
For instance, I can change input locale through setxkbmap (which Wine sees when detecting keyboard or xkb groups btw), or using GNOME language switcher (which Wine doesn't see). Both aren't synced at all, and I don't think adding Wine to the party would help.
Everything under X11 goes via XKB these days, so if Wine doesn't see a layout switch from the GNOME switcher that's most likely a bug in Wine.
Not only Wine, but GNOME language switcher itself doesn't see changes made by setxkbmap. I can call setxkbmap to set any kind of keyboard layout, and GNOME switcher still believes it's the default one (doesn't see new layouts, or layout changes).
If I use setxkbmap us instead, Wine correctly detects "United States keyboard layout", and translates both chars and vkey accordingly.
But if I switch keyboard layout using GNOME, for instance setting en instead of the default fr, Wine still detects my keyboard layout as "French keyboard layout". The char messages are correctly translated but not the vkey messages.
FWIW I also tried your patch and it wasn't able to change anything as far as I could tell (I used setxkbmap fr,us to setup two groups and tried switching between them, but the fr translation was always active).
How are you testing layout switching? With a custom Windows application or some other way?
I did a quick try by calling "setxkbmap fr,us" first, then an application which calls ActivateKeyboardLayout(0x040c040c) / ActivateKeyboardLayout(0x04090409) from time to time. The char and vkey messages are always the one for the fr layout, regardless of the XkbLockGroup calls.
I had to tweak a bit the group names because my fr xkb group is actually named "French", but I checked that XkbLockGroup is actually called correctly when activating layouts back and forth.
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=89269
Your paranoid android.
=== debiant2 (32 bit Chinese:China report) ===
user32: win.c:11487: Test failed: got normal pos (200,88)-(400,288) win.c:11490: Test failed: got window rect (200,88)-(400,288) win.c:11503: Test failed: got normal pos (200,88)-(400,288)
On 4/23/21 7:30 PM, Rémi Bernon wrote:
diff --git a/dlls/user32/input.c b/dlls/user32/input.c index 805bfe3e9de..4f0be6be3f2 100644 --- a/dlls/user32/input.c +++ b/dlls/user32/input.c @@ -877,11 +877,82 @@ INT WINAPI ToUnicode(UINT virtKey, UINT scanCode, const BYTE *lpKeyState, /****************************************************************************
ToUnicodeEx (USER32.@)
*/ -INT WINAPI ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState,
LPWSTR lpwStr, int size, UINT flags, HKL hkl)
+INT WINAPI ToUnicodeEx( UINT virt, UINT scan, const BYTE *state,
{WCHAR *str, int size, UINT flags, HKL layout )
- if (!lpKeyState) return 0;
- return USER_Driver->pToUnicodeEx(virtKey, scanCode, lpKeyState, lpwStr, size, flags, hkl);
- static HKL us_layout = (HKL)MAKELONG(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT));
- BOOL shift, ctrl, numlock;
- WCHAR buffer[2];
- INT len;
- TRACE_(keyboard)( "virt %u, scan %u, state %p, str %p, size %d, flags %x, layout %p.\n",
virt, scan, state, str, size, flags, layout );
- if (!state) return 0;
- shift = state[VK_SHIFT] & 0x80;
- ctrl = state[VK_CONTROL] & 0x80;
- numlock = state[VK_NUMLOCK] & 0x01;
- if (layout != us_layout) buffer[0] = 0; /* FIXME: non-us layout */
I didn't want to pretend to have a default implementation for non-us layouts, but this is also then not equivalent to what the Android driver was doing.
I guess, we should ignore the layout here in the same way wineandroid.drv was doing, and let the driver override non-us layouts if they implement it.
Rémi Bernon rbernon@codeweavers.com writes:
- buffer[1] = 0;
- wcsncpy( str, buffer, size );
Please keep lstrcpynW, wcsncpy doesn't do the same thing and is pretty much always the wrong function to use.