From bfbda74851ae4de3b0b185862af566005b477930 Mon Sep 17 00:00:00 2001
From: Felix Yan <felixonmars@gmail.com>
Date: Tue, 23 Sep 2014 23:22:17 +0800
Subject: winex11.drv: Update a candidate window's position with over-the-spot
 style. (try 2)

In the current implementation, the candidate window position of a input
method is fixed because XNSpotLocation isn't updated after an input
context (XIC) is created in X11DRV_CreateIC().
X11DRV_UpdateCandidatePos() in this patch updates the position. You can
see the change of a position with ibus, scim or fcitx when input style
is set to "over the spot" in the registry key:

[HKEY_CURRENT_USER\Software\Wine\X11 Driver]
"InputStyle"="OverTheSpot"

This patch was based on the original work by Muneyuki Noguchi, and
received a lot of help from Sebastian Lackner.
---
 dlls/user32/caret.c               |  4 +++
 dlls/user32/driver.c              | 14 ++++++++
 dlls/user32/user_private.h        |  2 ++
 dlls/winex11.drv/winex11.drv.spec |  1 +
 dlls/winex11.drv/xim.c            | 72 ++++++++++++++++++++++++++++++++++++++-
 5 files changed, 92 insertions(+), 1 deletion(-)

diff --git a/dlls/user32/caret.c b/dlls/user32/caret.c
index 686dade..d1cde7f 100644
--- a/dlls/user32/caret.c
+++ b/dlls/user32/caret.c
@@ -30,6 +30,8 @@
 #include "winbase.h"
 #include "wingdi.h"
 #include "winuser.h"
+#include "user_private.h"
+
 #include "wine/server.h"
 #include "wine/debug.h"
 
@@ -277,6 +279,7 @@ BOOL WINAPI SetCaretPos( INT x, INT y )
         r.left = x;
         r.top = y;
         CARET_DisplayCaret( hwnd, &r );
+        USER_Driver->pUpdateCandidatePos( hwnd, &r );
         SetSystemTimer( hwnd, TIMERID, Caret.timeout, CARET_Callback );
     }
     return ret;
@@ -355,6 +358,7 @@ BOOL WINAPI ShowCaret( HWND hwnd )
     if (ret && (hidden == 1))  /* hidden was 1 so it's now 0 */
     {
         CARET_DisplayCaret( hwnd, &r );
+        USER_Driver->pUpdateCandidatePos( hwnd, &r );
         SetSystemTimer( hwnd, TIMERID, Caret.timeout, CARET_Callback );
     }
     return ret;
diff --git a/dlls/user32/driver.c b/dlls/user32/driver.c
index 32acf28..33648b8 100644
--- a/dlls/user32/driver.c
+++ b/dlls/user32/driver.c
@@ -155,6 +155,7 @@ static const USER_DRIVER *load_driver(void)
         GET_USER_FUNC(WindowPosChanging);
         GET_USER_FUNC(WindowPosChanged);
         GET_USER_FUNC(SystemParametersInfo);
+        GET_USER_FUNC(UpdateCandidatePos);
         GET_USER_FUNC(ThreadDetach);
 #undef GET_USER_FUNC
     }
@@ -513,6 +514,10 @@ static BOOL CDECL nulldrv_SystemParametersInfo( UINT action, UINT int_param, voi
     return FALSE;
 }
 
+static void CDECL nulldrv_UpdateCandidatePos( HWND hwnd, const RECT *caret_rect )
+{
+}
+
 static void CDECL nulldrv_ThreadDetach( void )
 {
 }
@@ -578,6 +583,8 @@ static USER_DRIVER null_driver =
     nulldrv_WindowPosChanged,
     /* system parameters */
     nulldrv_SystemParametersInfo,
+    /* candidate pos functions */
+    nulldrv_UpdateCandidatePos,
     /* thread management */
     nulldrv_ThreadDetach
 };
@@ -774,6 +781,11 @@ static BOOL CDECL loaderdrv_UpdateLayeredWindow( HWND hwnd, const UPDATELAYEREDW
     return load_driver()->pUpdateLayeredWindow( hwnd, info, window_rect );
 }
 
+static void CDECL loaderdrv_UpdateCandidatePos( HWND hwnd, const RECT *caret_rect )
+{
+    load_driver()->pUpdateCandidatePos( hwnd, caret_rect );
+}
+
 static USER_DRIVER lazy_load_driver =
 {
     /* keyboard functions */
@@ -835,6 +847,8 @@ static USER_DRIVER lazy_load_driver =
     nulldrv_WindowPosChanged,
     /* system parameters */
     nulldrv_SystemParametersInfo,
+    /* candidate pos functions */
+    loaderdrv_UpdateCandidatePos,
     /* thread management */
     nulldrv_ThreadDetach
 };
diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h
index 68aa699..cba4aa9 100644
--- a/dlls/user32/user_private.h
+++ b/dlls/user32/user_private.h
@@ -117,6 +117,8 @@ typedef struct tagUSER_DRIVER {
     void   (CDECL *pWindowPosChanged)(HWND,HWND,UINT,const RECT *,const RECT *,const RECT *,const RECT *,struct window_surface*);
     /* system parameters */
     BOOL   (CDECL *pSystemParametersInfo)(UINT,UINT,void*,UINT);
+    /* candidate pos functions */
+    void   (CDECL *pUpdateCandidatePos)(HWND,const RECT *);
     /* thread management */
     void   (CDECL *pThreadDetach)(void);
 } USER_DRIVER;
diff --git a/dlls/winex11.drv/winex11.drv.spec b/dlls/winex11.drv/winex11.drv.spec
index ceb0f56..5552bdb 100644
--- a/dlls/winex11.drv/winex11.drv.spec
+++ b/dlls/winex11.drv/winex11.drv.spec
@@ -53,6 +53,7 @@
 @ cdecl WindowPosChanging(long long long ptr ptr ptr ptr) X11DRV_WindowPosChanging
 @ cdecl WindowPosChanged(long long long ptr ptr ptr ptr ptr) X11DRV_WindowPosChanged
 @ cdecl SystemParametersInfo(long long ptr long) X11DRV_SystemParametersInfo
+@ cdecl UpdateCandidatePos(long ptr) X11DRV_UpdateCandidatePos
 @ cdecl ThreadDetach() X11DRV_ThreadDetach
 
 # WinTab32
diff --git a/dlls/winex11.drv/xim.c b/dlls/winex11.drv/xim.c
index e51169d..069115d 100644
--- a/dlls/winex11.drv/xim.c
+++ b/dlls/winex11.drv/xim.c
@@ -31,6 +31,7 @@
 #include "x11drv.h"
 #include "imm.h"
 #include "wine/debug.h"
+#include "wine/server.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(xim);
 
@@ -471,6 +472,48 @@ static BOOL X11DRV_DestroyIC(XIC xic, XPointer p, XPointer data)
     return TRUE;
 }
 
+/***********************************************************************
+ *           X11DRV_UpdateCandidatePos
+ */
+void X11DRV_UpdateCandidatePos( HWND hwnd, const RECT *caret_rect )
+{
+    if (ximStyle & XIMPreeditPosition)
+    {
+        struct x11drv_win_data *data;
+        HWND parent;
+
+        for (parent = hwnd; parent && parent != GetDesktopWindow(); parent = GetAncestor( parent, GA_PARENT ))
+        {
+            if (!(data = get_win_data( parent ))) continue;
+            if (data->xic)
+            {
+                XVaNestedList preedit;
+                XPoint xpoint;
+                POINT pt;
+
+                pt.x = caret_rect->left;
+                pt.y = caret_rect->bottom;
+
+                if (hwnd != data->hwnd)
+                    MapWindowPoints( hwnd, data->hwnd, &pt, 1 );
+
+                if (GetWindowLongW( data->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;
+
+                preedit = XVaCreateNestedList( 0, XNSpotLocation, &xpoint, NULL );
+                if (preedit)
+                {
+                    XSetICValues( data->xic, XNPreeditAttributes, preedit, NULL );
+                    XFree( preedit );
+                }
+            }
+            release_win_data( data );
+        }
+    }
+}
 
 XIC X11DRV_CreateIC(XIM xim, struct x11drv_win_data *data)
 {
@@ -498,7 +541,7 @@ XIC X11DRV_CreateIC(XIM xim, struct x11drv_win_data *data)
                         XNDestroyCallback, &destroy,
                         NULL);
         data->xic = xic;
-        return xic;
+        goto return_xic;
     }
 
     /* create callbacks */
@@ -596,5 +639,32 @@ XIC X11DRV_CreateIC(XIM xim, struct x11drv_win_data *data)
     if (status != NULL)
         XFree(status);
 
+return_xic:
+    if (xic != NULL && (ximStyle & XIMPreeditPosition))
+    {
+        SERVER_START_REQ( set_caret_info )
+        {
+            req->flags  = 0;  /* don't set anything */
+            req->handle = 0;
+            req->x      = 0;
+            req->y      = 0;
+            req->hide   = 0;
+            req->state  = 0;
+            if (!wine_server_call_err( req ))
+            {
+                HWND hwnd;
+                RECT r;
+
+                hwnd      = wine_server_ptr_handle( reply->full_handle );
+                r.left    = reply->old_rect.left;
+                r.top     = reply->old_rect.top;
+                r.right   = reply->old_rect.right;
+                r.bottom  = reply->old_rect.bottom;
+                X11DRV_UpdateCandidatePos( hwnd, &r );
+            }
+        }
+        SERVER_END_REQ;
+    }
+
     return xic;
 }
-- 
2.9.0

