/* * The IME for interfacing with Mac input methods * * Copyright 2008, 2013 CodeWeavers, Aric Stewart * Copyright 2013 Ken Thomases for CodeWeavers Inc. * * 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. */ #include "config.h" #include <stdarg.h> #include "macdrv.h" #include "winuser.h" #include "imm.h" #include "ddk/imm.h" WINE_DEFAULT_DEBUG_CHANNEL(imm); #define FROM_MACDRV ((HIMC)0xcafe1337) typedef struct _IMEPRIVATE { BOOL bInComposition; BOOL bInternalState; HFONT textfont; HWND hwndDefault; UINT repeat; } IMEPRIVATE, *LPIMEPRIVATE; typedef struct _tagTRANSMSG { UINT message; WPARAM wParam; LPARAM lParam; } TRANSMSG, *LPTRANSMSG; static const WCHAR UI_CLASS_NAME[] = {'W','i','n','e',' ','M','a','c',' ','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 HIMC RealIMC(HIMC hIMC) { if (hIMC == FROM_MACDRV) { 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 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, LPCWSTR compstr, DWORD len, DWORD *flags) { /* We need to make sure the CompStr, CompClause 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(WCHAR); needed_size += lpcs->dwResultReadClauseLen; needed_size += lpcs->dwResultReadStrLen * sizeof(WCHAR); needed_size += lpcs->dwResultClauseLen; needed_size += lpcs->dwResultStrLen * sizeof(WCHAR); 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; new_one->dwCursorPos = lpcs->dwCursorPos; 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); } else { new_one->dwCursorPos = len; *flags |= GCS_CURSORPOS; } /* 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); } else new_one->dwCompClauseLen = 0; /* CompStr */ new_one->dwCompStrLen = len; if (len > 0) { new_one->dwCompStrOffset = current_offset; memcpy(&newdata[current_offset], compstr, len * sizeof(WCHAR)); } 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(WCHAR); needed_size += lpcs->dwCompAttrLen; needed_size += lpcs->dwCompClauseLen; needed_size += lpcs->dwCompStrLen * sizeof(WCHAR); needed_size += lpcs->dwResultReadClauseLen; needed_size += lpcs->dwResultReadStrLen * sizeof(WCHAR); 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); } else new_one->dwResultClauseLen = 0; /* 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 BOOL GenerateMessageToTransKey(LPDWORD lpTransBuf, UINT *uNumTranMsgs, UINT msg, WPARAM wParam, LPARAM lParam) { LPTRANSMSG ptr; if (*uNumTranMsgs + 1 >= (UINT)*lpTransBuf) return FALSE; ptr = (LPTRANSMSG)(lpTransBuf + 1 + *uNumTranMsgs * 3); ptr->message = msg; ptr->wParam = wParam; ptr->lParam = lParam; (*uNumTranMsgs)++; return TRUE; } 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; } 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); } 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) { TRACE("%x %p\n", uSubFunc, lpData); return 0; } BOOL WINAPI ImeProcessKey(HIMC hIMC, UINT vKey, LPARAM lKeyData, const LPBYTE lpbKeyState) { LPINPUTCONTEXT lpIMC; BOOL inIME; TRACE("hIMC %p vKey 0x%04x lKeyData 0x%08lx lpbKeyState %p\n", hIMC, vKey, lKeyData, lpbKeyState); switch (vKey) { case VK_SHIFT: case VK_CONTROL: case VK_CAPITAL: case VK_MENU: return FALSE; } inIME = macdrv_using_input_method(); lpIMC = LockRealIMC(hIMC); if (lpIMC) { LPIMEPRIVATE myPrivate; myPrivate = ImmLockIMCC(lpIMC->hPrivate); if (inIME && !myPrivate->bInternalState) ImmSetOpenStatus(RealIMC(FROM_MACDRV), TRUE); else if (!inIME && myPrivate->bInternalState) { ShowWindow(myPrivate->hwndDefault, SW_HIDE); ImmDestroyIMCC(lpIMC->hCompStr); lpIMC->hCompStr = ImeCreateBlankCompStr(); ImmSetOpenStatus(RealIMC(FROM_MACDRV), FALSE); } myPrivate->repeat = (lKeyData >> 30) & 0x1; myPrivate->bInternalState = inIME; ImmUnlockIMCC(lpIMC->hPrivate); } UnlockRealIMC(hIMC); return inIME; } BOOL WINAPI ImeSelect(HIMC hIMC, BOOL fSelect) { LPINPUTCONTEXT lpIMC; TRACE("%p %s\n", hIMC, fSelect ? "TRUE" : "FALSE"); if (hIMC == FROM_MACDRV) { ERR("ImeSelect should never be called from Cocoa\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); if (myPrivate->bInComposition) GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0); if (myPrivate->bInternalState) ImmSetOpenStatus(RealIMC(FROM_MACDRV), FALSE); myPrivate->bInComposition = FALSE; myPrivate->bInternalState = FALSE; myPrivate->textfont = NULL; myPrivate->hwndDefault = NULL; myPrivate->repeat = 0; 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) { UINT vkey; LPINPUTCONTEXT lpIMC; LPIMEPRIVATE myPrivate; HWND hwndDefault; UINT repeat; int done = 0; TRACE("uVKey 0x%04x uScanCode 0x%04x fuState %u hIMC %p\n", uVKey, uScanCode, fuState, hIMC); vkey = LOWORD(uVKey); if (vkey == VK_KANA || vkey == VK_KANJI || vkey == VK_MENU) { TRACE("Skipping metakey\n"); return 0; } lpIMC = LockRealIMC(hIMC); myPrivate = ImmLockIMCC(lpIMC->hPrivate); if (!myPrivate->bInternalState) { ImmUnlockIMCC(lpIMC->hPrivate); UnlockRealIMC(hIMC); return 0; } repeat = myPrivate->repeat; hwndDefault = myPrivate->hwndDefault; ImmUnlockIMCC(lpIMC->hPrivate); UnlockRealIMC(hIMC); TRACE("Processing Mac 0x%04x\n", vkey); macdrv_process_text_input(uVKey, uScanCode, repeat, lpbKeyState, hIMC, &done); while (!done) MsgWaitForMultipleObjectsEx(0, NULL, INFINITE, QS_POSTMESSAGE | QS_SENDMESSAGE, 0); if (done < 0) { UINT msgs = 0; UINT msg = (uScanCode & 0x8000) ? WM_KEYUP : WM_KEYDOWN; /* KeyStroke not processed by the IME * so we need to rebuild the KeyDown message and pass it on to WINE */ if (!GenerateMessageToTransKey(lpdwTransKey, &msgs, msg, vkey, MAKELONG(0x0001, uScanCode))) GenerateIMEMessage(hIMC, msg, vkey, MAKELONG(0x0001, uScanCode)); return msgs; } else UpdateDataInDefaultIMEWindow(hIMC, hwndDefault, FALSE); 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("NI_CONTEXTUPDATED: IMC_SETCOMPOSITIONWINDOW\n"); break; case IMC_SETCONVERSIONMODE: FIXME("NI_CONTEXTUPDATED: IMC_SETCONVERSIONMODE\n"); break; case IMC_SETSENTENCEMODE: FIXME("NI_CONTEXTUPDATED: IMC_SETSENTENCEMODE\n"); break; case IMC_SETCANDIDATEPOS: FIXME("NI_CONTEXTUPDATED: IMC_SETCANDIDATEPOS\n"); break; case IMC_SETCOMPOSITIONFONT: { LPIMEPRIVATE myPrivate; TRACE("NI_CONTEXTUPDATED: 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("NI_CONTEXTUPDATED: IMC_SETOPENSTATUS\n"); myPrivate = ImmLockIMCC(lpIMC->hPrivate); if (lpIMC->fOpen != myPrivate->bInternalState && myPrivate->bInComposition) { if(lpIMC->fOpen == FALSE) { 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("NI_CONTEXTUPDATED: 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("NI_COMPOSITIONSTR: 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); } myPrivate = ImmLockIMCC(lpIMC->hPrivate); if (cplen > 0) { WCHAR param = cpstr[0]; DWORD flags = GCS_COMPSTR; newCompStr = updateResultStr(lpIMC->hCompStr, cpstr, cplen); ImmDestroyIMCC(lpIMC->hCompStr); lpIMC->hCompStr = newCompStr; newCompStr = updateCompStr(lpIMC->hCompStr, NULL, 0, &flags); ImmDestroyIMCC(lpIMC->hCompStr); lpIMC->hCompStr = newCompStr; GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, 0, flags); GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, param, GCS_RESULTSTR | GCS_RESULTCLAUSE); GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0); } else if (myPrivate->bInComposition) GenerateIMEMessage(hIMC, WM_IME_ENDCOMPOSITION, 0, 0); myPrivate->bInComposition = FALSE; ImmUnlockIMCC(lpIMC->hPrivate); bRet = TRUE; } break; case CPS_CONVERT: FIXME("NI_COMPOSITIONSTR: CPS_CONVERT\n"); break; case CPS_REVERT: FIXME("NI_COMPOSITIONSTR: CPS_REVERT\n"); break; case CPS_CANCEL: { LPIMEPRIVATE myPrivate; TRACE("NI_COMPOSITIONSTR: CPS_CANCEL\n"); macdrv_clear_ime_text(); 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("NI_COMPOSITIONSTR: 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; } static BOOL IME_SetCompositionString(void* hIMC, DWORD dwIndex, LPCVOID lpComp, DWORD dwCompLen, DWORD cursor_pos, BOOL cursor_valid) { LPINPUTCONTEXT lpIMC; DWORD flags = 0; WCHAR wParam = 0; LPIMEPRIVATE myPrivate; BOOL sendMessage = TRUE; TRACE("(%p, %d, %p, %d):\n", hIMC, dwIndex, lpComp, dwCompLen); /* * Explanation: * this sets the composition string in the imm32.dll level * of the composition buffer. * TODO: set the Cocoa window's marked text string and tell text input context */ 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; } /* clear existing result */ newCompStr = updateResultStr(lpIMC->hCompStr, NULL, 0); ImmDestroyIMCC(lpIMC->hCompStr); lpIMC->hCompStr = newCompStr; flags = GCS_COMPSTR; if (dwCompLen && lpComp) { newCompStr = updateCompStr(lpIMC->hCompStr, (LPCWSTR)lpComp, dwCompLen / sizeof(WCHAR), &flags); ImmDestroyIMCC(lpIMC->hCompStr); lpIMC->hCompStr = newCompStr; wParam = ((const WCHAR*)lpComp)[0]; flags |= GCS_COMPCLAUSE | GCS_COMPATTR | GCS_DELTASTART; if (cursor_valid) { LPCOMPOSITIONSTRING compstr; compstr = ImmLockIMCC(lpIMC->hCompStr); compstr->dwCursorPos = cursor_pos; ImmUnlockIMCC(lpIMC->hCompStr); flags |= GCS_CURSORPOS; } } else { NotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0); sendMessage = FALSE; } } if (sendMessage) { GenerateIMEMessage(hIMC, WM_IME_COMPOSITION, wParam, flags); ImmUnlockIMCC(lpIMC->hPrivate); UnlockRealIMC(hIMC); } return TRUE; } BOOL WINAPI ImeSetCompositionString(HIMC hIMC, DWORD dwIndex, LPCVOID lpComp, DWORD dwCompLen, LPCVOID lpRead, DWORD dwReadLen) { TRACE("(%p, %d, %p, %d, %p, %d):\n", hIMC, dwIndex, lpComp, dwCompLen, lpRead, dwReadLen); if (lpRead && dwReadLen) FIXME("Reading string unimplemented\n"); return IME_SetCompositionString(hIMC, dwIndex, lpComp, dwCompLen, 0, FALSE); } 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; } static void IME_NotifyComplete(void* hIMC) { NotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); } /***** * 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 because 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 DefaultIMEComposition(HIMC hIMC, HWND hwnd, LPARAM lParam) { TRACE("IME message WM_IME_COMPOSITION 0x%lx\n", lParam); if (!(lParam & GCS_RESULTSTR)) 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_MACDRV); /* 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; } static BOOL WINAPI register_classes( INIT_ONCE *once, void *param, void **context ) { WNDCLASSW wndClass; ZeroMemory(&wndClass, sizeof(WNDCLASSW)); wndClass.style = CS_GLOBALCLASS | CS_IME | CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = (WNDPROC) IME_WindowProc; wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 2 * sizeof(LONG_PTR); wndClass.hInstance = macdrv_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"); return TRUE; } BOOL WINAPI ImeInquire(LPIMEINFO lpIMEInfo, LPWSTR lpszUIClass, LPCWSTR lpszOption) { static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; TRACE("\n"); InitOnceExecuteOnce( &init_once, register_classes, NULL, NULL ); lpIMEInfo->dwPrivateDataSize = sizeof(IMEPRIVATE); lpIMEInfo->fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET; lpIMEInfo->fdwConversionCaps = IME_CMODE_NATIVE | IME_CMODE_FULLSHAPE; lpIMEInfo->fdwSentenceCaps = IME_SMODE_AUTOMATIC; lpIMEInfo->fdwUICaps = UI_CAP_2700; /* Tell App we cannot accept ImeSetCompositionString calls */ /* FIXME: Can we? */ lpIMEInfo->fdwSCSCaps = 0; lpIMEInfo->fdwSelectCaps = SELECT_CAP_CONVERSION; lstrcpyW(lpszUIClass, UI_CLASS_NAME); return TRUE; } /* Interfaces to other parts of the Mac driver */ /*********************************************************************** * macdrv_im_set_text */ void macdrv_im_set_text(const macdrv_event *event) { HWND hwnd = macdrv_get_window_hwnd(event->window); void *himc = event->im_set_text.data; TRACE("win %p/%p himc %p text %s complete %u\n", hwnd, event->window, himc, debugstr_cf(event->im_set_text.text), event->im_set_text.complete); if (!himc) himc = RealIMC(FROM_MACDRV); if (event->im_set_text.text) { CFIndex length = CFStringGetLength(event->im_set_text.text); const UniChar *chars = CFStringGetCharactersPtr(event->im_set_text.text); UniChar *buffer = NULL; if (!chars) { buffer = HeapAlloc(GetProcessHeap(), 0, length * sizeof(*buffer)); CFStringGetCharacters(event->im_set_text.text, CFRangeMake(0, length), buffer); chars = buffer; } if (himc) IME_SetCompositionString(himc, SCS_SETSTR, chars, length * sizeof(*chars), event->im_set_text.cursor_pos, !event->im_set_text.complete); else { INPUT input; CFIndex i; input.type = INPUT_KEYBOARD; input.ki.wVk = 0; input.ki.time = 0; input.ki.dwExtraInfo = 0; for (i = 0; i < length; i++) { input.ki.wScan = chars[i]; input.ki.dwFlags = KEYEVENTF_UNICODE; __wine_send_input(hwnd, &input); input.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP; __wine_send_input(hwnd, &input); } } HeapFree(GetProcessHeap(), 0, buffer); } if (event->im_set_text.complete) IME_NotifyComplete(himc); } /*********************************************************************** * macdrv_sent_text_input */ void macdrv_sent_text_input(const macdrv_event *event) { TRACE("handled: %s\n", event->sent_text_input.handled ? "TRUE" : "FALSE"); *event->sent_text_input.done = event->sent_text_input.handled ? 1 : -1; } /************************************************************************** * query_ime_char_rect */ BOOL query_ime_char_rect(macdrv_query* query) { HWND hwnd = macdrv_get_window_hwnd(query->window); void *himc = query->ime_char_rect.data; CFRange* range = &query->ime_char_rect.range; CGRect* rect = &query->ime_char_rect.rect; IMECHARPOSITION charpos; BOOL ret = FALSE; TRACE("win %p/%p himc %p range %ld-%ld\n", hwnd, query->window, himc, range->location, range->length); if (!himc) himc = RealIMC(FROM_MACDRV); charpos.dwSize = sizeof(charpos); charpos.dwCharPos = range->location; if (ImmRequestMessageW(himc, IMR_QUERYCHARPOSITION, (ULONG_PTR)&charpos)) { int i; *rect = CGRectMake(charpos.pt.x, charpos.pt.y, 0, charpos.cLineHeight); /* iterate over rest of length to extend rect */ for (i = 1; i < range->length; i++) { charpos.dwSize = sizeof(charpos); charpos.dwCharPos = range->location + i; if (!ImmRequestMessageW(himc, IMR_QUERYCHARPOSITION, (ULONG_PTR)&charpos) || charpos.pt.y != rect->origin.y) { range->length = i; break; } rect->size.width = charpos.pt.x - rect->origin.x; } ret = TRUE; } if (!ret) { LPINPUTCONTEXT ic = ImmLockIMC(himc); if (ic) { LPIMEPRIVATE private = ImmLockIMCC(ic->hPrivate); LPBYTE compdata = ImmLockIMCC(ic->hCompStr); LPCOMPOSITIONSTRING compstr = (LPCOMPOSITIONSTRING)compdata; LPWSTR str = (LPWSTR)(compdata + compstr->dwCompStrOffset); if (private->hwndDefault && compstr->dwCompStrOffset && IsWindowVisible(private->hwndDefault)) { HDC dc = GetDC(private->hwndDefault); HFONT oldfont = NULL; SIZE size; if (private->textfont) oldfont = SelectObject(dc, private->textfont); if (range->location > compstr->dwCompStrLen) range->location = compstr->dwCompStrLen; if (range->location + range->length > compstr->dwCompStrLen) range->length = compstr->dwCompStrLen - range->location; GetTextExtentPoint32W(dc, str, range->location, &size); charpos.rcDocument.left = size.cx; charpos.rcDocument.top = 0; GetTextExtentPoint32W(dc, str, range->location + range->length, &size); charpos.rcDocument.right = size.cx; charpos.rcDocument.bottom = size.cy; if (ic->cfCompForm.dwStyle == CFS_DEFAULT) OffsetRect(&charpos.rcDocument, 10, 10); LPtoDP(dc, (POINT*)&charpos.rcDocument, 2); MapWindowPoints(private->hwndDefault, 0, (POINT*)&charpos.rcDocument, 2); *rect = cgrect_from_rect(charpos.rcDocument); ret = TRUE; if (oldfont) SelectObject(dc, oldfont); ReleaseDC(private->hwndDefault, dc); } ImmUnlockIMCC(ic->hCompStr); ImmUnlockIMCC(ic->hPrivate); } ImmUnlockIMC(himc); } if (!ret) { GUITHREADINFO gti; gti.cbSize = sizeof(gti); if (GetGUIThreadInfo(0, >i)) { MapWindowPoints(gti.hwndCaret, 0, (POINT*)>i.rcCaret, 2); *rect = cgrect_from_rect(gti.rcCaret); ret = TRUE; } } if (ret && range->length && !rect->size.width) rect->size.width = 1; TRACE(" -> %s range %ld-%ld rect %s\n", ret ? "TRUE" : "FALSE", range->location, range->length, wine_dbgstr_cgrect(*rect)); return ret; }