Based on the wine-staging patch winex11-CandidateWindowPos from Felix Yan with works from Muneyuki Noguchi and Sebastian Lackner.
-- v3: win32u: Set host IME composition window position in NtUserShowCaret(). win32u: Set host IME composition window position in set_caret_pos(). win32u: Support setting host IME composition window position for ImmSetCompositionWindow().
From: Zhiyi Zhang zzhang@codeweavers.com
Based on the wine-staging patch winex11-CandidateWindowPos from Felix Yan with work from Muneyuki Noguchi and Sebastian Lackner. --- dlls/imm32/imm.c | 16 ++++++++++++++++ dlls/win32u/driver.c | 8 ++++++++ dlls/win32u/input.c | 15 +++++++++++++++ dlls/win32u/sysparams.c | 3 +++ dlls/win32u/win32u_private.h | 1 + dlls/winex11.drv/init.c | 1 + dlls/winex11.drv/x11drv.h | 1 + dlls/winex11.drv/xim.c | 33 +++++++++++++++++++++++++++++++++ include/ntuser.h | 1 + include/wine/gdi_driver.h | 4 +++- 10 files changed, 82 insertions(+), 1 deletion(-)
diff --git a/dlls/imm32/imm.c b/dlls/imm32/imm.c index cab531e4342..661a08c0d8c 100644 --- a/dlls/imm32/imm.c +++ b/dlls/imm32/imm.c @@ -2637,6 +2637,7 @@ BOOL WINAPI ImmSetCompositionStringW( BOOL WINAPI ImmSetCompositionWindow( HIMC himc, COMPOSITIONFORM *composition ) { INPUTCONTEXT *ctx; + POINT point;
TRACE( "himc %p, composition %s\n", himc, debugstr_composition( composition ) );
@@ -2649,6 +2650,21 @@ BOOL WINAPI ImmSetCompositionWindow( HIMC himc, COMPOSITIONFORM *composition ) ImmNotifyIME( himc, NI_CONTEXTUPDATED, 0, IMC_SETCOMPOSITIONWINDOW ); SendMessageW( ctx->hWnd, WM_IME_NOTIFY, IMN_SETCOMPOSITIONWINDOW, 0 );
+ if (composition->dwStyle & (CFS_RECT | CFS_POINT | CFS_FORCE_POSITION)) + { + if (composition->dwStyle & CFS_RECT) + { + point.x = composition->rcArea.left; + point.y = composition->rcArea.top; + } + else + { + point = composition->ptCurrentPos; + } + + NtUserCallTwoParam( (ULONG_PTR)ctx->hWnd, (ULONG_PTR)&point, NtUserCallTwoParam_SetIMECompositionWindowPos ); + } + ImmUnlockIMC( himc );
return TRUE; diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index ca31eec5f49..87b2971216d 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -914,6 +914,11 @@ static void nulldrv_ThreadDetach( void ) { }
+static BOOL nulldrv_SetIMECompositionWindowPos( HWND hwnd, const POINT *point ) +{ + return FALSE; +} + static const WCHAR guid_key_prefixW[] = { '\','R','e','g','i','s','t','r','y', @@ -1294,6 +1299,8 @@ static const struct user_driver_funcs lazy_load_driver = nulldrv_wine_get_wgl_driver, /* thread management */ nulldrv_ThreadDetach, + /* IME support */ + nulldrv_SetIMECompositionWindowPos, };
const struct user_driver_funcs *user_driver = &lazy_load_driver; @@ -1378,6 +1385,7 @@ void __wine_set_user_driver( const struct user_driver_funcs *funcs, UINT version SET_USER_FUNC(VulkanInit); SET_USER_FUNC(wine_get_wgl_driver); SET_USER_FUNC(ThreadDetach); + SET_USER_FUNC(SetIMECompositionWindowPos); #undef SET_USER_FUNC
prev = InterlockedCompareExchangePointer( (void **)&user_driver, driver, (void *)&lazy_load_driver ); diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 1cf174760da..489632fe1d1 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2351,6 +2351,21 @@ BOOL WINAPI NtUserGetCaretPos( POINT *pt ) return ret; }
+BOOL set_ime_composition_window_pos( HWND hwnd, const POINT *point ) +{ + HWND root_hwnd; + POINT pt; + + if (!NtUserIsWindow( hwnd )) + return FALSE; + + root_hwnd = NtUserGetAncestor( hwnd, GA_ROOT ); + pt = *point; + NtUserMapWindowPoints( hwnd, root_hwnd, &pt, 1, 0 /* per-monitor DPI */ ); + + return user_driver->pSetIMECompositionWindowPos( root_hwnd, &pt ); +} + /******************************************************************* * set_caret_pos */ diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 094a963d305..a5e6a244264 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -6562,6 +6562,9 @@ ULONG_PTR WINAPI NtUserCallTwoParam( ULONG_PTR arg1, ULONG_PTR arg2, ULONG code case NtUserCallTwoParam_SetIconParam: return set_icon_param( UlongToHandle(arg1), arg2 );
+ case NtUserCallTwoParam_SetIMECompositionWindowPos: + return set_ime_composition_window_pos( UlongToHandle(arg1), (const POINT *)arg2 ); + case NtUserCallTwoParam_UnhookWindowsHook: return unhook_windows_hook( arg1, (HOOKPROC)arg2 );
diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index c4521899299..47b50c87f14 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -102,6 +102,7 @@ extern BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret ); extern BOOL set_caret_blink_time( unsigned int time ); extern BOOL set_caret_pos( int x, int y ); extern BOOL set_foreground_window( HWND hwnd, BOOL mouse ); +extern BOOL set_ime_composition_window_pos( HWND hwnd, const POINT *point ); extern void toggle_caret( HWND hwnd ); extern void update_mouse_tracking_info( HWND hwnd ); extern BOOL get_clip_cursor( RECT *rect, UINT dpi ); diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c index 297a451f82b..3b919335624 100644 --- a/dlls/winex11.drv/init.c +++ b/dlls/winex11.drv/init.c @@ -430,6 +430,7 @@ static const struct user_driver_funcs x11drv_funcs = .pVulkanInit = X11DRV_VulkanInit, .pwine_get_wgl_driver = X11DRV_wine_get_wgl_driver, .pThreadDetach = X11DRV_ThreadDetach, + .pSetIMECompositionWindowPos = X11DRV_SetIMECompositionWindowPos, };
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 61d532d9ba1..72527c7b92d 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -250,6 +250,7 @@ extern void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flag extern BOOL X11DRV_SystemParametersInfo( UINT action, UINT int_param, void *ptr_param, UINT flags ); extern void X11DRV_ThreadDetach(void); +extern BOOL X11DRV_SetIMECompositionWindowPos( HWND hwnd, const POINT *point );
/* X11 driver internal functions */
diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c index c6a93eb5e16..1bb20c845f3 100644 --- a/dlls/winex11.drv/xim.c +++ b/dlls/winex11.drv/xim.c @@ -488,3 +488,36 @@ void xim_set_focus( HWND hwnd, BOOL focus ) if (focus) XSetICFocus( xic ); else XUnsetICFocus( xic ); } + +BOOL X11DRV_SetIMECompositionWindowPos( HWND hwnd, const POINT *point ) +{ + struct x11drv_win_data *data = NULL; + XVaNestedList attr; + XPoint xpoint; + POINT pt; + + if (!(input_style & XIMPreeditPosition)) + return FALSE; + + if (!(data = get_win_data( hwnd )) || !data->xic) + { + if (data) release_win_data( data ); + return FALSE; + } + + pt = *point; + if (NtUserGetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) + pt.x = data->client_rect.right - data->client_rect.left - 1 - pt.x; + + xpoint.x = pt.x + data->client_rect.left - data->whole_rect.left; + xpoint.y = pt.y + data->client_rect.top - data->whole_rect.top; + attr = XVaCreateNestedList( 0, XNSpotLocation, &xpoint, NULL ); + if (attr) + { + XSetICValues( data->xic, XNPreeditAttributes, attr, NULL ); + XFree( attr ); + } + + release_win_data( data ); + return TRUE; +} diff --git a/include/ntuser.h b/include/ntuser.h index 61dfd7697c2..adeb56afe79 100644 --- a/include/ntuser.h +++ b/include/ntuser.h @@ -1053,6 +1053,7 @@ enum NtUserCallTwoParam_MonitorFromRect, NtUserCallTwoParam_SetCaretPos, NtUserCallTwoParam_SetIconParam, + NtUserCallTwoParam_SetIMECompositionWindowPos, NtUserCallTwoParam_UnhookWindowsHook, NtUserCallTwoParam_AdjustWindowRect, NtUserCallTwoParam_IsWindowRectFullScreen, diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index feda5594234..999f958365d 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -193,7 +193,7 @@ struct gdi_dc_funcs };
/* increment this when you change the DC function table */ -#define WINE_GDI_DRIVER_VERSION 93 +#define WINE_GDI_DRIVER_VERSION 94
#define GDI_PRIORITY_NULL_DRV 0 /* null driver */ #define GDI_PRIORITY_FONT_DRV 100 /* any font driver */ @@ -370,6 +370,8 @@ struct user_driver_funcs struct opengl_funcs * (*pwine_get_wgl_driver)(UINT); /* thread management */ void (*pThreadDetach)(void); + /* IME support */ + BOOL (*pSetIMECompositionWindowPos)(HWND, const POINT *); };
W32KAPI void __wine_set_user_driver( const struct user_driver_funcs *funcs, UINT version );
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/win32u/input.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 489632fe1d1..8ba0544d504 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2374,6 +2374,7 @@ BOOL set_caret_pos( int x, int y ) int old_state = 0; int hidden = 0; HWND hwnd = 0; + POINT pt; BOOL ret; RECT r;
@@ -2403,7 +2404,10 @@ BOOL set_caret_pos( int x, int y ) r.bottom += y - r.top; r.left = x; r.top = y; + pt.x = x; + pt.y = y; display_caret( hwnd, &r ); + set_ime_composition_window_pos( hwnd, &pt ); NtUserSetSystemTimer( hwnd, SYSTEM_TIMER_CARET, caret.timeout ); } return ret;
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/win32u/input.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 8ba0544d504..0324909c954 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2419,6 +2419,7 @@ BOOL set_caret_pos( int x, int y ) BOOL WINAPI NtUserShowCaret( HWND hwnd ) { int hidden = 0; + POINT pt; BOOL ret; RECT r;
@@ -2441,7 +2442,10 @@ BOOL WINAPI NtUserShowCaret( HWND hwnd )
if (ret && hidden == 1) /* hidden was 1 so it's now 0 */ { + pt.x = r.left; + pt.y = r.top; display_caret( hwnd, &r ); + set_ime_composition_window_pos( hwnd, &pt ); NtUserSetSystemTimer( hwnd, SYSTEM_TIMER_CARET, caret.timeout ); } return ret;
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=147854
Your paranoid android.
=== debian11b (64 bit WoW report) ===
dinput: joystick8.c:5762: Test failed: input 1: WaitForSingleObject returned 0x102 joystick8.c:5763: Test failed: input 1: got 0 WM_INPUT messages joystick8.c:5766: Test failed: input 1: got dwType 0 joystick8.c:5767: Test failed: input 1: got header.dwSize 0 joystick8.c:5769: Test failed: input 1: got hDevice 0000000000000000 joystick8.c:5771: Test failed: input 1: got dwSizeHid 0 joystick8.c:5772: Test failed: input 1: got dwCount 0
This merge request was approved by Rémi Bernon.