You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2794 lines
83 KiB
2794 lines
83 KiB
/**************************************************************************\
|
|
* 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;
|
|
}
|
|
|