|
|
/**************************************************************************\
* 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; }
|