| /* |
| * 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, 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(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 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 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); |
| 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"); |
| |
| 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; |
| |
| 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; |
| } |
| |
| 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; |
| } |
| else |
| { |
| newCompStr = updateCompStr(lpIMC->hCompStr, NULL, 0, &flags); |
| ImmDestroyIMCC(lpIMC->hCompStr); |
| lpIMC->hCompStr = newCompStr; |
| } |
| |
| if (cursor_valid) |
| { |
| LPCOMPOSITIONSTRING compstr; |
| compstr = ImmLockIMCC(lpIMC->hCompStr); |
| compstr->dwCursorPos = cursor_pos; |
| ImmUnlockIMCC(lpIMC->hCompStr); |
| flags |= GCS_CURSORPOS; |
| } |
| |
| } |
| |
| 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) |
| { |
| 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_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; |
| } |