/**************************************************************************\ * Module Name: context.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * Context management routines for imm32 dll * * History: * 03-Jan-1996 wkwok Created \**************************************************************************/ #include "precomp.h" #pragma hdrstop #define IMCC_ALLOC_TOOLARGE 0x1000 /**************************************************************************\ * ImmCreateContext * * Creates and initializes an input context. * * 17-Jan-1996 wkwok Created \**************************************************************************/ HIMC WINAPI ImmCreateContext(void) { PCLIENTIMC pClientImc; HIMC hImc = NULL_HIMC; if (!IS_IME_ENABLED()) { return NULL_HIMC; } pClientImc = ImmLocalAlloc(HEAP_ZERO_MEMORY, sizeof(CLIENTIMC)); if (pClientImc != NULL) { hImc = NtUserCreateInputContext((ULONG_PTR)pClientImc); if (hImc == NULL_HIMC) { ImmLocalFree(pClientImc); return NULL_HIMC; } InitImcCrit(pClientImc); pClientImc->dwImeCompatFlags = (DWORD)NtUserGetThreadState(UserThreadStateImeCompatFlags); } return hImc; } /**************************************************************************\ * ImmDestroyContext * * Destroys an input context. * * 17-Jan-1996 wkwok Created \**************************************************************************/ BOOL WINAPI ImmDestroyContext( HIMC hImc) { if (!IS_IME_ENABLED()) { return FALSE; } if (GetInputContextThread(hImc) != GetCurrentThreadId()) { RIPMSG1(RIP_WARNING, "ImmDestroyContext: Invalid input context access %lx.", hImc); return FALSE; } return DestroyInputContext(hImc, GetKeyboardLayout(0), FALSE); } /**************************************************************************\ * ImmAssociateContext * * Associates an input context to the specified window handle. * * 17-Jan-1996 wkwok Created \**************************************************************************/ HIMC WINAPI ImmAssociateContext( HWND hWnd, HIMC hImc) { PWND pWnd; HIMC hPrevImc; AIC_STATUS Status; // early out if (!IS_IME_ENABLED()) { return NULL_HIMC; } if ((pWnd = ValidateHwnd(hWnd)) == (PWND)NULL) { RIPMSG1(RIP_WARNING, "ImmAssociateContext: invalid window handle %x", hWnd); return NULL_HIMC; } if (hImc != NULL_HIMC && GetInputContextThread(hImc) != GetCurrentThreadId()) { RIPMSG1(RIP_WARNING, "ImmAssociateContext: Invalid input context access %lx.", hImc); return NULL_HIMC; } /* * associate to the same input context, do nothing. */ if (pWnd->hImc == hImc) return hImc; hPrevImc = KHIMC_TO_HIMC(pWnd->hImc); Status = NtUserAssociateInputContext(hWnd, hImc, 0); switch (Status) { case AIC_FOCUSCONTEXTCHANGED: if (IsWndEqual(NtUserQueryWindow(hWnd, WindowFocusWindow), hWnd)) { ImmSetActiveContext(hWnd, hPrevImc, FALSE); ImmSetActiveContext(hWnd, hImc, TRUE); } // Fall thru. case AIC_SUCCESS: return hPrevImc; default: return NULL_HIMC; } } BOOL WINAPI ImmAssociateContextEx( HWND hWnd, HIMC hImc, DWORD dwFlag) { HWND hWndFocus; PWND pWndFocus; HIMC hImcFocusOld; AIC_STATUS Status; if (!IS_IME_ENABLED()) { return FALSE; } hWndFocus = NtUserQueryWindow(hWnd, WindowFocusWindow); if (hImc != NULL_HIMC && !(dwFlag & IACE_DEFAULT) && GetInputContextThread(hImc) != GetCurrentThreadId()) { RIPMSG1(RIP_WARNING, "ImmAssociateContextEx: Invalid input context access %lx.", hImc); return FALSE; } if ((pWndFocus = ValidateHwnd(hWndFocus)) != (PWND)NULL) hImcFocusOld = KHIMC_TO_HIMC(pWndFocus->hImc); else hImcFocusOld = NULL_HIMC; Status = NtUserAssociateInputContext(hWnd, hImc, dwFlag); switch (Status) { case AIC_FOCUSCONTEXTCHANGED: if ((pWndFocus = ValidateHwnd(hWndFocus)) != (PWND)NULL) { hImc = KHIMC_TO_HIMC(pWndFocus->hImc); if (hImc != hImcFocusOld) { ImmSetActiveContext(hWndFocus, hImcFocusOld, FALSE); ImmSetActiveContext(hWndFocus, hImc, TRUE); }; }; // Fall thru. case AIC_SUCCESS: return TRUE; default: return FALSE; } } /**************************************************************************\ * ImmGetContext * * Retrieves the input context that is associated to the given window. * * 17-Jan-1996 wkwok Created \**************************************************************************/ HIMC WINAPI ImmGetContext( HWND hWnd) { if ( hWnd == NULL ) { RIPMSG1(RIP_WARNING, "ImmGetContext: invalid window handle %x", hWnd); return NULL_HIMC; } /* * for non-NULL hWnd, ImmGetSaveContext will do the * validation and "same process" checking. */ return ImmGetSaveContext( hWnd, IGSC_WINNLSCHECK ); } /**************************************************************************\ * ImmGetSaveContext * * Retrieves the input context that is associated to the given window. * * 15-Mar-1996 wkwok Created \**************************************************************************/ HIMC ImmGetSaveContext( HWND hWnd, DWORD dwFlag) { HIMC hRetImc; PCLIENTIMC pClientImc; PWND pwnd; if (!IS_IME_ENABLED()) { return NULL_HIMC; } if (hWnd == NULL) { /* * Retrieves the default input context of current thread. */ hRetImc = (HIMC)NtUserGetThreadState(UserThreadStateDefaultInputContext); } else { /* * Retrieves the input context associated to the given window. */ if ((pwnd = ValidateHwnd(hWnd)) == (PWND)NULL) { RIPMSG1(RIP_WARNING, "ImmGetSaveContext: invalid window handle %x", hWnd); return NULL_HIMC; } /* * Don't allow other process to access input context */ if (!TestWindowProcess(pwnd)) { RIPMSG0(RIP_WARNING, "ImmGetSaveContext: can not get input context of other process"); return NULL_HIMC; } hRetImc = KHIMC_TO_HIMC(pwnd->hImc); if (hRetImc == NULL_HIMC && (dwFlag & IGSC_DEFIMCFALLBACK)) { /* * hWnd associated with NULL input context, retrieves the * default input context of the hWnd's creator thread. */ hRetImc = (HIMC)NtUserQueryWindow(hWnd, WindowDefaultInputContext); } } pClientImc = ImmLockClientImc(hRetImc); if (pClientImc == NULL) return NULL_HIMC; if ((dwFlag & IGSC_WINNLSCHECK) && TestICF(pClientImc, IMCF_WINNLSDISABLE)) hRetImc = NULL_HIMC; ImmUnlockClientImc(pClientImc); return hRetImc; } /**************************************************************************\ * ImmReleaseContext * * Releases the input context retrieved by ImmGetContext(). * * 17-Jan-1996 wkwok Created \**************************************************************************/ BOOL WINAPI ImmReleaseContext( HWND hWnd, HIMC hImc) { UNREFERENCED_PARAMETER(hWnd); UNREFERENCED_PARAMETER(hImc); return TRUE; } /**************************************************************************\ * ImmSetActiveContext * * 15-Mar-1996 wkwok Created \**************************************************************************/ BOOL ImmSetActiveContext( HWND hWnd, HIMC hImc, BOOL fActivate) { PCLIENTIMC pClientImc; PINPUTCONTEXT pInputContext; PIMEDPI pImeDpi; DWORD dwISC; HIMC hSaveImc; HWND hDefImeWnd; DWORD dwOpenStatus = 0; DWORD dwConversion = 0; #ifdef DEBUG PWND pWnd = ValidateHwnd(hWnd); if (pWnd != NULL && GETPTI(pWnd) != PtiCurrent()) { RIPMSG1(RIP_WARNING, "hWnd (=%lx) is not of current thread.", hWnd); } #endif if (!IS_IME_ENABLED()) { return FALSE; } dwISC = ISC_SHOWUIALL; pClientImc = ImmLockClientImc(hImc); if (!fActivate) { if (pClientImc != NULL) ClrICF(pClientImc, IMCF_ACTIVE); goto NotifySetActive; } if (hImc == NULL_HIMC) { hSaveImc = ImmGetSaveContext(hWnd, IGSC_DEFIMCFALLBACK); pInputContext = ImmLockIMC(hSaveImc); if (pInputContext != NULL) { pInputContext->hWnd = hWnd; ImmUnlockIMC(hSaveImc); } goto NotifySetActive; } /* * Non-NULL input context, window handle have to be updated. */ if (pClientImc == NULL) return FALSE; pInputContext = ImmLockIMC(hImc); if (pInputContext == NULL) { ImmUnlockClientImc(pClientImc); return FALSE; } pInputContext->hWnd = hWnd; SetICF(pClientImc, IMCF_ACTIVE); #ifdef LATER // Do uNumLangVKey checking later #endif if (pInputContext->fdw31Compat & F31COMPAT_MCWHIDDEN) dwISC = ISC_SHOWUIALL - ISC_SHOWUICOMPOSITIONWINDOW; dwOpenStatus = (DWORD)pInputContext->fOpen; dwConversion = pInputContext->fdwConversion; ImmUnlockIMC(hImc); NotifySetActive: #ifdef CUAS_ENABLE { HKL hKL = GetKeyboardLayout(0); // // call msctfime's ImeSetActiveContextAlways() no matter what is the cuurnet // hkl if we in Cicero Unaware App Support. // if (IS_CICERO_ENABLED_AND_NOT16BIT()) { Internal_CtfImeSetActiveContextAlways(hImc, fActivate, hWnd, hKL); } } #endif // CUAS_ENABLE #if !defined(CUAS_ENABLE) pImeDpi = ImmLockImeDpi(GetKeyboardLayout(0)); if (pImeDpi != NULL) { (*pImeDpi->pfn.ImeSetActiveContext)(hImc, fActivate); ImmUnlockImeDpi(pImeDpi); } #else // // msctfime's SetFocus might be change hKL to Cicero. // // call IME's ImeSetActiveContext(). // { HKL hKL; pImeDpi = ImmLockImeDpi(hKL=GetKeyboardLayout(0)); if (pImeDpi != NULL) { if (IS_IME_KBDLAYOUT(hKL)) { (*pImeDpi->pfn.ImeSetActiveContext)(hImc, fActivate); } ImmUnlockImeDpi(pImeDpi); } } #endif /* * Notify UI */ if (IsWindow(hWnd)) { SendMessage(hWnd, WM_IME_SETCONTEXT, fActivate, dwISC); /* * send notify to shell / keyboard driver */ if ( fActivate ) NtUserNotifyIMEStatus( hWnd, dwOpenStatus, dwConversion ); } else if (!fActivate) { /* * Because hWnd is not there (maybe destroyed), we send * WM_IME_SETCONTEXT to the default IME window. */ if ((hDefImeWnd = ImmGetDefaultIMEWnd(NULL)) != NULL) { SendMessage(hDefImeWnd, WM_IME_SETCONTEXT, fActivate, dwISC); } else { RIPMSG0(RIP_WARNING, "ImmSetActiveContext: can't send WM_IME_SETCONTEXT(FALSE)."); } } #ifdef DEBUG else { RIPMSG0(RIP_WARNING, "ImmSetActiveContext: can't send WM_IME_SETCONTEXT(TRUE)."); } #endif #ifdef LATER // Implements ProcessIMCEvent() later. #endif if (pClientImc != NULL) ImmUnlockClientImc(pClientImc); return TRUE; } /**************************************************************************\ * ModeSaver related routines * * Dec-1998 hiroyama Created \**************************************************************************/ PIMEMODESAVER GetImeModeSaver( PINPUTCONTEXT pInputContext, HKL hkl) { PIMEMODESAVER pModeSaver; USHORT langId = PRIMARYLANGID(HKL_TO_LANGID(hkl)); for (pModeSaver = pInputContext->pImeModeSaver; pModeSaver; pModeSaver = pModeSaver->next) { if (pModeSaver->langId == langId) { break; } } if (pModeSaver == NULL) { TAGMSG1(DBGTAG_IMM, "GetImeModeSaver: creating ModeSaver for langId=%04x", langId); pModeSaver = ImmLocalAlloc(HEAP_ZERO_MEMORY, sizeof *pModeSaver); if (pModeSaver == NULL) { RIPMSG1(RIP_WARNING, "GetImeModeSaver: failed to create ModeSaver for langId=%04x", langId); return NULL; } pModeSaver->langId = langId; pModeSaver->next = pInputContext->pImeModeSaver; pInputContext->pImeModeSaver = pModeSaver; } return pModeSaver; } VOID DestroyImeModeSaver( PINPUTCONTEXT pInputContext) { PIMEMODESAVER pModeSaver = pInputContext->pImeModeSaver; // // Destroy mode savers // while (pModeSaver) { PIMEMODESAVER pNext = pModeSaver->next; PIMEPRIVATEMODESAVER pPrivateModeSaver = pModeSaver->pImePrivateModeSaver; // // Destroy private mode savers // while (pPrivateModeSaver) { PIMEPRIVATEMODESAVER pPrivateNext = pPrivateModeSaver->next; ImmLocalFree(pPrivateModeSaver); pPrivateModeSaver = pPrivateNext; } ImmLocalFree(pModeSaver); pModeSaver = pNext; } pInputContext->pImeModeSaver = NULL; } PIMEPRIVATEMODESAVER GetImePrivateModeSaver( PIMEMODESAVER pImeModeSaver, HKL hkl) { PIMEPRIVATEMODESAVER pPrivateModeSaver; for (pPrivateModeSaver = pImeModeSaver->pImePrivateModeSaver; pPrivateModeSaver; pPrivateModeSaver = pPrivateModeSaver->next) { if (pPrivateModeSaver->hkl == hkl) { break; } } if (pPrivateModeSaver == NULL) { TAGMSG1(DBGTAG_IMM, "GetImePrivateModeSaver: creating private mode saver for hkl=%08x", hkl); pPrivateModeSaver = ImmLocalAlloc(0, sizeof *pPrivateModeSaver); if (pPrivateModeSaver == NULL) { RIPMSG1(RIP_WARNING, "GetImePrivateModeSaver: failed to create PrivateModeSaver for hlk=%08x", hkl); return NULL; } pPrivateModeSaver->hkl = hkl; pPrivateModeSaver->fdwSentence = 0; pPrivateModeSaver->next = pImeModeSaver->pImePrivateModeSaver; pImeModeSaver->pImePrivateModeSaver = pPrivateModeSaver; } return pPrivateModeSaver; } BOOL SavePrivateMode( PINPUTCONTEXT pInputContext, PIMEMODESAVER pImeModeSaver, HKL hkl) { PIMEPRIVATEMODESAVER pPrivateModeSaver = GetImePrivateModeSaver(pImeModeSaver, hkl); if (pPrivateModeSaver == NULL) { return FALSE; } // // Save private sentence mode // pPrivateModeSaver->fdwSentence = pInputContext->fdwSentence & 0xffff0000; return TRUE; } BOOL RestorePrivateMode( PINPUTCONTEXT pInputContext, PIMEMODESAVER pImeModeSaver, HKL hkl) { PIMEPRIVATEMODESAVER pPrivateModeSaver = GetImePrivateModeSaver(pImeModeSaver, hkl); if (pPrivateModeSaver == NULL) { return FALSE; } // // Restore private sentence mode // ImmAssert(LOWORD(pPrivateModeSaver->fdwSentence) == 0); pInputContext->fdwSentence |= pPrivateModeSaver->fdwSentence; return TRUE; } /**************************************************************************\ * CreateInputContext * * 20-Feb-1996 wkwok Created \**************************************************************************/ BOOL CreateInputContext( HIMC hImc, HKL hKL, BOOL fCanCallImeSelect) { PIMEDPI pImeDpi; PCLIENTIMC pClientImc; DWORD dwPrivateDataSize; DWORD fdwInitConvMode = 0; // do it later BOOL fInitOpen = FALSE; // do it later PINPUTCONTEXT pInputContext; PCOMPOSITIONSTRING pCompStr; PCANDIDATEINFO pCandInfo; PGUIDELINE pGuideLine; int i; pInputContext = ImmLockIMC(hImc); if (!pInputContext) { RIPMSG1(RIP_WARNING, "CreateContext: Lock hIMC %x failure", hImc); goto CrIMCLockErrOut; } /* * Initialize the member of INPUTCONTEXT */ pInputContext->hCompStr = ImmCreateIMCC(sizeof(COMPOSITIONSTRING)); if (!pInputContext->hCompStr) { RIPMSG0(RIP_WARNING, "CreateContext: Create hCompStr failure"); goto CrIMCUnlockIMC; } pCompStr = (PCOMPOSITIONSTRING)ImmLockIMCC(pInputContext->hCompStr); if (!pCompStr) { RIPMSG1(RIP_WARNING, "CreateContext: Lock hCompStr %x failure", pInputContext->hCompStr); goto CrIMCFreeCompStr; } pCompStr->dwSize = sizeof(COMPOSITIONSTRING); ImmUnlockIMCC(pInputContext->hCompStr); pInputContext->hCandInfo = ImmCreateIMCC(sizeof(CANDIDATEINFO)); if (!pInputContext->hCandInfo) { RIPMSG0(RIP_WARNING, "CreateContext: Create hCandInfo failure"); goto CrIMCFreeCompStr; } pCandInfo = (PCANDIDATEINFO)ImmLockIMCC(pInputContext->hCandInfo); if (!pCandInfo) { RIPMSG1(RIP_WARNING, "CreateContext: Lock hCandInfo %x failure", pInputContext->hCandInfo); goto CrIMCFreeCandInfo; } pCandInfo->dwSize = sizeof(CANDIDATEINFO); ImmUnlockIMCC(pInputContext->hCandInfo); pInputContext->hGuideLine = ImmCreateIMCC(sizeof(GUIDELINE)); if (!pInputContext->hGuideLine) { RIPMSG0(RIP_WARNING, "CreateContext: Create hGuideLine failure"); goto CrIMCFreeCandInfo; } pGuideLine = (PGUIDELINE)ImmLockIMCC(pInputContext->hGuideLine); if (!pGuideLine) { RIPMSG1(RIP_WARNING, "CreateContext: Lock hGuideLine %x failure", pInputContext->hGuideLine); goto CrIMCFreeGuideLine; } pGuideLine->dwSize = sizeof(GUIDELINE); ImmUnlockIMCC(pInputContext->hGuideLine); pInputContext->hMsgBuf = ImmCreateIMCC(sizeof(UINT)); if (!pInputContext->hMsgBuf) { RIPMSG0(RIP_WARNING, "CreateContext: Create hMsgBuf failure"); goto CrIMCFreeGuideLine; } pInputContext->dwNumMsgBuf = 0; pInputContext->fOpen = fInitOpen; pInputContext->fdwConversion = fdwInitConvMode; pInputContext->fdwSentence = 0; for (i = 0; i < 4; i++) { pInputContext->cfCandForm[i].dwIndex = (DWORD)(-1); } pImeDpi = ImmLockImeDpi(hKL); if (pImeDpi != NULL) { if ((pClientImc = ImmLockClientImc(hImc)) == NULL) { RIPMSG0(RIP_WARNING, "CreateContext: ImmLockClientImc() failure"); ImmUnlockImeDpi(pImeDpi); goto CrIMCFreeMsgBuf; } /* * Unicode based IME expects an Uncode based input context. */ if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_UNICODE) SetICF(pClientImc, IMCF_UNICODE); pClientImc->dwCodePage = IMECodePage(pImeDpi); ImmUnlockClientImc(pClientImc); dwPrivateDataSize = pImeDpi->ImeInfo.dwPrivateDataSize; } else { dwPrivateDataSize = sizeof(UINT); } pInputContext->hPrivate = ImmCreateIMCC(dwPrivateDataSize); if (!pInputContext->hPrivate) { RIPMSG0(RIP_WARNING, "CreateContext: Create hPrivate failure"); ImmUnlockImeDpi(pImeDpi); goto CrIMCFreeMsgBuf; } pInputContext->pImeModeSaver = NULL; #ifdef CUAS_ENABLE /* * Create Cicero Input Context. */ CtfImmTIMCreateInputContext(hImc); #endif // CUAS_ENABLE #if !defined(CUAS_ENABLE) if (pImeDpi != NULL) { if (fCanCallImeSelect) { (*pImeDpi->pfn.ImeSelect)(hImc, TRUE); } ImmUnlockImeDpi(pImeDpi); } #else if (pImeDpi != NULL) { if (fCanCallImeSelect) { if (IS_IME_KBDLAYOUT(hKL)) { (*pImeDpi->pfn.ImeSelect)(hImc, TRUE); } else if (IS_CICERO_ENABLED_AND_NOT16BIT()) { (*pImeDpi->pfn.CtfImeSelectEx)(hImc, TRUE, hKL); } } if ((pClientImc = ImmLockClientImc(hImc)) != NULL) { pClientImc->SelectedHKL = hKL; ImmUnlockClientImc(pClientImc); } ImmUnlockImeDpi(pImeDpi); } #endif ImmUnlockIMC(hImc); return TRUE; /* * context failure case */ CrIMCFreeMsgBuf: ImmDestroyIMCC(pInputContext->hMsgBuf); CrIMCFreeGuideLine: ImmDestroyIMCC(pInputContext->hGuideLine); CrIMCFreeCandInfo: ImmDestroyIMCC(pInputContext->hCandInfo); CrIMCFreeCompStr: ImmDestroyIMCC(pInputContext->hCompStr); CrIMCUnlockIMC: ImmUnlockIMC(hImc); CrIMCLockErrOut: return FALSE; } /**************************************************************************\ * DestroyInputContext * * 20-Feb-1996 wkwok Created \**************************************************************************/ BOOL DestroyInputContext( HIMC hImc, HKL hKL, BOOL bTerminate) { PINPUTCONTEXT pInputContext; PIMEDPI pImeDpi; PIMC pImc; PCLIENTIMC pClientImc; if (!IS_IME_ENABLED()) { return FALSE; } if (hImc == NULL_HIMC) { RIPMSG0(RIP_VERBOSE, "DestroyInputContext: hImc is NULL."); return FALSE; } pImc = HMValidateHandle((HANDLE)hImc, TYPE_INPUTCONTEXT); /* * Cannot destroy input context from other thread. */ if (pImc == NULL || GETPTI(pImc) != PtiCurrent()) return FALSE; /* * We are destroying this hImc so we don't bother calling * ImmLockClientImc() to get the pClientImc. Instead, we * reference the pImc->dwClientImcData directly and call * InterlockedIncrement(&pClientImc->cLockObj) right after * several quick checks. */ pClientImc = (PCLIENTIMC)pImc->dwClientImcData; if (pClientImc == NULL) { /* * Client side Imc has not been initialzed yet. * We simply destroy this input context from kernel. */ if (bTerminate) { /* * If called from THREAD_DETACH, we don't * have to destroy kernel side Input Context. */ return TRUE; } return NtUserDestroyInputContext(hImc); } if (TestICF(pClientImc, IMCF_DEFAULTIMC) && !bTerminate) { /* * Cannot destroy default input context unless the * thread is terminating. */ return FALSE; } if (TestICF(pClientImc, IMCF_INDESTROY)) { /* * This hImc is being destroyed. Returns as success. */ return TRUE; } /* * Time to lock up the pClientImc. */ InterlockedIncrement(&pClientImc->cLockObj); if (pClientImc->hInputContext != NULL) { pInputContext = ImmLockIMC(hImc); if (!pInputContext) { RIPMSG1(RIP_WARNING, "DestroyContext: Lock hImc %x failure", hImc); ImmUnlockClientImc(pClientImc); return FALSE; } #ifdef CUAS_ENABLE /* * Destroy Cicero Input Context. */ CtfImmTIMDestroyInputContext(hImc); #endif // CUAS_ENABLE #if !defined(CUAS_ENABLE) pImeDpi = ImmLockImeDpi(hKL); if (pImeDpi != NULL) { (*pImeDpi->pfn.ImeSelect)(hImc, FALSE); ImmUnlockImeDpi(pImeDpi); } #else if (pClientImc->SelectedHKL == hKL) { pImeDpi = ImmLockImeDpi(hKL); if (pImeDpi != NULL) { if (IS_IME_KBDLAYOUT(hKL)) { (*pImeDpi->pfn.ImeSelect)(hImc, FALSE); } else if (IS_CICERO_ENABLED_AND_NOT16BIT()) { (*pImeDpi->pfn.CtfImeSelectEx)(hImc, FALSE, hKL); } ImmUnlockImeDpi(pImeDpi); } pClientImc->SelectedHKL = NULL; } #endif ImmDestroyIMCC(pInputContext->hPrivate); ImmDestroyIMCC(pInputContext->hMsgBuf); ImmDestroyIMCC(pInputContext->hGuideLine); ImmDestroyIMCC(pInputContext->hCandInfo); ImmDestroyIMCC(pInputContext->hCompStr); /* * Free all ImeModeSaver. */ DestroyImeModeSaver(pInputContext); ImmUnlockIMC(hImc); } SetICF(pClientImc, IMCF_INDESTROY); /* * ImmUnlockClientImc() will free up the pClientImc * when InterlockedDecrement(&pClientImc->cLockObj) * reaches 0. */ ImmUnlockClientImc(pClientImc); return (bTerminate) ? TRUE : NtUserDestroyInputContext(hImc); } /**************************************************************************\ * SelectInputContext * * 20-Feb-1996 wkwok Created \**************************************************************************/ VOID SelectInputContext( HKL hSelKL, HKL hUnSelKL, HIMC hImc) { PIMEDPI pSelImeDpi, pUnSelImeDpi; PCLIENTIMC pClientImc; PINPUTCONTEXT pInputContext; DWORD dwSelPriv = 0, dwUnSelPriv = 0, dwSize; HIMCC hImcc; PCOMPOSITIONSTRING pCompStr; PCANDIDATEINFO pCandInfo; PGUIDELINE pGuideLine; BOOLEAN fLogFontInited; #ifdef CUAS_ENABLE BOOLEAN fUseImeSaverForSelIme = TRUE; BOOLEAN fUseImeSaverForUnSelIme = TRUE; #endif TAGMSG3(DBGTAG_IMM, "SelectInputContext: called for sel=%08p unsel=%08p hImc=%08p", hSelKL, hUnSelKL, hImc); pClientImc = ImmLockClientImc(hImc); if (pClientImc == NULL) { RIPMSG0(RIP_VERBOSE, "SelectInputContext: cannot lock client Imc. Bailing out."); return; } pSelImeDpi = ImmLockImeDpi(hSelKL); if (hSelKL != hUnSelKL) { /* * If those new sel and unsel do no match but * somehow SelectInput is called, that means * we should initialize the input contex again * without dumping the old information. */ pUnSelImeDpi = ImmLockImeDpi(hUnSelKL); } else { pUnSelImeDpi = NULL; } if (pSelImeDpi != NULL) { /* * According to private memory size of the two layout, we decide * whether we nee to reallocate this memory block */ dwSelPriv = pSelImeDpi->ImeInfo.dwPrivateDataSize; /* * Setup the code page of the newly selected IME. */ pClientImc->dwCodePage = IMECodePage(pSelImeDpi); } else { pClientImc->dwCodePage = CP_ACP; } if (pUnSelImeDpi != NULL) dwUnSelPriv = pUnSelImeDpi->ImeInfo.dwPrivateDataSize; dwSelPriv = max(dwSelPriv, sizeof(UINT)); dwUnSelPriv = max(dwUnSelPriv, sizeof(UINT)); /* * Unselect the input context. */ #if !defined(CUAS_ENABLE) if (pUnSelImeDpi != NULL) (*pUnSelImeDpi->pfn.ImeSelect)(hImc, FALSE); #else if (pClientImc->SelectedHKL == hUnSelKL) { if (pUnSelImeDpi != NULL) { if (IS_IME_KBDLAYOUT(hUnSelKL)) { (*pUnSelImeDpi->pfn.ImeSelect)(hImc, FALSE); } else if (IS_CICERO_ENABLED_AND_NOT16BIT()) { (*pUnSelImeDpi->pfn.CtfImeSelectEx)(hImc, FALSE, hUnSelKL); } } pClientImc->SelectedHKL = NULL; } // // don't use a mode saver for non IME or non CUAS. // if (CtfImmIsTextFrameServiceDisabled()) { if (IS_CICERO_ENABLED_AND_NOT16BIT()) { if (!IS_IME_KBDLAYOUT(hSelKL)) fUseImeSaverForSelIme = FALSE; if (!IS_IME_KBDLAYOUT(hUnSelKL)) fUseImeSaverForUnSelIme = FALSE; } } #endif /* * Reinitialize the client side input context for the selected layout. */ if ((pInputContext = InternalImmLockIMC(hImc, FALSE)) != NULL) { DWORD fdwOldConversion = pInputContext->fdwConversion; DWORD fdwOldSentence = pInputContext->fdwSentence; BOOL fOldOpen = pInputContext->fOpen; PIMEMODESAVER pUnSelModeSaver, pSelModeSaver; const DWORD fdwConvPreserve = IME_CMODE_EUDC; fLogFontInited = ((pInputContext->fdwInit & INIT_LOGFONT) == INIT_LOGFONT); if (TestICF(pClientImc, IMCF_UNICODE) && pSelImeDpi != NULL && !(pSelImeDpi->ImeInfo.fdwProperty & IME_PROP_UNICODE)) { /* * Check if there is any LOGFONT to be converted. */ if (fLogFontInited) { LOGFONTA LogFontA; LFontWtoLFontA(&pInputContext->lfFont.W, &LogFontA); RtlCopyMemory(&pInputContext->lfFont.A, &LogFontA, sizeof(LOGFONTA)); } ClrICF(pClientImc, IMCF_UNICODE); } else if (!TestICF(pClientImc, IMCF_UNICODE) && pSelImeDpi != NULL && (pSelImeDpi->ImeInfo.fdwProperty & IME_PROP_UNICODE)) { /* * Check if there is any LOGFONT to be converted. */ if (fLogFontInited) { LOGFONTW LogFontW; LFontAtoLFontW(&pInputContext->lfFont.A, &LogFontW); RtlCopyMemory(&pInputContext->lfFont.W, &LogFontW, sizeof(LOGFONTW)); } SetICF(pClientImc, IMCF_UNICODE); } /* * hPrivate */ if (dwUnSelPriv != dwSelPriv) { hImcc = ImmReSizeIMCC(pInputContext->hPrivate, dwSelPriv); if (hImcc) { pInputContext->hPrivate = hImcc; } else { RIPMSG1(RIP_WARNING, "SelectContext: resize hPrivate %lX failure", pInputContext->hPrivate); ImmDestroyIMCC(pInputContext->hPrivate); pInputContext->hPrivate = ImmCreateIMCC(dwSelPriv); } } /* * hMsgBuf */ dwSize = ImmGetIMCCSize(pInputContext->hMsgBuf); if (ImmGetIMCCLockCount(pInputContext->hMsgBuf) != 0 || dwSize > IMCC_ALLOC_TOOLARGE) { RIPMSG0(RIP_WARNING, "SelectContext: create new hMsgBuf"); ImmDestroyIMCC(pInputContext->hMsgBuf); pInputContext->hMsgBuf = ImmCreateIMCC(sizeof(UINT)); pInputContext->dwNumMsgBuf = 0; } /* * hGuideLine */ dwSize = ImmGetIMCCSize(pInputContext->hGuideLine); if (ImmGetIMCCLockCount(pInputContext->hGuideLine) != 0 || dwSize < sizeof(GUIDELINE) || dwSize > IMCC_ALLOC_TOOLARGE) { RIPMSG0(RIP_WARNING, "SelectContext: create new hGuideLine"); ImmDestroyIMCC(pInputContext->hGuideLine); pInputContext->hGuideLine = ImmCreateIMCC(sizeof(GUIDELINE)); pGuideLine = (PGUIDELINE)ImmLockIMCC(pInputContext->hGuideLine); if (pGuideLine != NULL) { pGuideLine->dwSize = sizeof(GUIDELINE); ImmUnlockIMCC(pInputContext->hGuideLine); } } /* * hCandInfo */ dwSize = ImmGetIMCCSize(pInputContext->hCandInfo); if (ImmGetIMCCLockCount(pInputContext->hCandInfo) != 0 || dwSize < sizeof(CANDIDATEINFO) || dwSize > IMCC_ALLOC_TOOLARGE) { RIPMSG0(RIP_WARNING, "SelectContext: create new hCandInfo"); ImmDestroyIMCC(pInputContext->hCandInfo); pInputContext->hCandInfo = ImmCreateIMCC(sizeof(CANDIDATEINFO)); pCandInfo = (PCANDIDATEINFO)ImmLockIMCC(pInputContext->hCandInfo); if (pCandInfo != NULL) { pCandInfo->dwSize = sizeof(CANDIDATEINFO); ImmUnlockIMCC(pInputContext->hCandInfo); } } /* * hCompStr */ dwSize = ImmGetIMCCSize(pInputContext->hCompStr); if (ImmGetIMCCLockCount(pInputContext->hCompStr) != 0 || dwSize < sizeof(COMPOSITIONSTRING) || dwSize > IMCC_ALLOC_TOOLARGE) { RIPMSG0(RIP_WARNING, "SelectContext: create new hCompStr"); ImmDestroyIMCC(pInputContext->hCompStr); pInputContext->hCompStr = ImmCreateIMCC(sizeof(COMPOSITIONSTRING)); pCompStr = (PCOMPOSITIONSTRING)ImmLockIMCC(pInputContext->hCompStr); if (pCompStr != NULL) { pCompStr->dwSize = sizeof(COMPOSITIONSTRING); ImmUnlockIMCC(pInputContext->hCompStr); } } // // Save and restore the IME modes when the primary // language changes. // #if !defined(CUAS_ENABLE) if (pUnSelImeDpi) #else if (pUnSelImeDpi && fUseImeSaverForUnSelIme) #endif { // // If UnSelKL is IME, get ModeSaver per language. // pUnSelModeSaver = GetImeModeSaver(pInputContext, hUnSelKL); TAGMSG1(DBGTAG_IMM, "pUnSelModeSaver=%p", pUnSelModeSaver); if (pUnSelModeSaver) { // // Firstly save the private sentence mode per IME. // SavePrivateMode(pInputContext, pUnSelModeSaver, hUnSelKL); } } else { pUnSelModeSaver = NULL; } #if !defined(CUAS_ENABLE) if (pSelImeDpi) #else if (pSelImeDpi && fUseImeSaverForSelIme) #endif { // // If SelKL is IME, get is ModeSaver per language. // pSelModeSaver = GetImeModeSaver(pInputContext, hSelKL); TAGMSG1(DBGTAG_IMM, "pSelImeDpi. pImeModeSaver=%p", pSelModeSaver); } else { pSelModeSaver = NULL; } // // If the primary language of KL changes, save the current mode // and restore the previous modes of new language. // if (pUnSelModeSaver != pSelModeSaver) { // // If old KL is IME, save the current conversion, sentence and open mode. // if (pUnSelModeSaver) { pUnSelModeSaver->fOpen = (pInputContext->fOpen != FALSE); // // Don't have to save the preserved bits for conversion mode. // pUnSelModeSaver->fdwConversion = pInputContext->fdwConversion & ~fdwConvPreserve; pUnSelModeSaver->fdwSentence = LOWORD(pInputContext->fdwSentence); pUnSelModeSaver->fdwInit = pInputContext->fdwInit; } // // If new KL is IME, restore the previous conversion, sentence and open mode. // if (pSelModeSaver) { if (pInputContext->fdwDirty & IMSS_INIT_OPEN) { // // HKL change may be kicked from private IME hotkey, and // a user wants it opened when switched. // pInputContext->fOpen = TRUE; pInputContext->fdwDirty &= ~IMSS_INIT_OPEN; } else { pInputContext->fOpen = pSelModeSaver->fOpen; } // // Some bits are preserved across the languages. // pInputContext->fdwConversion &= fdwConvPreserve; ImmAssert((pSelModeSaver->fdwConversion & fdwConvPreserve) == 0); pInputContext->fdwConversion |= pSelModeSaver->fdwConversion & ~fdwConvPreserve; ImmAssert(HIWORD(pSelModeSaver->fdwSentence) == 0); pInputContext->fdwSentence = pSelModeSaver->fdwSentence; pInputContext->fdwInit = pSelModeSaver->fdwInit; } } if (pSelModeSaver) { // // Restore the private sentence mode per IME. // RestorePrivateMode(pInputContext, pSelModeSaver, hSelKL); } /* * Select the input context. */ #if !defined(CUAS_ENABLE) if (pSelImeDpi != NULL) (*pSelImeDpi->pfn.ImeSelect)(hImc, TRUE); #else if (pSelImeDpi != NULL) { if (IS_IME_KBDLAYOUT(hSelKL)) { (*pSelImeDpi->pfn.ImeSelect)(hImc, TRUE); } else if (IS_CICERO_ENABLED_AND_NOT16BIT()) { (*pSelImeDpi->pfn.CtfImeSelectEx)(hImc, TRUE, hSelKL); } pClientImc->SelectedHKL = hSelKL; } #endif // // Set the dirty bits so that IMM can send notifications later. // See SendNotificatonProc. // pInputContext->fdwDirty = 0; if (pInputContext->fOpen != fOldOpen) { pInputContext->fdwDirty |= IMSS_UPDATE_OPEN; } if (pInputContext->fdwConversion != fdwOldConversion) { pInputContext->fdwDirty |= IMSS_UPDATE_CONVERSION; } if (pInputContext->fdwSentence != fdwOldSentence) { pInputContext->fdwDirty |= IMSS_UPDATE_SENTENCE; } TAGMSG4(DBGTAG_IMM, "fOpen:%d fdwConv:%08x fdwSent:%08x dirty:%02x", pInputContext->fOpen, pInputContext->fdwConversion, pInputContext->fdwSentence, pInputContext->fdwDirty); ImmUnlockIMC(hImc); } else { // // To keep the backward compatibility, // select the input context here. // #if !defined(CUAS_ENABLE) if (pSelImeDpi != NULL) (*pSelImeDpi->pfn.ImeSelect)(hImc, TRUE); #else if (pSelImeDpi != NULL) { if (IS_IME_KBDLAYOUT(hSelKL)) { (*pSelImeDpi->pfn.ImeSelect)(hImc, TRUE); } else if (IS_CICERO_ENABLED_AND_NOT16BIT()) { (*pSelImeDpi->pfn.CtfImeSelectEx)(hImc, TRUE, hSelKL); } pClientImc->SelectedHKL = hSelKL; } #endif } ImmUnlockImeDpi(pUnSelImeDpi); ImmUnlockImeDpi(pSelImeDpi); ImmUnlockClientImc(pClientImc); } BOOL SendNotificationProc( HIMC hImc, LPARAM lParam) { PINPUTCONTEXT pInputContext = ImmLockIMC(hImc); UNREFERENCED_PARAMETER(lParam); if (pInputContext != NULL) { HWND hwnd = pInputContext->hWnd; if (IsWindow(hwnd)) { TAGMSG2(DBGTAG_IMM, "SendNotificationProc: updating hImc=%08x dirty=%04x", hImc, pInputContext->fdwDirty); if (pInputContext->fdwDirty & IMSS_UPDATE_OPEN) { SendMessageW(hwnd, WM_IME_NOTIFY, IMN_SETOPENSTATUS, 0); } if (pInputContext->fdwDirty & IMSS_UPDATE_CONVERSION) { SendMessageW(hwnd, WM_IME_NOTIFY, IMN_SETCONVERSIONMODE, 0); } if (pInputContext->fdwDirty & (IMSS_UPDATE_OPEN | IMSS_UPDATE_CONVERSION)) { NtUserNotifyIMEStatus(hwnd, pInputContext->fOpen, pInputContext->fdwConversion); } if (pInputContext->fdwDirty & IMSS_UPDATE_SENTENCE) { SendMessageW(hwnd, WM_IME_NOTIFY, IMN_SETSENTENCEMODE, 0); } } pInputContext->fdwDirty = 0; } return TRUE; } VOID ImmSendNotification( BOOL fForProcess) { DWORD dwThreadId; if (fForProcess) { dwThreadId = -1; } else { dwThreadId = 0; } ImmEnumInputContext(dwThreadId, (IMCENUMPROC)SendNotificationProc, 0); } /**************************************************************************\ * ImmEnumInputContext * * 20-Feb-1996 wkwok Created \**************************************************************************/ BOOL WINAPI ImmEnumInputContext( DWORD idThread, IMCENUMPROC lpfn, LPARAM lParam) { UINT i; UINT cHimc; HIMC *phimcT; HIMC *phimcFirst; BOOL fSuccess = TRUE; /* * Get the himc list. It is returned in a block of memory * allocated with ImmLocalAlloc. */ if ((cHimc = BuildHimcList(idThread, &phimcFirst)) == 0) { return FALSE; } /* * Loop through the input contexts, call the function pointer back for * each one. End loop if either FALSE is returned or the end-of-list is * reached. */ phimcT = phimcFirst; for (i = 0; i < cHimc; i++) { if (RevalidateHimc(*phimcT)) { if (!(fSuccess = (*lpfn)(*phimcT, lParam))) break; } phimcT++; } /* * Free up buffer and return status - TRUE if entire list was enumerated, * FALSE otherwise. */ ImmLocalFree(phimcFirst); return fSuccess; } /**************************************************************************\ * BuildHimcList * * 20-Feb-1996 wkwok Created \**************************************************************************/ DWORD BuildHimcList( DWORD idThread, HIMC **pphimcFirst) { UINT cHimc; HIMC *phimcFirst; NTSTATUS Status; int cTries; /* * Allocate a buffer to hold the names. */ cHimc = 64; phimcFirst = ImmLocalAlloc(0, cHimc * sizeof(HIMC)); if (phimcFirst == NULL) return 0; Status = NtUserBuildHimcList(idThread, cHimc, phimcFirst, &cHimc); /* * If the buffer wasn't big enough, reallocate * the buffer and try again. */ cTries = 0; while (Status == STATUS_BUFFER_TOO_SMALL) { ImmLocalFree(phimcFirst); /* * If we can't seem to get it right, * call it quits */ if (cTries++ == 10) return 0; phimcFirst = ImmLocalAlloc(0, cHimc * sizeof(HIMC)); if (phimcFirst == NULL) return 0; Status = NtUserBuildHimcList(idThread, cHimc, phimcFirst, &cHimc); } if (!NT_SUCCESS(Status) || cHimc == 0) { ImmLocalFree(phimcFirst); return 0; } *pphimcFirst = phimcFirst; return cHimc; }