[PATCH v2 0/5] MR11193: Imm32: Send WM_IME_ENDCOMPOSITION / reset composition string more consistently.
-- v2: imm32: Reset composition string when deactivating context. imm32: Clear in-composition state when empty composition string is set. imm32: Strip off tralining zero in ImeSetCompositionString(). imm32/tests: Add tests for resetting in-composition state. imm32/tests: Flush events in test_ImmSetCompositionWindow(). https://gitlab.winehq.org/wine/wine/-/merge_requests/11193
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/imm32/tests/imm32.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index c4d1404f3ea..491f826a26e 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -6491,6 +6491,7 @@ static void test_ImmSetCompositionWindow(void) hwnd = CreateWindowW( test_class.lpszClassName, NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 100, 100, NULL, NULL, NULL, NULL ); ok( !!hwnd, "CreateWindowW failed, error %lu\n", GetLastError() ); + flush_events(); ok_ret( 1, ImmActivateLayout( hkl ) ); ok_ret( 1, ImmLoadIME( hkl ) ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11193
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/imm32/tests/imm32.c | 90 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 491f826a26e..82d33642713 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -1083,28 +1083,108 @@ static void test_SCS_SETSTR(void) LONG alen,wlen; BOOL ret; DWORD prop; + imm_msgs *msg; + BOOL ends_comp_in_set; imc = ImmGetContext(hwnd); + flush_events(); + msg_spy_flush_msgs(); ret = ImmSetCompositionStringW(imc, SCS_SETSTR, string, sizeof(string), NULL, 0); if (!ret) { win_skip("Composition isn't supported\n"); ImmReleaseContext(hwnd, imc); return; } + msg = msg_spy_find_msg(WM_IME_STARTCOMPOSITION); + ok(!!msg, "did not find WM_IME_STARTCOMPOSITION.\n"); + msg = msg_spy_find_msg(WM_IME_COMPOSITION); + ok(!!msg, "did not find WM_IME_COMPOSITION.\n"); + /* Before Win10 1909 WM_IME_ENDCOMPOSITION arrives after ImmSetCompositionStringW with non-empty string. On the + * newer Windows version WM_IME_ENDCOMPOSITION is only sent when the composition string is cleared or context + * is deactivated. */ + ends_comp_in_set = !!msg_spy_find_msg(WM_IME_ENDCOMPOSITION); + msg_spy_flush_msgs(); ret = ImmSetCompositionStringW(imc, SCS_SETSTR, NULL, 128, NULL, 128); ok(ret, "got error %lu.\n", GetLastError()); + msg = msg_spy_find_msg(WM_IME_ENDCOMPOSITION); + todo_wine ok(!!msg || broken(ends_comp_in_set && !msg), "did not find WM_IME_ENDCOMPOSITION.\n"); alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20); ok(!alen, "got %ld.\n", alen); wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20); ok(!wlen, "got %ld.\n", alen); + msg_spy_flush_msgs(); + ret = ImmSetCompositionStringW(imc, SCS_SETSTR, string, sizeof(string), NULL, 0); + ok(ret, "got error %lu.\n", GetLastError()); + msg = msg_spy_find_msg(WM_IME_STARTCOMPOSITION); + todo_wine ok(!!msg, "did not find WM_IME_STARTCOMPOSITION.\n"); + msg = msg_spy_find_msg(WM_IME_COMPOSITION); + ok(!!msg, "did not find WM_IME_COMPOSITION.\n"); + + msg_spy_flush_msgs(); + ret = ImmSetCompositionStringW(imc, SCS_SETSTR, L"12", 6, NULL, 0); + ok(ret, "got error %lu.\n", GetLastError()); + msg = msg_spy_find_msg(WM_IME_ENDCOMPOSITION); + ok(!msg || broken(ends_comp_in_set && msg), "found WM_IME_ENDCOMPOSITION.\n"); + + alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20); + todo_wine ok(alen == 2 || broken(ends_comp_in_set && !alen), "got %ld.\n", alen); + wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20); + todo_wine ok(wlen == 4 || broken(ends_comp_in_set && !wlen), "got %ld.\n", wlen); + + msg_spy_flush_msgs(); + ret = ImmSetCompositionStringW(imc, SCS_SETSTR, L"", 2, NULL, 0); + ok(ret, "got error %lu.\n", GetLastError()); + msg = msg_spy_find_msg(WM_IME_ENDCOMPOSITION); + todo_wine ok(!!msg || broken(ends_comp_in_set && !msg), "did not find WM_IME_ENDCOMPOSITION.\n"); + + alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20); + todo_wine ok(!alen, "got %ld.\n", alen); + wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20); + todo_wine ok(!wlen, "got %ld.\n", alen); + + msg_spy_flush_msgs(); ret = ImmSetCompositionStringW(imc, SCS_SETSTR, string, sizeof(string), NULL, 2); ok(ret, "got error %lu.\n", GetLastError()); + msg = msg_spy_find_msg(WM_IME_STARTCOMPOSITION); + todo_wine ok(!!msg, "did not find WM_IME_STARTCOMPOSITION.\n"); + msg = msg_spy_find_msg(WM_IME_COMPOSITION); + ok(!!msg, "did not find WM_IME_COMPOSITION.\n"); + msg = msg_spy_find_msg(WM_IME_ENDCOMPOSITION); + ok(!msg || broken(ends_comp_in_set && msg), "found WM_IME_ENDCOMPOSITION.\n"); + + msg_spy_flush_msgs(); + ImmSetActiveContext( hwnd, imc, FALSE ); + msg = msg_spy_find_msg(WM_IME_ENDCOMPOSITION); + todo_wine ok(!!msg || broken(ends_comp_in_set && !msg), "did not find WM_IME_ENDCOMPOSITION.\n"); + alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20); + todo_wine ok(!alen, "got %ld.\n", alen); + wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20); + todo_wine ok(!wlen, "got %ld.\n", alen); + + msg_spy_flush_msgs(); + ImmSetActiveContext( hwnd, imc, TRUE ); + msg = msg_spy_find_msg(WM_IME_STARTCOMPOSITION); + ok(!msg, "found WM_IME_STARTCOMPOSITION.\n"); + msg = msg_spy_find_msg(WM_IME_COMPOSITION); + ok(!msg, "did not find WM_IME_COMPOSITION.\n"); msg_spy_flush_msgs(); + ret = ImmSetCompositionStringW(imc, SCS_SETSTR, string, sizeof(string), NULL, 2); + ok(ret, "got error %lu.\n", GetLastError()); + msg = msg_spy_find_msg(WM_IME_STARTCOMPOSITION); + todo_wine ok(!!msg, "did not find WM_IME_STARTCOMPOSITION.\n"); + msg = msg_spy_find_msg(WM_IME_COMPOSITION); + ok(!!msg, "did not find WM_IME_COMPOSITION.\n"); + msg = msg_spy_find_msg(WM_IME_ENDCOMPOSITION); + ok(!msg || broken(ends_comp_in_set && msg), "found WM_IME_ENDCOMPOSITION.\n"); + + process_messages(); + msg_spy_flush_msgs(); + alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20); wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20); /* windows machines without any IME installed just return 0 above */ @@ -1170,9 +1250,19 @@ static void test_SCS_SETSTR(void) imc = ImmGetContext(hwnd); msg_spy_flush_msgs(); + msg = msg_spy_find_msg(WM_IME_COMPOSITION); + ok(!msg, "found WM_IME_COMPOSITION.\n"); + msg = msg_spy_find_msg(WM_IME_STARTCOMPOSITION); + ok(!msg, "found WM_IME_STARTCOMPOSITION.\n"); ret = ImmSetCompositionStringW(imc, SCS_SETSTR, string, sizeof(string), NULL,0); ok(ret, "ImmSetCompositionStringW failed\n"); + + msg = msg_spy_find_msg(WM_IME_STARTCOMPOSITION); + ok(!msg, "found WM_IME_STARTCOMPOSITION.\n"); + msg = msg_spy_find_msg(WM_IME_COMPOSITION); + ok(!!msg, "did not find WM_IME_COMPOSITION.\n"); + wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, sizeof(wstring)); if (wlen > 0) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11193
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/imm32/ime.c | 5 ++++- dlls/imm32/tests/imm32.c | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index 3cf7c1ed00a..253ce2c3610 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -683,7 +683,10 @@ BOOL WINAPI ImeSetCompositionString( HIMC himc, DWORD index, const void *comp, D { UINT msg, flags = GCS_COMPSTR | GCS_COMPCLAUSE | GCS_COMPATTR | GCS_DELTASTART | GCS_CURSORPOS; WCHAR wparam = comp && comp_len >= sizeof(WCHAR) ? *(WCHAR *)comp : 0; - input_context_set_comp_str( ctx, comp, comp_len / sizeof(WCHAR) ); + DWORD len = comp_len / sizeof(WCHAR); + + if (len && comp && !((WCHAR *)comp)[len - 1]) --len; + input_context_set_comp_str( ctx, comp, len ); if ((msg = ime_set_composition_status( himc, TRUE ))) ime_send_message( himc, msg, 0, 0 ); ime_send_message( himc, WM_IME_COMPOSITION, wparam, flags ); } diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 82d33642713..f5b87d328c4 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -1130,9 +1130,9 @@ static void test_SCS_SETSTR(void) ok(!msg || broken(ends_comp_in_set && msg), "found WM_IME_ENDCOMPOSITION.\n"); alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20); - todo_wine ok(alen == 2 || broken(ends_comp_in_set && !alen), "got %ld.\n", alen); + ok(alen == 2 || broken(ends_comp_in_set && !alen), "got %ld.\n", alen); wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20); - todo_wine ok(wlen == 4 || broken(ends_comp_in_set && !wlen), "got %ld.\n", wlen); + ok(wlen == 4 || broken(ends_comp_in_set && !wlen), "got %ld.\n", wlen); msg_spy_flush_msgs(); ret = ImmSetCompositionStringW(imc, SCS_SETSTR, L"", 2, NULL, 0); @@ -1141,9 +1141,9 @@ static void test_SCS_SETSTR(void) todo_wine ok(!!msg || broken(ends_comp_in_set && !msg), "did not find WM_IME_ENDCOMPOSITION.\n"); alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20); - todo_wine ok(!alen, "got %ld.\n", alen); + ok(!alen, "got %ld.\n", alen); wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20); - todo_wine ok(!wlen, "got %ld.\n", alen); + ok(!wlen, "got %ld.\n", alen); msg_spy_flush_msgs(); ret = ImmSetCompositionStringW(imc, SCS_SETSTR, string, sizeof(string), NULL, 2); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11193
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/imm32/ime.c | 2 +- dlls/imm32/tests/imm32.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index 253ce2c3610..96be851f5a7 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -687,7 +687,7 @@ BOOL WINAPI ImeSetCompositionString( HIMC himc, DWORD index, const void *comp, D if (len && comp && !((WCHAR *)comp)[len - 1]) --len; input_context_set_comp_str( ctx, comp, len ); - if ((msg = ime_set_composition_status( himc, TRUE ))) ime_send_message( himc, msg, 0, 0 ); + if ((msg = ime_set_composition_status( himc, !!len ))) ime_send_message( himc, msg, 0, 0 ); ime_send_message( himc, WM_IME_COMPOSITION, wparam, flags ); } diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index f5b87d328c4..2a9e945e2e3 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -1108,7 +1108,7 @@ static void test_SCS_SETSTR(void) ret = ImmSetCompositionStringW(imc, SCS_SETSTR, NULL, 128, NULL, 128); ok(ret, "got error %lu.\n", GetLastError()); msg = msg_spy_find_msg(WM_IME_ENDCOMPOSITION); - todo_wine ok(!!msg || broken(ends_comp_in_set && !msg), "did not find WM_IME_ENDCOMPOSITION.\n"); + ok(!!msg || broken(ends_comp_in_set && !msg), "did not find WM_IME_ENDCOMPOSITION.\n"); alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20); ok(!alen, "got %ld.\n", alen); @@ -1119,7 +1119,7 @@ static void test_SCS_SETSTR(void) ret = ImmSetCompositionStringW(imc, SCS_SETSTR, string, sizeof(string), NULL, 0); ok(ret, "got error %lu.\n", GetLastError()); msg = msg_spy_find_msg(WM_IME_STARTCOMPOSITION); - todo_wine ok(!!msg, "did not find WM_IME_STARTCOMPOSITION.\n"); + ok(!!msg, "did not find WM_IME_STARTCOMPOSITION.\n"); msg = msg_spy_find_msg(WM_IME_COMPOSITION); ok(!!msg, "did not find WM_IME_COMPOSITION.\n"); @@ -1138,7 +1138,7 @@ static void test_SCS_SETSTR(void) ret = ImmSetCompositionStringW(imc, SCS_SETSTR, L"", 2, NULL, 0); ok(ret, "got error %lu.\n", GetLastError()); msg = msg_spy_find_msg(WM_IME_ENDCOMPOSITION); - todo_wine ok(!!msg || broken(ends_comp_in_set && !msg), "did not find WM_IME_ENDCOMPOSITION.\n"); + ok(!!msg || broken(ends_comp_in_set && !msg), "did not find WM_IME_ENDCOMPOSITION.\n"); alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20); ok(!alen, "got %ld.\n", alen); @@ -1149,7 +1149,7 @@ static void test_SCS_SETSTR(void) ret = ImmSetCompositionStringW(imc, SCS_SETSTR, string, sizeof(string), NULL, 2); ok(ret, "got error %lu.\n", GetLastError()); msg = msg_spy_find_msg(WM_IME_STARTCOMPOSITION); - todo_wine ok(!!msg, "did not find WM_IME_STARTCOMPOSITION.\n"); + ok(!!msg, "did not find WM_IME_STARTCOMPOSITION.\n"); msg = msg_spy_find_msg(WM_IME_COMPOSITION); ok(!!msg, "did not find WM_IME_COMPOSITION.\n"); msg = msg_spy_find_msg(WM_IME_ENDCOMPOSITION); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11193
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/imm32/ime.c | 12 ++++++++++++ dlls/imm32/tests/imm32.c | 8 ++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index 96be851f5a7..67ee18067d2 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -534,7 +534,19 @@ BOOL WINAPI ImeSelect( HIMC himc, BOOL select ) BOOL WINAPI ImeSetActiveContext( HIMC himc, BOOL flag ) { + INPUTCONTEXT *ctx; + UINT msg; + TRACE( "himc %p, flag %#x stub!\n", himc, flag ); + if (!flag && (msg = ime_set_composition_status( himc, FALSE ))) + { + if ((ctx = ImmLockIMC( himc ))) + { + input_context_set_comp_str( ctx, NULL, 0 ); + ImmUnlockIMC( himc ); + } + ime_send_message( himc, msg, 0, 0 ); + } return TRUE; } diff --git a/dlls/imm32/tests/imm32.c b/dlls/imm32/tests/imm32.c index 2a9e945e2e3..d82fe22783a 100644 --- a/dlls/imm32/tests/imm32.c +++ b/dlls/imm32/tests/imm32.c @@ -1158,11 +1158,11 @@ static void test_SCS_SETSTR(void) msg_spy_flush_msgs(); ImmSetActiveContext( hwnd, imc, FALSE ); msg = msg_spy_find_msg(WM_IME_ENDCOMPOSITION); - todo_wine ok(!!msg || broken(ends_comp_in_set && !msg), "did not find WM_IME_ENDCOMPOSITION.\n"); + ok(!!msg || broken(ends_comp_in_set && !msg), "did not find WM_IME_ENDCOMPOSITION.\n"); alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20); - todo_wine ok(!alen, "got %ld.\n", alen); + ok(!alen, "got %ld.\n", alen); wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20); - todo_wine ok(!wlen, "got %ld.\n", alen); + ok(!wlen, "got %ld.\n", alen); msg_spy_flush_msgs(); ImmSetActiveContext( hwnd, imc, TRUE ); @@ -1176,7 +1176,7 @@ static void test_SCS_SETSTR(void) ret = ImmSetCompositionStringW(imc, SCS_SETSTR, string, sizeof(string), NULL, 2); ok(ret, "got error %lu.\n", GetLastError()); msg = msg_spy_find_msg(WM_IME_STARTCOMPOSITION); - todo_wine ok(!!msg, "did not find WM_IME_STARTCOMPOSITION.\n"); + ok(!!msg, "did not find WM_IME_STARTCOMPOSITION.\n"); msg = msg_spy_find_msg(WM_IME_COMPOSITION); ok(!!msg, "did not find WM_IME_COMPOSITION.\n"); msg = msg_spy_find_msg(WM_IME_ENDCOMPOSITION); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11193
v2: - flush events right after CreateWindowW() in test_ImmSetCompositionWindow(). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11193#note_143644
Rémi Bernon (@rbernon) commented about dlls/imm32/ime.c:
{ UINT msg, flags = GCS_COMPSTR | GCS_COMPCLAUSE | GCS_COMPATTR | GCS_DELTASTART | GCS_CURSORPOS; WCHAR wparam = comp && comp_len >= sizeof(WCHAR) ? *(WCHAR *)comp : 0; - input_context_set_comp_str( ctx, comp, comp_len / sizeof(WCHAR) ); + DWORD len = comp_len / sizeof(WCHAR); + + if (len && comp && !((WCHAR *)comp)[len - 1]) --len;
```suggestion:-0+0 while (len && comp && !((WCHAR *)comp)[len - 1]) --len; ``` It seems that it strips any number of trailing zeros. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11193#note_143802
Rémi Bernon (@rbernon) commented about dlls/imm32/tests/imm32.c:
+ + alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20); + todo_wine ok(alen == 2 || broken(ends_comp_in_set && !alen), "got %ld.\n", alen); + wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20); + todo_wine ok(wlen == 4 || broken(ends_comp_in_set && !wlen), "got %ld.\n", wlen); + + msg_spy_flush_msgs(); + ret = ImmSetCompositionStringW(imc, SCS_SETSTR, L"", 2, NULL, 0); + ok(ret, "got error %lu.\n", GetLastError()); + msg = msg_spy_find_msg(WM_IME_ENDCOMPOSITION); + todo_wine ok(!!msg || broken(ends_comp_in_set && !msg), "did not find WM_IME_ENDCOMPOSITION.\n"); + + alen = ImmGetCompositionStringA(imc, GCS_COMPSTR, cstring, 20); + todo_wine ok(!alen, "got %ld.\n", alen); + wlen = ImmGetCompositionStringW(imc, GCS_COMPSTR, wstring, 20); + todo_wine ok(!wlen, "got %ld.\n", alen); This is failing with the Korean MS IME (https://testbot.winehq.org/JobDetails.pl?Key=163520), probably because on Windows several languages use a different IME module (ko_KR, ja_JP, zh_CN among others) and their implementation differs a lot.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11193#note_143805
On Mon Jun 22 08:11:56 2026 +0000, Rémi Bernon wrote:
This is failing with the Korean MS IME (https://testbot.winehq.org/JobDetails.pl?Key=163520), probably because on Windows several languages use a different IME module (ko_KR, ja_JP, zh_CN among others) and their implementation differs a lot. Yes, thanks, reproduced that on up to date Win11. I was testing with ENG + Chinese (default and old compatibility IME version) but not Korean. I will add broken() based in ImmSetCompositionStringW() failure.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11193#note_143853
participants (3)
-
Paul Gofman -
Paul Gofman (@gofman) -
Rémi Bernon (@rbernon)