reactos/dll/win32/imm32/imm.c
2022-11-13 20:04:34 +09:00

1371 lines
35 KiB
C

/*
* 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 <james.tabor@reactos.org>
* Copyright 2018 Amine Khaldi <amine.khaldi@reactos.org>
* Copyright 2020-2022 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
*/
#include "precomp.h"
#include <ndk/exfuncs.h>
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.
}
/***********************************************************************
* ImmCreateSoftKeyboard(IMM32.@)
*/
HWND WINAPI ImmCreateSoftKeyboard(UINT uType, UINT hOwner, int x, int y)
{
FIXME("(%d, %d, %d, %d): stub\n", uType, hOwner, x, y);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return 0;
}
/***********************************************************************
* ImmDestroySoftKeyboard(IMM32.@)
*/
BOOL WINAPI ImmDestroySoftKeyboard(HWND hSoftWnd)
{
TRACE("(%p)\n", hSoftWnd);
return DestroyWindow(hSoftWnd);
}
/***********************************************************************
* ImmShowSoftKeyboard(IMM32.@)
*/
BOOL WINAPI ImmShowSoftKeyboard(HWND hSoftWnd, int nCmdShow)
{
TRACE("(%p, %d)\n", hSoftWnd, nCmdShow);
if (hSoftWnd)
return ShowWindow(hSoftWnd, nCmdShow);
return FALSE;
}
/***********************************************************************
* ImmDisableTextFrameService(IMM32.@)
*/
BOOL WINAPI ImmDisableTextFrameService(DWORD dwThreadId)
{
FIXME("Stub\n");
return FALSE;
}
/***********************************************************************
* 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;
}