/**************************************************************************\ * Module Name: ntimm.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * This module contains IMM functionality * * History: * 21-Dec-1995 wkwok \**************************************************************************/ #include "precomp.h" #pragma hdrstop static CONST WCHAR wszDefaultIme[] = L"Default IME"; #if DBG BOOL CheckOwnerCirculate(PWND pwnd) { PWND pwndT = pwnd->spwndOwner; while (pwndT) { UserAssert(pwndT->spwndOwner != pwnd); pwndT = pwndT->spwndOwner; } return TRUE; } #endif /**************************************************************************\ * CreateInputContext * * Create input context object. * * History: * 21-Dec-1995 wkwok Created \**************************************************************************/ PIMC CreateInputContext( ULONG_PTR dwClientImcData) { PTHREADINFO ptiCurrent; PIMC pImc; PDESKTOP pdesk = NULL; ptiCurrent = PtiCurrentShared(); /* * Only for thread that wants IME processing. */ if ((ptiCurrent->TIF_flags & TIF_DISABLEIME) || !IS_IME_ENABLED()) { RIPMSG1(RIP_VERBOSE, "CreateInputContext: TIF_DISABLEIME or !IME Enabled. pti=%#p", ptiCurrent); return NULL; } /* * If pti->spDefaultImc is NULL (means this is the first instance) * but dwClientImcData is not 0, some bogus application like NtCrash * has tried to trick the kernel. Just bail out. */ if (dwClientImcData != 0 && ptiCurrent->spDefaultImc == NULL) { RIPMSG2(RIP_WARNING, "CreateInputContext: bogus value(0x%08x) is passed. pti=%#p", dwClientImcData, ptiCurrent); return NULL; } /* * If the windowstation has been initialized, allocate from * the current desktop. */ pdesk = ptiCurrent->rpdesk; #ifdef LATER RETURN_IF_ACCESS_DENIED(ptiCurrent->amdesk, DESKTOP_CREATEINPUTCONTEXT, NULL); #else if (ptiCurrent->rpdesk == NULL) { return NULL; } #endif pImc = HMAllocObject(ptiCurrent, pdesk, TYPE_INPUTCONTEXT, sizeof(IMC)); if (pImc == NULL) { RIPMSG0(RIP_WARNING, "CreateInputContext: out of memory"); return NULL; } if (dwClientImcData == 0) { /* * We are creating default input context for current thread. * Initialize the default input context as head of the * per-thread IMC list. */ UserAssert(ptiCurrent->spDefaultImc == NULL); Lock(&ptiCurrent->spDefaultImc, pImc); pImc->pImcNext = NULL; } else { /* * Link it to the per-thread IMC list. */ UserAssert(ptiCurrent->spDefaultImc != NULL); pImc->pImcNext = ptiCurrent->spDefaultImc->pImcNext; ptiCurrent->spDefaultImc->pImcNext = pImc; } pImc->dwClientImcData = dwClientImcData; return pImc; } /**************************************************************************\ * DestroyInputContext * * Destroy the specified input context object. * * History: * 21-Dec-1995 wkwok Created \**************************************************************************/ BOOL DestroyInputContext( IN PIMC pImc) { PTHREADINFO ptiImcOwner; PBWL pbwl; PWND pwnd; HWND *phwnd; PHE phe; ptiImcOwner = GETPTI(pImc); /* * Cannot destroy input context from other thread. */ if (ptiImcOwner != PtiCurrent()) { RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING, "DestroyInputContext: pImc not of current pti"); return FALSE; } /* * Cannot destroy default input context. */ if (pImc == ptiImcOwner->spDefaultImc) { RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "DestroyInputContext: can't destroy default Imc"); return FALSE; } /* * Cleanup destroyed input context from each associated window. */ pbwl = BuildHwndList(ptiImcOwner->rpdesk->pDeskInfo->spwnd->spwndChild, BWL_ENUMLIST|BWL_ENUMCHILDREN, ptiImcOwner); if (pbwl != NULL) { for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) { /* * Make sure this hwnd is still around. */ if ((pwnd = RevalidateHwnd(*phwnd)) == NULL) continue; /* * Cleanup by associating the default input context. */ if (pwnd->hImc == (HIMC)PtoH(pImc)) AssociateInputContext(pwnd, ptiImcOwner->spDefaultImc); } FreeHwndList(pbwl); } phe = HMPheFromObject(pImc); /* * Make sure this object isn't already marked to be destroyed - we'll * do no good if we try to destroy it now since it is locked. */ if (!(phe->bFlags & HANDLEF_DESTROY)) HMDestroyUnlockedObject(phe); return TRUE; } /**************************************************************************\ * FreeInputContext * * Free up the specified input context object. * * History: * 21-Dec-1995 wkwok Created \**************************************************************************/ VOID FreeInputContext( IN PIMC pImc) { PIMC pImcT; /* * Mark it for destruction. If it the object is locked it can't * be freed right now. */ if (!HMMarkObjectDestroy((PVOID)pImc)) return; /* * Unlink it. */ pImcT = GETPTI(pImc)->spDefaultImc; while (pImcT != NULL && pImcT->pImcNext != pImc) pImcT = pImcT->pImcNext; if (pImcT != NULL) pImcT->pImcNext = pImc->pImcNext; /* * We're really going to free the input context. */ HMFreeObject((PVOID)pImc); return; } /**************************************************************************\ * UpdateInputContext * * Update the specified input context object according to UpdateType. * * History: * 21-Dec-1995 wkwok Created \**************************************************************************/ BOOL UpdateInputContext( IN PIMC pImc, IN UPDATEINPUTCONTEXTCLASS UpdateType, IN ULONG_PTR UpdateValue) { PTHREADINFO ptiCurrent, ptiImcOwner; ptiCurrent = PtiCurrent(); ptiImcOwner = GETPTI(pImc); /* * Cannot update input context from other process. */ if (ptiImcOwner->ppi != ptiCurrent->ppi) { RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING, "UpdateInputContext: pImc not of current ppi"); return FALSE; } switch (UpdateType) { case UpdateClientInputContext: if (pImc->dwClientImcData != 0) { RIPERR0(RIP_WARNING, RIP_WARNING, "UpdateInputContext: pImc->dwClientImcData != 0"); return FALSE; } pImc->dwClientImcData = UpdateValue; break; case UpdateInUseImeWindow: pImc->hImeWnd = (HWND)UpdateValue; break; default: return FALSE; } return TRUE; } /**************************************************************************\ * AssociateInputContext * * Associate input context object to the specified window. * * History: * 21-Dec-1995 wkwok Created \**************************************************************************/ HIMC AssociateInputContext( IN PWND pWnd, IN PIMC pImc) { HIMC hImcRet = pWnd->hImc; pWnd->hImc = (HIMC)PtoH(pImc); return hImcRet; } AIC_STATUS AssociateInputContextEx( IN PWND pWnd, IN PIMC pImc, IN DWORD dwFlag) { PTHREADINFO ptiWnd = GETPTI(pWnd); PWND pWndFocus = ptiWnd->pq->spwndFocus; BOOL fIgnoreNoContext = (dwFlag & IACE_IGNORENOCONTEXT) == IACE_IGNORENOCONTEXT; AIC_STATUS Status = AIC_SUCCESS; if (dwFlag & IACE_DEFAULT) { /* * use default input context. */ pImc = ptiWnd->spDefaultImc; } else if (pImc != NULL && GETPTI(pImc) != ptiWnd) { /* * Cannot associate input context to window created * by other thread. */ RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING, "AssociateInputContextEx: pwnd not of Imc pti"); return AIC_ERROR; } /* * Cannot do association under different process context. */ if (GETPTI(pWnd)->ppi != PtiCurrent()->ppi) { RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING, "AssociateInputContextEx: pwnd not of current ppi"); return AIC_ERROR; } /* * Finally, make sure they are on the same desktop. */ if (pImc != NULL && pImc->head.rpdesk != pWnd->head.rpdesk) { RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING, "AssociateInputContextEx: no desktop access"); return AIC_ERROR; } /* * If IACE_CHILDREN is specified, associate the input context * to the child windows of pWnd as well. */ if ((dwFlag & IACE_CHILDREN) && pWnd->spwndChild != NULL) { PBWL pbwl; PWND pwndT; HWND *phwndT; pbwl = BuildHwndList(pWnd->spwndChild, BWL_ENUMLIST|BWL_ENUMCHILDREN, ptiWnd); if (pbwl != NULL) { for (phwndT = pbwl->rghwnd; *phwndT != (HWND)1; phwndT++) { /* * Make sure this hwnd is still around. */ if ((pwndT = RevalidateHwnd(*phwndT)) == NULL) continue; if (pwndT->hImc == (HIMC)PtoH(pImc)) continue; if (pwndT->hImc == NULL_HIMC && fIgnoreNoContext) continue; AssociateInputContext(pwndT, pImc); if (pwndT == pWndFocus) Status = AIC_FOCUSCONTEXTCHANGED; } FreeHwndList(pbwl); } } /* * Associate the input context to pWnd. */ if (pWnd->hImc != NULL_HIMC || !fIgnoreNoContext) { if (pWnd->hImc != (HIMC)PtoH(pImc)) { AssociateInputContext(pWnd, pImc); if (pWnd == pWndFocus) Status = AIC_FOCUSCONTEXTCHANGED; } } return Status; } /**************************************************************************\ * xxxFocusSetInputContext * * Set active input context upon focus change. * * History: * 21-Mar-1996 wkwok Created \**************************************************************************/ VOID xxxFocusSetInputContext( IN PWND pWnd, IN BOOL fActivate, IN BOOL fQueueMsg) { PTHREADINFO pti; PWND pwndDefaultIme; TL tlpwndDefaultIme; CheckLock(pWnd); pti = GETPTI(pWnd); /* * CS_IME class or "IME" class windows can not be SetActivated to hImc. * WinWord 6.0 US Help calls ShowWindow with the default IME window. * HELPMACROS get the default IME window by calling GetNextWindow(). */ if (TestCF(pWnd, CFIME) || (pWnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])) return; /* * Do nothing if the thread does not have default IME window. */ if ((pwndDefaultIme = pti->spwndDefaultIme) == NULL) return; /* * If the thread is going away or the default IME window is being vanished, * then do nothing. */ if (pti->TIF_flags & TIF_INCLEANUP) return; UserAssert(!TestWF(pwndDefaultIme, WFDESTROYED)); ThreadLockAlways(pwndDefaultIme, &tlpwndDefaultIme); if (fQueueMsg) { xxxSendMessageCallback(pwndDefaultIme, WM_IME_SYSTEM, fActivate ? IMS_ACTIVATECONTEXT : IMS_DEACTIVATECONTEXT, (LPARAM)HWq(pWnd), NULL, 1L, 0); } else { xxxSendMessage(pwndDefaultIme, WM_IME_SYSTEM, fActivate ? IMS_ACTIVATECONTEXT : IMS_DEACTIVATECONTEXT, (LPARAM)HWq(pWnd)); } #if _DBG if (pti->spwndDefaultIme != pwndDefaultIme) { RIPMSG1(RIP_WARNING, "pti(%#p)->spwndDefaultIme got freed during the callback.", pti); } #endif ThreadUnlock(&tlpwndDefaultIme); return; } /**************************************************************************\ * BuildHimcList * * Retrieve the list of input context handles created by given thread. * * History: * 21-Feb-1995 wkwok Created \**************************************************************************/ UINT BuildHimcList( PTHREADINFO pti, UINT cHimcMax, HIMC *ccxphimcFirst) { PIMC pImcT; UINT i = 0; if (pti == NULL) { /* * Build the list which contains all IMCs created by calling process. */ for (pti = PtiCurrent()->ppi->ptiList; pti != NULL; pti = pti->ptiSibling) { pImcT = pti->spDefaultImc; while (pImcT != NULL) { if (i < cHimcMax) { try { ccxphimcFirst[i] = (HIMC)PtoH(pImcT); } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { } } i++; pImcT = pImcT->pImcNext; } } } else { /* * Build the list which contains all IMCs created by specified thread. */ pImcT = pti->spDefaultImc; while (pImcT != NULL) { if (i < cHimcMax) { try { ccxphimcFirst[i] = (HIMC)PtoH(pImcT); } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { } } i++; pImcT = pImcT->pImcNext; } } return i; } /**************************************************************************\ * xxxCreateDefaultImeWindow * * Create per-thread based default IME window. * * History: * 21-Mar-1996 wkwok Created \**************************************************************************/ PWND xxxCreateDefaultImeWindow( IN PWND pwnd, IN ATOM atomT, IN HANDLE hInst) { LARGE_STRING strWindowName; PWND pwndDefaultIme; TL tlpwnd; PIMEUI pimeui; PTHREADINFO ptiCurrent = PtiCurrentShared(); LPWSTR pwszDefaultIme; UserAssert(ptiCurrent == GETPTI(pwnd) && ptiCurrent->spwndDefaultIme == NULL); /* * Those conditions should have been checked by WantImeWindow() * before xxxCreateDefaultImeWindow gets called. */ UserAssert(!(ptiCurrent->TIF_flags & TIF_DISABLEIME)); UserAssert(!TestWF(pwnd, WFSERVERSIDEPROC)); /* * The first Winlogon thread starts without default input context. * Create it now. */ if (ptiCurrent->spDefaultImc == NULL && PsGetThreadProcessId(ptiCurrent->pEThread) == gpidLogon) CreateInputContext(0); /* * No default IME window for thread that doesn't have * default input context */ if (ptiCurrent->spDefaultImc == NULL) return (PWND)NULL; /* * Avoid recursion */ if (atomT == gpsi->atomSysClass[ICLS_IME] || TestCF(pwnd, CFIME)) return (PWND)NULL; /* * B#12165-win95b * Yet MFC does another nice. We need to avoid to give an IME window * to the child of desktop window which is in different process. */ if (TestwndChild(pwnd) && GETPTI(pwnd->spwndParent)->ppi != ptiCurrent->ppi && !(pwnd->style & WS_VISIBLE)) return (PWND)NULL; if (ptiCurrent->rpdesk->pheapDesktop == NULL) return (PWND)NULL; /* * Allocate storage for L"Default IME" string from desktop heap * so that it can be referenced from USER32.DLL in user mode. */ pwszDefaultIme = (LPWSTR)DesktopAlloc(ptiCurrent->rpdesk, sizeof(wszDefaultIme), DTAG_IMETEXT); if (pwszDefaultIme == NULL) return (PWND)NULL; RtlCopyMemory(pwszDefaultIme, wszDefaultIme, sizeof(wszDefaultIme)); RtlInitLargeUnicodeString((PLARGE_UNICODE_STRING)&strWindowName, pwszDefaultIme, (UINT)-1); ThreadLock(pwnd, &tlpwnd); pwndDefaultIme = xxxNVCreateWindowEx( (DWORD)0, (PLARGE_STRING)gpsi->atomSysClass[ICLS_IME], (PLARGE_STRING)&strWindowName, WS_POPUP | WS_DISABLED, 0, 0, 0, 0, pwnd, (PMENU)NULL, hInst, NULL, VER40); if (pwndDefaultIme != NULL) { pimeui = ((PIMEWND)pwndDefaultIme)->pimeui; UserAssert(pimeui != NULL && (LONG_PTR)pimeui != (LONG_PTR)-1); try { ProbeForWrite(pimeui, sizeof *pimeui, sizeof(DWORD)); pimeui->fDefault = TRUE; if (TestwndChild(pwnd) && GETPTI(pwnd->spwndParent) != ptiCurrent) { pimeui->fChildThreadDef = TRUE; } } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { } } ThreadUnlock(&tlpwnd); DesktopFree(ptiCurrent->rpdesk, pwszDefaultIme); return pwndDefaultIme; } /**************************************************************************\ * xxxImmActivateThreadsLayout * * Activate keyboard layout for multiple threads. * * Return: * TRUE if at least one thread has changed its active keyboard layout. * FALSE otherwise * * History: * 11-Apr-1996 wkwok Created \**************************************************************************/ BOOL xxxImmActivateThreadsLayout( PTHREADINFO pti, PTLBLOCK ptlBlockPrev, PKL pkl) { TLBLOCK tlBlock; PTHREADINFO ptiCurrent, ptiT; UINT cThreads = 0; INT i; CheckLock(pkl); ptiCurrent = PtiCurrentShared(); /* * Build a list of threads that we need to update their active layouts. * We can't just walk the ptiT list while we're doing the work, because * for IME based keyboard layout, we will do callback to client side * and the ptiT could get deleted out while we leave the critical section. */ for (ptiT = pti; ptiT != NULL; ptiT = ptiT->ptiSibling) { /* * Skip all the *do nothing* cases in xxxImmActivateLayout * so as to minimize the # of TLBLOCK required. */ if (ptiT->spklActive == pkl || (ptiT->TIF_flags & TIF_INCLEANUP)) continue; UserAssert(ptiT->pClientInfo != NULL); UserAssert(ptiT->ppi == PpiCurrent()); // can't access pClientInfo of other process if (ptiT->spwndDefaultIme == NULL) { /* * Keyboard layout is being switched but there's no way to callback * the client side to activate&initialize input context now. * Let's do hkl switching only in the kernel side for this thread * but remember the input context needs to be re-initialized * when this GUI thread recreates the default IME window later. */ ptiT->hklPrev = ptiT->spklActive->hkl; Lock(&ptiT->spklActive, pkl); if (ptiT->spDefaultImc) { try { ptiT->pClientInfo->CI_flags |= CI_INPUTCONTEXT_REINIT; } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { continue; } RIPMSG1(RIP_VERBOSE, "xxxImmActivateThreadsLayout: ptiT(%08p) will be re-initialized.", ptiT); } UserAssert((ptiT->TIF_flags & TIF_INCLEANUP) == 0); try { ptiT->pClientInfo->hKL = pkl->hkl; ptiT->pClientInfo->CodePage = pkl->CodePage; } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { } continue; } ThreadLockPti(ptiCurrent, ptiT, &tlBlock.list[cThreads].tlpti); tlBlock.list[cThreads++].pti = ptiT; if (cThreads == THREADS_PER_TLBLOCK) break; } /* * Return FALSE if all the threads already had the pkl active. */ if (ptlBlockPrev == NULL && ptiT == NULL && cThreads == 0) return FALSE; /* * If we can't service all the threads in this run, * call ImmActivateThreadsLayout() again for a new TLBLOCK. */ if (ptiT != NULL && ptiT->ptiSibling != NULL) { tlBlock.ptlBlockPrev = ptlBlockPrev; return xxxImmActivateThreadsLayout(ptiT->ptiSibling, &tlBlock, pkl); } /* * Finally, we can do the actual keyboard layout activation * starting from this run. Work on current TLBLOCK first. * We walk the list backwards so that the pti unlocks will * be done in the right order. */ tlBlock.ptlBlockPrev = ptlBlockPrev; ptlBlockPrev = &tlBlock; while (ptlBlockPrev != NULL) { for (i = cThreads - 1; i >= 0; --i) { if ((ptlBlockPrev->list[i].pti->TIF_flags & TIF_INCLEANUP) == 0) { ptiT = ptlBlockPrev->list[i].pti; UserAssert(ptiT); xxxImmActivateLayout(ptiT, pkl); if ((ptiT->TIF_flags & TIF_INCLEANUP) == 0) { try { ptiT->pClientInfo->hKL = pkl->hkl; ptiT->pClientInfo->CodePage = pkl->CodePage; } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { } } } ThreadUnlockPti(ptiCurrent, &ptlBlockPrev->list[i].tlpti); } ptlBlockPrev = ptlBlockPrev->ptlBlockPrev; cThreads = THREADS_PER_TLBLOCK; } return TRUE; } VOID xxxImmActivateAndUnloadThreadsLayout( IN PTHREADINFO *ptiList, IN UINT nEntries, IN PTLBLOCK ptlBlockPrev, PKL pklCurrent, DWORD dwHklReplace) { TLBLOCK tlBlock; PTHREADINFO ptiCurrent; int i, cThreads; enum { RUN_ACTIVATE = 1, RUN_UNLOAD = 2, RUN_FLAGS_MASK = RUN_ACTIVATE | RUN_UNLOAD, RUN_INVALID = 0xffff0000 }; CheckLock(pklCurrent); ptiCurrent = PtiCurrentShared(); tlBlock.ptlBlockPrev = ptlBlockPrev; /* * Build a list of threads that we need to unload their IME DLL(s). * We can't just walk the ptiList while we're doing the work, because * for IME based keyboard layout, we will do callback to client side * and the pti could get deleted out while we leave the critical section. */ for (i = 0, cThreads = 0; i < (INT)nEntries; i++) { DWORD dwFlags = 0; /* * Skip all the *do nothing* cases in xxxImmActivateLayout * so as to minimize the # of TLBLOCKs required. */ if (ptiList[i]->TIF_flags & TIF_INCLEANUP) { dwFlags = RUN_INVALID; } else if (ptiList[i]->spklActive != pklCurrent) { if (ptiList[i]->spwndDefaultIme == NULL) { BOOLEAN fAttached = FALSE; Lock(&ptiList[i]->spklActive, pklCurrent); if (ptiList[i]->pClientInfo != ptiCurrent->pClientInfo && ptiList[i]->ppi != ptiCurrent->ppi) { /* * If the thread is in another process, attach * to that process so that we can access its ClientInfo. */ KeAttachProcess(PsGetProcessPcb(ptiList[i]->ppi->Process)); fAttached = TRUE; } try { ptiList[i]->pClientInfo->CodePage = pklCurrent->CodePage; ptiList[i]->pClientInfo->hKL = pklCurrent->hkl; } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { dwFlags = RUN_INVALID; } if (fAttached) { KeDetachProcess(); } } else { dwFlags = RUN_ACTIVATE; } } /* * Skip all the *do nothing* cases in xxxImmUnloadLayout() * so as to minimize the # of TLBLOCK required. * (#99321) */ if (ptiList[i]->spwndDefaultIme != NULL && ptiList[i]->spklActive != NULL && (dwHklReplace != IFL_DEACTIVATEIME || IS_IME_KBDLAYOUT(ptiList[i]->spklActive->hkl) #ifdef CUAS_ENABLE || IS_CICERO_ENABLED_AND_NOT16BIT() #endif // CUAS_ENABLE ) && dwFlags != RUN_INVALID) { dwFlags |= RUN_UNLOAD; } if (dwFlags && dwFlags != RUN_INVALID) { ThreadLockPti(ptiCurrent, ptiList[i], &tlBlock.list[cThreads].tlpti); #if DBG tlBlock.list[cThreads].dwUnlockedCount = 0; #endif tlBlock.list[cThreads].pti = ptiList[i]; tlBlock.list[cThreads++].dwFlags = dwFlags; if (cThreads == THREADS_PER_TLBLOCK) { i++; // 1 more before exit the loop. break; } } } /* * If we can't service all the threads in this run, * call xxxImmActivateAndUnloadThreadsLayout again for a new TLBLOCK. */ if (i < (INT)nEntries) { ptiList += i; nEntries -= i; xxxImmActivateAndUnloadThreadsLayout(ptiList, nEntries, &tlBlock, pklCurrent, dwHklReplace); return; } /* * Finally, we can do the actual keyboard layout activation * starting from this run. Work on current TLBLOCK first. * We walk the list backwards so that the pti unlocks will * be done in the right order. */ i = cThreads - 1; for (ptlBlockPrev = &tlBlock; ptlBlockPrev != NULL; ptlBlockPrev = ptlBlockPrev->ptlBlockPrev) { for ( ; i >= 0; i--) { if ((ptlBlockPrev->list[i].dwFlags & RUN_ACTIVATE) && !(ptlBlockPrev->list[i].pti->TIF_flags & TIF_INCLEANUP)) { xxxImmActivateLayout(ptlBlockPrev->list[i].pti, pklCurrent); } // unlock the thread if the thread is only locked for the first run if ((ptlBlockPrev->list[i].dwFlags & RUN_FLAGS_MASK) == RUN_ACTIVATE) { ThreadUnlockPti(ptiCurrent, &ptlBlockPrev->list[i].tlpti); #if DBG ptlBlockPrev->list[i].dwUnlockedCount++; #endif } } i = THREADS_PER_TLBLOCK - 1; } i = cThreads - 1; for (ptlBlockPrev = &tlBlock; ptlBlockPrev != NULL; ptlBlockPrev = ptlBlockPrev->ptlBlockPrev) { for ( ; i >= 0; --i) { if (ptlBlockPrev->list[i].dwFlags & RUN_UNLOAD) { if (!(ptlBlockPrev->list[i].pti->TIF_flags & TIF_INCLEANUP)) { xxxImmUnloadLayout(ptlBlockPrev->list[i].pti, dwHklReplace); } else { RIPMSG1(RIP_WARNING, "xxxImmActivateAndUnloadThreadsLayout: thread %#p is cleaned up.", ptlBlockPrev->list[i].pti); } // unlock the thread UserAssert((ptlBlockPrev->list[i].dwFlags & RUN_FLAGS_MASK) != RUN_ACTIVATE); UserAssert(ptlBlockPrev->list[i].dwUnlockedCount == 0); ThreadUnlockPti(ptiCurrent, &ptlBlockPrev->list[i].tlpti); #if DBG ptlBlockPrev->list[i].dwUnlockedCount++; #endif } } i = THREADS_PER_TLBLOCK - 1; } #if DBG // Check if all the locked thread is properly unlocked i = cThreads - 1; for (ptlBlockPrev = &tlBlock; ptlBlockPrev; ptlBlockPrev = ptlBlockPrev->ptlBlockPrev) { for ( ; i >= 0; --i) { UserAssert(ptlBlockPrev->list[i].dwUnlockedCount == 1); } i = THREADS_PER_TLBLOCK - 1; } #endif return; } /**************************************************************************\ * xxxImmActivateLayout * * Activate IME based keyboard layout. * * History: * 21-Mar-1996 wkwok Created \**************************************************************************/ VOID xxxImmActivateLayout( IN PTHREADINFO pti, IN PKL pkl) { TL tlpwndDefaultIme; PTHREADINFO ptiCurrent; CheckLock(pkl); /* * Do nothing if it's already been the current active layout. */ if (pti->spklActive == pkl) return; if (pti->spwndDefaultIme == NULL) { /* * Only activate kernel side keyboard layout if this pti * doesn't have the default IME window. */ Lock(&pti->spklActive, pkl); return; } ptiCurrent = PtiCurrentShared(); /* * Activate client side IME based keyboard layout. */ ThreadLockAlwaysWithPti(ptiCurrent, pti->spwndDefaultIme, &tlpwndDefaultIme); xxxSendMessage(pti->spwndDefaultIme, WM_IME_SYSTEM, (WPARAM)IMS_ACTIVATETHREADLAYOUT, (LPARAM)pkl->hkl); ThreadUnlock(&tlpwndDefaultIme); Lock(&pti->spklActive, pkl); return; } VOID xxxImmUnloadThreadsLayout( IN PTHREADINFO *ptiList, IN UINT nEntries, IN PTLBLOCK ptlBlockPrev, IN DWORD dwFlag) { TLBLOCK tlBlock; PTHREADINFO ptiCurrent; INT i, cThreads; BOOLEAN fPerformUnlock; ptiCurrent = PtiCurrentShared(); tlBlock.ptlBlockPrev = ptlBlockPrev; /* * Build a list of threads that we need to unload their IME DLL(s). * We can't just walk the ptiList while we're doing the work, because * for IME based keyboard layout, we will do callback to client side * and the pti could get deleted out while we leave the critical section. */ for (i = 0, cThreads = 0; i < (INT)nEntries; i++) { /* * Skip all the *do nothing* cases in xxxImmUnloadLayout() * so as to minimize the # of TLBLOCK required. */ if ((ptiList[i]->TIF_flags & TIF_INCLEANUP) || ptiList[i]->spwndDefaultIme == NULL) continue; if (ptiList[i]->spklActive == NULL) continue; #if !defined(CUAS_ENABLE) if (dwFlag == IFL_DEACTIVATEIME && !IS_IME_KBDLAYOUT(ptiList[i]->spklActive->hkl)) // #99321 continue; #else if (dwFlag == IFL_DEACTIVATEIME && ((! IS_CICERO_ENABLED() && ! IS_IME_KBDLAYOUT(ptiList[i]->spklActive->hkl)) || ( IS_CICERO_ENABLED() && (PtiCurrent()->TIF_flags & TIF_16BIT))) ) // #99321 continue; #endif #if DBG tlBlock.list[cThreads].dwUnlockedCount = 0; #endif ThreadLockPti(ptiCurrent, ptiList[i], &tlBlock.list[cThreads].tlpti); tlBlock.list[cThreads++].pti = ptiList[i]; if (cThreads == THREADS_PER_TLBLOCK) { i++; // 1 more before exit the loop. break; } } if (i < (INT)nEntries) { ptiList += i; nEntries -= i; xxxImmUnloadThreadsLayout(ptiList, nEntries, &tlBlock, dwFlag); return; } UserAssert(dwFlag == IFL_UNLOADIME || dwFlag == IFL_DEACTIVATEIME); if (dwFlag == IFL_UNLOADIME) { dwFlag = IFL_DEACTIVATEIME; fPerformUnlock = FALSE; } else { fPerformUnlock = TRUE; } RepeatForUnload: /* * Finally, we can unload the IME based keyboard layout * starting from this run. Work on current TLBLOCK first. * We walk the list backwards so that the pti unlocks will * be done in the right order. */ i = cThreads - 1; for (ptlBlockPrev = &tlBlock; ptlBlockPrev; ptlBlockPrev = ptlBlockPrev->ptlBlockPrev) { for ( ; i >= 0; --i) { if (!(ptlBlockPrev->list[i].pti->TIF_flags & TIF_INCLEANUP)) { xxxImmUnloadLayout(ptlBlockPrev->list[i].pti, dwFlag); } else { RIPMSG2(RIP_WARNING, "Thread %#p is cleaned during the loop for %x!", ptlBlockPrev->list[i].pti, dwFlag); } if (fPerformUnlock) { #if DBG ptlBlockPrev->list[i].dwUnlockedCount++; #endif ThreadUnlockPti(ptiCurrent, &ptlBlockPrev->list[i].tlpti); } } i = THREADS_PER_TLBLOCK - 1; } if (!fPerformUnlock) { fPerformUnlock = TRUE; dwFlag = IFL_UNLOADIME; goto RepeatForUnload; } #if DBG // Check if all the locked thread is properly unlocked i = cThreads - 1; for (ptlBlockPrev = &tlBlock; ptlBlockPrev; ptlBlockPrev = ptlBlockPrev->ptlBlockPrev) { for ( ; i >= 0; --i) { UserAssert(ptlBlockPrev->list[i].dwUnlockedCount == 1); } i = THREADS_PER_TLBLOCK - 1; } #endif return; } VOID xxxImmUnloadLayout( IN PTHREADINFO pti, IN DWORD dwFlag) { TL tlpwndDefaultIme; PTHREADINFO ptiCurrent; ULONG_PTR dwResult; LRESULT r; /* * Do nothing if the thread does not have default IME window. */ if (pti->spwndDefaultIme == NULL) return; if (pti->spklActive == NULL) return; #if !defined(CUAS_ENABLE) if (dwFlag == IFL_DEACTIVATEIME && !IS_IME_KBDLAYOUT(pti->spklActive->hkl)) return; #else if (dwFlag == IFL_DEACTIVATEIME && ((! IS_CICERO_ENABLED() && !IS_IME_KBDLAYOUT(pti->spklActive->hkl)) || ( IS_CICERO_ENABLED() && (PtiCurrent()->TIF_flags & TIF_16BIT))) ) return; #endif ptiCurrent = PtiCurrentShared(); ThreadLockAlwaysWithPti(ptiCurrent, pti->spwndDefaultIme, &tlpwndDefaultIme); r = xxxSendMessageTimeout(pti->spwndDefaultIme, WM_IME_SYSTEM, IMS_UNLOADTHREADLAYOUT, (LONG)dwFlag, SMTO_NOTIMEOUTIFNOTHUNG, CMSHUNGAPPTIMEOUT, &dwResult); if (!r) { RIPMSG1(RIP_WARNING, "Timeout in xxxImmUnloadLayout: perhaps this thread (0x%x) is not pumping messages.", GETPTIID(pti)); } ThreadUnlock(&tlpwndDefaultIme); return; } /**************************************************************************\ * xxxImmLoadLayout * * Retrieves extended IMEINFO for the given IME based keyboard layout. * * History: * 21-Mar-1996 wkwok Created \**************************************************************************/ PIMEINFOEX xxxImmLoadLayout( IN HKL hKL) { PIMEINFOEX piiex; PTHREADINFO ptiCurrent; TL tlPool; /* * No IMEINFOEX for non-IME based keyboard layout. */ #if !defined(CUAS_ENABLE) if (!IS_IME_KBDLAYOUT(hKL)) return (PIMEINFOEX)NULL; #else if ((! IS_CICERO_ENABLED() && !IS_IME_KBDLAYOUT(hKL)) || ( IS_CICERO_ENABLED() && (PtiCurrent()->TIF_flags & TIF_16BIT)) ) return (PIMEINFOEX)NULL; #endif piiex = (PIMEINFOEX)UserAllocPool(sizeof(IMEINFOEX), TAG_IME); if (piiex == NULL) { RIPMSG1(RIP_WARNING, "xxxImmLoadLayout: failed to create piiex for hkl = %lx", hKL); return (PIMEINFOEX)NULL; } ptiCurrent = PtiCurrent(); /* * Lock this allocations since we are going to the client side */ ThreadLockPool(ptiCurrent, piiex, &tlPool); if (!ClientImmLoadLayout(hKL, piiex)) { ThreadUnlockAndFreePool(ptiCurrent, &tlPool); return (PIMEINFOEX)NULL; } ThreadUnlockPool(ptiCurrent, &tlPool); return piiex; } /**************************************************************************\ * GetImeInfoEx * * Query extended IMEINFO. * * History: * 21-Mar-1996 wkwok Created \**************************************************************************/ BOOL GetImeInfoEx( PWINDOWSTATION pwinsta, PIMEINFOEX piiex, IMEINFOEXCLASS SearchType) { PKL pkl, pklFirst; /* * Note: this check was forced to insert due to winmm.dll who indirectly * loads imm32.dll in CSRSS context. CSRSS is not always bound to * specific window station, thus pwinsta could be NULL. * This has been avoided by not loading imm32.dll. * After winmm.dll gets removed from CSRSS, this if statement should be * removed, or substituted as an assertion. */ if (pwinsta == NULL) { return FALSE; } /* * Keyboard layer has not been initialized. */ if (pwinsta->spklList == NULL) return FALSE; pkl = pklFirst = pwinsta->spklList; switch (SearchType) { case ImeInfoExKeyboardLayout: do { if (pkl->hkl == piiex->hkl) { if (pkl->piiex == NULL) break; RtlCopyMemory(piiex, pkl->piiex, sizeof(IMEINFOEX)); return TRUE; } pkl = pkl->pklNext; } while (pkl != pklFirst); break; case ImeInfoExImeFileName: do { if (pkl->piiex != NULL && !_wcsnicmp(pkl->piiex->wszImeFile, piiex->wszImeFile, IM_FILE_SIZE)) { RtlCopyMemory(piiex, pkl->piiex, sizeof(IMEINFOEX)); return TRUE; } pkl = pkl->pklNext; } while (pkl != pklFirst); break; default: break; } return FALSE; } /**************************************************************************\ * SetImeInfoEx * * Set extended IMEINFO. * * History: * 21-Mar-1996 wkwok Created \**************************************************************************/ BOOL SetImeInfoEx( PWINDOWSTATION pwinsta, PIMEINFOEX piiex) { PKL pkl, pklFirst; if (pwinsta == NULL) { return FALSE; } UserAssert(pwinsta->spklList != NULL); pkl = pklFirst = pwinsta->spklList; do { if (pkl->hkl == piiex->hkl) { /* * Error out for non-IME based keyboard layout. */ if (pkl->piiex == NULL) return FALSE; /* * Update kernel side IMEINFOEX for this keyboard layout * only if this is its first loading. */ if (pkl->piiex->fLoadFlag == IMEF_NONLOAD) { RtlCopyMemory(pkl->piiex, piiex, sizeof(IMEINFOEX)); } return TRUE; } pkl = pkl->pklNext; } while (pkl != pklFirst); return FALSE; } /***************************************************************************\ * xxxImmProcessKey * * * History: * 03-03-96 TakaoK Created. \***************************************************************************/ DWORD xxxImmProcessKey( IN PQ pq, IN PWND pwnd, IN UINT message, IN WPARAM wParam, IN LPARAM lParam) { UINT uVKey; PKL pkl; DWORD dwHotKeyID; DWORD dwReturn = 0; PIMC pImc = NULL; BOOL fDBERoman = FALSE; PIMEHOTKEYOBJ pImeHotKeyObj; HKL hklTarget; CheckLock(pwnd); // // we're interested in only keyboard messages. // if ( message != WM_KEYDOWN && message != WM_SYSKEYDOWN && message != WM_KEYUP && message != WM_SYSKEYUP ) { return dwReturn; } // // Check if it's IME hotkey. This must be done before checking // the keyboard layout because IME hotkey handler should be // called even if current keyboard layout is non-IME layout. // pkl = GETPTI(pwnd)->spklActive; if ( pkl == NULL ) { return dwReturn; } uVKey = (UINT)wParam & 0xff; pImeHotKeyObj = CheckImeHotKey(pq, uVKey, lParam); if (pImeHotKeyObj) { dwHotKeyID = pImeHotKeyObj->hk.dwHotKeyID; hklTarget = pImeHotKeyObj->hk.hKL; } else { dwHotKeyID = IME_INVALID_HOTKEY; hklTarget = (HKL)NULL; } // // Handle Direct KL switching here. // if (dwHotKeyID >= IME_HOTKEY_DSWITCH_FIRST && dwHotKeyID <= IME_HOTKEY_DSWITCH_LAST) { UserAssert(hklTarget != NULL); if (pkl->hkl != hklTarget) { // // Post the message only if the new Keyboard Layout is different from // the current Keyboard Layout. // _PostMessage(pwnd, WM_INPUTLANGCHANGEREQUEST, (pkl->dwFontSigs & gSystemFS) ? INPUTLANGCHANGE_SYSCHARSET : 0, (LPARAM)hklTarget); } if (GetAppImeCompatFlags(GETPTI(pwnd)) & IMECOMPAT_HYDRACLIENT) { return 0; } return IPHK_HOTKEY; } if (!IS_IME_ENABLED()) { // // Since IMM is disabled, no need to process further. // Just bail out. // return 0; } if ( dwHotKeyID != IME_INVALID_HOTKEY ) { // // if it's a valid hotkey, go straight and call back // the IME in the client side. // goto ProcessKeyCallClient; } // // if it's not a hotkey, we may want to check something // before calling back. // if ( pkl->piiex == NULL ) { return dwReturn; } // // Check input context // pImc = HtoP(pwnd->hImc); if ( pImc == NULL ) { return dwReturn; } #ifdef LATER // // If there is an easy way to check the input context open/close status // from the kernel side, IME_PROP_NO_KEYS_ON_CLOSE checking should be // done here in kernel side. [ 3/10/96 takaok] // // // Check IME_PROP_NO_KEYS_ON_CLOSE bit // // if the current imc is not open and IME doesn't need // keys when being closed, we don't pass any keyboard // input to ime except hotkey and keys that change // the keyboard status. // if ( (piix->ImeInfo.fdwProperty & IME_PROP_NO_KEYS_ON_CLOSE) && (!pimc->fdwState & IMC_OPEN) && uVKey != VK_SHIFT && // 0x10 uVKey != VK_CONTROL && // 0x11 uVKey != VK_CAPITAL && // 0x14 uVKey != VK_KANA && // 0x15 uVKey != VK_NUMLOCK && // 0x90 uVKey != VK_SCROLL ) // 0x91 { // Check if Korea Hanja conversion mode if( !(pimc->fdwConvMode & IME_CMODE_HANJACONVERT) ) { return dwReturn; } } #endif // // if the IME doesn't need key up messages, we don't call ime. // if ( lParam & 0x80000000 && // set if key up, clear if key down pkl->piiex->ImeInfo.fdwProperty & IME_PROP_IGNORE_UPKEYS ) { return dwReturn; } // // we don't want to handle sys keys since many functions for // acceelerators won't work without this // fDBERoman = (BOOL)( (uVKey == VK_DBE_ROMAN) || (uVKey == VK_DBE_NOROMAN) || (uVKey == VK_DBE_HIRAGANA) || (uVKey == VK_DBE_KATAKANA) || (uVKey == VK_DBE_CODEINPUT) || (uVKey == VK_DBE_NOCODEINPUT) || (uVKey == VK_DBE_IME_WORDREGISTER) || (uVKey == VK_DBE_IME_DIALOG) ); if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP ) { // // IME may be waiting for VK_MENU, VK_F10 or VK_DBE_xxx // if ( uVKey != VK_MENU && uVKey != VK_F10 && !fDBERoman ) { return dwReturn; } } // // check if the IME doesn't need ALT key // if ( !(pkl->piiex->ImeInfo.fdwProperty & IME_PROP_NEED_ALTKEY) ) { // // IME doesn't need ALT key // // we don't pass the ALT and ALT+xxx except VK_DBE_xxx keys. // if ( ! fDBERoman && (uVKey == VK_MENU || (lParam & 0x20000000)) // KF_ALTDOWN ) { return dwReturn; } } // // finaly call back the client // ProcessKeyCallClient: if ((uVKey & 0xff) == VK_PACKET) { // // need to retrieve UNICODE character from pti // uVKey = MAKELONG(wParam, PtiCurrent()->wchInjected); } dwReturn = ClientImmProcessKey( PtoH(pwnd), pkl->hkl, uVKey, lParam, dwHotKeyID); // // Hydra server wants to see the IME hotkeys. // if (GetAppImeCompatFlags(GETPTI(pwnd)) & IMECOMPAT_HYDRACLIENT) { dwReturn &= ~IPHK_HOTKEY; } return dwReturn; } /**************************************************************************\ * ImeCanDestroyDefIME * * History: * 02-Apr-1996 wkwok Ported from FE Win95 (imeclass.c) \**************************************************************************/ BOOL ImeCanDestroyDefIME( PWND pwndDefaultIme, PWND pwndDestroy) { PWND pwnd; PIMEUI pimeui; pimeui = ((PIMEWND)pwndDefaultIme)->pimeui; if (pimeui == NULL || (LONG_PTR)pimeui == (LONG_PTR)-1) return FALSE; try { if (ProbeAndReadStructure(pimeui, IMEUI).fDestroy) { return FALSE; } } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { } /* * If the pwndDestroy has no owner/ownee relationship with * pwndDefaultIme, don't bother to change anything. * * If pwndDefaultIme->spwndOwner is NULL, this means we need * to search for a new good owner window. */ if ( pwndDefaultIme->spwndOwner != NULL ) { for (pwnd = pwndDefaultIme->spwndOwner; pwnd != pwndDestroy && pwnd != NULL; pwnd = pwnd->spwndOwner) ; if (pwnd == NULL) return FALSE; } /* * If the destroying window is IME or UI window, do nothing */ pwnd = pwndDestroy; while (pwnd != NULL) { if (TestCF(pwnd, CFIME) || pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]) return FALSE; pwnd = pwnd->spwndOwner; } ImeSetFutureOwner(pwndDefaultIme, pwndDestroy); /* * If new owner is lower z-order than IME class window, * we need to check topmost to change z-order. */ pwnd = pwndDefaultIme->spwndOwner; while (pwnd != NULL && pwnd != pwndDefaultIme) pwnd = pwnd->spwndNext; if (pwnd == pwndDefaultIme) ImeCheckTopmost(pwndDefaultIme); #if DBG CheckOwnerCirculate(pwndDefaultIme); #endif /* * If ImeSetFutureOwner can not find the owner window any * more, this IME window should be destroyed. */ if (pwndDefaultIme->spwndOwner == NULL || pwndDestroy == pwndDefaultIme->spwndOwner) { // RIPMSG1(RIP_WARNING, "ImeCanDestroyDefIME: TRUE for pwnd=%#p", pwndDestroy); Unlock(&pwndDefaultIme->spwndOwner); /* * Return TRUE! Please destroy me. */ return TRUE; } return FALSE; } /**************************************************************************\ * IsChildSameThread (IsChildSameQ) * * History: * 02-Apr-1996 wkwok Ported from FE Win95 (imeclass.c) \**************************************************************************/ BOOL IsChildSameThread( PWND pwndParent, PWND pwndChild) { PWND pwnd; PTHREADINFO ptiChild = GETPTI(pwndChild); for (pwnd = pwndParent->spwndChild; pwnd; pwnd = pwnd->spwndNext) { /* * If pwnd is not child window, we need to skip MENU window and * IME related window. */ if (!TestwndChild(pwnd)) { PWND pwndOwner = pwnd; BOOL fFoundOwner = FALSE; /* * Skip MENU window. */ if (pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_MENU]) continue; while (pwndOwner != NULL) { /* * CS_IME class or "IME" class windows can not be the owner of * IME windows. */ if (TestCF(pwndOwner, CFIME) || pwndOwner->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]) { fFoundOwner = TRUE; break; } pwndOwner = pwndOwner->spwndOwner; } if (fFoundOwner) continue; } /* * We need to skip pwndChild. */ if (pwnd == pwndChild) continue; /* * pwnd and pwndChild are on same thread? */ if (GETPTI(pwnd) == ptiChild) { PWND pwndT = pwnd; BOOL fFoundImeWnd = FALSE; /* * Check again. If hwndT is children or ownee of * IME related window, skip it. */ if (TestwndChild(pwndT)) { for (; TestwndChild(pwndT) && GETPTI(pwndT) == ptiChild; pwndT = pwndT->spwndParent) { if (TestCF(pwndT, CFIME) || pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]) fFoundImeWnd = TRUE; } } if (!TestwndChild(pwndT)) { for (; pwndT != NULL && GETPTI(pwndT) == ptiChild; pwndT = pwndT->spwndOwner) { if (TestCF(pwndT, CFIME) || pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]) fFoundImeWnd = TRUE; } } if (!fFoundImeWnd) return TRUE; } } return FALSE; } /**************************************************************************\ * ImeCanDestroyDefIMEforChild * * History: * 02-Apr-1996 wkwok Ported from FE Win95 (imeclass.c) \**************************************************************************/ BOOL ImeCanDestroyDefIMEforChild( PWND pwndDefaultIme, PWND pwndDestroy) { PWND pwnd; PIMEUI pimeui; pimeui = ((PIMEWND)pwndDefaultIme)->pimeui; /* * If this window is not for Child Thread..... */ if (pimeui == NULL || (LONG_PTR)pimeui == (LONG_PTR)-1) return FALSE; try { if (!ProbeAndReadStructure(pimeui, IMEUI).fChildThreadDef) { return FALSE; } } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { } /* * If parent belongs to different thread, * we don't need to check any more... */ if (pwndDestroy->spwndParent == NULL || GETPTI(pwndDestroy) == GETPTI(pwndDestroy->spwndParent)) return FALSE; pwnd = pwndDestroy; while (pwnd != NULL && pwnd != PWNDDESKTOP(pwnd)) { if (IsChildSameThread(pwnd->spwndParent, pwndDestroy)) return FALSE; pwnd = pwnd->spwndParent; } /* * We could not find any other window created by GETPTI(pwndDestroy). * Let's destroy the default IME window of this Q. */ return TRUE; } /**************************************************************************\ * ImeCheckTopmost * * History: * 02-Apr-1996 wkwok Ported from FE Win95 (imeclass.c) \**************************************************************************/ VOID ImeCheckTopmost( PWND pwndIme) { if (pwndIme->spwndOwner) { PWND pwndInsertBeforeThis; /* * The ime window have to be same topmost tyle with the owner window. * If the Q of this window is not foreground Q, we don't need to * forground the IME window. * But the topmost attribute of owner was changed, this IME window * should be re-calced. */ if (GETPTI(pwndIme) == gptiForeground) { pwndInsertBeforeThis = NULL; } else { pwndInsertBeforeThis = pwndIme->spwndOwner; } ImeSetTopmost(pwndIme, TestWF(pwndIme->spwndOwner, WEFTOPMOST) != 0, pwndInsertBeforeThis); } } /**************************************************************************\ * ImeSetOwnerWindow * * Before re-owning the IME window, several checks must be done on the new * owner. Of great importance, the new owner cannot be an IME window itself, * must be a top level window and there can never be an ownership cycle or * it would throw win32k into an un-recoverable spin. * * History: * 17-July-2001 Mohamed Created. \**************************************************************************/ VOID ImeSetOwnerWindow( IN PWND pwndIme, IN PWND pwndNewOwner) { PWND pwndTopLevel; PWND pwndT; if (TestCF(pwndNewOwner, CFIME) || pwndNewOwner->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]) { RIPMSG1(RIP_WARNING, "New owner window (pwnd=%p) should not be an IME/UI window!!", pwndNewOwner); return; } /* * Child window cannot be an owner window. Therefore, we get that window's top * level parent. */ pwndTopLevel = pwndT = GetTopLevelWindow(pwndNewOwner); /* * To prevent an IME window from becoming the owner of another IME window. */ while (pwndT != NULL) { if (pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]) { RIPMSG1(RIP_WARNING, "The new owner (pwnd=%p) of an IME window should not itself be an IME window!!", pwndT); pwndTopLevel = NULL; break; } pwndT = pwndT->spwndOwner; } UserAssert(pwndIme->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]); UserAssert(pwndTopLevel == NULL || !TestCF(pwndTopLevel, CFIME)); Lock(&pwndIme->spwndOwner, pwndTopLevel); } /**************************************************************************\ * ImeSetFutureOwner * * History: * 02-Apr-1996 wkwok Ported from FE Win95 (imeclass.c) \**************************************************************************/ VOID ImeSetFutureOwner( PWND pwndIme, PWND pwndOrgOwner) { PWND pwnd, pwndOwner; PTHREADINFO ptiImeWnd = GETPTI(pwndIme); if (pwndOrgOwner == NULL || TestWF(pwndOrgOwner, WFCHILD)) return; pwnd = pwndOrgOwner; /* * Get top of owner created by the same thread. */ while ((pwndOwner = pwnd->spwndOwner) != NULL && GETPTI(pwndOwner) == ptiImeWnd) pwnd = pwndOwner; /* * Bottom window can not be the owner of IME window easily... */ if (TestWF(pwnd, WFBOTTOMMOST) && !TestWF(pwndOrgOwner, WFBOTTOMMOST)) pwnd = pwndOrgOwner; /* * CS_IME class or "IME" class windows can not be the owner of * IME windows. */ if (TestCF(pwnd, CFIME) || pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]) pwnd = pwndOrgOwner; /* * If hwndOrgOwner is a top of owner, we start to search * another top owner window in same queue. */ if (pwndOrgOwner == pwnd && pwnd->spwndParent != NULL) { PWND pwndT; for (pwndT = pwnd->spwndParent->spwndChild; pwndT != NULL; pwndT = pwndT->spwndNext) { if (GETPTI(pwnd) != GETPTI(pwndT)) continue; if (pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_MENU]) continue; /* * CS_IME class or "IME" class windows can not be the owner of * IME windows. */ if (TestCF(pwndT, CFIME) || pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]) continue; // We don't like the window that is being destroyed. if (TestWF(pwndT, WFINDESTROY)) continue; /* * !!!!WARNING!!!!! * Is hwndT a good owner of hIMEwnd?? * 1. Of cource, it should no CHILD window! * 2. If it is hwnd,.. I know it and find next! * 3. Does hwndT have owner in the same thread? */ if (!TestWF(pwndT, WFCHILD) && pwnd != pwndT && (pwndT->spwndOwner == NULL || GETPTI(pwndT) != GETPTI(pwndT->spwndOwner))) { UserAssert(GETPTI(pwndIme) == GETPTI(pwndT)); pwnd = pwndT; break; } } } UserAssert(!TestCF(pwnd, CFIME)); Lock(&pwndIme->spwndOwner, pwnd); return; } /**************************************************************************\ * ImeSetTopmostChild * * History: * 02-Apr-1996 wkwok Ported from FE Win95 (imeclass.c) \**************************************************************************/ VOID ImeSetTopmostChild( PWND pwndParent, BOOL fMakeTopmost) { PWND pwnd = pwndParent->spwndChild; while (pwnd != NULL) { if (fMakeTopmost) SetWF(pwnd, WEFTOPMOST); else ClrWF(pwnd, WEFTOPMOST); ImeSetTopmostChild(pwnd, fMakeTopmost); pwnd = pwnd->spwndNext; } return; } /**************************************************************************\ * * GetLastTopMostWindowNoIME() - * * Get the last topmost window which is not the ownee of pwndRoot (IME window). * \**************************************************************************/ PWND GetLastTopMostWindowNoIME(PWND pwndRoot) { PWND pwndT = _GetDesktopWindow(); PWND pwndRet = NULL; /* * pwndRoot should not be NULL, and should be IME window. */ UserAssert(pwndRoot && pwndRoot->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]); if (pwndT == NULL || pwndT->spwndChild == NULL) { #if _DBG if (pwndT == NULL) { RIPMSG0(RIP_WARNING, "GetLastTopMostWindowNoIME: there's no desktop window !!"); } else { RIPMSG0(RIP_WARNING, "GetLastTopMostWindowNoIME: there is no toplevel window !!"); } #endif return NULL; } /* * Get the first child of the desktop window. */ pwndT = pwndT->spwndChild; /* * Loop through the toplevel windows while they are topmost. */ while (TestWF(pwndT, WEFTOPMOST)) { PWND pwndOwner = pwndT; BOOL fOwned = FALSE; /* * If pwndT is a IME related window, track the owner. If pwndRoot is not * pwndT's owner, remember pwndT as a candidate. */ if (TestCF(pwndT,CFIME) || (pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])) { while (pwndOwner != NULL) { if (pwndRoot == pwndOwner) { fOwned = TRUE; break; } pwndOwner = pwndOwner->spwndOwner; } } if (!fOwned) pwndRet = pwndT; /* * Next toplevel window. */ pwndT = pwndT->spwndNext; UserAssert(pwndT->spwndParent == _GetDesktopWindow()); } return pwndRet; } #if DBG void ImeCheckSetTopmostLink(PWND pwnd, PWND pwndInsFirst, PWND pwndIns) { PWND pwndDebT0 = pwndInsFirst; BOOL fFound = FALSE; if (pwndDebT0) { while (pwndDebT0 && (pwndDebT0 != pwndIns)) { if (pwndDebT0 == pwnd) fFound = TRUE; pwndDebT0 = pwndDebT0->spwndNext; } if (pwndDebT0 == NULL) { RIPMSG3(RIP_ERROR, "pwndIns(%#p) is upper that pwndInsFirst(%#p) pwnd is (%#p)", pwndIns, pwndInsFirst, pwnd); } else if (fFound) { RIPMSG3(RIP_ERROR, "pwnd(%#p) is between pwndInsFirst(%#p) and pwndIns(%#p)", pwnd, pwndInsFirst, pwndIns); } } else if (pwndIns) { pwndDebT0 = pwnd->spwndParent->spwndChild; while (pwndDebT0 && (pwndDebT0 != pwndIns)) { if (pwndDebT0 == pwnd) fFound = TRUE; pwndDebT0 = pwndDebT0->spwndNext; } if (fFound) { RIPMSG3(RIP_ERROR, "pwnd(%#p) is between TOPLEVEL pwndInsFirst(%#p) and pwndIns(%#p)", pwnd, pwndInsFirst, pwndIns); } } } #endif /**************************************************************************\ * ImeSetTopmost * * History: * 02-Apr-1996 wkwok Ported from FE Win95 (imeclass.c) \**************************************************************************/ VOID ImeSetTopmost( PWND pwndRootIme, BOOL fMakeTopmost, PWND pwndInsertBefore) { PWND pwndParent = pwndRootIme->spwndParent; PWND pwndInsert = PWND_TOP; // pwnd which should be prior to pwndRootIme. PWND pwnd, pwndT; PWND pwndInsertFirst; BOOLEAN fFound; if (pwndParent == NULL) return; pwnd = pwndParent->spwndChild; if (!fMakeTopmost) { /* * Get the last topmost window. This should be after unlink pwndRootIme * because pwndRootIme may be the last topmost window. */ pwndInsert = GetLastTopMostWindowNoIME(pwndRootIme); if (pwndInsertBefore) { fFound = FALSE; pwndT = pwndInsert; while (pwndT != NULL && pwndT->spwndNext != pwndInsertBefore) { if (pwndT == pwndRootIme) fFound = TRUE; pwndT = pwndT->spwndNext; } if (pwndT == NULL || fFound) return; pwndInsert = pwndT; } if (TestWF(pwndRootIme->spwndOwner, WFBOTTOMMOST)) { pwndT = pwndInsert; while (pwndT != NULL && pwndT != pwndRootIme->spwndOwner) { if (!TestCF(pwndT, CFIME) && pwndT->pcls->atomClassName != gpsi->atomSysClass[ICLS_IME]) { pwndInsert = pwndT; } pwndT = pwndT->spwndNext; } } } pwndInsertFirst = pwndInsert; /* * Enum the all toplevel windows and if the owner of the window is same as * the owner of pwndRootIme, the window should be changed the position of * window link. */ while (pwnd != NULL) { /* * Get the next window before calling ImeSetTopmost. * Because the next window will be changed in LinkWindow. */ PWND pwndNext = pwnd->spwndNext; /* * the owner relation between IME and UI window is in same thread. */ if (GETPTI(pwnd) != GETPTI(pwndRootIme)) goto ist_next; /* * pwnd have to be CS_IME class or "IME" class. */ if (!TestCF(pwnd, CFIME) && pwnd->pcls->atomClassName != gpsi->atomSysClass[ICLS_IME]) goto ist_next; /* * If pwnd is pwndInsert, we don't need to do anything... */ if (pwnd == pwndInsert) goto ist_next; pwndT = pwnd; while (pwndT != NULL) { if (pwndT == pwndRootIme) { /* * Found!! * pwnd is the ownee of pwndRootIme. */ UserAssert(GETPTI(pwnd) == GETPTI(pwndRootIme)); UserAssert(TestCF(pwnd,CFIME) || (pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])); UserAssert(pwnd != pwndInsert); UnlinkWindow(pwnd, pwndParent); if (fMakeTopmost) { if (pwndInsert != PWND_TOP) UserAssert(TestWF(pwndInsert, WEFTOPMOST)); SetWF(pwnd, WEFTOPMOST); } else { if (pwndInsert == PWND_TOP) { /* * In rare cases, the first toplevel window could be the one we'll look next, * who may still have obscure topmost flag. */ UserAssert(pwndParent->spwndChild == pwndNext || !TestWF(pwndParent->spwndChild, WEFTOPMOST)); } else if (pwndInsert->spwndNext != NULL) { /* * In rare cases, pwndInsert->spwndNext could be the one we'll look next, * who may still have obscure topmost flag. */ UserAssert(pwndInsert->spwndNext == pwndNext || !TestWF(pwndInsert->spwndNext, WEFTOPMOST)); } ClrWF(pwnd, WEFTOPMOST); } LinkWindow(pwnd, pwndInsert, pwndParent); #if 0 // Let's see what happens if we disable this ImeSetTopmostChild(pwnd, fMakeTopmost); #endif pwndInsert = pwnd; break; // goto ist_next; } pwndT = pwndT->spwndOwner; } ist_next: pwnd = pwndNext; /* * Skip the windows that were inserted before. */ if (pwnd != NULL && pwnd == pwndInsertFirst) pwnd = pwndInsert->spwndNext; #if DBG if (pwnd) ImeCheckSetTopmostLink(pwnd, pwndInsertFirst, pwndInsert); #endif } } /**************************************************************************\ * ProbeAndCaptureSoftKbdData * * Captures SoftKbdData that comes from user mode. * * 23-Apr-1996 wkwok created \**************************************************************************/ PSOFTKBDDATA ProbeAndCaptureSoftKbdData( PSOFTKBDDATA Source) { PSOFTKBDDATA Destination = NULL; DWORD cbSize; UINT uCount; try { uCount = ProbeAndReadUlong((PULONG)Source); #if defined(_X86_) ProbeForReadBuffer(&Source->wCode, uCount, sizeof(BYTE)); #else ProbeForReadBuffer(&Source->wCode, uCount, sizeof(WORD)); #endif cbSize = FIELD_OFFSET(SOFTKBDDATA, wCode[0]) + uCount * sizeof(WORD) * 256; Destination = (PSOFTKBDDATA)UserAllocPool(cbSize, TAG_IME); if (Destination != NULL) { RtlCopyMemory(Destination, Source, cbSize); } else { ExRaiseStatus(STATUS_NO_MEMORY); } } except (W32ExceptionHandler(TRUE, RIP_WARNING)) { if (Destination != NULL) { UserFreePool(Destination); } return NULL; } return Destination; } // // ported from Win95:ctxtman.c\SetConvMode() // VOID SetConvMode( PTHREADINFO pti, DWORD dwConversion ) { if ( pti->spklActive == NULL ) return; switch ( PRIMARYLANGID(HandleToUlong(pti->spklActive->hkl)) ) { case LANG_JAPANESE: ClearKeyStateDown(pti->pq, VK_DBE_ALPHANUMERIC); ClearKeyStateToggle(pti->pq, VK_DBE_ALPHANUMERIC); ClearKeyStateDown(pti->pq, VK_DBE_KATAKANA); ClearKeyStateToggle(pti->pq, VK_DBE_KATAKANA); ClearKeyStateDown(pti->pq, VK_DBE_HIRAGANA); ClearKeyStateToggle(pti->pq, VK_DBE_HIRAGANA); if ( dwConversion & IME_CMODE_NATIVE ) { if ( dwConversion & IME_CMODE_KATAKANA ) { SetKeyStateDown( pti->pq, VK_DBE_KATAKANA); SetKeyStateToggle( pti->pq, VK_DBE_KATAKANA); } else { SetKeyStateDown( pti->pq, VK_DBE_HIRAGANA); SetKeyStateToggle( pti->pq, VK_DBE_HIRAGANA); } } else { SetKeyStateDown( pti->pq, VK_DBE_ALPHANUMERIC); SetKeyStateToggle( pti->pq, VK_DBE_ALPHANUMERIC); } if ( dwConversion & IME_CMODE_FULLSHAPE ) { SetKeyStateDown( pti->pq, VK_DBE_DBCSCHAR); SetKeyStateToggle( pti->pq, VK_DBE_DBCSCHAR); ClearKeyStateDown(pti->pq, VK_DBE_SBCSCHAR); ClearKeyStateToggle(pti->pq, VK_DBE_SBCSCHAR); } else { SetKeyStateDown( pti->pq, VK_DBE_SBCSCHAR); SetKeyStateToggle( pti->pq, VK_DBE_SBCSCHAR); ClearKeyStateDown(pti->pq, VK_DBE_DBCSCHAR); ClearKeyStateToggle(pti->pq, VK_DBE_DBCSCHAR); } if ( dwConversion & IME_CMODE_ROMAN ) { SetKeyStateDown( pti->pq, VK_DBE_ROMAN); SetKeyStateToggle( pti->pq, VK_DBE_ROMAN); ClearKeyStateDown(pti->pq, VK_DBE_NOROMAN); ClearKeyStateToggle(pti->pq, VK_DBE_NOROMAN); } else { SetKeyStateDown( pti->pq, VK_DBE_NOROMAN); SetKeyStateToggle( pti->pq, VK_DBE_NOROMAN); ClearKeyStateDown(pti->pq, VK_DBE_ROMAN); ClearKeyStateToggle(pti->pq, VK_DBE_ROMAN); } if ( dwConversion & IME_CMODE_CHARCODE ) { SetKeyStateDown( pti->pq, VK_DBE_CODEINPUT); SetKeyStateToggle( pti->pq, VK_DBE_CODEINPUT); ClearKeyStateDown(pti->pq, VK_DBE_NOCODEINPUT); ClearKeyStateToggle(pti->pq, VK_DBE_NOCODEINPUT); } else { SetKeyStateDown( pti->pq, VK_DBE_NOCODEINPUT); SetKeyStateToggle( pti->pq, VK_DBE_NOCODEINPUT); ClearKeyStateDown(pti->pq, VK_DBE_CODEINPUT); ClearKeyStateToggle(pti->pq, VK_DBE_CODEINPUT); } break; case LANG_KOREAN: if ( dwConversion & IME_CMODE_NATIVE) { SetKeyStateToggle( pti->pq, VK_HANGUL); } else { ClearKeyStateToggle( pti->pq, VK_HANGUL); } if ( dwConversion & IME_CMODE_FULLSHAPE ) { SetKeyStateToggle( pti->pq, VK_JUNJA); } else { ClearKeyStateToggle( pti->pq, VK_JUNJA); } if ( dwConversion & IME_CMODE_HANJACONVERT ) { SetKeyStateToggle( pti->pq, VK_HANJA); } else { ClearKeyStateToggle( pti->pq, VK_HANJA); } break; default: break; } return; } // // called by IMM32 client when: // // input focus is switched // or IME open status is changed // or IME conversion status is changed // // VOID xxxNotifyIMEStatus( IN PWND pwnd, IN DWORD dwOpen, IN DWORD dwConversion ) { PTHREADINFO pti; CheckLock(pwnd); if ( (pti = GETPTI(pwnd)) != NULL && gptiForeground != NULL ) { if ( pti == gptiForeground || pti->pq == gptiForeground->pq ) { if ( gHimcFocus != pwnd->hImc || gdwIMEOpenStatus != dwOpen || gdwIMEConversionStatus != dwConversion ) { // // save the new status // gHimcFocus = pwnd->hImc; if ( gHimcFocus != (HIMC)NULL ) { RIPMSG2(RIP_VERBOSE, "xxxNotifyIMEStatus: newOpen=%x newConv=%x", dwOpen, dwConversion); gdwIMEOpenStatus = dwOpen; gdwIMEConversionStatus = dwConversion; // // set keyboard states that are related to IME conversion status // SetConvMode(pti, dwOpen ? dwConversion : 0); } // // notify shell the IME status change // // Implementation note: [takaok 9/5/96] // // Using HSHELL_LANGUAGE is not the best way to inform shell // IME status change because we didn't change the keyboard layout. // ( The spec says HSHELL_LANGUAGE is for keyboard layout change. // Also passing window handle as WPARAM is not documented ) // // This is same as what Win95 does. I won't change this for now // because in the future shell will be developed by a different // group in MS. // // Currently only Korean Windows is interested in getting // the conversion status change. // if (IsHooked(pti, WHF_SHELL)) { HKL hkl = NULL; if (pti->spklActive != NULL) { hkl = pti->spklActive->hkl; } xxxCallHook(HSHELL_LANGUAGE, (WPARAM)HWq(pwnd), (LPARAM)hkl, WH_SHELL); } // // notify keyboard driver // NlsKbdSendIMENotification(dwOpen,dwConversion); } } } } //--------------------------------------------------------------------------- // // xxxCheckImeShowStatus() - // // Only one Status Window should be shown in the System. // This functsion enums all IME window and check the show status of them. // // If pti is NULL, check all toplevel windows regardless their owners. // If pti is not NULL, check only windows belong to the thread. // //---------------------------------------------------------------------------- BOOL xxxCheckImeShowStatus(PWND pwndIme, PTHREADINFO pti) { PBWL pbwl; PHWND phwnd; BOOL fRet = FALSE; PTHREADINFO ptiCurrent = PtiCurrent(); if (TestWF(pwndIme, WFINDESTROY)) { return FALSE; } // Parent window of IME window should be the desktop window UserAssert(pwndIme); UserAssert(pwndIme->spwndParent == GETPTI(pwndIme)->pDeskInfo->spwnd); pbwl = BuildHwndList(pwndIme->spwndParent->spwndChild, BWL_ENUMLIST, NULL); if (pbwl != NULL) { fRet = TRUE; for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) { PWND pwndT = RevalidateHwnd(*phwnd); // If pwndT is the current active IME window, we should skip it // since it's the only one window allowed to show status, and // we've already taken care of it. if (pwndT == NULL || (/*pwndIme && */pwndIme == pwndT)) { // Can skip pwndIme != NULL test continue; } // We are going to touch IME windows only if (pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME] && !TestWF(pwndT, WFINDESTROY)) { PIMEUI pimeui = ((PIMEWND)pwndT)->pimeui; if (pimeui == NULL || pimeui == (PIMEUI)-1) { continue; } if (pti == NULL || pti == GETPTI(pwndT)) { BOOLEAN fAttached = FALSE; PWND pwndIMC; // If pwndT is not a window of the current process, we have to138163 // attach the process to get access to pimeui. if (GETPTI(pwndT)->ppi != ptiCurrent->ppi) { RIPMSG0(RIP_VERBOSE, "Attaching process in xxxCheckImeShowStatus"); KeAttachProcess(PsGetProcessPcb(GETPTI(pwndT)->ppi->Process)); fAttached = TRUE; } try { if (ProbeAndReadStructure(pimeui, IMEUI).fShowStatus) { if (pti == NULL) { RIPMSG0(RIP_VERBOSE, "xxxCheckImeShowStatus: the status window is shown !!"); } if ((pwndIMC = RevalidateHwnd(pimeui->hwndIMC)) != NULL) { pimeui->fShowStatus = FALSE; } } else { pwndIMC = NULL; } } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { pwndIMC = NULL; } if (fAttached) { KeDetachProcess(); } if (pwndIMC && GETPTI(pwndIMC) && !(GETPTI(pwndIMC)->TIF_flags & TIF_INCLEANUP)) { TL tl; ThreadLockAlways(pwndIMC, &tl); RIPMSG1(RIP_VERBOSE, "Sending WM_IME_NOTIFY to %#p, IMN_CLOSESTATUSWINDOW", pwndIMC); xxxSendMessage(pwndIMC, WM_IME_NOTIFY, IMN_CLOSESTATUSWINDOW, 0L); ThreadUnlock(&tl); } } } } FreeHwndList(pbwl); } return fRet; } LRESULT xxxSendMessageToUI( PTHREADINFO ptiIme, PIMEUI pimeui, UINT message, WPARAM wParam, LPARAM lParam) { PWND pwndUI; LRESULT lRet = 0L; TL tl; BOOL fAttached = FALSE; CheckCritIn(); if (ptiIme != PtiCurrent()) { fAttached = TRUE; KeAttachProcess(PsGetProcessPcb(ptiIme->ppi->Process)); } try { pwndUI = RevalidateHwnd(ProbeAndReadStructure(pimeui, IMEUI).hwndUI); } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { pwndUI = NULL; } if (pwndUI != NULL){ try { ProbeAndReadUlong((PULONG)&pimeui->nCntInIMEProc); InterlockedIncrement(&pimeui->nCntInIMEProc); // Mark to avoid recursion. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { goto skip_it; } if (fAttached) { KeDetachProcess(); } ThreadLockAlways(pwndUI, &tl); RIPMSG3(RIP_VERBOSE, "Sending message UI pwnd=%#p, msg:%x to wParam=%#p", pwndUI, message, wParam); lRet = xxxSendMessage(pwndUI, message, wParam, lParam); ThreadUnlock(&tl); if (fAttached) { KeAttachProcess(PsGetProcessPcb(ptiIme->ppi->Process)); } try { InterlockedDecrement(&pimeui->nCntInIMEProc); // Mark to avoid recursion. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { } } skip_it: if (fAttached) { KeDetachProcess(); } return lRet; } VOID xxxSendOpenStatusNotify( PTHREADINFO ptiIme, PIMEUI pimeui, PWND pwndApp, BOOL fOpen) { WPARAM wParam = fOpen ? IMN_OPENSTATUSWINDOW : IMN_CLOSESTATUSWINDOW; UserAssert(GETPTI(pwndApp)); if (LOWORD(GETPTI(pwndApp)->dwExpWinVer >= VER40) && pwndApp->hImc != NULL) { TL tl; ThreadLockAlways(pwndApp, &tl); RIPMSG2(RIP_VERBOSE, "Sending %s to pwnd=%#p", fOpen ? "IMN_OPENSTATUSWINDOW" : "IMN_CLOSESTATUSWINDOW", pwndApp); xxxSendMessage(pwndApp, WM_IME_NOTIFY, wParam, 0L); ThreadUnlock(&tl); } else { xxxSendMessageToUI(ptiIme, pimeui, WM_IME_NOTIFY, wParam, 0L); } return; } VOID xxxNotifyImeShowStatus(PWND pwndIme) { PIMEUI pimeui; BOOL fShow; PWND pwnd; PTHREADINFO ptiIme, ptiCurrent; BOOL fSendOpenStatusNotify = FALSE; if (!IS_IME_ENABLED() || TestWF(pwndIme, WFINDESTROY)) { RIPMSG0(RIP_WARNING, "IME is not enabled or in destroy."); return; } ptiCurrent = PtiCurrent(); ptiIme = GETPTI(pwndIme); if (ptiIme != ptiCurrent) { RIPMSG1(RIP_VERBOSE, "Attaching pti=%#p", ptiIme); KeAttachProcess(PsGetProcessPcb(GETPTI(pwndIme)->ppi->Process)); } try { pimeui = ((PIMEWND)pwndIme)->pimeui; fShow = gfIMEShowStatus && ProbeAndReadStructure(pimeui, IMEUI).fCtrlShowStatus; pwnd = RevalidateHwnd(pimeui->hwndIMC); if (pwnd != NULL || (pwnd = GETPTI(pwndIme)->pq->spwndFocus) != NULL) { RIPMSG0(RIP_VERBOSE, "Setting new show status."); fSendOpenStatusNotify = TRUE; pimeui->fShowStatus = fShow; } } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { if (ptiIme != ptiCurrent) { KeDetachProcess(); } return; } if (ptiIme != ptiCurrent) { KeDetachProcess(); } if (fSendOpenStatusNotify) { RIPMSG1(RIP_VERBOSE, "Sending OpenStatus fShow=%d", fShow); xxxSendOpenStatusNotify(ptiIme, pimeui, pwnd, fShow); } // Check the show status of all IME windows in the system. if (!TestWF(pwndIme, WFINDESTROY)) { xxxCheckImeShowStatus(pwndIme, NULL); } } /***************************************************************************\ * xxxSetIMEShowStatus() - * * Set IME Status windows' show status. Called from SystemParametersInfo() * handler. * \***************************************************************************/ BOOL xxxSetIMEShowStatus(IN BOOL fShow) { CheckCritIn(); UserAssert(fShow == FALSE || fShow == TRUE); if (gfIMEShowStatus == fShow) { return TRUE; } if (gfIMEShowStatus == IMESHOWSTATUS_NOTINITIALIZED) { /* * Called for the first time after logon. * No need to write the value to the registry. */ gfIMEShowStatus = fShow; } else { /* * We need to save the new fShow status to the registry. */ TL tlName; PUNICODE_STRING pProfileUserName; BOOL fOK = FALSE; pProfileUserName = CreateProfileUserName(&tlName); if (pProfileUserName) { UserAssert(pProfileUserName != NULL); fOK = UpdateWinIniInt(pProfileUserName, PMAP_INPUTMETHOD, STR_SHOWIMESTATUS, fShow); FreeProfileUserName(pProfileUserName, &tlName); } if (!fOK) { return FALSE; } gfIMEShowStatus = fShow; } /* * If IME is not enabled, further processing is not needed */ if (!IS_IME_ENABLED()) { return TRUE; } /* * Let the current active IME window know the change. */ if (gpqForeground && gpqForeground->spwndFocus) { PTHREADINFO ptiFocus = GETPTI(gpqForeground->spwndFocus); TL tl; UserAssert(ptiFocus); if (ptiFocus->spwndDefaultIme && !(ptiFocus->TIF_flags & TIF_INCLEANUP)) { ThreadLockAlways(ptiFocus->spwndDefaultIme, &tl); xxxNotifyImeShowStatus(ptiFocus->spwndDefaultIme); ThreadUnlock(&tl); } } return TRUE; } /***************************************************************************\ * xxxBroadcastImeShowStatusChange() - * * Let all IME windows in the desktop, including myself know about the * status change. * This routine does not touch the registry, assuming internat.exe updated * the registry. * \***************************************************************************/ VOID xxxBroadcastImeShowStatusChange(PWND pwndIme, BOOL fShow) { CheckCritIn(); gfIMEShowStatus = !!fShow; xxxNotifyImeShowStatus(pwndIme); } /***************************************************************************\ * xxxCheckImeShowStatusInThread() - * * Let all IME windows in the same thread know about the status change. * Called from ImeSetContextHandler(). * \***************************************************************************/ VOID xxxCheckImeShowStatusInThread(PWND pwndIme) { if (IS_IME_ENABLED()) { UserAssert(pwndIme); if (!TestWF(pwndIme, WFINDESTROY)) { xxxCheckImeShowStatus(pwndIme, GETPTI(pwndIme)); } } } BOOL _GetIMEShowStatus(VOID) { return gfIMEShowStatus != FALSE; }