Based on the wine-staging patch winex11-CandidateWindowPos from Felix Yan with works from Muneyuki Noguchi and Sebastian Lackner.
-- v2: 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 362f32b93d7..a13afb6d20f 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -913,6 +913,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', @@ -1293,6 +1298,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; @@ -1376,6 +1383,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 afa75928c94..1b830722128 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -6541,6 +6541,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 fc0f0c62c93..a1f11215294 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 4a78073cb9b..4a01aca7e28 100644 --- a/dlls/winex11.drv/init.c +++ b/dlls/winex11.drv/init.c @@ -429,6 +429,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 eddbc74a726..97d379c6341 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -253,6 +253,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 4148c8cf0b6..70efa44f85c 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 8eec0ada4ca..ff03c69847f 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -179,7 +179,7 @@ struct gdi_dc_funcs };
/* increment this when you change the DC function table */ -#define WINE_GDI_DRIVER_VERSION 88 +#define WINE_GDI_DRIVER_VERSION 89
#define GDI_PRIORITY_NULL_DRV 0 /* null driver */ #define GDI_PRIORITY_FONT_DRV 100 /* any font driver */ @@ -356,6 +356,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 full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=147853
Your paranoid android.
=== debian11 (build log) ===
error: patch failed: include/wine/gdi_driver.h:179 Task: Patch failed to apply
=== debian11b (build log) ===
error: patch failed: include/wine/gdi_driver.h:179 Task: Patch failed to apply