-- v3: winex11: Use an IME UI message to set composition status. winex11: Use an IME UI message to set cursor pos. winex11: Use an IME UI message to get cursor pos. winex11: Use an IME UI message to set open status. win32u: Keep the IME UI window in win32u struct imc.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/imm32/imm.c | 35 ++++++++++++++++++++++++++--------- dlls/win32u/imm.c | 10 ++++++++++ include/ntuser.h | 1 + 3 files changed, 37 insertions(+), 9 deletions(-)
diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index d29684f34a2..42a6a568c1a 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -87,8 +87,6 @@ struct imc
struct ime *ime; UINT lastVK; - - HWND ui_hwnd; /* IME UI window, on the default input context */ };
#define WINE_IMC_VALID_MAGIC 0x56434D49 @@ -623,12 +621,25 @@ static INPUTCONTEXT *ime_find_input_context( struct ime *ime, HIMC himc ) return &entry->context; }
+static BOOL CALLBACK enum_set_ui_window( HIMC himc, LPARAM lparam ) +{ + NtUserUpdateInputContext( himc, NtUserInputContextUIHwnd, lparam ); + return TRUE; +} + static void imc_release_ime( struct imc *imc, struct ime *ime ) { + HIMC default_himc = UlongToHandle( NtUserGetThreadInfo()->default_imc ); INPUTCONTEXT *ctx; + HWND hwnd; + + if (imc->handle != default_himc) NtUserUpdateInputContext( imc->handle, NtUserInputContextUIHwnd, 0 ); + else if ((hwnd = (HWND)NtUserQueryInputContext( default_himc, NtUserInputContextUIHwnd ))) + { + DestroyWindow( hwnd ); + ImmEnumInputContext( 0, enum_set_ui_window, 0 ); + }
- if (imc->ui_hwnd) DestroyWindow( imc->ui_hwnd ); - imc->ui_hwnd = NULL; ime->pImeSelect( imc->handle, FALSE );
if ((ctx = ime_find_input_context( ime, imc->handle ))) *ctx = imc->IMC; @@ -651,11 +662,14 @@ static struct ime *imc_select_ime( struct imc *imc ) WARN( "Failed to acquire IME for HKL %p\n", hkl ); else { + HIMC default_himc = UlongToHandle( NtUserGetThreadInfo()->default_imc ); + HWND hwnd = (HWND)NtUserQueryInputContext( default_himc, NtUserInputContextUIHwnd ); INPUTCONTEXT *ctx;
if ((ctx = ime_find_input_context( imc->ime, imc->handle ))) imc->IMC = *ctx; else ime_save_input_context( imc->ime, imc->handle, &imc->IMC );
+ NtUserUpdateInputContext( imc->handle, NtUserInputContextUIHwnd, (LPARAM)hwnd ); imc->ime->pImeSelect( imc->handle, TRUE ); }
@@ -1010,16 +1024,19 @@ static HWND get_ime_ui_window(void) { struct imc *imc = default_input_context(); struct ime *ime; + HWND hwnd;
if (!(ime = imc_select_ime( imc ))) return 0;
- if (!imc->ui_hwnd) + if (!(hwnd = (HWND)NtUserQueryInputContext( imc->handle, NtUserInputContextUIHwnd ))) { - imc->ui_hwnd = CreateWindowExW( WS_EX_TOOLWINDOW, ime->ui_class, NULL, WS_POPUP, 0, 0, 1, 1, - ImmGetDefaultIMEWnd( 0 ), 0, ime->module, 0 ); - SetWindowLongPtrW( imc->ui_hwnd, IMMGWL_IMC, (LONG_PTR)imc->handle ); + hwnd = CreateWindowExW( WS_EX_TOOLWINDOW, ime->ui_class, NULL, WS_POPUP, 0, 0, 1, 1, + ImmGetDefaultIMEWnd( 0 ), 0, ime->module, 0 ); + SetWindowLongPtrW( hwnd, IMMGWL_IMC, (LONG_PTR)imc->handle ); + ImmEnumInputContext( 0, enum_set_ui_window, (LPARAM)hwnd ); } - return imc->ui_hwnd; + + return hwnd; }
/*********************************************************************** diff --git a/dlls/win32u/imm.c b/dlls/win32u/imm.c index 7dee4912e27..7be44306dda 100644 --- a/dlls/win32u/imm.c +++ b/dlls/win32u/imm.c @@ -40,6 +40,7 @@ struct imc struct user_object obj; DWORD thread_id; UINT_PTR client_ptr; + HWND ui_hwnd; };
struct imm_thread_data @@ -80,6 +81,7 @@ HIMC WINAPI NtUserCreateInputContext( UINT_PTR client_ptr ) if (!(imc = malloc( sizeof(*imc) ))) return 0; imc->client_ptr = client_ptr; imc->thread_id = GetCurrentThreadId(); + imc->ui_hwnd = 0; if (!(handle = alloc_user_handle( &imc->obj, NTUSER_OBJ_IMC ))) { free( imc ); @@ -127,6 +129,10 @@ BOOL WINAPI NtUserUpdateInputContext( HIMC handle, UINT attr, UINT_PTR value ) imc->client_ptr = value; break;
+ case NtUserInputContextUIHwnd: + imc->ui_hwnd = (HWND)value; + break; + default: FIXME( "unknown attr %u\n", attr ); ret = FALSE; @@ -156,6 +162,10 @@ UINT_PTR WINAPI NtUserQueryInputContext( HIMC handle, UINT attr ) ret = imc->thread_id; break;
+ case NtUserInputContextUIHwnd: + ret = (UINT_PTR)imc->ui_hwnd; + break; + default: FIXME( "unknown attr %u\n", attr ); ret = 0; diff --git a/include/ntuser.h b/include/ntuser.h index 3d43eb476e4..7356c6096e9 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -453,6 +453,7 @@ struct draw_scroll_bar_params /* NtUserUpdateInputContext param, not compatible with Window */ enum input_context_attr { + NtUserInputContextUIHwnd = -1, NtUserInputContextClientPtr, NtUserInputContextThreadId, };
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/imm32/ime.c | 3 +++ dlls/imm32/imm_private.h | 1 + dlls/winex11.drv/dllmain.c | 1 - dlls/winex11.drv/ime.c | 9 --------- dlls/winex11.drv/unixlib.h | 1 - dlls/winex11.drv/x11drv_dll.h | 1 - dlls/winex11.drv/xim.c | 21 +++++++++++++++++---- include/ntuser.h | 3 +++ 8 files changed, 24 insertions(+), 16 deletions(-)
diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index 3c4550c3cd9..35edcfe7f46 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -291,6 +291,9 @@ static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LP FIXME( "hwnd %p, himc %p, msg %s, wparam %s, lparam %#Ix stub!\n", hwnd, himc, debugstr_wm_ime(msg), debugstr_imc(wparam), lparam ); return 1; + + case WM_WINE_IME_SET_OPEN_STATUS: + return ImmSetOpenStatus( himc, wparam ); }
return DefWindowProcW( hwnd, msg, wparam, lparam ); diff --git a/dlls/imm32/imm_private.h b/dlls/imm32/imm_private.h index 8a957494ce9..eccfd223ac3 100644 --- a/dlls/imm32/imm_private.h +++ b/dlls/imm32/imm_private.h @@ -61,6 +61,7 @@ static const char *debugstr_wm_ime( UINT msg ) case WM_IME_REQUEST: return "WM_IME_REQUEST"; case WM_IME_KEYDOWN: return "WM_IME_KEYDOWN"; case WM_IME_KEYUP: return "WM_IME_KEYUP"; + case WM_WINE_IME_SET_OPEN_STATUS: return "WM_WINE_IME_SET_OPEN_STATUS"; default: if (msg == WM_MSIME_SERVICE) return "WM_MSIME_SERVICE"; else if (msg == WM_MSIME_RECONVERTOPTIONS) return "WM_MSIME_RECONVERTOPTIONS"; diff --git a/dlls/winex11.drv/dllmain.c b/dlls/winex11.drv/dllmain.c index 500a4a6bc44..54093d36b6e 100644 --- a/dlls/winex11.drv/dllmain.c +++ b/dlls/winex11.drv/dllmain.c @@ -33,7 +33,6 @@ static const callback_func callback_funcs[] = x11drv_ime_get_cursor_pos, x11drv_ime_set_composition_status, x11drv_ime_set_cursor_pos, - x11drv_ime_set_open_status, x11drv_ime_update_association, };
diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index 55485bfbfcf..72ada45f774 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -755,15 +755,6 @@ BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp,
/* Interfaces to XIM and other parts of winex11drv */
-NTSTATUS x11drv_ime_set_open_status( UINT open ) -{ - HIMC imc; - - imc = RealIMC(FROM_X11); - ImmSetOpenStatus(imc, open); - return 0; -} - NTSTATUS x11drv_ime_set_composition_status( UINT open ) { HIMC imc; diff --git a/dlls/winex11.drv/unixlib.h b/dlls/winex11.drv/unixlib.h index 7dc1d9f0ca7..ed5dbac6535 100644 --- a/dlls/winex11.drv/unixlib.h +++ b/dlls/winex11.drv/unixlib.h @@ -99,7 +99,6 @@ enum client_callback client_ime_get_cursor_pos, client_ime_set_composition_status, client_ime_set_cursor_pos, - client_ime_set_open_status, client_ime_update_association, client_funcs_count }; diff --git a/dlls/winex11.drv/x11drv_dll.h b/dlls/winex11.drv/x11drv_dll.h index 047bb430d39..80fe9ab2a3d 100644 --- a/dlls/winex11.drv/x11drv_dll.h +++ b/dlls/winex11.drv/x11drv_dll.h @@ -39,7 +39,6 @@ extern NTSTATUS x11drv_dnd_leave_event( UINT arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_ime_get_cursor_pos( UINT arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_ime_set_composition_status( UINT arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_ime_set_cursor_pos( UINT pos ) DECLSPEC_HIDDEN; -extern NTSTATUS x11drv_ime_set_open_status( UINT open ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_ime_update_association( UINT arg ) DECLSPEC_HIDDEN;
extern LRESULT WINAPI foreign_window_proc( HWND hwnd, UINT msg, WPARAM wparam, diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index b7f5f696ba5..382de3e006e 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -72,6 +72,21 @@ static const char *debugstr_xim_style( XIMStyle style ) return wine_dbg_sprintf( "%s", buffer ); }
+/* sends a message to the IME UI window */ +static LRESULT send_ime_ui_message( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + static const WCHAR WINE_IME_UI_CLASS[] = {'W','i','n','e',' ','I','M','E',0}; + WCHAR buffer[64]; + UNICODE_STRING name = {.Buffer = buffer, .MaximumLength = sizeof(buffer)}; + HIMC himc; + + if (!(himc = NtUserGetWindowInputContext( hwnd ))) return 0; + if (!(hwnd = (HWND)NtUserQueryInputContext( himc, NtUserInputContextUIHwnd ))) return 0; + if (!NtUserGetClassName( hwnd, 0, &name ) || wcscmp( buffer, WINE_IME_UI_CLASS )) return 0; + + return send_message( hwnd, msg, wparam, lparam ); +} + static void X11DRV_ImmSetInternalString(UINT offset, UINT selLength, LPWSTR lpComp, UINT len) { /* Composition strings are edited in chunks */ @@ -131,12 +146,10 @@ static BOOL xic_preedit_state_notify( XIC xic, XPointer user, XPointer arg ) switch (state) { case XIMPreeditEnable: - x11drv_client_call( client_ime_set_open_status, TRUE ); + send_ime_ui_message( hwnd, WM_WINE_IME_SET_OPEN_STATUS, TRUE, 0 ); break; case XIMPreeditDisable: - x11drv_client_call( client_ime_set_open_status, FALSE ); - break; - default: + send_ime_ui_message( hwnd, WM_WINE_IME_SET_OPEN_STATUS, FALSE, 0 ); break; }
diff --git a/include/ntuser.h b/include/ntuser.h index 7356c6096e9..f54a0eb902a 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -491,6 +491,9 @@ enum wine_internal_message #define IME_INTERNAL_HKL_ACTIVATE 0x19 #define IME_INTERNAL_HKL_DEACTIVATE 0x20
+/* default IME UI messages */ +#define WM_WINE_IME_SET_OPEN_STATUS (WM_USER + 0) + /* internal IME private */ typedef struct ime_private {
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/imm32/ime.c | 21 +++++++++++++++++++++ dlls/imm32/imm_private.h | 1 + dlls/winex11.drv/dllmain.c | 1 - dlls/winex11.drv/ime.c | 20 -------------------- dlls/winex11.drv/unixlib.h | 1 - dlls/winex11.drv/x11drv_dll.h | 1 - dlls/winex11.drv/xim.c | 2 +- include/ntuser.h | 1 + 8 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index 35edcfe7f46..86834546a58 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -242,6 +242,25 @@ static void ime_ui_start_composition( HIMC himc, HWND hwnd ) ImmUnlockIMC( himc ); }
+static UINT ime_get_cursor_pos( HIMC himc ) +{ + COMPOSITIONSTRING *string; + INPUTCONTEXT *ctx; + UINT pos = 0; + + TRACE( "himc %p\n", himc ); + + if (!(ctx = ImmLockIMC( himc ))) return 0; + if ((string = ImmLockIMCC( ctx->hCompStr ))) + { + pos = string->dwCursorPos; + ImmUnlockIMCC( ctx->hCompStr ); + } + ImmUnlockIMC( himc ); + + return pos; +} + static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { HIMC himc = (HIMC)GetWindowLongPtrW( hwnd, IMMGWL_IMC ); @@ -294,6 +313,8 @@ static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LP
case WM_WINE_IME_SET_OPEN_STATUS: return ImmSetOpenStatus( himc, wparam ); + case WM_WINE_IME_GET_CURSOR_POS: + return ime_get_cursor_pos( himc ); }
return DefWindowProcW( hwnd, msg, wparam, lparam ); diff --git a/dlls/imm32/imm_private.h b/dlls/imm32/imm_private.h index eccfd223ac3..4a3afce09ba 100644 --- a/dlls/imm32/imm_private.h +++ b/dlls/imm32/imm_private.h @@ -62,6 +62,7 @@ static const char *debugstr_wm_ime( UINT msg ) case WM_IME_KEYDOWN: return "WM_IME_KEYDOWN"; case WM_IME_KEYUP: return "WM_IME_KEYUP"; case WM_WINE_IME_SET_OPEN_STATUS: return "WM_WINE_IME_SET_OPEN_STATUS"; + case WM_WINE_IME_GET_CURSOR_POS: return "WM_WINE_IME_GET_CURSOR_POS"; default: if (msg == WM_MSIME_SERVICE) return "WM_MSIME_SERVICE"; else if (msg == WM_MSIME_RECONVERTOPTIONS) return "WM_MSIME_RECONVERTOPTIONS"; diff --git a/dlls/winex11.drv/dllmain.c b/dlls/winex11.drv/dllmain.c index 54093d36b6e..a3375e60957 100644 --- a/dlls/winex11.drv/dllmain.c +++ b/dlls/winex11.drv/dllmain.c @@ -30,7 +30,6 @@ static const callback_func callback_funcs[] = { x11drv_dnd_drop_event, x11drv_dnd_leave_event, - x11drv_ime_get_cursor_pos, x11drv_ime_set_composition_status, x11drv_ime_set_cursor_pos, x11drv_ime_update_association, diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index 72ada45f774..b92c13ff08a 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -786,26 +786,6 @@ NTSTATUS x11drv_ime_set_composition_status( UINT open ) return 0; }
-NTSTATUS x11drv_ime_get_cursor_pos( UINT arg ) -{ - LPINPUTCONTEXT lpIMC; - INT rc = 0; - LPCOMPOSITIONSTRING compstr; - - if (!hSelectedFrom) - return rc; - - lpIMC = LockRealIMC(FROM_X11); - if (lpIMC) - { - compstr = ImmLockIMCC(lpIMC->hCompStr); - rc = compstr->dwCursorPos; - ImmUnlockIMCC(lpIMC->hCompStr); - } - UnlockRealIMC(FROM_X11); - return rc; -} - NTSTATUS x11drv_ime_set_cursor_pos( UINT pos ) { LPINPUTCONTEXT lpIMC; diff --git a/dlls/winex11.drv/unixlib.h b/dlls/winex11.drv/unixlib.h index ed5dbac6535..332c4e4b3fa 100644 --- a/dlls/winex11.drv/unixlib.h +++ b/dlls/winex11.drv/unixlib.h @@ -96,7 +96,6 @@ enum client_callback { client_dnd_drop_event, client_dnd_leave_event, - client_ime_get_cursor_pos, client_ime_set_composition_status, client_ime_set_cursor_pos, client_ime_update_association, diff --git a/dlls/winex11.drv/x11drv_dll.h b/dlls/winex11.drv/x11drv_dll.h index 80fe9ab2a3d..2cc27abd362 100644 --- a/dlls/winex11.drv/x11drv_dll.h +++ b/dlls/winex11.drv/x11drv_dll.h @@ -36,7 +36,6 @@ extern NTSTATUS WINAPI x11drv_systray_change_owner( void *params, ULONG size ) D
extern NTSTATUS x11drv_dnd_drop_event( UINT arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_dnd_leave_event( UINT arg ) DECLSPEC_HIDDEN; -extern NTSTATUS x11drv_ime_get_cursor_pos( UINT arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_ime_set_composition_status( UINT arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_ime_set_cursor_pos( UINT pos ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_ime_update_association( UINT arg ) DECLSPEC_HIDDEN; diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 382de3e006e..2cba4ca540d 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -236,7 +236,7 @@ static int xic_preedit_caret( XIC xic, XPointer user, XPointer arg )
if (!params) return 0;
- pos = x11drv_client_call( client_ime_get_cursor_pos, 0 ); + pos = send_ime_ui_message( hwnd, WM_WINE_IME_GET_CURSOR_POS, 0, 0 ); switch (params->direction) { case XIMForwardChar: diff --git a/include/ntuser.h b/include/ntuser.h index f54a0eb902a..b3824bec7a5 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -493,6 +493,7 @@ enum wine_internal_message
/* default IME UI messages */ #define WM_WINE_IME_SET_OPEN_STATUS (WM_USER + 0) +#define WM_WINE_IME_GET_CURSOR_POS (WM_USER + 1)
/* internal IME private */ typedef struct ime_private
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/imm32/ime.c | 40 +++++++++++++++++++++++++++++++++++ dlls/imm32/imm_private.h | 1 + dlls/winex11.drv/dllmain.c | 1 - dlls/winex11.drv/ime.c | 26 ----------------------- dlls/winex11.drv/unixlib.h | 1 - dlls/winex11.drv/x11drv_dll.h | 1 - dlls/winex11.drv/xim.c | 5 ++--- include/ntuser.h | 1 + 8 files changed, 44 insertions(+), 32 deletions(-)
diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index 86834546a58..dcea00c788b 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -93,6 +93,25 @@ static HFONT input_context_select_ui_font( INPUTCONTEXT *ctx, HDC hdc ) return font; }
+static void ime_send_message( HIMC himc, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + TRANSMSG *messages = NULL, message = {.message = msg, .wParam = wparam, .lParam = lparam}; + INPUTCONTEXT *ctx; + HIMCC tmp; + + TRACE( "himc %p, msg %#x, wparam %#Ix, lparam %#Ix\n", himc, msg, wparam, lparam ); + + if (!(ctx = ImmLockIMC( himc ))) return; + if (!(tmp = ImmReSizeIMCC( ctx->hMsgBuf, (ctx->dwNumMsgBuf + 1) * sizeof(message) ))) goto done; + if (!(messages = ImmLockIMCC( (ctx->hMsgBuf = tmp) ))) goto done; + messages[ctx->dwNumMsgBuf++] = message; + ImmUnlockIMCC( ctx->hMsgBuf ); + +done: + ImmUnlockIMC( himc ); + if (!messages || !ImmGenerateMessage( himc )) WARN( "Failed to generate himc %p messages, error %lu\n", himc, GetLastError() ); +} + static void ime_ui_paint( HIMC himc, HWND hwnd ) { PAINTSTRUCT ps; @@ -261,6 +280,25 @@ static UINT ime_get_cursor_pos( HIMC himc ) return pos; }
+static UINT ime_set_cursor_pos( HIMC himc, UINT new_pos ) +{ + COMPOSITIONSTRING *string; + INPUTCONTEXT *ctx; + + TRACE( "himc %p, new_pos %u\n", himc, new_pos ); + + if (!(ctx = ImmLockIMC( himc ))) return 0; + if ((string = ImmLockIMCC( ctx->hCompStr ))) + { + string->dwCursorPos = new_pos; + ImmUnlockIMCC( ctx->hCompStr ); + } + ImmUnlockIMC( himc ); + + ime_send_message( himc, WM_IME_COMPOSITION, new_pos, GCS_CURSORPOS ); + return 0; +} + static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { HIMC himc = (HIMC)GetWindowLongPtrW( hwnd, IMMGWL_IMC ); @@ -315,6 +353,8 @@ static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LP return ImmSetOpenStatus( himc, wparam ); case WM_WINE_IME_GET_CURSOR_POS: return ime_get_cursor_pos( himc ); + case WM_WINE_IME_SET_CURSOR_POS: + return ime_set_cursor_pos( himc, wparam ); }
return DefWindowProcW( hwnd, msg, wparam, lparam ); diff --git a/dlls/imm32/imm_private.h b/dlls/imm32/imm_private.h index 4a3afce09ba..b10d04d77ff 100644 --- a/dlls/imm32/imm_private.h +++ b/dlls/imm32/imm_private.h @@ -63,6 +63,7 @@ static const char *debugstr_wm_ime( UINT msg ) case WM_IME_KEYUP: return "WM_IME_KEYUP"; case WM_WINE_IME_SET_OPEN_STATUS: return "WM_WINE_IME_SET_OPEN_STATUS"; case WM_WINE_IME_GET_CURSOR_POS: return "WM_WINE_IME_GET_CURSOR_POS"; + case WM_WINE_IME_SET_CURSOR_POS: return "WM_WINE_IME_SET_CURSOR_POS"; default: if (msg == WM_MSIME_SERVICE) return "WM_MSIME_SERVICE"; else if (msg == WM_MSIME_RECONVERTOPTIONS) return "WM_MSIME_RECONVERTOPTIONS"; diff --git a/dlls/winex11.drv/dllmain.c b/dlls/winex11.drv/dllmain.c index a3375e60957..71fddfe4449 100644 --- a/dlls/winex11.drv/dllmain.c +++ b/dlls/winex11.drv/dllmain.c @@ -31,7 +31,6 @@ static const callback_func callback_funcs[] = x11drv_dnd_drop_event, x11drv_dnd_leave_event, x11drv_ime_set_composition_status, - x11drv_ime_set_cursor_pos, x11drv_ime_update_association, };
diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index b92c13ff08a..cbf9c488f3a 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -786,32 +786,6 @@ NTSTATUS x11drv_ime_set_composition_status( UINT open ) return 0; }
-NTSTATUS x11drv_ime_set_cursor_pos( UINT pos ) -{ - LPINPUTCONTEXT lpIMC; - LPCOMPOSITIONSTRING compstr; - - if (!hSelectedFrom) - return 0; - - lpIMC = LockRealIMC(FROM_X11); - if (!lpIMC) - return 0; - - compstr = ImmLockIMCC(lpIMC->hCompStr); - if (!compstr) - { - UnlockRealIMC(FROM_X11); - return 0; - } - - compstr->dwCursorPos = pos; - ImmUnlockIMCC(lpIMC->hCompStr); - UnlockRealIMC(FROM_X11); - GenerateIMEMessage(FROM_X11, WM_IME_COMPOSITION, pos, GCS_CURSORPOS); - return 0; -} - NTSTATUS x11drv_ime_update_association( UINT arg ) { HWND focus = UlongToHandle( arg ); diff --git a/dlls/winex11.drv/unixlib.h b/dlls/winex11.drv/unixlib.h index 332c4e4b3fa..e48a2c61669 100644 --- a/dlls/winex11.drv/unixlib.h +++ b/dlls/winex11.drv/unixlib.h @@ -97,7 +97,6 @@ enum client_callback client_dnd_drop_event, client_dnd_leave_event, client_ime_set_composition_status, - client_ime_set_cursor_pos, client_ime_update_association, client_funcs_count }; diff --git a/dlls/winex11.drv/x11drv_dll.h b/dlls/winex11.drv/x11drv_dll.h index 2cc27abd362..5180969eeb7 100644 --- a/dlls/winex11.drv/x11drv_dll.h +++ b/dlls/winex11.drv/x11drv_dll.h @@ -37,7 +37,6 @@ extern NTSTATUS WINAPI x11drv_systray_change_owner( void *params, ULONG size ) D extern NTSTATUS x11drv_dnd_drop_event( UINT arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_dnd_leave_event( UINT arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_ime_set_composition_status( UINT arg ) DECLSPEC_HIDDEN; -extern NTSTATUS x11drv_ime_set_cursor_pos( UINT pos ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_ime_update_association( UINT arg ) DECLSPEC_HIDDEN;
extern LRESULT WINAPI foreign_window_proc( HWND hwnd, UINT msg, WPARAM wparam, diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 2cba4ca540d..0b6ac637c64 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -221,8 +221,7 @@ static int xic_preedit_draw( XIC xic, XPointer user, XPointer arg ) if (str != text->string.multi_byte) free( str ); }
- x11drv_client_call( client_ime_set_cursor_pos, params->caret ); - + send_ime_ui_message( hwnd, WM_WINE_IME_SET_CURSOR_POS, params->caret, 0 ); return 0; }
@@ -264,7 +263,7 @@ static int xic_preedit_caret( XIC xic, XPointer user, XPointer arg ) FIXME( "Not implemented\n" ); break; } - x11drv_client_call( client_ime_set_cursor_pos, pos ); + send_ime_ui_message( hwnd, WM_WINE_IME_SET_CURSOR_POS, pos, 0 ); params->position = pos;
return 0; diff --git a/include/ntuser.h b/include/ntuser.h index b3824bec7a5..6f7e206be86 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -494,6 +494,7 @@ enum wine_internal_message /* default IME UI messages */ #define WM_WINE_IME_SET_OPEN_STATUS (WM_USER + 0) #define WM_WINE_IME_GET_CURSOR_POS (WM_USER + 1) +#define WM_WINE_IME_SET_CURSOR_POS (WM_USER + 2)
/* internal IME private */ typedef struct ime_private
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/imm32/ime.c | 42 +++++++++++++++++++++++++++++++++++ dlls/imm32/imm_private.h | 1 + dlls/winex11.drv/dllmain.c | 1 - dlls/winex11.drv/ime.c | 31 -------------------------- dlls/winex11.drv/unixlib.h | 1 - dlls/winex11.drv/x11drv_dll.h | 1 - dlls/winex11.drv/xim.c | 5 +++-- include/ntuser.h | 1 + 8 files changed, 47 insertions(+), 36 deletions(-)
diff --git a/dlls/imm32/ime.c b/dlls/imm32/ime.c index dcea00c788b..29d1c12de43 100644 --- a/dlls/imm32/ime.c +++ b/dlls/imm32/ime.c @@ -299,6 +299,46 @@ static UINT ime_set_cursor_pos( HIMC himc, UINT new_pos ) return 0; }
+static UINT ime_set_composition_status( HIMC himc, HWND hwnd, BOOL new_status ) +{ + COMPOSITIONSTRING *string; + struct ime_private *priv; + INPUTCONTEXT *ctx; + HIMCC tmp; + + TRACE( "himc %p, hwnd %p, new_status %u\n", himc, hwnd, new_status ); + + if (!(ctx = ImmLockIMC( himc ))) return 0; + if (!(priv = ImmLockIMCC( ctx->hPrivate ))) goto done; + + if (new_status && !priv->bInComposition) + ime_send_message( himc, WM_IME_STARTCOMPOSITION, 0, 0 ); + else if (!new_status && priv->bInComposition) + { + ShowWindow( priv->hwndDefault, SW_HIDE ); + + if ((tmp = ImmReSizeIMCC( ctx->hCompStr, sizeof(COMPOSITIONSTRING) ))) ctx->hCompStr = tmp; + else WARN( "Failed to shrink composition string for himc %p\n", himc ); + + if ((string = ImmLockIMCC( ctx->hCompStr ))) + { + memset( string, 0, sizeof(COMPOSITIONSTRING) ); + string->dwSize = sizeof(COMPOSITIONSTRING); + ImmUnlockIMCC( ctx->hCompStr ); + } + + ime_send_message( himc, WM_IME_ENDCOMPOSITION, 0, 0 ); + } + + priv->bInComposition = new_status; + ImmUnlockIMCC( ctx->hPrivate ); + +done: + ImmUnlockIMC( himc ); + + return 0; +} + static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { HIMC himc = (HIMC)GetWindowLongPtrW( hwnd, IMMGWL_IMC ); @@ -355,6 +395,8 @@ static LRESULT WINAPI ime_ui_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LP return ime_get_cursor_pos( himc ); case WM_WINE_IME_SET_CURSOR_POS: return ime_set_cursor_pos( himc, wparam ); + case WM_WINE_IME_SET_COMP_STATUS: + return ime_set_composition_status( himc, hwnd, wparam ); }
return DefWindowProcW( hwnd, msg, wparam, lparam ); diff --git a/dlls/imm32/imm_private.h b/dlls/imm32/imm_private.h index b10d04d77ff..876d92d4aa2 100644 --- a/dlls/imm32/imm_private.h +++ b/dlls/imm32/imm_private.h @@ -64,6 +64,7 @@ static const char *debugstr_wm_ime( UINT msg ) case WM_WINE_IME_SET_OPEN_STATUS: return "WM_WINE_IME_SET_OPEN_STATUS"; case WM_WINE_IME_GET_CURSOR_POS: return "WM_WINE_IME_GET_CURSOR_POS"; case WM_WINE_IME_SET_CURSOR_POS: return "WM_WINE_IME_SET_CURSOR_POS"; + case WM_WINE_IME_SET_COMP_STATUS: return "WM_WINE_IME_SET_COMP_STATUS"; default: if (msg == WM_MSIME_SERVICE) return "WM_MSIME_SERVICE"; else if (msg == WM_MSIME_RECONVERTOPTIONS) return "WM_MSIME_RECONVERTOPTIONS"; diff --git a/dlls/winex11.drv/dllmain.c b/dlls/winex11.drv/dllmain.c index 71fddfe4449..8445c21470e 100644 --- a/dlls/winex11.drv/dllmain.c +++ b/dlls/winex11.drv/dllmain.c @@ -30,7 +30,6 @@ static const callback_func callback_funcs[] = { x11drv_dnd_drop_event, x11drv_dnd_leave_event, - x11drv_ime_set_composition_status, x11drv_ime_update_association, };
diff --git a/dlls/winex11.drv/ime.c b/dlls/winex11.drv/ime.c index cbf9c488f3a..09440140568 100644 --- a/dlls/winex11.drv/ime.c +++ b/dlls/winex11.drv/ime.c @@ -755,37 +755,6 @@ BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp,
/* Interfaces to XIM and other parts of winex11drv */
-NTSTATUS x11drv_ime_set_composition_status( UINT open ) -{ - HIMC imc; - LPINPUTCONTEXT lpIMC; - LPIMEPRIVATE myPrivate; - - imc = RealIMC(FROM_X11); - lpIMC = ImmLockIMC(imc); - if (lpIMC == NULL) - return 0; - - myPrivate = ImmLockIMCC(lpIMC->hPrivate); - - if (open && !myPrivate->bInComposition) - { - GenerateIMEMessage(imc, WM_IME_STARTCOMPOSITION, 0, 0); - } - else if (!open && myPrivate->bInComposition) - { - ShowWindow(myPrivate->hwndDefault, SW_HIDE); - ImmDestroyIMCC(lpIMC->hCompStr); - lpIMC->hCompStr = ImeCreateBlankCompStr(); - GenerateIMEMessage(imc, WM_IME_ENDCOMPOSITION, 0, 0); - } - myPrivate->bInComposition = open; - - ImmUnlockIMCC(lpIMC->hPrivate); - ImmUnlockIMC(imc); - return 0; -} - NTSTATUS x11drv_ime_update_association( UINT arg ) { HWND focus = UlongToHandle( arg ); diff --git a/dlls/winex11.drv/unixlib.h b/dlls/winex11.drv/unixlib.h index e48a2c61669..484ed4e5725 100644 --- a/dlls/winex11.drv/unixlib.h +++ b/dlls/winex11.drv/unixlib.h @@ -96,7 +96,6 @@ enum client_callback { client_dnd_drop_event, client_dnd_leave_event, - client_ime_set_composition_status, client_ime_update_association, client_funcs_count }; diff --git a/dlls/winex11.drv/x11drv_dll.h b/dlls/winex11.drv/x11drv_dll.h index 5180969eeb7..a96e286ae83 100644 --- a/dlls/winex11.drv/x11drv_dll.h +++ b/dlls/winex11.drv/x11drv_dll.h @@ -36,7 +36,6 @@ extern NTSTATUS WINAPI x11drv_systray_change_owner( void *params, ULONG size ) D
extern NTSTATUS x11drv_dnd_drop_event( UINT arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_dnd_leave_event( UINT arg ) DECLSPEC_HIDDEN; -extern NTSTATUS x11drv_ime_set_composition_status( UINT arg ) DECLSPEC_HIDDEN; extern NTSTATUS x11drv_ime_update_association( UINT arg ) DECLSPEC_HIDDEN;
extern LRESULT WINAPI foreign_window_proc( HWND hwnd, UINT msg, WPARAM wparam, diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index 0b6ac637c64..69d90849b28 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -162,7 +162,7 @@ static int xic_preedit_start( XIC xic, XPointer user, XPointer arg )
TRACE( "xic %p, hwnd %p, arg %p\n", xic, hwnd, arg );
- x11drv_client_call( client_ime_set_composition_status, TRUE ); + send_ime_ui_message( hwnd, WM_WINE_IME_SET_COMP_STATUS, TRUE, 0 ); ximInComposeMode = TRUE; return -1; } @@ -179,7 +179,8 @@ static int xic_preedit_done( XIC xic, XPointer user, XPointer arg ) dwCompStringSize = 0; dwCompStringLength = 0; CompositionString = NULL; - x11drv_client_call( client_ime_set_composition_status, FALSE ); + + send_ime_ui_message( hwnd, WM_WINE_IME_SET_COMP_STATUS, FALSE, 0 ); return 0; }
diff --git a/include/ntuser.h b/include/ntuser.h index 6f7e206be86..77f383d12ac 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -495,6 +495,7 @@ enum wine_internal_message #define WM_WINE_IME_SET_OPEN_STATUS (WM_USER + 0) #define WM_WINE_IME_GET_CURSOR_POS (WM_USER + 1) #define WM_WINE_IME_SET_CURSOR_POS (WM_USER + 2) +#define WM_WINE_IME_SET_COMP_STATUS (WM_USER + 3)
/* internal IME private */ typedef struct ime_private
v3: Only introduce simple messages for now, keep handler logic closer to the old callbacks.
Kind of, composition string updates are indeed preceded by WM_IME_STARTCOMPOSITION but only if the composition status indicates that we were not composing already. I think that wasn't done before and I added it as I think WM_IME_COMPOSITION messages are only supposed to be sent between WM_IME_STARTCOMPOSITION and WM_IME_ENDCOMPOSITION.
I think this code will satisfy the condition. I don't insist on moving the code to xim_set_result_string(). ``` if (!old_status) ime_set_composition_status( himc, hwnd, TRUE ); ime_send_message( himc, WM_IME_COMPOSITION, len ? str[0] : 0, flags ); if (!old_status) ime_set_composition_status( himc, hwnd, FALSE ); ```
WM_IME_STARTCOMPOSITION > WM_IME_COMPOSITION(GCS_COMPSTR) (* n) > WM_IME_COMPOSITION(GCS_RESULTSTR) > WM_IME_ENDCOMPOSITION, is this wrong in any way?
For Chinese and common Japanese input, this is the correct order. Korean and "some Japanese input style" may have a slightly different order. The point is that there are situations where a result_string is immediately followed by a preedit_string.
callback example: ``` preedit_start preedit_draw x n result_string preedit_draw ```
In old code(x11drv_ime_set_result): ``` WM_IME_STARTCOMPOSITION WM_IME_COMPOSITION(GCS_COMPSTR) x n WM_IME_COMPOSITION(GCS_RESULTSTR) WM_IME_ENDCOMPOSITION WM_IME_COMPOSITION(GCS_COMPSTR) ``` This is a bad situation.
In this MR: ``` WM_IME_STARTCOMPOSITION WM_IME_STARTCOMPOSITION WM_IME_COMPOSITION(GCS_COMPSTR) WM_IME_STARTCOMPOSITION AND WM_IME_COMPOSITION(GCS_COMPSTR) x n-1 WM_IME_COMPOSITION(GCS_RESULTSTR) WM_IME_ENDCOMPOSITION WM_IME_STARTCOMPOSITION WM_IME_COMPOSITION(GCS_COMPSTR) ``` This is not bad because the last preedit_string is inside the composition_status. But it would be better if it came like this:
``` WM_IME_STARTCOMPOSITION WM_IME_COMPOSITION(GCS_COMPSTR) x n WM_IME_COMPOSITION(GCS_RESULTSTR) WM_IME_COMPOSITION(GCS_COMPSTR) ```
On Fri Apr 14 07:31:40 2023 +0000, Byeongsik Jeon wrote:
Kind of, composition string updates are indeed preceded by
WM_IME_STARTCOMPOSITION but only if the composition status indicates that we were not composing already. I think that wasn't done before and I added it as I think WM_IME_COMPOSITION messages are only supposed to be sent between WM_IME_STARTCOMPOSITION and WM_IME_ENDCOMPOSITION. I think this code will satisfy the condition. I don't insist on moving the code to xim_set_result_string().
if (!old_status) ime_set_composition_status( himc, hwnd, TRUE ); ime_send_message( himc, WM_IME_COMPOSITION, len ? str[0] : 0, flags ); if (!old_status) ime_set_composition_status( himc, hwnd, FALSE );
WM_IME_STARTCOMPOSITION > WM_IME_COMPOSITION(GCS_COMPSTR) (* n) >
WM_IME_COMPOSITION(GCS_RESULTSTR) > WM_IME_ENDCOMPOSITION, is this wrong in any way? For Chinese and common Japanese input, this is the correct order. Korean and "some Japanese input style" may have a slightly different order. The point is that there are situations where a result_string is immediately followed by a preedit_string. callback example:
preedit_start preedit_draw x n result_string preedit_draw
In old code(x11drv_ime_set_result):
WM_IME_STARTCOMPOSITION WM_IME_COMPOSITION(GCS_COMPSTR) x n WM_IME_COMPOSITION(GCS_RESULTSTR) WM_IME_ENDCOMPOSITION WM_IME_COMPOSITION(GCS_COMPSTR)
This is a bad situation. In this MR:
WM_IME_STARTCOMPOSITION WM_IME_STARTCOMPOSITION WM_IME_COMPOSITION(GCS_COMPSTR) WM_IME_STARTCOMPOSITION AND WM_IME_COMPOSITION(GCS_COMPSTR) x n-1 WM_IME_COMPOSITION(GCS_RESULTSTR) WM_IME_ENDCOMPOSITION WM_IME_STARTCOMPOSITION WM_IME_COMPOSITION(GCS_COMPSTR)
This is not bad because the last preedit_string is inside the composition_status. But it would be better if it came like this:
WM_IME_STARTCOMPOSITION WM_IME_COMPOSITION(GCS_COMPSTR) x n WM_IME_COMPOSITION(GCS_RESULTSTR) WM_IME_COMPOSITION(GCS_COMPSTR)
I see, I'll try to keep that in mind.
If you have an example of how to reproduce this kind of sequence using Linux IME I'd be very grateful. I don't read any CJK language and I'm mostly just playing around with various IBus engines and checking that it produces some result that look sensible, so if you know which IBus engine I can use, and maybe a simple combination of key that trigger this?
In this MR:
WM_IME_STARTCOMPOSITION WM_IME_STARTCOMPOSITION WM_IME_COMPOSITION(GCS_COMPSTR) WM_IME_STARTCOMPOSITION AND WM_IME_COMPOSITION(GCS_COMPSTR) x n-1 WM_IME_COMPOSITION(GCS_RESULTSTR) WM_IME_ENDCOMPOSITION WM_IME_STARTCOMPOSITION WM_IME_COMPOSITION(GCS_COMPSTR)
Yeah, it's not how I intended it anyway, I'll have a better look.
If you have an example of how to reproduce this kind of sequence using Linux IME I'd be very grateful. I don't read any CJK language and I'm mostly just playing around with various IBus engines and checking that it produces some result that look sensible, so if you know which IBus engine I can use, and maybe a simple combination of key that trigger this?
== Korean ==
If it does, I'd be very grateful. You can test it with ibus-hangul. ``` $ gsettings reset-recursively org.freedesktop.ibus.engine.hangul $ gsettings set org.freedesktop.ibus.engine.hangul auto-reorder false $ gsettings set org.freedesktop.ibus.engine.hangul hangul-keyboard ro # Romanji $ gsettings set org.freedesktop.ibus.engine.hangul initial-input-mode hangul ``` This setting allows you to type Hangul in Romanji keymap input mode, without any additional hotkey setting.
The test string is "가나다". The pronunciation is "GA NA DA" and the keystroke sequence is "ganada".
1. 'g' - preedit_start - preedit_draw 'ㄱ' 2. 'a' - preedit_draw '가' 3. 'n' - preedit_draw '간' 4. 'a' - result_string '가' - preedit_draw '나' 5. 'd' - preedit_draw '낟' 6. 'a' - result_string '나' - preedit_draw '다'
MS Korean IME works in this order, and 'fcitx5-hangul' worked in this order when I tested it.
ibus-hangul is complicated in step 4 and 6. 'UIM' also works in this order.
4. 'a' - preedit_draw ''(null) - preedit_done - result_string '가' - preedit_start - preedit_draw '나'
I think that by separating the preedit_string and result_string sections, it will work for applications that don't consider Korean input. In fact, I recently saw a case where a newly developed rust-based IM changed the code to this complex pattern because of some issues.
Because "result_string '가'" is outside of composition_status, Wine wraps it around with WM_IME_STARTCOMPOSITION, WM_IME_ENDCOMPOSITION, and ImmSetOpenStatus.
Unfortunately, the 2 calls are amplified into 9 calls. But, it's OK.
== Japanese ==
The step.4 pattern can also be reproduced on Japanese input. It can be tested on ibus-anthy, ibus-mozc.
The test string is "日本語の". There are various ways to get this string. Here, the test romanji keystroke sequence is "nihongo<SPACE>no". Let's assume the conversion key is <SPACE>.
1. 'n' - preedit_start - preedit_draw 'n' 2. 'hongo' - preedit_draw 'にほんご' 3. '<SPACE>' - preedit_draw '日本語' 4. 'n' - result_string '日本語' - preedit_draw 'n' 5. 'o' - preedit_draw 'の'
In Japanese input, you can avoid the step.4 pattern by typing in a different way. But it seems to be a real issue. #53860
I'm attaching two patches that fixes some issues. I think it will be helpful for your testing. If you think it would be valuable, please apply.
[0001-winex11-Call-event-handlers-immediately-on-non-merge.patch](/uploads/873e0e8d9f9168d4747c386b34c4e591/0001-winex11-Call-event-handlers-immediately-on-non-merge.patch)
[0002-winex11-Use-NtUserGetAncestor-for-the-foreign-window.patch](/uploads/6685e01c87f0abe5555c78aed54819f3/0002-winex11-Use-NtUserGetAncestor-for-the-foreign-window.patch)
I'm attaching an enhanced patchset that appears to have no input issues in the edit control. I hope this helps with testing and helps you figure out the problem.
[53860.patch](/uploads/4742eeeea259622c1ffb61a91d2682b6/53860.patch)
Thanks a lot @bsjeon! I'll try to make this work.