| /* | 
 |  * The IME for interfacing with XIM | 
 |  * | 
 |  * Copyright 2008 CodeWeavers, Aric Stewart | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  * | 
 |  * This library is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 |  * Lesser General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU Lesser General Public | 
 |  * License along with this library; if not, write to the Free Software | 
 |  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
 |  */ | 
 |  | 
 | /* | 
 |  * Notes: | 
 |  *  The normal flow for IMM/IME Processing is as follows. | 
 |  * 1) The Keyboard Driver generates key messages which are first passed to | 
 |  *    the IMM and then to IME via ImeProcessKey. If the IME returns 0  then | 
 |  *    it does not want the key and the keyboard driver then generates the | 
 |  *    WM_KEYUP/WM_KEYDOWN messages.  However if the IME is going to process the | 
 |  *    key it returns non-zero. | 
 |  * 2) If the IME is going to process the key then the IMM calls ImeToAsciiEx to | 
 |  *    process the key.  the IME modifies the HIMC structure to reflect the | 
 |  *    current state and generates any messages it needs the IMM to process. | 
 |  * 3) IMM checks the messages and send them to the application in question. From | 
 |  *    here the IMM level deals with if the application is IME aware or not. | 
 |  * | 
 |  *  This flow does not work well for the X11 driver and XIM. | 
 |  *   (It works fine for Mac) | 
 |  *  As such we will have to reroute step 1.  Instead the x11drv driver will | 
 |  *  generate an XIM events and call directly into this IME implimenetaion. | 
 |  *  As such we will have to use the alternative ImmGenerateMessage path to be | 
 |  *  generate the messages that we want the IMM layer to send to the application. | 
 |  */ | 
 |  | 
 | #include "config.h" | 
 |  | 
 | #include <stdarg.h> | 
 | #include "windef.h" | 
 | #include "winbase.h" | 
 | #include "wingdi.h" | 
 | #include "winuser.h" | 
 | #include "winerror.h" | 
 | #include "wine/debug.h" | 
 | #include "imm.h" | 
 | #include "ddk/imm.h" | 
 | #include "winnls.h" | 
 | #include "x11drv.h" | 
 |  | 
 | WINE_DEFAULT_DEBUG_CHANNEL(imm); | 
 |  | 
 | #define FROM_X11 ((HIMC)0xcafe1337) | 
 |  | 
 | typedef struct _IMEPRIVATE { | 
 |     BOOL bInComposition; | 
 |     BOOL bInternalState; | 
 |     HFONT textfont; | 
 |     HWND hwndDefault; | 
 | } IMEPRIVATE, *LPIMEPRIVATE; | 
 |  | 
 | typedef struct _tagTRANSMSG { | 
 |     UINT message; | 
 |     WPARAM wParam; | 
 |     LPARAM lParam; | 
 | } TRANSMSG, *LPTRANSMSG; | 
 |  | 
 | static const WCHAR UI_CLASS_NAME[] = {'W','i','n','e','X','1','1','I','M','E',0}; | 
 |  | 
 | static HIMC *hSelectedFrom = NULL; | 
 | static INT  hSelectedCount = 0; | 
 |  | 
 | /* MSIME messages */ | 
 | static UINT WM_MSIME_SERVICE; | 
 | static UINT WM_MSIME_RECONVERTOPTIONS; | 
 | static UINT WM_MSIME_MOUSE; | 
 | static UINT WM_MSIME_RECONVERTREQUEST; | 
 | static UINT WM_MSIME_RECONVERT; | 
 | static UINT WM_MSIME_QUERYPOSITION; | 
 | static UINT WM_MSIME_DOCUMENTFEED; | 
 |  | 
 | static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, | 
 |                                           LPARAM lParam); | 
 | static void UpdateDataInDefaultIMEWindow(HIMC hHIMC, HWND hwnd, BOOL showable); | 
 |  | 
 | static HIMC RealIMC(HIMC hIMC) | 
 | { | 
 |     if (hIMC == FROM_X11) | 
 |     { | 
 |         INT i; | 
 |         HWND wnd = GetFocus(); | 
 |         HIMC winHimc = ImmGetContext(wnd); | 
 |         for (i = 0; i < hSelectedCount; i++) | 
 |             if (winHimc == hSelectedFrom[i]) | 
 |                 return winHimc; | 
 |         return NULL; | 
 |     } | 
 |     else | 
 |         return hIMC; | 
 | } | 
 |  | 
 | static LPINPUTCONTEXT LockRealIMC(HIMC hIMC) | 
 | { | 
 |     HIMC real_imc = RealIMC(hIMC); | 
 |     if (real_imc) | 
 |         return ImmLockIMC(real_imc); | 
 |     else | 
 |         return NULL; | 
 | } | 
 |  | 
 | static BOOL UnlockRealIMC(HIMC hIMC) | 
 | { | 
 |     HIMC real_imc = RealIMC(hIMC); | 
 |     if (real_imc) | 
 |         return ImmUnlockIMC(real_imc); | 
 |     else | 
 |         return FALSE; | 
 | } | 
 |  | 
 | static void IME_RegisterClasses(void) | 
 | { | 
 |     static int done; | 
 |     WNDCLASSW wndClass; | 
 |  | 
 |     if (done) return; | 
 |     done = 1; | 
 |  | 
 |     ZeroMemory(&wndClass, sizeof(WNDCLASSW)); | 
 |     wndClass.style = CS_GLOBALCLASS | CS_IME | CS_HREDRAW | CS_VREDRAW; | 
 |     wndClass.lpfnWndProc = IME_WindowProc; | 
 |     wndClass.cbClsExtra = 0; | 
 |     wndClass.cbWndExtra = 2 * sizeof(LONG_PTR); | 
 |     wndClass.hInstance = x11drv_module; | 
 |     wndClass.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_ARROW); | 
 |     wndClass.hIcon = LoadIconW(NULL, (LPWSTR)IDI_APPLICATION); | 
 |     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW +1); | 
 |     wndClass.lpszMenuName   = 0; | 
 |     wndClass.lpszClassName = UI_CLASS_NAME; | 
 |  | 
 |     RegisterClassW(&wndClass); | 
 |  | 
 |     WM_MSIME_SERVICE = RegisterWindowMessageA("MSIMEService"); | 
 |     WM_MSIME_RECONVERTOPTIONS = RegisterWindowMessageA("MSIMEReconvertOptions"); | 
 |     WM_MSIME_MOUSE = RegisterWindowMessageA("MSIMEMouseOperation"); | 
 |     WM_MSIME_RECONVERTREQUEST = RegisterWindowMessageA("MSIMEReconvertRequest"); | 
 |     WM_MSIME_RECONVERT = RegisterWindowMessageA("MSIMEReconvert"); | 
 |     WM_MSIME_QUERYPOSITION = RegisterWindowMessageA("MSIMEQueryPosition"); | 
 |     WM_MSIME_DOCUMENTFEED = RegisterWindowMessageA("MSIMEDocumentFeed"); | 
 | } | 
 |  | 
 | void IME_UnregisterClasses(void) | 
 | { | 
 |     UnregisterClassW(UI_CLASS_NAME, x11drv_module); | 
 | } | 
 |  | 
 | static HIMCC ImeCreateBlankCompStr(void) | 
 | { | 
 |     HIMCC rc; | 
 |     LPCOMPOSITIONSTRING ptr; | 
 |     rc = ImmCreateIMCC(sizeof(COMPOSITIONSTRING)); | 
 |     ptr = ImmLockIMCC(rc); | 
 |     memset(ptr,0,sizeof(COMPOSITIONSTRING)); | 
 |     ptr->dwSize = sizeof(COMPOSITIONSTRING); | 
 |     ImmUnlockIMCC(rc); | 
 |     return rc; | 
 | } | 
 |  | 
 | static int updateField(DWORD origLen, DWORD origOffset, DWORD currentOffset, | 
 |                        LPBYTE target, LPBYTE source, DWORD* lenParam, | 
 |                        DWORD* offsetParam, BOOL wchars ) | 
 | { | 
 |      if (origLen > 0 && origOffset > 0) | 
 |      { | 
 |         int truelen = origLen; | 
 |         if (wchars) | 
 |             truelen *= sizeof(WCHAR); | 
 |  | 
 |         memcpy(&target[currentOffset], &source[origOffset], truelen); | 
 |  | 
 |         *lenParam = origLen; | 
 |         *offsetParam = currentOffset; | 
 |         currentOffset += truelen; | 
 |      } | 
 |      return currentOffset; | 
 | } | 
 |  | 
 | static HIMCC updateCompStr(HIMCC old, LPWSTR compstr, DWORD len) | 
 | { | 
 |     /* we need to make sure the CompStr, CompClaus and CompAttr fields are all | 
 |      * set and correct */ | 
 |     int needed_size; | 
 |     HIMCC   rc; | 
 |     LPBYTE newdata = NULL; | 
 |     LPBYTE olddata = NULL; | 
 |     LPCOMPOSITIONSTRING new_one; | 
 |     LPCOMPOSITIONSTRING lpcs = NULL; | 
 |     INT current_offset = 0; | 
 |  | 
 |     TRACE("%s, %i\n",debugstr_wn(compstr,len),len); | 
 |  | 
 |     if (old == NULL && compstr == NULL && len == 0) | 
 |         return NULL; | 
 |  | 
 |     if (compstr == NULL && len != 0) | 
 |     { | 
 |         ERR("compstr is NULL however we have a len!  Please report\n"); | 
 |         len = 0; | 
 |     } | 
 |  | 
 |     if (old != NULL) | 
 |     { | 
 |         olddata = ImmLockIMCC(old); | 
 |         lpcs = (LPCOMPOSITIONSTRING)olddata; | 
 |     } | 
 |  | 
 |     needed_size = sizeof(COMPOSITIONSTRING) + len * sizeof(WCHAR) + | 
 |                   len + sizeof(DWORD) * 2; | 
 |  | 
 |     if (lpcs != NULL) | 
 |     { | 
 |         needed_size += lpcs->dwCompReadAttrLen; | 
 |         needed_size += lpcs->dwCompReadClauseLen; | 
 |         needed_size += lpcs->dwCompReadStrLen * sizeof(DWORD); | 
 |         needed_size += lpcs->dwResultReadClauseLen; | 
 |         needed_size += lpcs->dwResultReadStrLen * sizeof(DWORD); | 
 |         needed_size += lpcs->dwResultClauseLen; | 
 |         needed_size += lpcs->dwResultStrLen * sizeof(DWORD); | 
 |         needed_size += lpcs->dwPrivateSize; | 
 |     } | 
 |     rc = ImmCreateIMCC(needed_size); | 
 |     newdata = ImmLockIMCC(rc); | 
 |     new_one = (LPCOMPOSITIONSTRING)newdata; | 
 |  | 
 |     new_one->dwSize = needed_size; | 
 |     current_offset = sizeof(COMPOSITIONSTRING); | 
 |     if (lpcs != NULL) | 
 |     { | 
 |         current_offset = updateField(lpcs->dwCompReadAttrLen, | 
 |                                      lpcs->dwCompReadAttrOffset, | 
 |                                      current_offset, newdata, olddata, | 
 |                                      &new_one->dwCompReadAttrLen, | 
 |                                      &new_one->dwCompReadAttrOffset, FALSE); | 
 |  | 
 |         current_offset = updateField(lpcs->dwCompReadClauseLen, | 
 |                                      lpcs->dwCompReadClauseOffset, | 
 |                                      current_offset, newdata, olddata, | 
 |                                      &new_one->dwCompReadClauseLen, | 
 |                                      &new_one->dwCompReadClauseOffset, FALSE); | 
 |  | 
 |         current_offset = updateField(lpcs->dwCompReadStrLen, | 
 |                                      lpcs->dwCompReadStrOffset, | 
 |                                      current_offset, newdata, olddata, | 
 |                                      &new_one->dwCompReadStrLen, | 
 |                                      &new_one->dwCompReadStrOffset, TRUE); | 
 |  | 
 |         /* new CompAttr, CompClause, CompStr, dwCursorPos */ | 
 |         new_one->dwDeltaStart = 0; | 
 |  | 
 |         current_offset = updateField(lpcs->dwResultReadClauseLen, | 
 |                                      lpcs->dwResultReadClauseOffset, | 
 |                                      current_offset, newdata, olddata, | 
 |                                      &new_one->dwResultReadClauseLen, | 
 |                                      &new_one->dwResultReadClauseOffset, FALSE); | 
 |  | 
 |         current_offset = updateField(lpcs->dwResultReadStrLen, | 
 |                                      lpcs->dwResultReadStrOffset, | 
 |                                      current_offset, newdata, olddata, | 
 |                                      &new_one->dwResultReadStrLen, | 
 |                                      &new_one->dwResultReadStrOffset, TRUE); | 
 |  | 
 |         current_offset = updateField(lpcs->dwResultClauseLen, | 
 |                                      lpcs->dwResultClauseOffset, | 
 |                                      current_offset, newdata, olddata, | 
 |                                      &new_one->dwResultClauseLen, | 
 |                                      &new_one->dwResultClauseOffset, FALSE); | 
 |  | 
 |         current_offset = updateField(lpcs->dwResultStrLen, | 
 |                                      lpcs->dwResultStrOffset, | 
 |                                      current_offset, newdata, olddata, | 
 |                                      &new_one->dwResultStrLen, | 
 |                                      &new_one->dwResultStrOffset, TRUE); | 
 |  | 
 |         current_offset = updateField(lpcs->dwPrivateSize, | 
 |                                      lpcs->dwPrivateOffset, | 
 |                                      current_offset, newdata, olddata, | 
 |                                      &new_one->dwPrivateSize, | 
 |                                      &new_one->dwPrivateOffset, FALSE); | 
 |     } | 
 |  | 
 |     /* set new data */ | 
 |     /* CompAttr */ | 
 |     new_one->dwCompAttrLen = len; | 
 |     if (len > 0) | 
 |     { | 
 |         new_one->dwCompAttrOffset = current_offset; | 
 |         memset(&newdata[current_offset],ATTR_INPUT,len); | 
 |         current_offset += len; | 
 |     } | 
 |  | 
 |     /* CompClause */ | 
 |     if (len > 0) | 
 |     { | 
 |         new_one->dwCompClauseLen = sizeof(DWORD) * 2; | 
 |         new_one->dwCompClauseOffset = current_offset; | 
 |         *(DWORD*)(&newdata[current_offset]) = 0; | 
 |         current_offset += sizeof(DWORD); | 
 |         *(DWORD*)(&newdata[current_offset]) = len; | 
 |         current_offset += sizeof(DWORD); | 
 |     } | 
 |  | 
 |     /* CompStr */ | 
 |     new_one->dwCompStrLen = len; | 
 |     if (len > 0) | 
 |     { | 
 |         new_one->dwCompStrOffset = current_offset; | 
 |         memcpy(&newdata[current_offset],compstr,len*sizeof(WCHAR)); | 
 |     } | 
 |  | 
 |     /* CursorPos */ | 
 |     new_one->dwCursorPos = len; | 
 |  | 
 |     ImmUnlockIMCC(rc); | 
 |     if (lpcs) | 
 |         ImmUnlockIMCC(old); | 
 |  | 
 |     return rc; | 
 | } | 
 |  | 
 | static HIMCC updateResultStr(HIMCC old, LPWSTR resultstr, DWORD len) | 
 | { | 
 |     /* we need to make sure the ResultStr and ResultClause fields are all | 
 |      * set and correct */ | 
 |     int needed_size; | 
 |     HIMCC   rc; | 
 |     LPBYTE newdata = NULL; | 
 |     LPBYTE olddata = NULL; | 
 |     LPCOMPOSITIONSTRING new_one; | 
 |     LPCOMPOSITIONSTRING lpcs = NULL; | 
 |     INT current_offset = 0; | 
 |  | 
 |     TRACE("%s, %i\n",debugstr_wn(resultstr,len),len); | 
 |  | 
 |     if (old == NULL && resultstr == NULL && len == 0) | 
 |         return NULL; | 
 |  | 
 |     if (resultstr == NULL && len != 0) | 
 |     { | 
 |         ERR("resultstr is NULL however we have a len!  Please report\n"); | 
 |         len = 0; | 
 |     } | 
 |  | 
 |     if (old != NULL) | 
 |     { | 
 |         olddata = ImmLockIMCC(old); | 
 |         lpcs = (LPCOMPOSITIONSTRING)olddata; | 
 |     } | 
 |  | 
 |     needed_size = sizeof(COMPOSITIONSTRING) + len * sizeof(WCHAR) + | 
 |                   sizeof(DWORD) * 2; | 
 |  | 
 |     if (lpcs != NULL) | 
 |     { | 
 |         needed_size += lpcs->dwCompReadAttrLen; | 
 |         needed_size += lpcs->dwCompReadClauseLen; | 
 |         needed_size += lpcs->dwCompReadStrLen * sizeof(DWORD); | 
 |         needed_size += lpcs->dwCompAttrLen; | 
 |         needed_size += lpcs->dwCompClauseLen; | 
 |         needed_size += lpcs->dwCompStrLen * sizeof(DWORD); | 
 |         needed_size += lpcs->dwResultReadClauseLen; | 
 |         needed_size += lpcs->dwResultReadStrLen * sizeof(DWORD); | 
 |         needed_size += lpcs->dwPrivateSize; | 
 |     } | 
 |     rc = ImmCreateIMCC(needed_size); | 
 |     newdata = ImmLockIMCC(rc); | 
 |     new_one = (LPCOMPOSITIONSTRING)newdata; | 
 |  | 
 |     new_one->dwSize = needed_size; | 
 |     current_offset = sizeof(COMPOSITIONSTRING); | 
 |     if (lpcs != NULL) | 
 |     { | 
 |         current_offset = updateField(lpcs->dwCompReadAttrLen, | 
 |                                      lpcs->dwCompReadAttrOffset, | 
 |                                      current_offset, newdata, olddata, | 
 |                                      &new_one->dwCompReadAttrLen, | 
 |                                      &new_one->dwCompReadAttrOffset, FALSE); | 
 |  | 
 |         current_offset = updateField(lpcs->dwCompReadClauseLen, | 
 |                                      lpcs->dwCompReadClauseOffset, | 
 |                                      current_offset, newdata, olddata, | 
 |                                      &new_one->dwCompReadClauseLen, | 
 |                                      &new_one->dwCompReadClauseOffset, FALSE); | 
 |  | 
 |         current_offset = updateField(lpcs->dwCompReadStrLen, | 
 |                                      lpcs->dwCompReadStrOffset, | 
 |                                      current_offset, newdata, olddata, | 
 |                                      &new_one->dwCompReadStrLen, | 
 |                                      &new_one->dwCompReadStrOffset, TRUE); | 
 |  | 
 |         current_offset = updateField(lpcs->dwCompAttrLen, | 
 |                                      lpcs->dwCompAttrOffset, | 
 |                                      current_offset, newdata, olddata, | 
 |                                      &new_one->dwCompAttrLen, | 
 |                                      &new_one->dwCompAttrOffset, FALSE); | 
 |  | 
 |         current_offset = updateField(lpcs->dwCompClauseLen, | 
 |                                      lpcs->dwCompClauseOffset, | 
 |                                      current_offset, newdata, olddata, | 
 |                                      &new_one->dwCompClauseLen, | 
 |                                      &new_one->dwCompClauseOffset, FALSE); | 
 |  | 
 |         current_offset = updateField(lpcs->dwCompStrLen, | 
 |                                      lpcs->dwCompStrOffset, | 
 |                                      current_offset, newdata, olddata, | 
 |                                      &new_one->dwCompStrLen, | 
 |                                      &new_one->dwCompStrOffset, TRUE); | 
 |  | 
 |         new_one->dwCursorPos = lpcs->dwCursorPos; | 
 |         new_one->dwDeltaStart = 0; | 
 |  | 
 |         current_offset = updateField(lpcs->dwResultReadClauseLen, | 
 |                                      lpcs->dwResultReadClauseOffset, | 
 |                                      current_offset, newdata, olddata, | 
 |                                      &new_one->dwResultReadClauseLen, | 
 |                                      &new_one->dwResultReadClauseOffset, FALSE); | 
 |  | 
 |         current_offset = updateField(lpcs->dwResultReadStrLen, | 
 |                                      lpcs->dwResultReadStrOffset, | 
 |                                      current_offset, newdata, olddata, | 
 |                                      &new_one->dwResultReadStrLen, | 
 |                                      &new_one->dwResultReadStrOffset, TRUE); | 
 |  | 
 |         /* new ResultClause , ResultStr */ | 
 |  | 
 |         current_offset = updateField(lpcs->dwPrivateSize, | 
 |                                      lpcs->dwPrivateOffset, | 
 |                                      current_offset, newdata, olddata, | 
 |                                      &new_one->dwPrivateSize, | 
 |                                      &new_one->dwPrivateOffset, FALSE); | 
 |     } | 
 |  | 
 |     /* set new data */ | 
 |     /* ResultClause */ | 
 |     if (len > 0) | 
 |     { | 
 |         new_one->dwResultClauseLen = sizeof(DWORD) * 2; | 
 |         new_one->dwResultClauseOffset = current_offset; | 
 |         *(DWORD*)(&newdata[current_offset]) = 0; | 
 |         current_offset += sizeof(DWORD); | 
 |         *(DWORD*)(&newdata[current_offset]) = len; | 
 |         current_offset += sizeof(DWORD); | 
 |     } | 
 |  | 
 |     /* ResultStr */ | 
 |     new_one->dwResultStrLen = len; | 
 |     if (len > 0) | 
 |     { | 
 |         new_one->dwResultStrOffset = current_offset; | 
 |         memcpy(&newdata[current_offset],resultstr,len*sizeof(WCHAR)); | 
 |     } | 
 |     ImmUnlockIMCC(rc); | 
 |     if (lpcs) | 
 |         ImmUnlockIMCC(old); | 
 |  | 
 |     return rc; | 
 | } | 
 |  | 
 | static void GenerateIMEMessage(HIMC hIMC, UINT msg, WPARAM wParam, | 
 |                                LPARAM lParam) | 
 | { | 
 |     LPINPUTCONTEXT lpIMC; | 
 |     LPTRANSMSG lpTransMsg; | 
 |  | 
 |     lpIMC = LockRealIMC(hIMC); | 
 |     if (lpIMC == NULL) | 
 |         return; | 
 |  | 
 |     lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, (lpIMC->dwNumMsgBuf + 1) * | 
 |                                     sizeof(TRANSMSG)); | 
 |     if (!lpIMC->hMsgBuf) | 
 |         return; | 
 |  | 
 |     lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf); | 
 |     if (!lpTransMsg) | 
 |         return; | 
 |  | 
 |     lpTransMsg += lpIMC->dwNumMsgBuf; | 
 |     lpTransMsg->message = msg; | 
 |     lpTransMsg->wParam = wParam; | 
 |     lpTransMsg->lParam = lParam; | 
 |  | 
 |     ImmUnlockIMCC(lpIMC->hMsgBuf); | 
 |     lpIMC->dwNumMsgBuf++; | 
 |  | 
 |     ImmGenerateMessage(RealIMC(hIMC)); | 
 |     UnlockRealIMC(hIMC); | 
 | } | 
 |  | 
 | static void GenerateIMECHARMessages(HIMC hIMC, LPWSTR String, DWORD length) | 
 | { | 
 |     LPINPUTCONTEXT lpIMC; | 
 |     LPTRANSMSG lpTransMsg; | 
 |     DWORD i; | 
 |  | 
 |     if (length <= 0) | 
 |         return; | 
 |  | 
 |     lpIMC = LockRealIMC(hIMC); | 
 |     if (lpIMC == NULL) | 
 |         return; | 
 |  | 
 |     lpIMC->hMsgBuf = ImmReSizeIMCC(lpIMC->hMsgBuf, | 
 |                                   (lpIMC->dwNumMsgBuf + length) * | 
 |                                     sizeof(TRANSMSG)); | 
 |     if (!lpIMC->hMsgBuf) | 
 |         return; | 
 |  | 
 |     lpTransMsg = ImmLockIMCC(lpIMC->hMsgBuf); | 
 |     if (!lpTransMsg) | 
 |         return; | 
 |  | 
 |     lpTransMsg += lpIMC->dwNumMsgBuf; | 
 |     for (i = 0; i < length; i++) | 
 |     { | 
 |         lpTransMsg->message = WM_IME_CHAR; | 
 |         lpTransMsg->wParam = String[i]; | 
 |         lpTransMsg->lParam = 1; | 
 |         lpTransMsg ++; | 
 |     } | 
 |  | 
 |     ImmUnlockIMCC(lpIMC->hMsgBuf); | 
 |     lpIMC->dwNumMsgBuf+=length; | 
 |  | 
 |     ImmGenerateMessage(RealIMC(hIMC)); | 
 |     UnlockRealIMC(hIMC); | 
 | } | 
 |  | 
 | static BOOL IME_RemoveFromSelected(HIMC hIMC) | 
 | { | 
 |     int i; | 
 |     for (i = 0; i < hSelectedCount; i++) | 
 |         if (hSelectedFrom[i] == hIMC) | 
 |         { | 
 |             if (i < hSelectedCount - 1) | 
 |                 memmove(&hSelectedFrom[i], &hSelectedFrom[i+1], (hSelectedCount - i - 1)*sizeof(HIMC)); | 
 |             hSelectedCount --; | 
 |             return TRUE; | 
 |         } | 
 |     return FALSE; | 
 | } | 
 |  | 
 | static void IME_AddToSelected(HIMC hIMC) | 
 | { | 
 |     hSelectedCount++; | 
 |     if (hSelectedFrom) | 
 |         hSelectedFrom = HeapReAlloc(GetProcessHeap(), 0, hSelectedFrom, hSelectedCount*sizeof(HIMC)); | 
 |     else | 
 |         hSelectedFrom = HeapAlloc(GetProcessHeap(), 0, sizeof(HIMC)); | 
 |     hSelectedFrom[hSelectedCount-1] = hIMC; | 
 | } | 
 |  | 
 | BOOL WINAPI ImeInquire(LPIMEINFO lpIMEInfo, LPWSTR lpszUIClass, | 
 |                        LPCWSTR lpszOption) | 
 | { | 
 |     TRACE("\n"); | 
 |     IME_RegisterClasses(); | 
 |     lpIMEInfo->dwPrivateDataSize = sizeof (IMEPRIVATE); | 
 |     lpIMEInfo->fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET; | 
 |     lpIMEInfo->fdwConversionCaps = IME_CMODE_NATIVE; | 
 |     lpIMEInfo->fdwSentenceCaps = IME_SMODE_AUTOMATIC; | 
 |     lpIMEInfo->fdwUICaps = UI_CAP_2700; | 
 |     /* Tell App we cannot accept ImeSetCompositionString calls */ | 
 |     lpIMEInfo->fdwSCSCaps = 0; | 
 |     lpIMEInfo->fdwSelectCaps = SELECT_CAP_CONVERSION; | 
 |  | 
 |     lstrcpyW(lpszUIClass,UI_CLASS_NAME); | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | BOOL WINAPI ImeConfigure(HKL hKL,HWND hWnd, DWORD dwMode, LPVOID lpData) | 
 | { | 
 |     FIXME("(%p, %p, %d, %p): stub\n", hKL, hWnd, dwMode, lpData); | 
 |     SetLastError(ERROR_CALL_NOT_IMPLEMENTED); | 
 |     return FALSE; | 
 | } | 
 |  | 
 | DWORD WINAPI ImeConversionList(HIMC hIMC, LPCWSTR lpSource, | 
 |                 LPCANDIDATELIST lpCandList, DWORD dwBufLen, UINT uFlag) | 
 |  | 
 | { | 
 |     FIXME("(%p, %s, %p, %d, %d): stub\n", hIMC, debugstr_w(lpSource), | 
 |                                           lpCandList, dwBufLen, uFlag); | 
 |     SetLastError(ERROR_CALL_NOT_IMPLEMENTED); | 
 |     return 0; | 
 | } | 
 |  | 
 | BOOL WINAPI ImeDestroy(UINT uForce) | 
 | { | 
 |     TRACE("\n"); | 
 |     HeapFree(GetProcessHeap(),0,hSelectedFrom); | 
 |     hSelectedFrom = NULL; | 
 |     hSelectedCount = 0; | 
 |     return TRUE; | 
 | } | 
 |  | 
 | LRESULT WINAPI ImeEscape(HIMC hIMC, UINT uSubFunc, LPVOID lpData) | 
 | { | 
 |     FIXME("(%p, %d, %p): stub\n", hIMC, uSubFunc, lpData); | 
 |     SetLastError(ERROR_CALL_NOT_IMPLEMENTED); | 
 |     return 0; | 
 | } | 
 |  | 
 | BOOL WINAPI ImeProcessKey(HIMC hIMC, UINT vKey, LPARAM lKeyData, | 
 |                              CONST LPBYTE lpbKeyState) | 
 | { | 
 |     /* See the comment at the head of this file */ | 
 |     TRACE("We do no processing via this route\n"); | 
 |     return FALSE; | 
 | } | 
 |  | 
 | BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) | 
 | { | 
 |     LPINPUTCONTEXT lpIMC; | 
 |     TRACE("%p %s\n",hIMC,(fSelect)?"TRUE":"FALSE"); | 
 |  | 
 |     if (hIMC == FROM_X11) | 
 |     { | 
 |         ERR("ImeSelect should never be called from X11\n"); | 
 |         return FALSE; | 
 |     } | 
 |  | 
 |     if (!hIMC) | 
 |         return TRUE; | 
 |  | 
 |     /* not selected */ | 
 |     if (!fSelect) | 
 |         return IME_RemoveFromSelected(hIMC); | 
 |  | 
 |     IME_AddToSelected(hIMC); | 
 |  | 
 |     /* Initialize our structures */ | 
 |     lpIMC = LockRealIMC(hIMC); | 
 |     if (lpIMC != NULL) | 
 |     { | 
 |         LPIMEPRIVATE myPrivate; | 
 |         myPrivate = ImmLockIMCC(lpIMC->hPrivate); | 
 |         myPrivate->bInComposition = FALSE; | 
 |         myPrivate->bInternalState = FALSE; | 
 |         myPrivate->textfont = NULL; | 
 |         myPrivate->hwndDefault = NULL; | 
 |         ImmUnlockIMCC(lpIMC->hPrivate); | 
 |         UnlockRealIMC(hIMC); | 
 |     } | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | BOOL WINAPI ImeSetActiveContext(HIMC hIMC,BOOL fFlag) | 
 | { | 
 |     FIXME("(%p, %x): stub\n", hIMC, fFlag); | 
 |     return TRUE; | 
 | } | 
 |  | 
 | UINT WINAPI ImeToAsciiEx (UINT uVKey, UINT uScanCode, | 
 |                           CONST LPBYTE lpbKeyState, LPDWORD lpdwTransKey, | 
 |                           UINT fuState, HIMC hIMC) | 
 | { | 
 |     /* See the comment at the head of this file */ | 
 |     TRACE("We do no processing via this route\n"); | 
 |     return 0; | 
 | } | 
 |  | 
 | BOOL WINAPI NotifyIME(HIMC hIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue) | 
 | { | 
 |     BOOL bRet = FALSE; | 
 |     LPINPUTCONTEXT lpIMC; | 
 |  | 
 |     TRACE("%p %i %i %i\n",hIMC,dwAction,dwIndex,dwValue); | 
 |  | 
 |     lpIMC = LockRealIMC(hIMC); | 
 |     if (lpIMC == NULL) | 
 |         return FALSE; | 
 |  | 
 |     switch (dwAction) | 
 |     { | 
 |         case NI_OPENCANDIDATE: FIXME("NI_OPENCANDIDATE\n"); break; | 
 |         case NI_CLOSECANDIDATE: FIXME("NI_CLOSECANDIDATE\n"); break; | 
 |         case NI_SELECTCANDIDATESTR: FIXME("NI_SELECTCANDIDATESTR\n"); break; | 
 |         case NI_CHANGECANDIDATELIST: FIXME("NI_CHANGECANDIDATELIST\n"); break; | 
 |         case NI_SETCANDIDATE_PAGESTART: FIXME("NI_SETCANDIDATE_PAGESTART\n"); break; | 
 |         case NI_SETCANDIDATE_PAGESIZE: FIXME("NI_SETCANDIDATE_PAGESIZE\n"); break; | 
 |         case NI_CONTEXTUPDATED: | 
 |             switch (dwValue) | 
 |             { | 
 |                 case IMC_SETCOMPOSITIONWINDOW: FIXME("IMC_SETCOMPOSITIONWINDOW\n"); break; | 
 |                 case IMC_SETCONVERSIONMODE: FIXME("IMC_SETCONVERSIONMODE\n"); break; | 
 |                 case IMC_SETSENTENCEMODE: FIXME("IMC_SETSENTENCEMODE\n"); break; | 
 |                 case IMC_SETCANDIDATEPOS: FIXME("IMC_SETCANDIDATEPOS\n"); break; | 
 |                 case IMC_SETCOMPOSITIONFONT: | 
 |                     { | 
 |                         LPIMEPRIVATE myPrivate; | 
 |                         TRACE("IMC_SETCOMPOSITIONFONT\n"); | 
 |  | 
 |                         myPrivate = ImmLockIMCC(lpIMC->hPrivate); | 
 |                         if (myPrivate->textfont) | 
 |                         { | 
 |                             DeleteObject(myPrivate->textfont); | 
 |                             myPrivate->textfont = NULL; | 
 |                         } | 
 |                         myPrivate->textfont = CreateFontIndirectW(&lpIMC->lfFont.W); | 
 |                         ImmUnlockIMCC(lpIMC->hPrivate); | 
 |                     } | 
 |                     break; | 
 |                 case IMC_SETOPENSTATUS: | 
 |                 { | 
 |                     LPIMEPRIVATE myPrivate; | 
 |                     TRACE("IMC_SETOPENSTATUS\n"); | 
 |  | 
 |                     myPrivate = ImmLockIMCC(lpIMC->hPrivate); | 
 |                     if (lpIMC->fOpen != myPrivate->bInternalState && | 
 |                         myPrivate->bInComposition) | 
 |                     { | 
 |                         if(lpIMC->fOpen == FALSE) | 
 |                         { | 
 |                             X11DRV_ForceXIMReset(lpIMC->hWnd); | 
 |                             GenerateIMEMessage(hIMC,WM_IME_ENDCOMPOSITION,0,0); | 
 |                             myPrivate->bInComposition = FALSE; | 
 |                         } | 
 |                         else | 
 |                         { | 
 |                             GenerateIMEMessage(hIMC,WM_IME_STARTCOMPOSITION,0,0); | 
 |                             GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, 0, 0); | 
 |                         } | 
 |                     } | 
 |                     myPrivate->bInternalState = lpIMC->fOpen; | 
 |                     bRet = TRUE; | 
 |                 } | 
 |                 break; | 
 |                 default: FIXME("Unknown\n"); break; | 
 |             } | 
 |             break; | 
 |         case NI_COMPOSITIONSTR: | 
 |             switch (dwIndex) | 
 |             { | 
 |                 case CPS_COMPLETE: | 
 |                 { | 
 |                     HIMCC newCompStr; | 
 |                     DWORD cplen = 0; | 
 |                     LPWSTR cpstr; | 
 |                     LPCOMPOSITIONSTRING cs = NULL; | 
 |                     LPBYTE cdata = NULL; | 
 |                     LPIMEPRIVATE myPrivate; | 
 |  | 
 |                     TRACE("CPS_COMPLETE\n"); | 
 |  | 
 |                     /* clear existing result */ | 
 |                     newCompStr = updateResultStr(lpIMC->hCompStr, NULL, 0); | 
 |  | 
 |                     ImmDestroyIMCC(lpIMC->hCompStr); | 
 |                     lpIMC->hCompStr = newCompStr; | 
 |  | 
 |                     if (lpIMC->hCompStr) | 
 |                     { | 
 |                         cdata = ImmLockIMCC(lpIMC->hCompStr); | 
 |                         cs = (LPCOMPOSITIONSTRING)cdata; | 
 |                         cplen = cs->dwCompStrLen; | 
 |                         cpstr = (LPWSTR)&(cdata[cs->dwCompStrOffset]); | 
 |                         ImmUnlockIMCC(lpIMC->hCompStr); | 
 |                     } | 
 |                     if (cplen > 0) | 
 |                     { | 
 |                         WCHAR param = cpstr[0]; | 
 |  | 
 |                         newCompStr = updateResultStr(lpIMC->hCompStr, cpstr, cplen); | 
 |                         ImmDestroyIMCC(lpIMC->hCompStr); | 
 |                         lpIMC->hCompStr = newCompStr; | 
 |                         newCompStr = updateCompStr(lpIMC->hCompStr, NULL, 0); | 
 |                         ImmDestroyIMCC(lpIMC->hCompStr); | 
 |                         lpIMC->hCompStr = newCompStr; | 
 |  | 
 |                         GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, 0, | 
 |                                                   GCS_COMPSTR); | 
 |  | 
 |                         GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, param, | 
 |                                             GCS_RESULTSTR|GCS_RESULTCLAUSE); | 
 |                     } | 
 |  | 
 |                     GenerateIMEMessage(hIMC,WM_IME_ENDCOMPOSITION, 0, 0); | 
 |  | 
 |                     myPrivate = ImmLockIMCC(lpIMC->hPrivate); | 
 |                     myPrivate->bInComposition = FALSE; | 
 |                     ImmUnlockIMCC(lpIMC->hPrivate); | 
 |  | 
 |                     bRet = TRUE; | 
 |                 } | 
 |                 break; | 
 |                 case CPS_CONVERT: FIXME("CPS_CONVERT\n"); break; | 
 |                 case CPS_REVERT: FIXME("CPS_REVERT\n"); break; | 
 |                 case CPS_CANCEL: | 
 |                 { | 
 |                     LPIMEPRIVATE myPrivate; | 
 |  | 
 |                     TRACE("CPS_CANCEL\n"); | 
 |  | 
 |                     X11DRV_ForceXIMReset(lpIMC->hWnd); | 
 |  | 
 |                     if (lpIMC->hCompStr) | 
 |                         ImmDestroyIMCC(lpIMC->hCompStr); | 
 |                     lpIMC->hCompStr = ImeCreateBlankCompStr(); | 
 |  | 
 |                     myPrivate = ImmLockIMCC(lpIMC->hPrivate); | 
 |                     if (myPrivate->bInComposition) | 
 |                     { | 
 |                         GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0); | 
 |                         myPrivate->bInComposition = FALSE; | 
 |                     } | 
 |                     ImmUnlockIMCC(lpIMC->hPrivate); | 
 |                     bRet = TRUE; | 
 |                 } | 
 |                 break; | 
 |                 default: FIXME("Unknown\n"); break; | 
 |             } | 
 |             break; | 
 |         default: FIXME("Unknown Message\n"); break; | 
 |     } | 
 |  | 
 |     UnlockRealIMC(hIMC); | 
 |     return bRet; | 
 | } | 
 |  | 
 | BOOL WINAPI ImeRegisterWord(LPCWSTR lpszReading, DWORD dwStyle, | 
 |                             LPCWSTR lpszRegister) | 
 | { | 
 |     FIXME("(%s, %d, %s): stub\n", debugstr_w(lpszReading), dwStyle, | 
 |                                   debugstr_w(lpszRegister)); | 
 |     SetLastError(ERROR_CALL_NOT_IMPLEMENTED); | 
 |     return FALSE; | 
 | } | 
 |  | 
 | BOOL WINAPI ImeUnregisterWord(LPCWSTR lpszReading, DWORD dwStyle, | 
 |                               LPCWSTR lpszUnregister) | 
 | { | 
 |     FIXME("(%s, %d, %s): stub\n", debugstr_w(lpszReading), dwStyle, | 
 |                                   debugstr_w(lpszUnregister)); | 
 |     SetLastError(ERROR_CALL_NOT_IMPLEMENTED); | 
 |     return FALSE; | 
 | } | 
 |  | 
 | UINT WINAPI ImeGetRegisterWordStyle(UINT nItem, LPSTYLEBUFW lpStyleBuf) | 
 | { | 
 |     FIXME("(%d, %p): stub\n", nItem, lpStyleBuf); | 
 |     SetLastError(ERROR_CALL_NOT_IMPLEMENTED); | 
 |     return 0; | 
 | } | 
 |  | 
 | UINT WINAPI ImeEnumRegisterWord(REGISTERWORDENUMPROCW lpfnEnumProc, | 
 |                                 LPCWSTR lpszReading, DWORD dwStyle, | 
 |                                 LPCWSTR lpszRegister, LPVOID lpData) | 
 | { | 
 |     FIXME("(%p, %s, %d, %s, %p): stub\n", lpfnEnumProc, | 
 |             debugstr_w(lpszReading), dwStyle, debugstr_w(lpszRegister), | 
 |             lpData); | 
 |     SetLastError(ERROR_CALL_NOT_IMPLEMENTED); | 
 |     return 0; | 
 | } | 
 |  | 
 | BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp, | 
 |                                     DWORD dwCompLen, LPCVOID lpRead, | 
 |                                     DWORD dwReadLen) | 
 | { | 
 |     LPINPUTCONTEXT lpIMC; | 
 |     DWORD flags = 0; | 
 |     WCHAR wParam  = 0; | 
 |     LPIMEPRIVATE myPrivate; | 
 |  | 
 |     TRACE("(%p, %d, %p, %d, %p, %d):\n", | 
 |          hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen); | 
 |  | 
 |  | 
 |     if (hIMC != FROM_X11) | 
 |         FIXME("PROBLEM: This only sets the wine level string\n"); | 
 |  | 
 |     /* | 
 |     * Explanation: | 
 |     *  this sets the composition string in the imm32.dll level | 
 |     *  of the composition buffer. we cannot manipulate the xim level | 
 |     *  buffer, which means that once the xim level buffer changes again | 
 |     *  any call to this function from the application will be lost | 
 |     */ | 
 |  | 
 |     if (lpRead && dwReadLen) | 
 |         FIXME("Reading string unimplemented\n"); | 
 |  | 
 |     lpIMC = LockRealIMC(hIMC); | 
 |  | 
 |     if (lpIMC == NULL) | 
 |         return FALSE; | 
 |  | 
 |     myPrivate = ImmLockIMCC(lpIMC->hPrivate); | 
 |  | 
 |     if (dwIndex == SCS_SETSTR) | 
 |     { | 
 |         HIMCC newCompStr; | 
 |  | 
 |         if (!myPrivate->bInComposition) | 
 |         { | 
 |             GenerateIMEMessage(hIMC, WM_IME_STARTCOMPOSITION, 0, 0); | 
 |             myPrivate->bInComposition = TRUE; | 
 |         } | 
 |  | 
 |         flags = GCS_COMPSTR; | 
 |  | 
 |         if (dwCompLen && lpComp) | 
 |         { | 
 |             newCompStr = updateCompStr(lpIMC->hCompStr, (LPWSTR)lpComp, dwCompLen / sizeof(WCHAR)); | 
 |             ImmDestroyIMCC(lpIMC->hCompStr); | 
 |             lpIMC->hCompStr = newCompStr; | 
 |  | 
 |              wParam = ((const WCHAR*)lpComp)[0]; | 
 |              flags |= GCS_COMPCLAUSE | GCS_COMPATTR | GCS_DELTASTART; | 
 |         } | 
 |         else | 
 |         { | 
 |             newCompStr = updateCompStr(lpIMC->hCompStr, NULL, 0); | 
 |             ImmDestroyIMCC(lpIMC->hCompStr); | 
 |             lpIMC->hCompStr = newCompStr; | 
 |         } | 
 |     } | 
 |  | 
 |     UpdateDataInDefaultIMEWindow(hIMC, myPrivate->hwndDefault,FALSE); | 
 |  | 
 |     GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, wParam, flags); | 
 |     ImmUnlockIMCC(lpIMC->hPrivate); | 
 |     UnlockRealIMC(hIMC); | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | DWORD WINAPI ImeGetImeMenuItems(HIMC hIMC,  DWORD dwFlags,  DWORD dwType, | 
 |             LPIMEMENUITEMINFOW lpImeParentMenu, LPIMEMENUITEMINFOW lpImeMenu, | 
 |             DWORD dwSize) | 
 | { | 
 |     FIXME("(%p, %x %x %p %p %x): stub\n", hIMC, dwFlags, dwType, | 
 |                                 lpImeParentMenu, lpImeMenu, dwSize); | 
 |     SetLastError(ERROR_CALL_NOT_IMPLEMENTED); | 
 |     return 0; | 
 | } | 
 |  | 
 | /* Interfaces to XIM and other parts of winex11drv */ | 
 |  | 
 | void IME_SetOpenStatus(BOOL fOpen) | 
 | { | 
 |     LPINPUTCONTEXT lpIMC; | 
 |     LPIMEPRIVATE myPrivate; | 
 |  | 
 |     lpIMC = LockRealIMC(FROM_X11); | 
 |     if (lpIMC == NULL) | 
 |         return; | 
 |  | 
 |     myPrivate = ImmLockIMCC(lpIMC->hPrivate); | 
 |  | 
 |     if (myPrivate->bInternalState && fOpen == FALSE) | 
 |     { | 
 |         ShowWindow(myPrivate->hwndDefault, SW_HIDE); | 
 |         ImmDestroyIMCC(lpIMC->hCompStr); | 
 |         lpIMC->hCompStr = ImeCreateBlankCompStr(); | 
 |     } | 
 |  | 
 |     ImmUnlockIMCC(lpIMC->hPrivate); | 
 |     UnlockRealIMC(FROM_X11); | 
 |  | 
 |     if (myPrivate->bInComposition && fOpen == FALSE) | 
 |     { | 
 |         GenerateIMEMessage(FROM_X11, WM_IME_ENDCOMPOSITION, 0, 0); | 
 |         myPrivate->bInComposition = FALSE; | 
 |     } | 
 |  | 
 |     if (!myPrivate->bInternalState && fOpen == TRUE) | 
 |         ImmSetOpenStatus(RealIMC(FROM_X11), fOpen); | 
 | } | 
 |  | 
 | INT IME_GetCursorPos(void) | 
 | { | 
 |     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; | 
 | } | 
 |  | 
 | void IME_SetCursorPos(DWORD pos) | 
 | { | 
 |     LPINPUTCONTEXT lpIMC; | 
 |     LPCOMPOSITIONSTRING compstr; | 
 |  | 
 |     if (!hSelectedFrom) | 
 |         return; | 
 |  | 
 |     lpIMC = LockRealIMC(FROM_X11); | 
 |     if (!lpIMC) | 
 |         return; | 
 |  | 
 |     compstr = ImmLockIMCC(lpIMC->hCompStr); | 
 |     if (!compstr) | 
 |     { | 
 |         UnlockRealIMC(FROM_X11); | 
 |         return; | 
 |     } | 
 |  | 
 |     compstr->dwCursorPos = pos; | 
 |     ImmUnlockIMCC(lpIMC->hCompStr); | 
 |     UnlockRealIMC(FROM_X11); | 
 |     GenerateIMEMessage(FROM_X11, WM_IME_COMPOSITION, pos, GCS_CURSORPOS); | 
 |     return; | 
 | } | 
 |  | 
 | void IME_UpdateAssociation(HWND focus) | 
 | { | 
 |     ImmGetContext(focus); | 
 |  | 
 |     if (!focus || !hSelectedFrom) | 
 |         return; | 
 |  | 
 |     ImmAssociateContext(focus,RealIMC(FROM_X11)); | 
 | } | 
 |  | 
 |  | 
 | BOOL IME_SetCompositionString(DWORD dwIndex, LPCVOID lpComp, DWORD dwCompLen, | 
 |                               LPCVOID lpRead, DWORD dwReadLen) | 
 | { | 
 |     return ImeSetCompositionString(FROM_X11, dwIndex, lpComp, dwCompLen, | 
 |                                     lpRead, dwReadLen); | 
 | } | 
 |  | 
 | BOOL IME_NotifyIME(DWORD dwAction, DWORD dwIndex, DWORD dwValue) | 
 | { | 
 |     return NotifyIME(FROM_X11, dwAction, dwIndex, dwValue); | 
 | } | 
 |  | 
 | /***** | 
 |  * Internal functions to help with IME window management | 
 |  */ | 
 | static void PaintDefaultIMEWnd(HIMC hIMC, HWND hwnd) | 
 | { | 
 |     PAINTSTRUCT ps; | 
 |     RECT rect; | 
 |     HDC hdc; | 
 |     LPCOMPOSITIONSTRING compstr; | 
 |     LPBYTE compdata = NULL; | 
 |     HMONITOR monitor; | 
 |     MONITORINFO mon_info; | 
 |     INT offX=0, offY=0; | 
 |     LPINPUTCONTEXT lpIMC; | 
 |  | 
 |     lpIMC = LockRealIMC(hIMC); | 
 |     if (lpIMC == NULL) | 
 |         return; | 
 |  | 
 |     hdc = BeginPaint(hwnd,&ps); | 
 |  | 
 |     GetClientRect(hwnd,&rect); | 
 |     FillRect(hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1)); | 
 |  | 
 |     compdata = ImmLockIMCC(lpIMC->hCompStr); | 
 |     compstr = (LPCOMPOSITIONSTRING)compdata; | 
 |  | 
 |     if (compstr->dwCompStrLen && compstr->dwCompStrOffset) | 
 |     { | 
 |         SIZE size; | 
 |         POINT pt; | 
 |         HFONT oldfont = NULL; | 
 |         LPWSTR CompString; | 
 |         LPIMEPRIVATE myPrivate; | 
 |  | 
 |         CompString = (LPWSTR)(compdata + compstr->dwCompStrOffset); | 
 |         myPrivate = ImmLockIMCC(lpIMC->hPrivate); | 
 |  | 
 |         if (myPrivate->textfont) | 
 |             oldfont = SelectObject(hdc,myPrivate->textfont); | 
 |  | 
 |         ImmUnlockIMCC(lpIMC->hPrivate); | 
 |  | 
 |         GetTextExtentPoint32W(hdc, CompString, compstr->dwCompStrLen, &size); | 
 |         pt.x = size.cx; | 
 |         pt.y = size.cy; | 
 |         LPtoDP(hdc,&pt,1); | 
 |  | 
 |         /* | 
 |          * How this works based on tests on windows: | 
 |          * CFS_POINT: then we start our window at the point and grow it as large | 
 |          *    as it needs to be for the string. | 
 |          * CFS_RECT:  we still use the ptCurrentPos as a starting point and our | 
 |          *    window is only as large as we need for the string, but we do not | 
 |          *    grow such that our window exceeds the given rect.  Wrapping if | 
 |          *    needed and possible.   If our ptCurrentPos is outside of our rect | 
 |          *    then no window is displayed. | 
 |          * CFS_FORCE_POSITION: appears to behave just like CFS_POINT | 
 |          *    maybe becase the default MSIME does not do any IME adjusting. | 
 |          */ | 
 |         if (lpIMC->cfCompForm.dwStyle != CFS_DEFAULT) | 
 |         { | 
 |             POINT cpt = lpIMC->cfCompForm.ptCurrentPos; | 
 |             ClientToScreen(lpIMC->hWnd,&cpt); | 
 |             rect.left = cpt.x; | 
 |             rect.top = cpt.y; | 
 |             rect.right = rect.left + pt.x; | 
 |             rect.bottom = rect.top + pt.y; | 
 |             monitor = MonitorFromPoint(cpt, MONITOR_DEFAULTTOPRIMARY); | 
 |         } | 
 |         else /* CFS_DEFAULT */ | 
 |         { | 
 |             /* Windows places the default IME window in the bottom left */ | 
 |             HWND target = lpIMC->hWnd; | 
 |             if (!target) target = GetFocus(); | 
 |  | 
 |             GetWindowRect(target,&rect); | 
 |             rect.top = rect.bottom; | 
 |             rect.right = rect.left + pt.x + 20; | 
 |             rect.bottom = rect.top + pt.y + 20; | 
 |             offX=offY=10; | 
 |             monitor = MonitorFromWindow(target, MONITOR_DEFAULTTOPRIMARY); | 
 |         } | 
 |  | 
 |         if (lpIMC->cfCompForm.dwStyle == CFS_RECT) | 
 |         { | 
 |             RECT client; | 
 |             client =lpIMC->cfCompForm.rcArea; | 
 |             MapWindowPoints( lpIMC->hWnd, 0, (POINT *)&client, 2 ); | 
 |             IntersectRect(&rect,&rect,&client); | 
 |             /* TODO:  Wrap the input if needed */ | 
 |         } | 
 |  | 
 |         if (lpIMC->cfCompForm.dwStyle == CFS_DEFAULT) | 
 |         { | 
 |             /* make sure we are on the desktop */ | 
 |             mon_info.cbSize = sizeof(mon_info); | 
 |             GetMonitorInfoW(monitor, &mon_info); | 
 |  | 
 |             if (rect.bottom > mon_info.rcWork.bottom) | 
 |             { | 
 |                 int shift = rect.bottom - mon_info.rcWork.bottom; | 
 |                 rect.top -= shift; | 
 |                 rect.bottom -= shift; | 
 |             } | 
 |             if (rect.left < 0) | 
 |             { | 
 |                 rect.right -= rect.left; | 
 |                 rect.left = 0; | 
 |             } | 
 |             if (rect.right > mon_info.rcWork.right) | 
 |             { | 
 |                 int shift = rect.right - mon_info.rcWork.right; | 
 |                 rect.left -= shift; | 
 |                 rect.right -= shift; | 
 |             } | 
 |         } | 
 |  | 
 |         SetWindowPos(hwnd, HWND_TOPMOST, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE); | 
 |  | 
 |         TextOutW(hdc, offX,offY, CompString, compstr->dwCompStrLen); | 
 |  | 
 |         if (oldfont) | 
 |             SelectObject(hdc,oldfont); | 
 |     } | 
 |  | 
 |     ImmUnlockIMCC(lpIMC->hCompStr); | 
 |  | 
 |     EndPaint(hwnd,&ps); | 
 |     UnlockRealIMC(hIMC); | 
 | } | 
 |  | 
 | static void UpdateDataInDefaultIMEWindow(HIMC hIMC, HWND hwnd, BOOL showable) | 
 | { | 
 |     LPCOMPOSITIONSTRING compstr; | 
 |     LPINPUTCONTEXT lpIMC; | 
 |  | 
 |     lpIMC = LockRealIMC(hIMC); | 
 |     if (lpIMC == NULL) | 
 |         return; | 
 |  | 
 |     if (lpIMC->hCompStr) | 
 |         compstr = ImmLockIMCC(lpIMC->hCompStr); | 
 |     else | 
 |         compstr = NULL; | 
 |  | 
 |     if (compstr == NULL || compstr->dwCompStrLen == 0) | 
 |         ShowWindow(hwnd,SW_HIDE); | 
 |     else if (showable) | 
 |         ShowWindow(hwnd,SW_SHOWNOACTIVATE); | 
 |  | 
 |     RedrawWindow(hwnd,NULL,NULL,RDW_ERASENOW|RDW_INVALIDATE); | 
 |  | 
 |     if (compstr != NULL) | 
 |         ImmUnlockIMCC(lpIMC->hCompStr); | 
 |  | 
 |     UnlockRealIMC(hIMC); | 
 | } | 
 |  | 
 | static void DefaultIMEComposition(HIMC hIMC, HWND hwnd, LPARAM lParam) | 
 | { | 
 |     TRACE("IME message WM_IME_COMPOSITION 0x%lx\n", lParam); | 
 |     if (lParam & GCS_RESULTSTR) | 
 |     { | 
 |         LPCOMPOSITIONSTRING compstr; | 
 |         LPBYTE compdata; | 
 |         LPWSTR ResultStr; | 
 |         HIMCC newCompStr; | 
 |         LPINPUTCONTEXT lpIMC; | 
 |  | 
 |         lpIMC = LockRealIMC(hIMC); | 
 |         if (lpIMC == NULL) | 
 |             return; | 
 |  | 
 |         TRACE("Posting result as IME_CHAR\n"); | 
 |         compdata = ImmLockIMCC(lpIMC->hCompStr); | 
 |         compstr = (LPCOMPOSITIONSTRING)compdata; | 
 |         ResultStr = (LPWSTR)(compdata + compstr->dwResultStrOffset); | 
 |         GenerateIMECHARMessages(hIMC, ResultStr, compstr->dwResultStrLen); | 
 |         ImmUnlockIMCC(lpIMC->hCompStr); | 
 |  | 
 |         /* clear the buffer */ | 
 |         newCompStr = updateResultStr(lpIMC->hCompStr, NULL, 0); | 
 |         ImmDestroyIMCC(lpIMC->hCompStr); | 
 |         lpIMC->hCompStr = newCompStr; | 
 |         UnlockRealIMC(hIMC); | 
 |     } | 
 |     else | 
 |          UpdateDataInDefaultIMEWindow(hIMC,hwnd,TRUE); | 
 | } | 
 |  | 
 | static void DefaultIMEStartComposition(HIMC hIMC, HWND hwnd ) | 
 | { | 
 |     LPINPUTCONTEXT lpIMC; | 
 |  | 
 |     lpIMC = LockRealIMC(hIMC); | 
 |     if (lpIMC == NULL) | 
 |         return; | 
 |  | 
 |     TRACE("IME message WM_IME_STARTCOMPOSITION\n"); | 
 |     lpIMC->hWnd = GetFocus(); | 
 |     ShowWindow(hwnd,SW_SHOWNOACTIVATE); | 
 |     UnlockRealIMC(hIMC); | 
 | } | 
 |  | 
 | static LRESULT ImeHandleNotify(HIMC hIMC, HWND hwnd, UINT msg, WPARAM wParam, | 
 |                                LPARAM lParam) | 
 | { | 
 |     switch (wParam) | 
 |     { | 
 |         case IMN_OPENSTATUSWINDOW: | 
 |             FIXME("WM_IME_NOTIFY:IMN_OPENSTATUSWINDOW\n"); | 
 |             break; | 
 |         case IMN_CLOSESTATUSWINDOW: | 
 |             FIXME("WM_IME_NOTIFY:IMN_CLOSESTATUSWINDOW\n"); | 
 |             break; | 
 |         case IMN_OPENCANDIDATE: | 
 |             FIXME("WM_IME_NOTIFY:IMN_OPENCANDIDATE\n"); | 
 |             break; | 
 |         case IMN_CHANGECANDIDATE: | 
 |             FIXME("WM_IME_NOTIFY:IMN_CHANGECANDIDATE\n"); | 
 |             break; | 
 |         case IMN_CLOSECANDIDATE: | 
 |             FIXME("WM_IME_NOTIFY:IMN_CLOSECANDIDATE\n"); | 
 |             break; | 
 |         case IMN_SETCONVERSIONMODE: | 
 |             FIXME("WM_IME_NOTIFY:IMN_SETCONVERSIONMODE\n"); | 
 |             break; | 
 |         case IMN_SETSENTENCEMODE: | 
 |             FIXME("WM_IME_NOTIFY:IMN_SETSENTENCEMODE\n"); | 
 |             break; | 
 |         case IMN_SETOPENSTATUS: | 
 |             FIXME("WM_IME_NOTIFY:IMN_SETOPENSTATUS\n"); | 
 |             break; | 
 |         case IMN_SETCANDIDATEPOS: | 
 |             FIXME("WM_IME_NOTIFY:IMN_SETCANDIDATEPOS\n"); | 
 |             break; | 
 |         case IMN_SETCOMPOSITIONFONT: | 
 |             FIXME("WM_IME_NOTIFY:IMN_SETCOMPOSITIONFONT\n"); | 
 |             break; | 
 |         case IMN_SETCOMPOSITIONWINDOW: | 
 |             FIXME("WM_IME_NOTIFY:IMN_SETCOMPOSITIONWINDOW\n"); | 
 |             break; | 
 |         case IMN_GUIDELINE: | 
 |             FIXME("WM_IME_NOTIFY:IMN_GUIDELINE\n"); | 
 |             break; | 
 |         case IMN_SETSTATUSWINDOWPOS: | 
 |             FIXME("WM_IME_NOTIFY:IMN_SETSTATUSWINDOWPOS\n"); | 
 |             break; | 
 |         default: | 
 |             FIXME("WM_IME_NOTIFY:<Unknown 0x%lx>\n",wParam); | 
 |             break; | 
 |     } | 
 |     return 0; | 
 | } | 
 |  | 
 | static LRESULT WINAPI IME_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, | 
 |                                           LPARAM lParam) | 
 | { | 
 |     LRESULT rc = 0; | 
 |     HIMC    hIMC; | 
 |  | 
 |     TRACE("Incoming Message 0x%x  (0x%08lx, 0x%08lx)\n", msg, wParam, lParam); | 
 |  | 
 |     /* | 
 |      * Each UI window contains the current Input Context. | 
 |      * This Input Context can be obtained by calling GetWindowLong | 
 |      * with IMMGWL_IMC when the UI window receives a WM_IME_xxx message. | 
 |      * The UI window can refer to this Input Context and handles the | 
 |      * messages. | 
 |      */ | 
 |  | 
 |     hIMC = (HIMC)GetWindowLongPtrW(hwnd,IMMGWL_IMC); | 
 |     if (!hIMC) | 
 |         hIMC = RealIMC(FROM_X11); | 
 |  | 
 |     /* if we have no hIMC there are many messages we cannot process */ | 
 |     if (hIMC == NULL) | 
 |     { | 
 |         switch (msg) { | 
 |         case WM_IME_STARTCOMPOSITION: | 
 |         case WM_IME_ENDCOMPOSITION: | 
 |         case WM_IME_COMPOSITION: | 
 |         case WM_IME_NOTIFY: | 
 |         case WM_IME_CONTROL: | 
 |         case WM_IME_COMPOSITIONFULL: | 
 |         case WM_IME_SELECT: | 
 |         case WM_IME_CHAR: | 
 |             return 0L; | 
 |         default: | 
 |             break; | 
 |         } | 
 |     } | 
 |  | 
 |     switch(msg) | 
 |     { | 
 |         case WM_CREATE: | 
 |         { | 
 |             LPIMEPRIVATE myPrivate; | 
 |             LPINPUTCONTEXT lpIMC; | 
 |  | 
 |             SetWindowTextA(hwnd,"Wine Ime Active"); | 
 |  | 
 |             lpIMC = LockRealIMC(hIMC); | 
 |             if (lpIMC) | 
 |             { | 
 |                 myPrivate = ImmLockIMCC(lpIMC->hPrivate); | 
 |                 myPrivate->hwndDefault = hwnd; | 
 |                 ImmUnlockIMCC(lpIMC->hPrivate); | 
 |             } | 
 |             UnlockRealIMC(hIMC); | 
 |  | 
 |             return TRUE; | 
 |         } | 
 |         case WM_PAINT: | 
 |             PaintDefaultIMEWnd(hIMC, hwnd); | 
 |             return FALSE; | 
 |  | 
 |         case WM_NCCREATE: | 
 |             return TRUE; | 
 |  | 
 |         case WM_SETFOCUS: | 
 |             if (wParam) | 
 |                 SetFocus((HWND)wParam); | 
 |             else | 
 |                 FIXME("Received focus, should never have focus\n"); | 
 |             break; | 
 |         case WM_IME_COMPOSITION: | 
 |             DefaultIMEComposition(hIMC, hwnd, lParam); | 
 |             break; | 
 |         case WM_IME_STARTCOMPOSITION: | 
 |             DefaultIMEStartComposition(hIMC, hwnd); | 
 |             break; | 
 |         case WM_IME_ENDCOMPOSITION: | 
 |             TRACE("IME message %s, 0x%lx, 0x%lx\n", | 
 |                     "WM_IME_ENDCOMPOSITION", wParam, lParam); | 
 |             ShowWindow(hwnd,SW_HIDE); | 
 |             break; | 
 |         case WM_IME_SELECT: | 
 |             TRACE("IME message %s, 0x%lx, 0x%lx\n","WM_IME_SELECT", wParam, lParam); | 
 |             break; | 
 |         case WM_IME_CONTROL: | 
 |             TRACE("IME message %s, 0x%lx, 0x%lx\n","WM_IME_CONTROL", wParam, lParam); | 
 |             rc = 1; | 
 |             break; | 
 |         case WM_IME_NOTIFY: | 
 |             rc = ImeHandleNotify(hIMC,hwnd,msg,wParam,lParam); | 
 |             break; | 
 |        default: | 
 |             TRACE("Non-standard message 0x%x\n",msg); | 
 |     } | 
 |     /* check the MSIME messages */ | 
 |     if (msg == WM_MSIME_SERVICE) | 
 |     { | 
 |             TRACE("IME message %s, 0x%lx, 0x%lx\n","WM_MSIME_SERVICE", wParam, lParam); | 
 |             rc = FALSE; | 
 |     } | 
 |     else if (msg == WM_MSIME_RECONVERTOPTIONS) | 
 |     { | 
 |             TRACE("IME message %s, 0x%lx, 0x%lx\n","WM_MSIME_RECONVERTOPTIONS", wParam, lParam); | 
 |     } | 
 |     else if (msg == WM_MSIME_MOUSE) | 
 |     { | 
 |             TRACE("IME message %s, 0x%lx, 0x%lx\n","WM_MSIME_MOUSE", wParam, lParam); | 
 |     } | 
 |     else if (msg == WM_MSIME_RECONVERTREQUEST) | 
 |     { | 
 |             TRACE("IME message %s, 0x%lx, 0x%lx\n","WM_MSIME_RECONVERTREQUEST", wParam, lParam); | 
 |     } | 
 |     else if (msg == WM_MSIME_RECONVERT) | 
 |     { | 
 |             TRACE("IME message %s, 0x%lx, 0x%lx\n","WM_MSIME_RECONVERT", wParam, lParam); | 
 |     } | 
 |     else if (msg == WM_MSIME_QUERYPOSITION) | 
 |     { | 
 |             TRACE("IME message %s, 0x%lx, 0x%lx\n","WM_MSIME_QUERYPOSITION", wParam, lParam); | 
 |     } | 
 |     else if (msg == WM_MSIME_DOCUMENTFEED) | 
 |     { | 
 |             TRACE("IME message %s, 0x%lx, 0x%lx\n","WM_MSIME_DOCUMENTFEED", wParam, lParam); | 
 |     } | 
 |     /* DefWndProc if not an IME message */ | 
 |     if (!rc && !((msg >= WM_IME_STARTCOMPOSITION && msg <= WM_IME_KEYLAST) || | 
 |                       (msg >= WM_IME_SETCONTEXT && msg <= WM_IME_KEYUP))) | 
 |         rc = DefWindowProcW(hwnd,msg,wParam,lParam); | 
 |  | 
 |     return rc; | 
 | } |