[PATCH v8 0/3] MR9992: Draft: Introduce ImeToAsciiEx() user driver function for use in winemac
-- v8: winemac: Implement and use macdrv_ImeToAsciiEx(). win32u: Move IME processing to ImeToAsciiEx. win32u: Introduce new ImeToAsciiEx user driver call. https://gitlab.winehq.org/wine/wine/-/merge_requests/9992
From: Marc-Aurel Zent <mzent@codeweavers.com> --- dlls/win32u/driver.c | 12 ++++++++++++ include/wine/gdi_driver.h | 1 + 2 files changed, 13 insertions(+) diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index c9e81016ee8..36f0205b8b3 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -675,6 +675,11 @@ static UINT nulldrv_ImeProcessKey( HIMC himc, UINT wparam, UINT lparam, const BY return 0; } +static UINT nulldrv_ImeToAsciiEx( UINT vkey, UINT vsc, const BYTE *state, HIMC himc ) +{ + return STATUS_NOT_IMPLEMENTED; +} + static void nulldrv_NotifyIMEStatus( HWND hwnd, UINT status ) { } @@ -1102,6 +1107,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,const BYTE *state, HIMC himc ) +{ + return load_driver()->pImeToAsciiEx( vkey, vsc, state, himc ); +} + static void loaderdrv_NotifyIMEStatus( HWND hwnd, UINT status ) { return load_driver()->pNotifyIMEStatus( hwnd, status ); @@ -1255,6 +1265,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 */ @@ -1359,6 +1370,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/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index f6390bce878..c5b200fb935 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,const BYTE*,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 ++++++++++++- dlls/win32u/driver.c | 2 +- dlls/win32u/imm.c | 14 +++++++++----- include/ntuser.h | 1 + 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index 9e6c96537c8..c2b65274395 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -563,6 +563,7 @@ UINT WINAPI ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, INPUTCONTEXT *ctx; NTSTATUS status; BOOL key_consumed = TRUE; + struct ime_driver_call_params params = {.himc = himc, .state = state}; TRACE( "vkey %#x, vsc %#x, state %p, msgs %p, flags %#x, himc %p\n", vkey, vsc, state, msgs, flags, himc ); @@ -573,9 +574,19 @@ UINT WINAPI ImeToAsciiEx( UINT vkey, UINT vsc, BYTE *state, TRANSMSGLIST *msgs, if (!(compstr = ImmLockIMCC( ctx->hCompStr ))) goto done; size = max( compstr->dwSize, sizeof(COMPOSITIONSTRING) ); + status = NtUserMessageCall( ctx->hWnd, WINE_IME_TO_ASCII_EX_USER, vkey, vsc, ¶ms, + NtUserImeDriverCall, FALSE ); + + if (!NT_SUCCESS(status)) + { + TRANSMSG msg = {.message = WM_KEYDOWN, .wParam = vkey, .lParam = vsc}; + msgs->TransMsg[count++] = msg; + ImmUnlockIMCC( ctx->hCompStr ); + goto done; + } + do { - struct ime_driver_call_params params = {.himc = himc, .state = state}; HIMCC himcc; ImmUnlockIMCC( ctx->hCompStr ); diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index 36f0205b8b3..40ccdefc6e0 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -672,7 +672,7 @@ static void nulldrv_ReleaseKbdTables( const KBDTABLES *tables ) static UINT nulldrv_ImeProcessKey( HIMC himc, UINT wparam, UINT lparam, const BYTE *state ) { - return 0; + return 1; } static UINT nulldrv_ImeToAsciiEx( UINT vkey, UINT vsc, const BYTE *state, HIMC himc ) diff --git a/dlls/win32u/imm.c b/dlls/win32u/imm.c index f585b80a383..3c650a9c5f9 100644 --- a/dlls/win32u/imm.c +++ b/dlls/win32u/imm.c @@ -664,25 +664,29 @@ LRESULT ime_driver_call( HWND hwnd, enum wine_ime_call call, WPARAM wparam, LPAR switch (call) { case WINE_IME_PROCESS_KEY: + res = user_driver->pImeProcessKey( params->himc, wparam, lparam, params->state ); + TRACE( "ImeProcessKey vkey %#x, scan %#x -> %lu\n", LOWORD(wparam), HIWORD(lparam), res ); + return res; + case WINE_IME_TO_ASCII_EX_USER: { struct imm_thread_data *data = get_imm_thread_data(); - data->ime_process_scan = HIWORD(lparam); + data->ime_process_scan = LOWORD(lparam); data->ime_process_vkey = LOWORD(wparam); - res = user_driver->pImeProcessKey( params->himc, wparam, lparam, params->state ); + res = user_driver->pImeToAsciiEx( wparam, lparam, (BYTE *)params->state, params->himc ); data->ime_process_vkey = data->ime_process_scan = 0; if (data->update) { - data->update->key_consumed = res; + data->update->key_consumed = NT_SUCCESS(res); pthread_mutex_lock( &imm_mutex ); list_add_tail( &ime_updates, &data->update->entry ); pthread_mutex_unlock( &imm_mutex ); data->update = NULL; - res = TRUE; + res = STATUS_SUCCESS; } - TRACE( "processing vkey %#x, scan %#x -> %lu\n", LOWORD(wparam), HIWORD(lparam), res ); + TRACE( "processing vkey %#x, scan %#x -> %lu\n", LOWORD(wparam), LOWORD(lparam), res ); return res; } case WINE_IME_TO_ASCII_EX: diff --git a/include/ntuser.h b/include/ntuser.h index 939771c9121..3ff7fd574e6 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -679,6 +679,7 @@ enum wine_ime_call WINE_IME_PROCESS_KEY, WINE_IME_TO_ASCII_EX, WINE_IME_POST_UPDATE, /* for the user drivers */ + WINE_IME_TO_ASCII_EX_USER, }; /* NtUserImeDriverCall params */ -- 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 | 47 ++++++++++++++++++++++----------- dlls/winemac.drv/macdrv.h | 1 + dlls/winemac.drv/macdrv_cocoa.h | 2 +- 5 files changed, 37 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 fb3c9bcf30e..962a8c50896 100644 --- a/dlls/winemac.drv/keyboard.c +++ b/dlls/winemac.drv/keyboard.c @@ -1110,15 +1110,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; @@ -1136,23 +1132,45 @@ 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, const BYTE *state, HIMC himc) +{ + struct macdrv_thread_data *thread_data = macdrv_thread_data(); + unsigned int flags; + 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); @@ -1164,10 +1182,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 0ef7a706d91..e5c922296bf 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -159,6 +159,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, const BYTE *state, 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
I think that with !10138 we can now move everything to ImeToAsciiEx, and simply return TRUE from ImeProcessKey? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9992#note_134483
On Tue Mar 31 15:21:42 2026 +0000, Rémi Bernon wrote:
I think that with !10138 we can now move everything to ImeToAsciiEx, and simply return TRUE from ImeProcessKey? In theory yes, but I believe ImeProcessKey does a bit more than that still...
It seems similar to a few things macdrv_ImeProcessKey does. At the very least, it returns false for VK_MENU, VK_CAPITAL, VK_CONTROL, and VK_SHIFT. Also, sometimes not VK_NUMPAD0 and friends, and also VK_BACK, when no composition is active. It doesn't seem to care about up or down keypresses though... I can try to refactor some of that logic back into the win32 ImeProcessKey side of things, and what does seem to work differently from native leave in macdrv_ImeToAsciiEx. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9992#note_134534
On Tue Mar 31 15:21:42 2026 +0000, Marc-Aurel Zent wrote:
In theory yes, but I believe ImeProcessKey does a bit more than that still... It seems similar to a few things macdrv_ImeProcessKey does. At the very least, it returns false for VK_MENU, VK_CAPITAL, VK_CONTROL, and VK_SHIFT. Also, sometimes not VK_NUMPAD0 and friends, and also VK_BACK, when no composition is active. It doesn't seem to care about up or down keypresses though... I can try to refactor some of that logic back into the win32 ImeProcessKey side of things, and what does seem to work differently from native leave in macdrv_ImeToAsciiEx. Well sure maybe there's a bit more than that but I meant that we shouldn't need to call into the user drivers for that, it could be implemented in the common PE code so we move all the driver logic to the new driver entry instead of splitting it.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/9992#note_134535
participants (3)
-
Marc-Aurel Zent -
Marc-Aurel Zent (@mzent) -
Rémi Bernon (@rbernon)