[PATCH v6 0/3] MR9992: Introduce ImeToAsciiEx() user driver function for use in winemac
-- v6: winemac: Implement and use macdrv_ImeToAsciiEx(). imm32: Generate WM_KEYDOWN in ImeToAsciiEx for STATUS_NOT_IMPLEMENTED. win32u: Introduce a new ImeToAsciiEx call through NtUserMessageCall. https://gitlab.winehq.org/wine/wine/-/merge_requests/9992
From: Marc-Aurel Zent <mzent@codeweavers.com> --- dlls/imm32/ime.c | 1 + dlls/win32u/driver.c | 12 ++++++++++++ dlls/win32u/imm.c | 2 ++ include/ntuser.h | 1 + include/wine/gdi_driver.h | 1 + 5 files changed, 17 insertions(+) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index 65f46f20550..ed3f262c47a 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -551,6 +551,7 @@ UINT WINAPI ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, params.compstr = compstr; params.key_consumed = &key_consumed; + params.msgs = msgs; status = NtUserMessageCall( ctx->hWnd, WINE_IME_TO_ASCII_EX, vkey, vsc, ¶ms, NtUserImeDriverCall, FALSE ); size = compstr->dwSize; diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index 60aba702d1a..9664c355fc2 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -676,6 +676,11 @@ static UINT nulldrv_ImeProcessKey( HIMC himc, UINT wparam, UINT lparam, const BY return 0; } +static UINT nulldrv_ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, UINT flags, HIMC himc ) +{ + return 0; +} + static void nulldrv_NotifyIMEStatus( HWND hwnd, UINT status ) { } @@ -1103,6 +1108,11 @@ static UINT loaderdrv_ImeProcessKey( HIMC himc, UINT wparam, UINT lparam, const return load_driver()->pImeProcessKey( himc, wparam, lparam, state ); } +static UINT loaderdrv_ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, UINT flags, HIMC himc ) +{ + return load_driver()->pImeToAsciiEx( vkey, vsc, state, msgs, flags, himc ); +} + static void loaderdrv_NotifyIMEStatus( HWND hwnd, UINT status ) { return load_driver()->pNotifyIMEStatus( hwnd, status ); @@ -1256,6 +1266,7 @@ static const struct user_driver_funcs lazy_load_driver = loaderdrv_KbdLayerDescriptor, loaderdrv_ReleaseKbdTables, loaderdrv_ImeProcessKey, + loaderdrv_ImeToAsciiEx, loaderdrv_NotifyIMEStatus, loaderdrv_SetIMECompositionRect, /* cursor/icon functions */ @@ -1360,6 +1371,7 @@ void __wine_set_user_driver( const struct user_driver_funcs *funcs, UINT version SET_USER_FUNC(KbdLayerDescriptor); SET_USER_FUNC(ReleaseKbdTables); SET_USER_FUNC(ImeProcessKey); + SET_USER_FUNC(ImeToAsciiEx); SET_USER_FUNC(NotifyIMEStatus); SET_USER_FUNC(SetIMECompositionRect); SET_USER_FUNC(DestroyCursorIcon); diff --git a/dlls/win32u/imm.c b/dlls/win32u/imm.c index 3337f66e291..2d392a08515 100644 --- a/dlls/win32u/imm.c +++ b/dlls/win32u/imm.c @@ -687,6 +687,8 @@ LRESULT ime_driver_call( HWND hwnd, enum wine_ime_call call, WPARAM wparam, LPAR return res; } case WINE_IME_TO_ASCII_EX: + res = user_driver->pImeToAsciiEx( wparam, lparam, (BYTE *)params->state, params->msgs, 0, params->himc ); + if (res) return res; return ime_to_tascii_ex( wparam, lparam, params->state, params->compstr, params->key_consumed, params->himc ); case WINE_IME_POST_UPDATE: post_ime_update( hwnd, wparam, (WCHAR *)lparam, (WCHAR *)params ); diff --git a/include/ntuser.h b/include/ntuser.h index ce1b18fdfd9..54a4949e407 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -682,6 +682,7 @@ struct ime_driver_call_params const BYTE *state; COMPOSITIONSTRING *compstr; BOOL *key_consumed; + TRANSMSGLIST *msgs; }; /* NtUserSystemTrayCall calls */ diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index f6390bce878..7e2a6005deb 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -373,6 +373,7 @@ struct user_driver_funcs void (*pReleaseKbdTables)(const KBDTABLES *); /* IME functions */ UINT (*pImeProcessKey)(HIMC,UINT,UINT,const BYTE*); + UINT (*pImeToAsciiEx)(UINT,UINT,BYTE*,TRANSMSGLIST*,UINT,HIMC); void (*pNotifyIMEStatus)(HWND,UINT); BOOL (*pSetIMECompositionRect)(HWND,RECT); /* cursor/icon functions */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9992
From: Marc-Aurel Zent <mzent@codeweavers.com> --- dlls/imm32/ime.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index ed3f262c47a..f3c18d38537 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -557,9 +557,9 @@ UINT WINAPI ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, size = compstr->dwSize; } while (status == STATUS_BUFFER_TOO_SMALL); - if (status) WARN( "WINE_IME_TO_ASCII_EX returned status %#lx\n", status ); - else + switch (status) { + case STATUS_SUCCESS: if (compstr->dwCompStrOffset || compstr->dwResultStrLen) { TRANSMSG msg = {.message = ime_set_composition_status( himc, TRUE )}; @@ -595,6 +595,15 @@ UINT WINAPI ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, TRANSMSG msg = {.message = WM_IME_KEYDOWN, .wParam = vkey, .lParam = vsc}; msgs->TransMsg[count++] = msg; } + break; + case STATUS_NOT_IMPLEMENTED: + { + TRANSMSG msg = {.message = WM_KEYDOWN, .wParam = vkey, .lParam = vsc}; + msgs->TransMsg[count++] = msg; + break; + } + default: + WARN( "WINE_IME_TO_ASCII_EX returned status %#lx\n", status ); } ImmUnlockIMCC( ctx->hCompStr ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9992
From: Marc-Aurel Zent <mzent@codeweavers.com> Also renames back macdrv_ime_process_key to macdrv_send_keydown_to_input_source, which is a more accurate name for the function. --- dlls/winemac.drv/cocoa_window.m | 4 +-- dlls/winemac.drv/gdi.c | 1 + dlls/winemac.drv/keyboard.c | 46 ++++++++++++++++++++++----------- dlls/winemac.drv/macdrv.h | 1 + dlls/winemac.drv/macdrv_cocoa.h | 2 +- 5 files changed, 36 insertions(+), 18 deletions(-) diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m index 7d6d704436c..4c1ac93524f 100644 --- a/dlls/winemac.drv/cocoa_window.m +++ b/dlls/winemac.drv/cocoa_window.m @@ -4034,13 +4034,13 @@ uint32_t macdrv_window_background_color(void) } /*********************************************************************** - * macdrv_ime_process_key + * macdrv_send_keydown_to_input_source * * Sends a key down event to the active window's inputContext so that it can be * processed by input sources (AKA IMEs). This is only called when there is an * active non-keyboard input source. */ -bool macdrv_ime_process_key(int keyc, unsigned int flags, int repeat, void *himc) +bool macdrv_send_keydown_to_input_source(int keyc, unsigned int flags, int repeat, void *himc) { __block bool ret; diff --git a/dlls/winemac.drv/gdi.c b/dlls/winemac.drv/gdi.c index 2ef941aedb4..1bf703459f5 100644 --- a/dlls/winemac.drv/gdi.c +++ b/dlls/winemac.drv/gdi.c @@ -278,6 +278,7 @@ static const struct user_driver_funcs macdrv_funcs = .pUpdateLayeredWindow = macdrv_UpdateLayeredWindow, .pVkKeyScanEx = macdrv_VkKeyScanEx, .pImeProcessKey = macdrv_ImeProcessKey, + .pImeToAsciiEx = macdrv_ImeToAsciiEx, .pNotifyIMEStatus = macdrv_NotifyIMEStatus, .pSetIMECompositionRect = macdrv_SetIMECompositionRect, .pWindowMessage = macdrv_WindowMessage, diff --git a/dlls/winemac.drv/keyboard.c b/dlls/winemac.drv/keyboard.c index 7c49be7849f..c41be6e873f 100644 --- a/dlls/winemac.drv/keyboard.c +++ b/dlls/winemac.drv/keyboard.c @@ -1085,15 +1085,11 @@ void macdrv_hotkey_press(const macdrv_event *event) */ UINT macdrv_ImeProcessKey(HIMC himc, UINT wparam, UINT lparam, const BYTE *key_state) { - struct macdrv_thread_data *thread_data = macdrv_thread_data(); - WORD scan = HIWORD(lparam) & 0x1ff, vkey = LOWORD(wparam); - BOOL repeat = !!(lparam >> 30), pressed = !(lparam >> 31); - unsigned int flags; - int keyc; - UINT ret; + WORD vkey = LOWORD(wparam); + BOOL pressed = !(lparam >> 31); - TRACE("himc %p, scan %#x, vkey %#x, repeat %u, pressed %u\n", - himc, scan, vkey, repeat, pressed); + TRACE("himc %p, vkey %#x, pressed %u\n", + himc, vkey, pressed); if (!macdrv_using_input_method()) return 0; @@ -1111,23 +1107,44 @@ UINT macdrv_ImeProcessKey(HIMC himc, UINT wparam, UINT lparam, const BYTE *key_s case VK_CONTROL: case VK_CAPITAL: case VK_MENU: + case VK_KANA: + case VK_KANJI: + TRACE("Skipping metakey\n"); return 0; } + return 1; +} + +/*********************************************************************** + * ImeToAsciiEx (MACDRV.@) + */ +UINT macdrv_ImeToAsciiEx(UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, UINT flags, HIMC himc) +{ + struct macdrv_thread_data *thread_data = macdrv_thread_data(); + int keyc; + bool ret; + BOOL repeat = !!(vsc & 0x4000); + + TRACE("himc %p, vkey %#x state %p repeat %u\n", + himc, vkey, state, repeat); + + if (!state) return 0; + flags = thread_data->last_modifiers; - if (key_state[VK_SHIFT] & 0x80) + if (state[VK_SHIFT] & 0x80) flags |= NX_SHIFTMASK; else flags &= ~(NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK); - if (key_state[VK_CAPITAL] & 0x01) + if (state[VK_CAPITAL] & 0x01) flags |= NX_ALPHASHIFTMASK; else flags &= ~NX_ALPHASHIFTMASK; - if (key_state[VK_CONTROL] & 0x80) + if (state[VK_CONTROL] & 0x80) flags |= NX_CONTROLMASK; else flags &= ~(NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK); - if (key_state[VK_MENU] & 0x80) + if (state[VK_MENU] & 0x80) flags |= NX_COMMANDMASK; else flags &= ~(NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK); @@ -1139,10 +1156,9 @@ UINT macdrv_ImeProcessKey(HIMC himc, UINT wparam, UINT lparam, const BYTE *key_s if (keyc >= ARRAY_SIZE(thread_data->keyc2vkey)) return 0; TRACE("flags 0x%08x keyc 0x%04x\n", flags, keyc); - ret = (UINT)macdrv_ime_process_key(keyc, flags, repeat, himc); + ret = macdrv_send_keydown_to_input_source(keyc, flags, repeat, himc); NtUserMsgWaitForMultipleObjectsEx(0, NULL, 0, QS_POSTMESSAGE | QS_SENDMESSAGE, 0); - TRACE("returning %u\n", ret); - return ret; + return ret ? STATUS_SUCCESS : STATUS_NOT_IMPLEMENTED; } diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index ac2a7853652..843541ede1b 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -160,6 +160,7 @@ extern void macdrv_WindowPosChanged(HWND hwnd, HWND insert_after, HWND owner_hin extern void macdrv_UnregisterHotKey(HWND hwnd, UINT modifiers, UINT vkey); extern SHORT macdrv_VkKeyScanEx(WCHAR wChar, HKL hkl); extern UINT macdrv_ImeProcessKey(HIMC himc, UINT wparam, UINT lparam, const BYTE *state); +extern UINT macdrv_ImeToAsciiEx(UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, UINT flags, HIMC himc); extern UINT macdrv_MapVirtualKeyEx(UINT wCode, UINT wMapType, HKL hkl); extern INT macdrv_ToUnicodeEx(UINT virtKey, UINT scanCode, const BYTE *lpKeyState, LPWSTR bufW, int bufW_size, UINT flags, HKL hkl); diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h index 6a6b6975f55..381808479ce 100644 --- a/dlls/winemac.drv/macdrv_cocoa.h +++ b/dlls/winemac.drv/macdrv_cocoa.h @@ -530,7 +530,7 @@ extern void macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev, extern bool macdrv_get_view_backing_size(macdrv_view v, int backing_size[2]); extern void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2]); extern uint32_t macdrv_window_background_color(void); -extern bool macdrv_ime_process_key(int keyc, unsigned int flags, int repeat, void *data); +extern bool macdrv_send_keydown_to_input_source(int keyc, unsigned int flags, int repeat, void *data); extern bool macdrv_is_any_wine_window_visible(void); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9992
On Wed Feb 18 13:49:16 2026 +0000, Marc-Aurel Zent wrote:
changed this line in [version 6 of the diff](/wine/wine/-/merge_requests/9992/diffs?diff_id=245384&start_sha=6ba821ee4fb377ad7f6f9eb356f124259bb5b181#8ad61927ca9cd33508c8ef07654574b3db25a865_1095_1094) Yeah that indeed works, thanks!
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9992#note_129885
On Wed Feb 18 13:49:16 2026 +0000, Marc-Aurel Zent wrote:
changed this line in [version 6 of the diff](/wine/wine/-/merge_requests/9992/diffs?diff_id=245384&start_sha=6ba821ee4fb377ad7f6f9eb356f124259bb5b181#16982e67620fef9320049b5ac240cefa4c44db98_695_691) Should be implemented now this way. I was originally thinking of doing something similar (or even appending the message in the user driver itself), but like it is now is probably indeed cleaner.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9992#note_129886
On Wed Feb 18 13:54:49 2026 +0000, Rémi Bernon wrote:
This call is only meant to gather the queued updates and generate composition messages out of it, it shouldn't cause a key event to be fed back into the host IME and thus doesn't need the keyboard state. The IMN_WINE_SET_COMP_STRING already comes from an asynchronous IME input from the host IME itself. Yeah, you are probably right... ImeToAsciiEx was crashing in the user driver before with a NULL keyboard state (which was why I originally added this), but it is now handled directly in the winemac ImeToAsciiEx implementation.
I dropped that commit in the latest version. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9992#note_129887
IMO instead of adding another driver entry, we should move things from ImeProcessKey to ImeToAsciiEx. Whether host input is an IME, and whether builtin IME be called at all, should be done using the dedicated bit in the HKL (IME HKL have 0xe000 high word bits set).
Currently ImeProcessKey does not need any mac driver specific functionality (save for `macdrv_using_input_method(void)`, which can be encoded with the 0xe000 high word bit, like you mentioned), so this can indeed be refactored into win32u. Since only winemac implements ImeProcessKey (I think), this should be fairly doable, but maybe it is best to split that change off into a separate MR later. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9992#note_129889
As you mentioned in your previous comment, I had misunderstood the intended direction of the MR. However, since I still believed that separating the functionality of macdrv_ImeProcessKey was necessary, I went ahead and worked on it independently. I initially started this because a memory leak occurs when ImeToAsciiEx is not invoked.
Does the memory leak still happen with the latest version? If you could give it a spin and see if everything is working as intended (at least on the Korean side of things), that would be much appreciated. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9992#note_129890
On Wed Feb 18 14:03:42 2026 +0000, Marc-Aurel Zent wrote:
As you mentioned in your previous comment, I had misunderstood the intended direction of the MR. However, since I still believed that separating the functionality of macdrv_ImeProcessKey was necessary, I went ahead and worked on it independently. I initially started this because a memory leak occurs when ImeToAsciiEx is not invoked. Does the memory leak still happen with the latest version? If you could give it a spin and see if everything is working as intended (at least on the Korean side of things), that would be much appreciated. Since you have moved the actual IME processing, the memory leak issue is no longer present.
I was not able to find any issues during Korean and Japanese keyboard testing. However, the current problem is that it is still operating in asynchronous mode (data->ime_process_vkey == 0). The imm_thread_data, ime_update related code in WINE_IME_PROCESS_KEY should be moved into WINE_IME_TO_ASCII_EX. I would like to note a few things that came to mind: - At this point, I do not think list_add_tail is still necessary here, but removing it would increase the scope of the changes. - When STATUS_BUFFER_TOO_SMALL is returned, macdrv_ImeToAsciiEx should not be executed again. - WM_KEYDOWN message should be generated when res == 0 && data->update == NULL. - IME messages should not be generated when res != 0 && data->update == NULL. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9992#note_129906
the current problem is that it is still operating in asynchronous mode (data-\>ime_process_vkey == 0). The imm_thread_data, ime_update related code in WINE_IME_PROCESS_KEY should be moved into WINE_IME_TO_ASCII_EX.
I would like to note a few things that came to mind:
* At this point, I do not think list_add_tail is still necessary here, but removing it would increase the scope of the changes. * When STATUS_BUFFER_TOO_SMALL is returned, macdrv_ImeToAsciiEx should not be executed again. * WM_KEYDOWN message should be generated when res == 0 && data-\>update == NULL. * IME messages should not be generated when res != 0 && data-\>update == NULL.
Here, `res` refers to `key_consumed`, not the value used in v5. I hope this avoids any confusion. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9992#note_130070
participants (3)
-
Byeongsik Jeon (@bsjeon) -
Marc-Aurel Zent -
Marc-Aurel Zent (@mzent)