-- v2: win32u: Send WM_INPUTLANGCHANGE when activating new layout. win32u: Move window query functions around. user32/tests: Add a WM_INPUTLANGCHANGE message test. user32/tests: Skip tests if layout failed to activate.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/user32/tests/input.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index c534880679a..7b155d80ede 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -3331,7 +3331,20 @@ static void test_keyboard_layout_name(void) for (i = len - 1; i >= 0; --i) { id = (DWORD_PTR)layouts[i]; + + winetest_push_context( "%08lx", id ); + ActivateKeyboardLayout(layouts[i], 0); + + tmplayout = GetKeyboardLayout(0); + todo_wine_if(tmplayout != layouts[i]) + ok( tmplayout == layouts[i], "Failed to activate keyboard layout\n"); + if (tmplayout != layouts[i]) + { + winetest_pop_context(); + continue; + } + GetKeyboardLayoutNameW(klid);
for (j = 0; j < len; ++j) @@ -3370,6 +3383,8 @@ static void test_keyboard_layout_name(void) /* The layout name only depends on the keyboard layout: the high word of HKL. */ GetKeyboardLayoutNameW(tmpklid); ok(!wcsicmp(klid, tmpklid), "GetKeyboardLayoutNameW returned %s, expected %s\n", debugstr_w(tmpklid), debugstr_w(klid)); + + winetest_pop_context(); }
ActivateKeyboardLayout(layout, 0);
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/user32/tests/input.c | 173 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+)
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c index 7b155d80ede..8f3d5750c2f 100644 --- a/dlls/user32/tests/input.c +++ b/dlls/user32/tests/input.c @@ -3393,6 +3393,178 @@ static void test_keyboard_layout_name(void) free(layouts_preload); }
+static HKL expect_hkl; +static HKL change_hkl; +static int got_setfocus; + +static LRESULT CALLBACK test_ActivateKeyboardLayout_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + ok( msg != WM_INPUTLANGCHANGEREQUEST, "got WM_INPUTLANGCHANGEREQUEST\n" ); + + if (msg == WM_SETFOCUS) got_setfocus = 1; + if (msg == WM_INPUTLANGCHANGE) + { + HKL layout = GetKeyboardLayout( 0 ); + CHARSETINFO info; + WCHAR klidW[64]; + UINT codepage; + LCID lcid; + + /* get keyboard layout lcid from its name, as the HKL might be aliased */ + GetKeyboardLayoutNameW( klidW ); + swscanf( klidW, L"%x", &lcid ); + lcid = LOWORD(lcid); + + if (!(HIWORD(layout) & 0x8000)) ok( lcid == HIWORD(layout), "got lcid %#lx\n", lcid ); + + GetLocaleInfoA( lcid, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, + (char *)&codepage, sizeof(codepage) ); + TranslateCharsetInfo( UlongToPtr( codepage ), &info, TCI_SRCCODEPAGE ); + + ok( !got_setfocus, "got WM_SETFOCUS before WM_INPUTLANGCHANGE\n" ); + ok( layout == expect_hkl, "got layout %p\n", layout ); + ok( wparam == info.ciCharset || broken(wparam == 0 && (HIWORD(layout) & 0x8000)), + "got wparam %#Ix\n", wparam ); + ok( lparam == (LPARAM)expect_hkl, "got lparam %#Ix\n", lparam ); + change_hkl = (HKL)lparam; + } + + return DefWindowProcW( hwnd, msg, wparam, lparam ); +} + +static DWORD CALLBACK test_ActivateKeyboardLayout_thread_proc( void *arg ) +{ + ActivateKeyboardLayout( arg, 0 ); + return 0; +} + +static void test_ActivateKeyboardLayout( char **argv ) +{ + HKL layout, tmp_layout, *layouts; + HWND hwnd1, hwnd2; + HANDLE thread; + UINT i, count; + DWORD ret; + + layout = GetKeyboardLayout( 0 ); + count = GetKeyboardLayoutList( 0, NULL ); + ok( count > 0, "GetKeyboardLayoutList returned %d\n", count ); + layouts = malloc( count * sizeof(HKL) ); + ok( layouts != NULL, "Could not allocate memory\n" ); + count = GetKeyboardLayoutList( count, layouts ); + ok( count > 0, "GetKeyboardLayoutList returned %d\n", count ); + + hwnd1 = CreateWindowA( "static", "static", WS_VISIBLE | WS_POPUP, + 100, 100, 100, 100, 0, NULL, NULL, NULL ); + ok( !!hwnd1, "CreateWindow failed, error %lu\n", GetLastError() ); + empty_message_queue(); + + SetWindowLongPtrA( hwnd1, GWLP_WNDPROC, (LONG_PTR)test_ActivateKeyboardLayout_window_proc ); + + for (i = 0; i < count; ++i) + { + BOOL broken_focus_activate = FALSE; + HKL other_layout = layouts[i]; + + winetest_push_context( "%08x / %08x", (UINT)(UINT_PTR)layout, (UINT)(UINT_PTR)other_layout ); + + /* test WM_INPUTLANGCHANGE message */ + + change_hkl = 0; + expect_hkl = other_layout; + got_setfocus = 0; + ActivateKeyboardLayout( other_layout, 0 ); + if (other_layout == layout) ok( change_hkl == 0, "got change_hkl %p\n", change_hkl ); + else todo_wine ok( change_hkl == other_layout, "got change_hkl %p\n", change_hkl ); + change_hkl = expect_hkl = 0; + + tmp_layout = GetKeyboardLayout( 0 ); + todo_wine_if(layout != other_layout) + ok( tmp_layout == other_layout, "got tmp_layout %p\n", tmp_layout ); + + /* changing the layout from another thread doesn't send the message */ + + thread = CreateThread( NULL, 0, test_ActivateKeyboardLayout_thread_proc, layout, 0, 0 ); + ret = WaitForSingleObject( thread, 1000 ); + ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); + CloseHandle( thread ); + + /* and has no immediate effect */ + + empty_message_queue(); + tmp_layout = GetKeyboardLayout( 0 ); + todo_wine_if(layout != other_layout) + ok( tmp_layout == other_layout, "got tmp_layout %p\n", tmp_layout ); + + /* but the change only takes effect after focus changes */ + + hwnd2 = CreateWindowA( "static", "static", WS_VISIBLE | WS_POPUP, + 100, 100, 100, 100, 0, NULL, NULL, NULL ); + ok( !!hwnd2, "CreateWindow failed, error %lu\n", GetLastError() ); + + tmp_layout = GetKeyboardLayout( 0 ); + todo_wine_if(layout != other_layout) + ok( tmp_layout == layout || broken(layout != other_layout && tmp_layout == other_layout) /* w7u */, + "got tmp_layout %p\n", tmp_layout ); + if (broken(layout != other_layout && tmp_layout == other_layout)) + { + win_skip( "Broken layout activation on focus change, skipping some tests\n" ); + broken_focus_activate = TRUE; + } + empty_message_queue(); + + /* only the focused window receives the WM_INPUTLANGCHANGE message */ + + ActivateKeyboardLayout( other_layout, 0 ); + ok( change_hkl == 0, "got change_hkl %p\n", change_hkl ); + + tmp_layout = GetKeyboardLayout( 0 ); + todo_wine_if(layout != other_layout) + ok( tmp_layout == other_layout, "got tmp_layout %p\n", tmp_layout ); + + thread = CreateThread( NULL, 0, test_ActivateKeyboardLayout_thread_proc, layout, 0, 0 ); + ret = WaitForSingleObject( thread, 1000 ); + ok( !ret, "WaitForSingleObject returned %#lx\n", ret ); + CloseHandle( thread ); + + tmp_layout = GetKeyboardLayout( 0 ); + todo_wine_if(layout != other_layout) + ok( tmp_layout == other_layout, "got tmp_layout %p\n", tmp_layout ); + + /* changing focus is enough for the layout change to take effect */ + + change_hkl = 0; + expect_hkl = layout; + got_setfocus = 0; + SetFocus( hwnd1 ); + + if (broken_focus_activate) + { + ok( got_setfocus == 1, "got got_setfocus %d\n", got_setfocus ); + ok( change_hkl == 0, "got change_hkl %p\n", change_hkl ); + got_setfocus = 0; + ActivateKeyboardLayout( layout, 0 ); + } + + if (other_layout == layout) ok( change_hkl == 0, "got change_hkl %p\n", change_hkl ); + else todo_wine ok( change_hkl == layout, "got change_hkl %p\n", change_hkl ); + change_hkl = expect_hkl = 0; + + tmp_layout = GetKeyboardLayout( 0 ); + todo_wine_if(layout != other_layout) + ok( tmp_layout == layout, "got tmp_layout %p\n", tmp_layout ); + + DestroyWindow( hwnd2 ); + empty_message_queue(); + + winetest_pop_context(); + } + + DestroyWindow( hwnd1 ); + + free( layouts ); +} + static void test_key_names(void) { char buffer[40]; @@ -4961,6 +5133,7 @@ START_TEST(input) test_ToAscii(); test_get_async_key_state(); test_keyboard_layout_name(); + test_ActivateKeyboardLayout( argv ); test_key_names(); test_attach_input(); test_GetKeyState();
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/input.c | 80 ++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 40 deletions(-)
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index d7e502a4513..8735dc9fa74 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -535,6 +535,46 @@ static WCHAR kbd_tables_vkey_to_wchar( const KBDTABLES *tables, UINT vkey, const
#undef NEXT_ENTRY
+/******************************************************************* + * NtUserGetForegroundWindow (win32u.@) + */ +HWND WINAPI NtUserGetForegroundWindow(void) +{ + HWND ret = 0; + + SERVER_START_REQ( get_thread_input ) + { + req->tid = 0; + if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->foreground ); + } + SERVER_END_REQ; + return ret; +} + +/* see GetActiveWindow */ +HWND get_active_window(void) +{ + GUITHREADINFO info; + info.cbSize = sizeof(info); + return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndActive : 0; +} + +/* see GetCapture */ +HWND get_capture(void) +{ + GUITHREADINFO info; + info.cbSize = sizeof(info); + return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndCapture : 0; +} + +/* see GetFocus */ +HWND get_focus(void) +{ + GUITHREADINFO info; + info.cbSize = sizeof(info); + return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndFocus : 0; +} + /********************************************************************** * NtUserAttachThreadInput (win32u.@) */ @@ -1712,46 +1752,6 @@ BOOL WINAPI release_capture(void) return ret; }
-/******************************************************************* - * NtUserGetForegroundWindow (win32u.@) - */ -HWND WINAPI NtUserGetForegroundWindow(void) -{ - HWND ret = 0; - - SERVER_START_REQ( get_thread_input ) - { - req->tid = 0; - if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->foreground ); - } - SERVER_END_REQ; - return ret; -} - -/* see GetActiveWindow */ -HWND get_active_window(void) -{ - GUITHREADINFO info; - info.cbSize = sizeof(info); - return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndActive : 0; -} - -/* see GetCapture */ -HWND get_capture(void) -{ - GUITHREADINFO info; - info.cbSize = sizeof(info); - return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndCapture : 0; -} - -/* see GetFocus */ -HWND get_focus(void) -{ - GUITHREADINFO info; - info.cbSize = sizeof(info); - return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndFocus : 0; -} - /***************************************************************** * set_focus_window *
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/win32u/input.c | 18 +++++++++++++++++- dlls/win32u/ntgdi_private.h | 1 - dlls/win32u/win32u_private.h | 1 + 3 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 8735dc9fa74..c11d542037d 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1193,6 +1193,7 @@ HKL WINAPI NtUserActivateKeyboardLayout( HKL layout, UINT flags ) { struct user_thread_info *info = get_user_thread_info(); HKL old_layout; + HWND focus;
TRACE_(keyboard)( "layout %p, flags %x\n", layout, flags );
@@ -1210,7 +1211,22 @@ HKL WINAPI NtUserActivateKeyboardLayout( HKL layout, UINT flags )
old_layout = info->kbd_layout; info->kbd_layout = layout; - if (old_layout != layout) info->kbd_layout_id = 0; + if (old_layout != layout) + { + const NLS_LOCALE_DATA *data; + CHARSETINFO cs = {0}; + + if (HIWORD(layout) & 0x8000) + FIXME( "Aliased keyboard layout not yet implemented\n" ); + else if (!(data = get_locale_data( HIWORD(layout) ))) + WARN( "Failed to find locale data for %04x\n", HIWORD(layout) ); + else + translate_charset_info( ULongToPtr(data->idefaultansicodepage), &cs, TCI_SRCCODEPAGE ); + + info->kbd_layout_id = 0; + if ((focus = get_focus()) && get_window_thread( focus, NULL ) == GetCurrentThreadId()) + send_message( focus, WM_INPUTLANGCHANGE, cs.ciCharset, (LPARAM)layout ); + }
if (!old_layout) return get_locale_kbd_layout(); return old_layout; diff --git a/dlls/win32u/ntgdi_private.h b/dlls/win32u/ntgdi_private.h index ce5e30ab07d..93be59c9d7e 100644 --- a/dlls/win32u/ntgdi_private.h +++ b/dlls/win32u/ntgdi_private.h @@ -368,7 +368,6 @@ extern BOOL opentype_enum_full_names( const struct tt_name_v0 *tt_name_v0,
extern BOOL opentype_get_properties( const void *data, size_t size, const struct ttc_sfnt_v1 *ttc_sfnt_v1, DWORD *version, FONTSIGNATURE *fs, DWORD *ntm_flags ) DECLSPEC_HIDDEN; -extern BOOL translate_charset_info( DWORD *src, CHARSETINFO *cs, DWORD flags ) DECLSPEC_HIDDEN;
/* gdiobj.c */ extern HGDIOBJ alloc_gdi_handle( struct gdi_obj_header *obj, DWORD type, diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index 44847829b75..32c6c2b905b 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -456,6 +456,7 @@ extern CPTABLEINFO ansi_cp DECLSPEC_HIDDEN;
CPTABLEINFO *get_cptable( WORD cp ) DECLSPEC_HIDDEN; const NLS_LOCALE_DATA *get_locale_data( LCID lcid ) DECLSPEC_HIDDEN; +extern BOOL translate_charset_info( DWORD *src, CHARSETINFO *cs, DWORD flags ) DECLSPEC_HIDDEN; DWORD win32u_mbtowc( CPTABLEINFO *info, WCHAR *dst, DWORD dstlen, const char *src, DWORD srclen ) DECLSPEC_HIDDEN; DWORD win32u_wctomb( CPTABLEINFO *info, char *dst, DWORD dstlen, const WCHAR *src,
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=130294
Your paranoid android.
=== w10pro64_ja (64 bit report) ===
user32: input.c:2921: Test failed: 6: unexpected WM_INPUT message input.c:2924: Test failed: 6: unexpected RIM_INPUT message
v2: Fix the WM_INPUTLANGCHANGE wparam which I completely forgot about.