This patch fixes a Japanese input issue in MS Office Visual Basic and Mery text editor ime inline mode. The following was considered:
1. Japanese ime: the result string message is always placed between WM_IME_STARTCOMPOSITION and WM_IME_ENDCOMPOSITION. Currently in Wine, the result string message follows WM_IME_ENDCOMPOSITION.
dlls/imm32/tests/imm32.c::test_nihongo_no().
2. Chinese ime: same as Japanese ime.
3. Korean ime: mostly the same, but there are some cases where it is not (e.g. CPS_COMPLETE, 'r-k-RETURN'). However, I haven't found any problem even if it behave like Japanese ime message order.
3. zero-length preedit string: as in MR !3115 commit d1f9aae, the message followed by WM_IME_ENDCOMPOSITION. Currently in Wine, it is associated with WM_IME_STARTCOMPOSITION.
4. zero-length result string: ignore.
-- v2: imm32: Fix the WM_IME_COMPOSITION messages to be between the WM_IME_{START|END}COMPOSITION message.
From: Byeongsik Jeon bsjeon@hanmail.net
This patch fixes a Japanese input issue in MS Office Visual Basic and Mery text editor ime inline mode. The following was considered:
1. Japanese ime: the result string message is always placed between WM_IME_STARTCOMPOSITION and WM_IME_ENDCOMPOSITION. Currently in Wine, the result string message follows WM_IME_ENDCOMPOSITION.
dlls/imm32/tests/imm32.c::test_nihongo_no().
2. Chinese ime: same as Japanese ime.
3. Korean ime: mostly the same, but there are some cases where it is not (e.g. CPS_COMPLETE, 'r-k-RETURN'). However, I haven't found any problem even if it behave like Japanese ime message order.
3. zero-length preedit string: as in MR !3115 commit d1f9aae, the message followed by WM_IME_ENDCOMPOSITION. Currently in Wine, it is associated with WM_IME_STARTCOMPOSITION.
4. zero-length result string: ignore. --- dlls/imm32/ime.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-)
diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index d7ffc3de115..f71833ab02d 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -561,26 +561,45 @@ UINT WINAPI ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, if (status) WARN( "WINE_IME_TO_ASCII_EX returned status %#lx\n", status ); else { - TRANSMSG status_msg = {.message = ime_set_composition_status( himc, !!compstr->dwCompStrOffset )}; - if (status_msg.message) msgs->TransMsg[count++] = status_msg; + if (compstr->dwCompStrLen || compstr->dwResultStrLen) + { + TRANSMSG msg = {.message = ime_set_composition_status( himc, TRUE )}; + if (msg.message == WM_IME_STARTCOMPOSITION) msgs->TransMsg[count++] = msg; + }
- if (compstr->dwResultStrOffset) + if (compstr->dwResultStrLen) { const WCHAR *result = (WCHAR *)((BYTE *)compstr + compstr->dwResultStrOffset); TRANSMSG msg = {.message = WM_IME_COMPOSITION, .wParam = result[0], .lParam = GCS_RESULTSTR}; - if (compstr->dwResultClauseOffset) msg.lParam |= GCS_RESULTCLAUSE; + if (compstr->dwResultClauseLen) msg.lParam |= GCS_RESULTCLAUSE; msgs->TransMsg[count++] = msg; }
- if (compstr->dwCompStrOffset) + if (compstr->dwCompStrLen) { const WCHAR *comp = (WCHAR *)((BYTE *)compstr + compstr->dwCompStrOffset); TRANSMSG msg = {.message = WM_IME_COMPOSITION, .wParam = comp[0], .lParam = GCS_COMPSTR | GCS_CURSORPOS | GCS_DELTASTART}; - if (compstr->dwCompAttrOffset) msg.lParam |= GCS_COMPATTR; - if (compstr->dwCompClauseOffset) msg.lParam |= GCS_COMPCLAUSE; + if (compstr->dwCompAttrLen) msg.lParam |= GCS_COMPATTR; + if (compstr->dwCompClauseLen) msg.lParam |= GCS_COMPCLAUSE; else msg.lParam |= CS_INSERTCHAR|CS_NOMOVECARET; msgs->TransMsg[count++] = msg; } + else + { + static const TRANSMSG empty_msg = + { + .message = WM_IME_COMPOSITION, + .wParam = 0x1b, + .lParam = GCS_CURSORPOS | GCS_DELTASTART | GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | + GCS_COMPREADSTR | GCS_COMPREADATTR | GCS_COMPREADCLAUSE + }; + TRANSMSG status_msg = {.message = ime_set_composition_status( himc, FALSE )}; + if (status_msg.message == WM_IME_ENDCOMPOSITION) + { + if (!compstr->dwResultStrLen) msgs->TransMsg[count++] = empty_msg; + msgs->TransMsg[count++] = status_msg; + } + }
if (!key_consumed) {
On Wed Apr 16 09:22:24 2025 +0000, Byeongsik Jeon wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/7827/diffs?diff_id=171207&start_sha=7425e7ca057045c278c3ee4683e8b5eded901135#a6e118022a51140e914e4401e0a11350ed41a39b_578_578)
I've updated the code now.
It works fine, but to be sure, I'd have to look at all values in INPUTCONTEXT::COMPOSITIONSTRING for the empty WM_IME_COMPOSITION message on MS Windows.
I don't have access to MS Windows right now, so this will take some time.
Hmm sorry I think I confused you, that special case for empty comp str feels awkward.
I prefer v1, but what about with this fixup: https://gitlab.winehq.org/rbernon/wine/-/commits/tmp-7827, so that we send WM_IME_STARTCOMPOSITION message if we're about to send any WM_IME_COMPOSITION message next -even with an empty compstr- and then we send WM_IME_ENDCOMPOSITION if composition string is empty.
On Wed Apr 16 12:07:20 2025 +0000, Rémi Bernon wrote:
Hmm sorry I think I confused you, that special case for empty comp str feels awkward. I prefer v1, but what about with this fixup: https://gitlab.winehq.org/rbernon/wine/-/commits/tmp-7827, so that we send WM_IME_STARTCOMPOSITION message if we're about to send any WM_IME_COMPOSITION message next -even with an empty compstr- and then we send WM_IME_ENDCOMPOSITION if composition string is empty.
Okay, it's fine on winex11.
I'm having issues on winewayland, I'll review and update.
special case for empty comp str feels awkward.
In situations like ‘n-BACKSPACE’, the native behavior is to generate empty comp WM_IME_COMPOSITION and WM_IME_ENDCOMPOSITION. WM_IME_ENDCOMPOSITION alone does not clear the composition string in some applications. Mery.exe does.
winex11 sends an empty comp string in this situation. All currently accessible implementations of the xim server do so. Off topic, preedit_start and preedit_done don't need to call post_ime_update. winemac and winewayland don't even have a counterpart for these callbacks.
winemac also generally returns an empty comp string. Only for “Korean 2-set Keyboard”, comp_str is returned as result_str and generates WM_IME_KEYDOWN::VK_BACK. Anyway, that's fine...
wayland returns a null comp string, not an empty comp string. the v1 approach only generates the WM_IME_ENDCOMPOSITION message. In my opinion, in the v1 approach, the null comp string should be replaced by an empty comp string. Probably in text_input_done.
In the v2 approach, it works in winewayland without any modification on the winewayland side. Even if lparam==0x1bf, it should be fine as long as the COMPOSITIONSTRING contains the correct value.
Honestly, I prefer the v2 approach, but either way is fine. Any advice would be appreciated.
Thank you for the detailed testing! I still prefer the v1 with the fixup, please add a tweak to winewayland to post an empty completion string in text_input_done.
Off topic, preedit_start and preedit_done don't need to call post_ime_update. winemac and winewayland don't even have a counterpart for these callbacks.
Yeah, I think I saw that before as well. They were possibly needed before some other IME updates refactor. Feel free to drop them either in a separate patch here or in a separate MR.