/* * PROJECT: ReactOS IMM32 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) * PURPOSE: Implementing Far-Eastern languages input * COPYRIGHT: Copyright 1998 Patrik Stridvall * Copyright 2002, 2003, 2007 CodeWeavers, Aric Stewart * Copyright 2017 James Tabor * Copyright 2018 Amine Khaldi * Copyright 2020-2022 Katayama Hirofumi MZ */ #include "precomp.h" #include WINE_DEFAULT_DEBUG_CHANNEL(imm); HMODULE ghImm32Inst = NULL; // Win: ghInst PSERVERINFO gpsi = NULL; // Win: gpsi SHAREDINFO gSharedInfo = { NULL }; // Win: gSharedInfo BYTE gfImmInitialized = FALSE; // Win: gfInitialized ULONG_PTR gHighestUserAddress = 0; // Win: ImmInitializeGlobals static BOOL APIENTRY ImmInitializeGlobals(HMODULE hMod) { NTSTATUS status; SYSTEM_BASIC_INFORMATION SysInfo; if (hMod) ghImm32Inst = hMod; if (gfImmInitialized) return TRUE; status = RtlInitializeCriticalSection(&gcsImeDpi); if (NT_ERROR(status)) { ERR("\n"); return FALSE; } status = NtQuerySystemInformation(SystemBasicInformation, &SysInfo, sizeof(SysInfo), NULL); if (NT_ERROR(status)) { ERR("\n"); return FALSE; } gHighestUserAddress = SysInfo.MaximumUserModeAddress; gfImmInitialized = TRUE; return TRUE; } /*********************************************************************** * ImmRegisterClient(IMM32.@) * ( Undocumented, called from user32.dll ) */ BOOL WINAPI ImmRegisterClient(PSHAREDINFO ptr, HINSTANCE hMod) { gSharedInfo = *ptr; gpsi = gSharedInfo.psi; return ImmInitializeGlobals(hMod); } /*********************************************************************** * ImmLoadLayout (IMM32.@) */ BOOL WINAPI ImmLoadLayout(HKL hKL, PIMEINFOEX pImeInfoEx) { DWORD cbData, dwType; HKEY hLayoutKey; LONG error; WCHAR szLayout[MAX_PATH]; TRACE("(%p, %p)\n", hKL, pImeInfoEx); ZeroMemory(pImeInfoEx, sizeof(IMEINFOEX)); if (IS_IME_HKL(hKL) || !IS_CICERO_MODE() || IS_16BIT_MODE()) { StringCchPrintfW(szLayout, _countof(szLayout), L"%s\\%08lX", REGKEY_KEYBOARD_LAYOUTS, HandleToUlong(hKL)); error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, szLayout, 0, KEY_READ, &hLayoutKey); if (IS_ERROR_UNEXPECTEDLY(error)) return FALSE; } else { error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, REGKEY_IMM, 0, KEY_READ, &hLayoutKey); if (IS_ERROR_UNEXPECTEDLY(error)) return FALSE; } cbData = sizeof(pImeInfoEx->wszImeFile); error = RegQueryValueExW(hLayoutKey, L"Ime File", NULL, &dwType, (LPBYTE)pImeInfoEx->wszImeFile, &cbData); pImeInfoEx->wszImeFile[_countof(pImeInfoEx->wszImeFile) - 1] = UNICODE_NULL; RegCloseKey(hLayoutKey); pImeInfoEx->fLoadFlag = 0; if (IS_ERROR_UNEXPECTEDLY(error)) return FALSE; if (dwType != REG_SZ) { ERR("\n"); return FALSE; } pImeInfoEx->hkl = hKL; return Imm32LoadImeVerInfo(pImeInfoEx); } /*********************************************************************** * ImmFreeLayout (IMM32.@) */ BOOL WINAPI ImmFreeLayout(DWORD dwUnknown) { WCHAR szKBD[KL_NAMELENGTH]; UINT iKL, cKLs; HKL hOldKL, hNewKL, *pList; PIMEDPI pImeDpi; LANGID LangID; TRACE("(0x%lX)\n", dwUnknown); hOldKL = GetKeyboardLayout(0); if (dwUnknown == 1) { if (!IS_IME_HKL(hOldKL)) return TRUE; LangID = LANGIDFROMLCID(GetSystemDefaultLCID()); cKLs = GetKeyboardLayoutList(0, NULL); if (cKLs) { pList = ImmLocalAlloc(0, cKLs * sizeof(HKL)); if (IS_NULL_UNEXPECTEDLY(pList)) return FALSE; cKLs = GetKeyboardLayoutList(cKLs, pList); for (iKL = 0; iKL < cKLs; ++iKL) { if (!IS_IME_HKL(pList[iKL])) { LangID = LOWORD(pList[iKL]); break; } } ImmLocalFree(pList); } StringCchPrintfW(szKBD, _countof(szKBD), L"%08X", LangID); if (!LoadKeyboardLayoutW(szKBD, KLF_ACTIVATE)) { WARN("Default to English US\n"); LoadKeyboardLayoutW(L"00000409", KLF_ACTIVATE | 0x200); } } else if (dwUnknown == 2) { RtlEnterCriticalSection(&gcsImeDpi); Retry: for (pImeDpi = gpImeDpiList; pImeDpi; pImeDpi = pImeDpi->pNext) { if (Imm32ReleaseIME(pImeDpi->hKL)) goto Retry; } RtlLeaveCriticalSection(&gcsImeDpi); } else { hNewKL = (HKL)(DWORD_PTR)dwUnknown; if (IS_IME_HKL(hNewKL) && hNewKL != hOldKL) Imm32ReleaseIME(hNewKL); } return TRUE; } // Win: SelectInputContext VOID APIENTRY Imm32SelectInputContext(HKL hNewKL, HKL hOldKL, HIMC hIMC) { PCLIENTIMC pClientImc; LPINPUTCONTEXTDX pIC; LPGUIDELINE pGL; LPCANDIDATEINFO pCI; LPCOMPOSITIONSTRING pCS; LOGFONTA LogFontA; LOGFONTW LogFontW; BOOL fOldOpen, bIsNewHKLIme = TRUE, bIsOldHKLIme = TRUE, bClientWide, bNewDpiWide; DWORD cbNewPrivate = 0, cbOldPrivate = 0, dwOldConversion, dwOldSentence, dwSize, dwNewSize; PIMEDPI pNewImeDpi = NULL, pOldImeDpi = NULL; HANDLE hPrivate; PIME_STATE pNewState = NULL, pOldState = NULL; pClientImc = ImmLockClientImc(hIMC); if (IS_NULL_UNEXPECTEDLY(pClientImc)) return; pNewImeDpi = ImmLockImeDpi(hNewKL); if (hNewKL != hOldKL) pOldImeDpi = ImmLockImeDpi(hOldKL); if (pNewImeDpi) { cbNewPrivate = pNewImeDpi->ImeInfo.dwPrivateDataSize; pClientImc->uCodePage = pNewImeDpi->uCodePage; } else { pClientImc->uCodePage = CP_ACP; } if (pOldImeDpi) cbOldPrivate = pOldImeDpi->ImeInfo.dwPrivateDataSize; cbNewPrivate = max(cbNewPrivate, sizeof(DWORD)); cbOldPrivate = max(cbOldPrivate, sizeof(DWORD)); if (pClientImc->hKL == hOldKL) { if (pOldImeDpi) { if (IS_IME_HKL(hOldKL)) pOldImeDpi->ImeSelect(hIMC, FALSE); else if (IS_CICERO_MODE() && !IS_16BIT_MODE()) pOldImeDpi->CtfImeSelectEx(hIMC, FALSE, hOldKL); } pClientImc->hKL = NULL; } if (CtfImmIsTextFrameServiceDisabled() && IS_CICERO_MODE() && !IS_16BIT_MODE()) { bIsNewHKLIme = IS_IME_HKL(hNewKL); bIsOldHKLIme = IS_IME_HKL(hOldKL); } pIC = (LPINPUTCONTEXTDX)Imm32InternalLockIMC(hIMC, FALSE); if (!pIC) { if (pNewImeDpi) { if (IS_IME_HKL(hNewKL)) pNewImeDpi->ImeSelect(hIMC, TRUE); else if (IS_CICERO_MODE() && !IS_16BIT_MODE()) pNewImeDpi->CtfImeSelectEx(hIMC, TRUE, hNewKL); pClientImc->hKL = hNewKL; } } else { dwOldConversion = pIC->fdwConversion; dwOldSentence = pIC->fdwSentence; fOldOpen = pIC->fOpen; if (pNewImeDpi) { bClientWide = (pClientImc->dwFlags & CLIENTIMC_WIDE); bNewDpiWide = ImeDpi_IsUnicode(pNewImeDpi); if (bClientWide && !bNewDpiWide) { if (pIC->fdwInit & INIT_LOGFONT) { LogFontWideToAnsi(&pIC->lfFont.W, &LogFontA); pIC->lfFont.A = LogFontA; } pClientImc->dwFlags &= ~CLIENTIMC_WIDE; } else if (!bClientWide && bNewDpiWide) { if (pIC->fdwInit & INIT_LOGFONT) { LogFontAnsiToWide(&pIC->lfFont.A, &LogFontW); pIC->lfFont.W = LogFontW; } pClientImc->dwFlags |= CLIENTIMC_WIDE; } } if (cbOldPrivate != cbNewPrivate) { hPrivate = ImmReSizeIMCC(pIC->hPrivate, cbNewPrivate); if (!hPrivate) { ImmDestroyIMCC(pIC->hPrivate); hPrivate = ImmCreateIMCC(cbNewPrivate); } pIC->hPrivate = hPrivate; } #define MAX_IMCC_SIZE 0x1000 dwSize = ImmGetIMCCSize(pIC->hMsgBuf); if (ImmGetIMCCLockCount(pIC->hMsgBuf) || dwSize > MAX_IMCC_SIZE) { ImmDestroyIMCC(pIC->hMsgBuf); pIC->hMsgBuf = ImmCreateIMCC(sizeof(UINT)); pIC->dwNumMsgBuf = 0; } dwSize = ImmGetIMCCSize(pIC->hGuideLine); dwNewSize = sizeof(GUIDELINE); if (ImmGetIMCCLockCount(pIC->hGuideLine) || dwSize < dwNewSize || dwSize > MAX_IMCC_SIZE) { ImmDestroyIMCC(pIC->hGuideLine); pIC->hGuideLine = ImmCreateIMCC(dwNewSize); pGL = ImmLockIMCC(pIC->hGuideLine); if (pGL) { pGL->dwSize = dwNewSize; ImmUnlockIMCC(pIC->hGuideLine); } } dwSize = ImmGetIMCCSize(pIC->hCandInfo); dwNewSize = sizeof(CANDIDATEINFO); if (ImmGetIMCCLockCount(pIC->hCandInfo) || dwSize < dwNewSize || dwSize > MAX_IMCC_SIZE) { ImmDestroyIMCC(pIC->hCandInfo); pIC->hCandInfo = ImmCreateIMCC(dwNewSize); pCI = ImmLockIMCC(pIC->hCandInfo); if (pCI) { pCI->dwSize = dwNewSize; ImmUnlockIMCC(pIC->hCandInfo); } } dwSize = ImmGetIMCCSize(pIC->hCompStr); dwNewSize = sizeof(COMPOSITIONSTRING); if (ImmGetIMCCLockCount(pIC->hCompStr) || dwSize < dwNewSize || dwSize > MAX_IMCC_SIZE) { ImmDestroyIMCC(pIC->hCompStr); pIC->hCompStr = ImmCreateIMCC(dwNewSize); pCS = ImmLockIMCC(pIC->hCompStr); if (pCS) { pCS->dwSize = dwNewSize; ImmUnlockIMCC(pIC->hCompStr); } } #undef MAX_IMCC_SIZE if (pOldImeDpi && bIsOldHKLIme) { pOldState = Imm32FetchImeState(pIC, hOldKL); if (pOldState) Imm32SaveImeStateSentence(pIC, pOldState, hOldKL); } if (pNewImeDpi && bIsNewHKLIme) pNewState = Imm32FetchImeState(pIC, hNewKL); if (pOldState != pNewState) { if (pOldState) { pOldState->fOpen = !!pIC->fOpen; pOldState->dwConversion = pIC->fdwConversion; pOldState->dwConversion &= ~IME_CMODE_EUDC; pOldState->dwSentence = pIC->fdwSentence; pOldState->dwInit = pIC->fdwInit; } if (pNewState) { if (pIC->dwChange & INPUTCONTEXTDX_CHANGE_FORCE_OPEN) { pIC->dwChange &= ~INPUTCONTEXTDX_CHANGE_FORCE_OPEN; pIC->fOpen = TRUE; } else { pIC->fOpen = pNewState->fOpen; } pIC->fdwConversion = pNewState->dwConversion; pIC->fdwConversion &= ~IME_CMODE_EUDC; pIC->fdwSentence = pNewState->dwSentence; pIC->fdwInit = pNewState->dwInit; } } if (pNewState) Imm32LoadImeStateSentence(pIC, pNewState, hNewKL); if (pNewImeDpi) { if (IS_IME_HKL(hNewKL)) pNewImeDpi->ImeSelect(hIMC, TRUE); else if (IS_CICERO_MODE() && !IS_16BIT_MODE()) pNewImeDpi->CtfImeSelectEx(hIMC, TRUE, hNewKL); pClientImc->hKL = hNewKL; } pIC->dwChange = 0; if (pIC->fOpen != fOldOpen) pIC->dwChange |= INPUTCONTEXTDX_CHANGE_OPEN; if (pIC->fdwConversion != dwOldConversion) pIC->dwChange |= INPUTCONTEXTDX_CHANGE_CONVERSION; if (pIC->fdwSentence != dwOldSentence) pIC->dwChange |= INPUTCONTEXTDX_CHANGE_SENTENCE; ImmUnlockIMC(hIMC); } ImmUnlockImeDpi(pOldImeDpi); ImmUnlockImeDpi(pNewImeDpi); ImmUnlockClientImc(pClientImc); } typedef struct SELECT_LAYOUT { HKL hNewKL; HKL hOldKL; } SELECT_LAYOUT, *LPSELECT_LAYOUT; // Win: SelectContextProc static BOOL CALLBACK Imm32SelectContextProc(HIMC hIMC, LPARAM lParam) { LPSELECT_LAYOUT pSelect = (LPSELECT_LAYOUT)lParam; Imm32SelectInputContext(pSelect->hNewKL, pSelect->hOldKL, hIMC); return TRUE; } // Win: NotifyIMEProc static BOOL CALLBACK Imm32NotifyIMEProc(HIMC hIMC, LPARAM lParam) { ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, (DWORD)lParam, 0); return TRUE; } /*********************************************************************** * ImmActivateLayout (IMM32.@) */ BOOL WINAPI ImmActivateLayout(HKL hKL) { PIMEDPI pImeDpi; HKL hOldKL; LPARAM lParam; HWND hwndDefIME = NULL; SELECT_LAYOUT SelectLayout; hOldKL = GetKeyboardLayout(0); if (hOldKL == hKL && !(GetWin32ClientInfo()->CI_flags & CI_IMMACTIVATE)) return TRUE; ImmLoadIME(hKL); if (hOldKL != hKL) { pImeDpi = ImmLockImeDpi(hOldKL); if (pImeDpi) { if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_COMPLETE_ON_UNSELECT) lParam = CPS_COMPLETE; else lParam = CPS_CANCEL; ImmUnlockImeDpi(pImeDpi); ImmEnumInputContext(0, Imm32NotifyIMEProc, lParam); } hwndDefIME = ImmGetDefaultIMEWnd(NULL); if (IsWindow(hwndDefIME)) SendMessageW(hwndDefIME, WM_IME_SELECT, FALSE, (LPARAM)hOldKL); NtUserSetThreadLayoutHandles(hKL, hOldKL); } SelectLayout.hNewKL = hKL; SelectLayout.hOldKL = hOldKL; ImmEnumInputContext(0, Imm32SelectContextProc, (LPARAM)&SelectLayout); if (IsWindow(hwndDefIME)) SendMessageW(hwndDefIME, WM_IME_SELECT, TRUE, (LPARAM)hKL); return TRUE; } /* Win: Internal_CtfImeSetActiveContextAlways */ static VOID APIENTRY Imm32CiceroSetActiveContext(HIMC hIMC, BOOL fActive, HWND hWnd, HKL hKL) { TRACE("We have to do something\n"); } /*********************************************************************** * ImmAssociateContext (IMM32.@) */ HIMC WINAPI ImmAssociateContext(HWND hWnd, HIMC hIMC) { PWND pWnd; HWND hwndFocus; DWORD dwValue; HIMC hOldIMC; TRACE("(%p, %p)\n", hWnd, hIMC); if (!IS_IMM_MODE()) { TRACE("\n"); return NULL; } pWnd = ValidateHwnd(hWnd); if (IS_NULL_UNEXPECTEDLY(pWnd)) return NULL; if (hIMC && IS_CROSS_THREAD_HIMC(hIMC)) return NULL; hOldIMC = pWnd->hImc; if (hOldIMC == hIMC) return hIMC; dwValue = NtUserAssociateInputContext(hWnd, hIMC, 0); switch (dwValue) { case 0: return hOldIMC; case 1: hwndFocus = (HWND)NtUserQueryWindow(hWnd, QUERY_WINDOW_FOCUS); if (hwndFocus == hWnd) { ImmSetActiveContext(hWnd, hOldIMC, FALSE); ImmSetActiveContext(hWnd, hIMC, TRUE); } return hOldIMC; default: return NULL; } } /*********************************************************************** * ImmAssociateContextEx (IMM32.@) */ BOOL WINAPI ImmAssociateContextEx(HWND hWnd, HIMC hIMC, DWORD dwFlags) { HWND hwndFocus; PWND pFocusWnd; HIMC hOldIMC = NULL; DWORD dwValue; TRACE("(%p, %p, 0x%lX)\n", hWnd, hIMC, dwFlags); if (!IS_IMM_MODE()) { TRACE("\n"); return FALSE; } if (hIMC && !(dwFlags & IACE_DEFAULT) && IS_CROSS_THREAD_HIMC(hIMC)) return FALSE; hwndFocus = (HWND)NtUserQueryWindow(hWnd, QUERY_WINDOW_FOCUS); pFocusWnd = ValidateHwnd(hwndFocus); if (pFocusWnd) hOldIMC = pFocusWnd->hImc; dwValue = NtUserAssociateInputContext(hWnd, hIMC, dwFlags); switch (dwValue) { case 0: return TRUE; case 1: pFocusWnd = ValidateHwnd(hwndFocus); if (pFocusWnd) { hIMC = pFocusWnd->hImc; if (hIMC != hOldIMC) { ImmSetActiveContext(hwndFocus, hOldIMC, FALSE); ImmSetActiveContext(hwndFocus, hIMC, TRUE); } } return TRUE; default: return FALSE; } } /*********************************************************************** * ImmCreateContext (IMM32.@) */ HIMC WINAPI ImmCreateContext(void) { PCLIENTIMC pClientImc; HIMC hIMC; TRACE("()\n"); if (!IS_IMM_MODE()) { TRACE("\n"); return NULL; } pClientImc = ImmLocalAlloc(HEAP_ZERO_MEMORY, sizeof(CLIENTIMC)); if (IS_NULL_UNEXPECTEDLY(pClientImc)) return NULL; hIMC = NtUserCreateInputContext((ULONG_PTR)pClientImc); if (IS_NULL_UNEXPECTEDLY(hIMC)) { ImmLocalFree(pClientImc); return NULL; } RtlInitializeCriticalSection(&pClientImc->cs); pClientImc->dwCompatFlags = (DWORD)NtUserGetThreadState(THREADSTATE_IMECOMPATFLAGS); return hIMC; } // Win: DestroyImeModeSaver static VOID APIENTRY Imm32DestroyImeModeSaver(LPINPUTCONTEXTDX pIC) { PIME_STATE pState, pNext; PIME_SUBSTATE pSubState, pSubNext; for (pState = pIC->pState; pState; pState = pNext) { pNext = pState->pNext; for (pSubState = pState->pSubState; pSubState; pSubState = pSubNext) { pSubNext = pSubState->pNext; ImmLocalFree(pSubState); } ImmLocalFree(pState); } pIC->pState = NULL; } // Win: DestroyInputContext BOOL APIENTRY Imm32DestroyInputContext(HIMC hIMC, HKL hKL, BOOL bKeep) { PIMEDPI pImeDpi; LPINPUTCONTEXTDX pIC; PCLIENTIMC pClientImc; PIMC pIMC; if (hIMC == NULL) return FALSE; if (!IS_IMM_MODE()) { TRACE("\n"); return FALSE; } pIMC = ValidateHandle(hIMC, TYPE_INPUTCONTEXT); if (IS_NULL_UNEXPECTEDLY(pIMC)) return FALSE; if (pIMC->head.pti != Imm32CurrentPti()) { ERR("Thread mismatch\n"); return FALSE; } pClientImc = (PCLIENTIMC)pIMC->dwClientImcData; if (pClientImc == NULL) { TRACE("pClientImc == NULL\n"); goto Finish; } if ((pClientImc->dwFlags & CLIENTIMC_UNKNOWN2) && !bKeep) { ERR("Can't destroy for CLIENTIMC_UNKNOWN2\n"); return FALSE; } if (pClientImc->dwFlags & CLIENTIMC_DESTROY) return TRUE; InterlockedIncrement(&pClientImc->cLockObj); if (IS_NULL_UNEXPECTEDLY(pClientImc->hInputContext)) goto Quit; pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC); if (IS_NULL_UNEXPECTEDLY(pIC)) { ImmUnlockClientImc(pClientImc); return FALSE; } CtfImmTIMDestroyInputContext(hIMC); if (pClientImc->hKL == hKL) { pImeDpi = ImmLockImeDpi(hKL); if (pImeDpi) { if (IS_IME_HKL(hKL)) pImeDpi->ImeSelect(hIMC, FALSE); else if (IS_CICERO_MODE() && !IS_16BIT_MODE()) pImeDpi->CtfImeSelectEx(hIMC, FALSE, hKL); ImmUnlockImeDpi(pImeDpi); } pClientImc->hKL = NULL; } ImmDestroyIMCC(pIC->hPrivate); ImmDestroyIMCC(pIC->hMsgBuf); ImmDestroyIMCC(pIC->hGuideLine); ImmDestroyIMCC(pIC->hCandInfo); ImmDestroyIMCC(pIC->hCompStr); Imm32DestroyImeModeSaver(pIC); ImmUnlockIMC(hIMC); Quit: pClientImc->dwFlags |= CLIENTIMC_DESTROY; ImmUnlockClientImc(pClientImc); Finish: if (bKeep) return TRUE; return NtUserDestroyInputContext(hIMC); } // NOTE: Windows does recursive call ImmLockIMC here but we don't do so. // Win: BOOL CreateInputContext(HIMC hIMC, HKL hKL, BOOL fSelect) BOOL APIENTRY Imm32CreateInputContext(HIMC hIMC, LPINPUTCONTEXT pIC, PCLIENTIMC pClientImc, HKL hKL, BOOL fSelect) { DWORD dwIndex, cbPrivate; PIMEDPI pImeDpi = NULL; LPCOMPOSITIONSTRING pCS; LPCANDIDATEINFO pCI; LPGUIDELINE pGL; /* Create IC components */ pIC->hCompStr = ImmCreateIMCC(sizeof(COMPOSITIONSTRING)); pIC->hCandInfo = ImmCreateIMCC(sizeof(CANDIDATEINFO)); pIC->hGuideLine = ImmCreateIMCC(sizeof(GUIDELINE)); pIC->hMsgBuf = ImmCreateIMCC(sizeof(UINT)); if (IS_NULL_UNEXPECTEDLY(pIC->hCompStr) || IS_NULL_UNEXPECTEDLY(pIC->hCandInfo) || IS_NULL_UNEXPECTEDLY(pIC->hGuideLine) || IS_NULL_UNEXPECTEDLY(pIC->hMsgBuf)) { goto Fail; } /* Initialize IC components */ pCS = ImmLockIMCC(pIC->hCompStr); if (IS_NULL_UNEXPECTEDLY(pCS)) goto Fail; pCS->dwSize = sizeof(COMPOSITIONSTRING); ImmUnlockIMCC(pIC->hCompStr); pCI = ImmLockIMCC(pIC->hCandInfo); if (IS_NULL_UNEXPECTEDLY(pCI)) goto Fail; pCI->dwSize = sizeof(CANDIDATEINFO); ImmUnlockIMCC(pIC->hCandInfo); pGL = ImmLockIMCC(pIC->hGuideLine); if (IS_NULL_UNEXPECTEDLY(pGL)) goto Fail; pGL->dwSize = sizeof(GUIDELINE); ImmUnlockIMCC(pIC->hGuideLine); pIC->dwNumMsgBuf = 0; pIC->fOpen = FALSE; pIC->fdwConversion = pIC->fdwSentence = 0; for (dwIndex = 0; dwIndex < MAX_CANDIDATEFORM; ++dwIndex) pIC->cfCandForm[dwIndex].dwIndex = IMM_INVALID_CANDFORM; /* Get private data size */ pImeDpi = ImmLockImeDpi(hKL); if (!pImeDpi) { cbPrivate = sizeof(DWORD); } else { /* Update CLIENTIMC */ pClientImc->uCodePage = pImeDpi->uCodePage; if (ImeDpi_IsUnicode(pImeDpi)) pClientImc->dwFlags |= CLIENTIMC_WIDE; cbPrivate = pImeDpi->ImeInfo.dwPrivateDataSize; } /* Create private data */ pIC->hPrivate = ImmCreateIMCC(cbPrivate); if (IS_NULL_UNEXPECTEDLY(pIC->hPrivate)) goto Fail; CtfImmTIMCreateInputContext(hIMC); if (pImeDpi) { /* Select the IME */ if (fSelect) { if (IS_IME_HKL(hKL)) pImeDpi->ImeSelect(hIMC, TRUE); else if (IS_CICERO_MODE() && !IS_16BIT_MODE()) pImeDpi->CtfImeSelectEx(hIMC, TRUE, hKL); } /* Set HKL */ pClientImc->hKL = hKL; ImmUnlockImeDpi(pImeDpi); } return TRUE; Fail: if (pImeDpi) ImmUnlockImeDpi(pImeDpi); pIC->hMsgBuf = ImmDestroyIMCC(pIC->hMsgBuf); pIC->hGuideLine = ImmDestroyIMCC(pIC->hGuideLine); pIC->hCandInfo = ImmDestroyIMCC(pIC->hCandInfo); pIC->hCompStr = ImmDestroyIMCC(pIC->hCompStr); return FALSE; } // Win: InternalImmLockIMC LPINPUTCONTEXT APIENTRY Imm32InternalLockIMC(HIMC hIMC, BOOL fSelect) { HANDLE hIC; LPINPUTCONTEXT pIC = NULL; PCLIENTIMC pClientImc; WORD LangID; DWORD dwThreadId; HKL hOldKL, hNewKL; PIMEDPI pImeDpi = NULL; pClientImc = ImmLockClientImc(hIMC); if (IS_NULL_UNEXPECTEDLY(pClientImc)) return NULL; RtlEnterCriticalSection(&pClientImc->cs); if (pClientImc->hInputContext) { pIC = LocalLock(pClientImc->hInputContext); if (IS_NULL_UNEXPECTEDLY(pIC)) goto Failure; CtfImmTIMCreateInputContext(hIMC); goto Success; } dwThreadId = (DWORD)NtUserQueryInputContext(hIMC, QIC_INPUTTHREADID); if (dwThreadId == GetCurrentThreadId() && IS_CICERO_MODE() && !IS_16BIT_MODE()) { hOldKL = GetKeyboardLayout(0); LangID = LOWORD(hOldKL); hNewKL = (HKL)(DWORD_PTR)MAKELONG(LangID, LangID); pImeDpi = Imm32FindOrLoadImeDpi(hNewKL); if (pImeDpi) { CtfImmTIMActivate(hNewKL); } } if (!NtUserQueryInputContext(hIMC, QIC_DEFAULTWINDOWIME)) { ERR("No default IME window\n"); goto Failure; } hIC = LocalAlloc(LHND, sizeof(INPUTCONTEXTDX)); pIC = LocalLock(hIC); if (IS_NULL_UNEXPECTEDLY(pIC)) { LocalFree(hIC); goto Failure; } pClientImc->hInputContext = hIC; hNewKL = GetKeyboardLayout(dwThreadId); if (!Imm32CreateInputContext(hIMC, pIC, pClientImc, hNewKL, fSelect)) { LocalUnlock(hIC); pClientImc->hInputContext = LocalFree(hIC); goto Failure; } Success: RtlLeaveCriticalSection(&pClientImc->cs); InterlockedIncrement(&pClientImc->cLockObj); ImmUnlockClientImc(pClientImc); return pIC; Failure: RtlLeaveCriticalSection(&pClientImc->cs); ImmUnlockClientImc(pClientImc); return NULL; } /*********************************************************************** * ImmDestroyContext (IMM32.@) */ BOOL WINAPI ImmDestroyContext(HIMC hIMC) { HKL hKL; TRACE("(%p)\n", hIMC); if (!IS_IMM_MODE()) { TRACE("\n"); return FALSE; } if (IS_CROSS_THREAD_HIMC(hIMC)) return FALSE; hKL = GetKeyboardLayout(0); return Imm32DestroyInputContext(hIMC, hKL, FALSE); } /*********************************************************************** * ImmLockClientImc (IMM32.@) */ PCLIENTIMC WINAPI ImmLockClientImc(HIMC hImc) { PIMC pIMC; PCLIENTIMC pClientImc; TRACE("(%p)\n", hImc); if (IS_NULL_UNEXPECTEDLY(hImc)) return NULL; pIMC = ValidateHandle(hImc, TYPE_INPUTCONTEXT); if (IS_NULL_UNEXPECTEDLY(pIMC) || !Imm32CheckImcProcess(pIMC)) return NULL; pClientImc = (PCLIENTIMC)pIMC->dwClientImcData; if (pClientImc) { if (pClientImc->dwFlags & CLIENTIMC_DESTROY) return NULL; goto Finish; } pClientImc = ImmLocalAlloc(HEAP_ZERO_MEMORY, sizeof(CLIENTIMC)); if (IS_NULL_UNEXPECTEDLY(pClientImc)) return NULL; RtlInitializeCriticalSection(&pClientImc->cs); pClientImc->dwCompatFlags = (DWORD)NtUserGetThreadState(THREADSTATE_IMECOMPATFLAGS); if (!NtUserUpdateInputContext(hImc, UIC_CLIENTIMCDATA, (DWORD_PTR)pClientImc)) { ERR("\n"); ImmLocalFree(pClientImc); return NULL; } pClientImc->dwFlags |= CLIENTIMC_UNKNOWN2; Finish: InterlockedIncrement(&pClientImc->cLockObj); return pClientImc; } /*********************************************************************** * ImmUnlockClientImc (IMM32.@) */ VOID WINAPI ImmUnlockClientImc(PCLIENTIMC pClientImc) { LONG cLocks; HANDLE hInputContext; TRACE("(%p)\n", pClientImc); cLocks = InterlockedDecrement(&pClientImc->cLockObj); if (cLocks != 0 || !(pClientImc->dwFlags & CLIENTIMC_DESTROY)) return; hInputContext = pClientImc->hInputContext; if (hInputContext) LocalFree(hInputContext); RtlDeleteCriticalSection(&pClientImc->cs); ImmLocalFree(pClientImc); } // Win: ImmGetSaveContext static HIMC APIENTRY ImmGetSaveContext(HWND hWnd, DWORD dwContextFlags) { HIMC hIMC; PCLIENTIMC pClientImc; PWND pWnd; if (!IS_IMM_MODE()) { TRACE("Not IMM mode.\n"); return NULL; } if (!hWnd) { hIMC = (HIMC)NtUserGetThreadState(THREADSTATE_DEFAULTINPUTCONTEXT); goto Quit; } pWnd = ValidateHwnd(hWnd); if (IS_NULL_UNEXPECTEDLY(pWnd) || IS_CROSS_PROCESS_HWND(hWnd)) return NULL; hIMC = pWnd->hImc; if (!hIMC && (dwContextFlags & 1)) hIMC = (HIMC)NtUserQueryWindow(hWnd, QUERY_WINDOW_DEFAULT_ICONTEXT); Quit: pClientImc = ImmLockClientImc(hIMC); if (IS_NULL_UNEXPECTEDLY(pClientImc)) return NULL; if ((dwContextFlags & 2) && (pClientImc->dwFlags & CLIENTIMC_DISABLEIME)) hIMC = NULL; ImmUnlockClientImc(pClientImc); return hIMC; } /*********************************************************************** * ImmGetContext (IMM32.@) */ HIMC WINAPI ImmGetContext(HWND hWnd) { TRACE("(%p)\n", hWnd); if (IS_NULL_UNEXPECTEDLY(hWnd)) return NULL; return ImmGetSaveContext(hWnd, 2); } /*********************************************************************** * ImmLockIMC(IMM32.@) * * NOTE: This is not ImmLockIMCC. Don't confuse. */ LPINPUTCONTEXT WINAPI ImmLockIMC(HIMC hIMC) { TRACE("(%p)\n", hIMC); return Imm32InternalLockIMC(hIMC, TRUE); } /*********************************************************************** * ImmUnlockIMC(IMM32.@) */ BOOL WINAPI ImmUnlockIMC(HIMC hIMC) { PCLIENTIMC pClientImc; pClientImc = ImmLockClientImc(hIMC); if (IS_NULL_UNEXPECTEDLY(pClientImc)) return FALSE; if (pClientImc->hInputContext) LocalUnlock(pClientImc->hInputContext); InterlockedDecrement(&pClientImc->cLockObj); ImmUnlockClientImc(pClientImc); return TRUE; } /*********************************************************************** * ImmReleaseContext (IMM32.@) */ BOOL WINAPI ImmReleaseContext(HWND hWnd, HIMC hIMC) { TRACE("(%p, %p)\n", hWnd, hIMC); UNREFERENCED_PARAMETER(hWnd); UNREFERENCED_PARAMETER(hIMC); return TRUE; // Do nothing. This is correct. } /*********************************************************************** * ImmEnumInputContext(IMM32.@) */ BOOL WINAPI ImmEnumInputContext(DWORD dwThreadId, IMCENUMPROC lpfn, LPARAM lParam) { HIMC *phList; DWORD dwIndex, dwCount; BOOL ret = TRUE; HIMC hIMC; TRACE("(%lu, %p, %p)\n", dwThreadId, lpfn, lParam); dwCount = Imm32BuildHimcList(dwThreadId, &phList); if (IS_ZERO_UNEXPECTEDLY(dwCount)) return FALSE; for (dwIndex = 0; dwIndex < dwCount; ++dwIndex) { hIMC = phList[dwIndex]; ret = (*lpfn)(hIMC, lParam); if (!ret) break; } ImmLocalFree(phList); return ret; } /*********************************************************************** * ImmSetActiveContext(IMM32.@) */ BOOL WINAPI ImmSetActiveContext(HWND hWnd, HIMC hIMC, BOOL fActive) { PCLIENTIMC pClientImc; LPINPUTCONTEXTDX pIC; PIMEDPI pImeDpi; HIMC hOldIMC; HKL hKL; BOOL fOpen = FALSE; DWORD dwConversion = 0, dwShowFlags = ISC_SHOWUIALL; HWND hwndDefIME; TRACE("(%p, %p, %d)\n", hWnd, hIMC, fActive); if (!IS_IMM_MODE()) { TRACE("\n"); return FALSE; } pClientImc = ImmLockClientImc(hIMC); if (!fActive) { if (pClientImc) pClientImc->dwFlags &= ~CLIENTIMC_ACTIVE; } else if (hIMC) { if (IS_NULL_UNEXPECTEDLY(pClientImc)) return FALSE; pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC); if (IS_NULL_UNEXPECTEDLY(pIC)) { ImmUnlockClientImc(pClientImc); return FALSE; } pIC->hWnd = hWnd; pClientImc->dwFlags |= CLIENTIMC_ACTIVE; if (pIC->dwUIFlags & 2) dwShowFlags = (ISC_SHOWUIGUIDELINE | ISC_SHOWUIALLCANDIDATEWINDOW); fOpen = pIC->fOpen; dwConversion = pIC->fdwConversion; ImmUnlockIMC(hIMC); } else { hOldIMC = ImmGetSaveContext(hWnd, 1); pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hOldIMC); if (pIC) { pIC->hWnd = hWnd; ImmUnlockIMC(hOldIMC); } } hKL = GetKeyboardLayout(0); if (IS_CICERO_MODE() && !IS_16BIT_MODE()) { Imm32CiceroSetActiveContext(hIMC, fActive, hWnd, hKL); hKL = GetKeyboardLayout(0); } pImeDpi = ImmLockImeDpi(hKL); if (pImeDpi) { if (IS_IME_HKL(hKL)) pImeDpi->ImeSetActiveContext(hIMC, fActive); ImmUnlockImeDpi(pImeDpi); } if (IsWindow(hWnd)) { SendMessageW(hWnd, WM_IME_SETCONTEXT, fActive, dwShowFlags); if (fActive) NtUserNotifyIMEStatus(hWnd, fOpen, dwConversion); } else if (!fActive) { hwndDefIME = ImmGetDefaultIMEWnd(NULL); if (hwndDefIME) SendMessageW(hwndDefIME, WM_IME_SETCONTEXT, 0, dwShowFlags); } if (pClientImc) ImmUnlockClientImc(pClientImc); return TRUE; } /*********************************************************************** * ImmWINNLSGetEnableStatus (IMM32.@) */ BOOL WINAPI ImmWINNLSGetEnableStatus(HWND hWnd) { if (!Imm32IsSystemJapaneseOrKorean()) { SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } return !!ImmGetSaveContext(hWnd, 2); } /*********************************************************************** * ImmSetActiveContextConsoleIME(IMM32.@) */ BOOL WINAPI ImmSetActiveContextConsoleIME(HWND hwnd, BOOL fFlag) { HIMC hIMC; TRACE("(%p, %d)\n", hwnd, fFlag); hIMC = ImmGetContext(hwnd); if (IS_NULL_UNEXPECTEDLY(hIMC)) return FALSE; return ImmSetActiveContext(hwnd, hIMC, fFlag); } #ifndef NDEBUG VOID APIENTRY Imm32UnitTest(VOID) { if (0) { DWORD dwValue; WCHAR szText[64]; Imm32StrToUInt(L"123", &dwValue, 10); ASSERT(dwValue == 123); Imm32StrToUInt(L"100", &dwValue, 16); ASSERT(dwValue == 0x100); Imm32UIntToStr(123, 10, szText, _countof(szText)); ASSERT(lstrcmpW(szText, L"123") == 0); Imm32UIntToStr(0x100, 16, szText, _countof(szText)); ASSERT(lstrcmpW(szText, L"100") == 0); } } #endif BOOL WINAPI User32InitializeImmEntryTable(DWORD); BOOL WINAPI ImmDllInitialize( _In_ HINSTANCE hDll, _In_ ULONG dwReason, _In_opt_ PVOID pReserved) { HKL hKL; HIMC hIMC; TRACE("(%p, 0x%X, %p)\n", hDll, dwReason, pReserved); switch (dwReason) { case DLL_PROCESS_ATTACH: if (!ImmInitializeGlobals(hDll)) { ERR("ImmInitializeGlobals failed\n"); return FALSE; } if (!User32InitializeImmEntryTable(IMM_INIT_MAGIC)) { ERR("User32InitializeImmEntryTable failed\n"); return FALSE; } #ifndef NDEBUG Imm32UnitTest(); #endif break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: if (!IS_IMM_MODE() || NtCurrentTeb()->Win32ThreadInfo == NULL) return TRUE; hKL = GetKeyboardLayout(0); hIMC = (HIMC)NtUserGetThreadState(THREADSTATE_DEFAULTINPUTCONTEXT); Imm32DestroyInputContext(hIMC, hKL, TRUE); break; case DLL_PROCESS_DETACH: RtlDeleteCriticalSection(&gcsImeDpi); TRACE("imm32.dll is unloaded\n"); break; } return TRUE; }