test: ntimm.c Source File

Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

# ntimm.c

Go to the documentation of this file.

    00001 /**************************************************************************\
    00002 * Module Name: ntimm.c
    00003 *
    00004 * Copyright (c) 1985 - 1999, Microsoft Corporation
    00005 *
    00006 * This module contains IMM functionality
    00007 *
    00008 * History:
    00009 * 21-Dec-1995 wkwok
    00010 \**************************************************************************/
    00011
    00012 #include "precomp.h"
    00013 #pragma hdrstop
    00014
    00015
    00016 static CONST WCHAR wszDefaultIme[] = L"Default IME";
    00017
    00018 #if DBG
    00019 BOOL CheckOwnerCirculate(PWND pwnd)
    00020 {
    00021     PWND pwndT = pwnd->spwndOwner;
    00022
    00023     while (pwndT) {
    00024         UserAssert(pwndT->spwndOwner != pwnd);
    00025         pwndT = pwndT->spwndOwner;
    00026     }
    00027     return TRUE;
    00028 }
    00029 #endif
    00030
    00031 /**************************************************************************\
    00032 * CreateInputContext
    00033 *
    00034 * Create input context object.
    00035 *
    00036 * History:
    00037 * 21-Dec-1995 wkwok       Created
    00038 \**************************************************************************/
    00039
    00040 PIMC CreateInputContext(
    00041     ULONG_PTR dwClientImcData)
    00042 {
    00043     PTHREADINFO    ptiCurrent;
    00044     PIMC           pImc;
    00045     PDESKTOP       pdesk = NULL;
    00046
    00047     ptiCurrent = PtiCurrentShared();
    00048
    00049     /*
    00050      * Only for thread that wants IME processing.
    00051      */
    00052     if ((ptiCurrent->TIF_flags & TIF_DISABLEIME) || !IS_IME_ENABLED()) {
    00053         RIPMSG1(RIP_VERBOSE, "CreateInputContext: TIF_DISABLEIME or !IME Enabled. pti=%#p", ptiCurrent);
    00054         return NULL;
    00055     }
    00056
    00057     /*
    00058      * If pti->spDefaultImc is NULL (means this is the first instance)
    00059      * but dwClientImcData is not 0, some bogus application like NtCrash
    00060      * has tried to trick the kernel. Just bail out.
    00061      */
    00062     if (dwClientImcData != 0 && ptiCurrent->spDefaultImc == NULL) {
    00063         RIPMSG2(RIP_WARNING, "CreateInputContext: bogus value(0x%08x) is passed. pti=%#p",
    00064                 dwClientImcData, ptiCurrent);
    00065         return NULL;
    00066     }
    00067
    00068     /*
    00069      * If the windowstation has been initialized, allocate from
    00070      * the current desktop.
    00071      */
    00072     pdesk = ptiCurrent->rpdesk;
    00073 #ifdef LATER
    00074     RETURN_IF_ACCESS_DENIED(ptiCurrent->amdesk, DESKTOP_CREATEINPUTCONTEXT, NULL);
    00075 #else
    00076     if (ptiCurrent->rpdesk == NULL) {
    00077         return NULL;
    00078     }
    00079 #endif
    00080
    00081     pImc = HMAllocObject(ptiCurrent, pdesk, TYPE_INPUTCONTEXT, sizeof(IMC));
    00082
    00083     if (pImc == NULL) {
    00084         RIPMSG0(RIP_WARNING, "CreateInputContext: out of memory");
    00085         return NULL;
    00086     }
    00087
    00088     if (dwClientImcData == 0) {
    00089         /*
    00090          * We are creating default input context for current thread.
    00091          * Initialize the default input context as head of the
    00092          * per-thread IMC list.
    00093          */
    00094         UserAssert(ptiCurrent->spDefaultImc == NULL);
    00095         Lock(&ptiCurrent->spDefaultImc, pImc);
    00096         pImc->pImcNext = NULL;
    00097     }
    00098     else {
    00099         /*
    00100          * Link it to the per-thread IMC list.
    00101          */
    00102         UserAssert(ptiCurrent->spDefaultImc != NULL);
    00103         pImc->pImcNext = ptiCurrent->spDefaultImc->pImcNext;
    00104         ptiCurrent->spDefaultImc->pImcNext = pImc;
    00105     }
    00106
    00107     pImc->dwClientImcData = dwClientImcData;
    00108
    00109     return pImc;
    00110 }
    00111
    00112
    00113 /**************************************************************************\
    00114 * DestroyInputContext
    00115 *
    00116 * Destroy the specified input context object.
    00117 *
    00118 * History:
    00119 * 21-Dec-1995 wkwok       Created
    00120 \**************************************************************************/
    00121
    00122 BOOL DestroyInputContext(
    00123     IN PIMC pImc)
    00124 {
    00125     PTHREADINFO ptiImcOwner;
    00126     PBWL        pbwl;
    00127     PWND        pwnd;
    00128     HWND       *phwnd;
    00129     PHE         phe;
    00130
    00131     ptiImcOwner = GETPTI(pImc);
    00132
    00133     /*
    00134      * Cannot destroy input context from other thread.
    00135      */
    00136     if (ptiImcOwner != PtiCurrent()) {
    00137         RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING,
    00138               "DestroyInputContext: pImc not of current pti");
    00139         return FALSE;
    00140     }
    00141
    00142     /*
    00143      * Cannot destroy default input context.
    00144      */
    00145     if (pImc == ptiImcOwner->spDefaultImc) {
    00146         RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING,
    00147               "DestroyInputContext: can't destroy default Imc");
    00148         return FALSE;
    00149     }
    00150
    00151     /*
    00152      * Cleanup destroyed input context from each associated window.
    00153      */
    00154     pbwl = BuildHwndList(ptiImcOwner->rpdesk->pDeskInfo->spwnd->spwndChild,
    00155                              BWL_ENUMLIST|BWL_ENUMCHILDREN, ptiImcOwner);
    00156
    00157     if (pbwl != NULL) {
    00158
    00159         for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) {
    00160             /*
    00161              * Make sure this hwnd is still around.
    00162              */
    00163             if ((pwnd = RevalidateHwnd(*phwnd)) == NULL)
    00164                 continue;
    00165
    00166             /*
    00167              * Cleanup by associating the default input context.
    00168              */
    00169             if (pwnd->hImc == (HIMC)PtoH(pImc))
    00170                 AssociateInputContext(pwnd, ptiImcOwner->spDefaultImc);
    00171         }
    00172
    00173         FreeHwndList(pbwl);
    00174     }
    00175
    00176     phe = HMPheFromObject(pImc);
    00177
    00178     /*
    00179      * Make sure this object isn't already marked to be destroyed - we'll
    00180      * do no good if we try to destroy it now since it is locked.
    00181      */
    00182     if (!(phe->bFlags & HANDLEF_DESTROY))
    00183         HMDestroyUnlockedObject(phe);
    00184
    00185     return TRUE;
    00186 }
    00187
    00188
    00189 /**************************************************************************\
    00190 * FreeInputContext
    00191 *
    00192 * Free up the specified input context object.
    00193 *
    00194 * History:
    00195 * 21-Dec-1995 wkwok       Created
    00196 \**************************************************************************/
    00197
    00198 VOID FreeInputContext(
    00199     IN PIMC pImc)
    00200 {
    00201     PIMC pImcT;
    00202
    00203     /*
    00204      * Mark it for destruction.  If it the object is locked it can't
    00205      * be freed right now.
    00206      */
    00207     if (!HMMarkObjectDestroy((PVOID)pImc))
    00208         return;
    00209
    00210     /*
    00211      * Unlink it.
    00212      */
    00213     pImcT = GETPTI(pImc)->spDefaultImc;
    00214
    00215     while (pImcT != NULL && pImcT->pImcNext != pImc)
    00216         pImcT = pImcT->pImcNext;
    00217
    00218     if (pImcT != NULL)
    00219         pImcT->pImcNext = pImc->pImcNext;
    00220
    00221     /*
    00222      * We're really going to free the input context.
    00223      */
    00224     HMFreeObject((PVOID)pImc);
    00225
    00226     return;
    00227 }
    00228
    00229
    00230 /**************************************************************************\
    00231 * UpdateInputContext
    00232 *
    00233 * Update the specified input context object according to UpdateType.
    00234 *
    00235 * History:
    00236 * 21-Dec-1995 wkwok       Created
    00237 \**************************************************************************/
    00238
    00239 BOOL UpdateInputContext(
    00240     IN PIMC pImc,
    00241     IN UPDATEINPUTCONTEXTCLASS UpdateType,
    00242     IN ULONG_PTR UpdateValue)
    00243 {
    00244     PTHREADINFO ptiCurrent, ptiImcOwner;
    00245
    00246     ptiCurrent = PtiCurrent();
    00247     ptiImcOwner = GETPTI(pImc);
    00248
    00249     /*
    00250      * Cannot update input context from other process.
    00251      */
    00252     if (ptiImcOwner->ppi != ptiCurrent->ppi) {
    00253         RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING, "UpdateInputContext: pImc not of current ppi");
    00254         return FALSE;
    00255     }
    00256
    00257
    00258     switch (UpdateType) {
    00259
    00260     case UpdateClientInputContext:
    00261         if (pImc->dwClientImcData != 0) {
    00262             RIPERR0(RIP_WARNING, RIP_WARNING, "UpdateInputContext: pImc->dwClientImcData != 0");
    00263             return FALSE;
    00264         }
    00265         pImc->dwClientImcData = UpdateValue;
    00266         break;
    00267
    00268     case UpdateInUseImeWindow:
    00269         pImc->hImeWnd = (HWND)UpdateValue;
    00270         break;
    00271
    00272     default:
    00273         return FALSE;
    00274     }
    00275
    00276     return TRUE;
    00277 }
    00278
    00279
    00280 /**************************************************************************\
    00281 * AssociateInputContext
    00282 *
    00283 * Associate input context object to the specified window.
    00284 *
    00285 * History:
    00286 * 21-Dec-1995 wkwok       Created
    00287 \**************************************************************************/
    00288
    00289 HIMC AssociateInputContext(
    00290     IN PWND  pWnd,
    00291     IN PIMC  pImc)
    00292 {
    00293     HIMC hImcRet = pWnd->hImc;
    00294     pWnd->hImc = (HIMC)PtoH(pImc);
    00295
    00296     return hImcRet;
    00297 }
    00298
    00299 AIC_STATUS AssociateInputContextEx(
    00300     IN PWND  pWnd,
    00301     IN PIMC  pImc,
    00302     IN DWORD dwFlag)
    00303 {
    00304     PTHREADINFO ptiWnd = GETPTI(pWnd);
    00305     PWND pWndFocus = ptiWnd->pq->spwndFocus;
    00306     HIMC hImcFocus = pWndFocus->hImc;
    00307     BOOL fIgnoreNoContext = (dwFlag & IACE_IGNORENOCONTEXT) == IACE_IGNORENOCONTEXT;
    00308     AIC_STATUS Status = AIC_SUCCESS;
    00309
    00310     if (dwFlag & IACE_DEFAULT) {
    00311         /*
    00312          * use default input context.
    00313          */
    00314         pImc = ptiWnd->spDefaultImc;
    00315
    00316     } else if (pImc != NULL && GETPTI(pImc) != ptiWnd) {
    00317         /*
    00318          * Cannot associate input context to window created
    00319          * by other thread.
    00320          */
    00321         RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING,
    00322                 "AssociateInputContextEx: pwnd not of Imc pti");
    00323         return AIC_ERROR;
    00324     }
    00325
    00326     /*
    00327      * Cannot do association under different process context.
    00328      */
    00329     if (GETPTI(pWnd)->ppi != PtiCurrent()->ppi) {
    00330         RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING,
    00331                 "AssociateInputContextEx: pwnd not of current ppi");
    00332         return AIC_ERROR;
    00333     }
    00334
    00335     /*
    00336      * Finally, make sure they are on the same desktop.
    00337      */
    00338     if (pImc != NULL && pImc->head.rpdesk != pWnd->head.rpdesk) {
    00339         RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING,
    00340                 "AssociateInputContextEx: no desktop access");
    00341         return AIC_ERROR;
    00342     }
    00343
    00344     /*
    00345      * If IACE_CHILDREN is specified, associate the input context
    00346      * to the child windows of pWnd as well.
    00347      */
    00348     if ((dwFlag & IACE_CHILDREN) && pWnd->spwndChild != NULL) {
    00349         PBWL        pbwl;
    00350         PWND        pwndT;
    00351         HWND       *phwndT;
    00352
    00353         pbwl = BuildHwndList(pWnd->spwndChild,
    00354                    BWL_ENUMLIST|BWL_ENUMCHILDREN, ptiWnd);
    00355
    00356         if (pbwl != NULL) {
    00357
    00358             for (phwndT = pbwl->rghwnd; *phwndT != (HWND)1; phwndT++) {
    00359                 /*
    00360                  * Make sure this hwnd is still around.
    00361                  */
    00362                 if ((pwndT = RevalidateHwnd(*phwndT)) == NULL)
    00363                     continue;
    00364
    00365                 if (pwndT->hImc == (HIMC)PtoH(pImc))
    00366                     continue;
    00367
    00368                 if (pwndT->hImc == NULL_HIMC && fIgnoreNoContext)
    00369                     continue;
    00370
    00371                 AssociateInputContext(pwndT, pImc);
    00372
    00373                 if (pwndT == pWndFocus)
    00374                     Status = AIC_FOCUSCONTEXTCHANGED;
    00375             }
    00376
    00377             FreeHwndList(pbwl);
    00378         }
    00379     }
    00380
    00381     /*
    00382      * Associate the input context to pWnd.
    00383      */
    00384     if (pWnd->hImc != NULL_HIMC || !fIgnoreNoContext) {
    00385         if (pWnd->hImc != (HIMC)PtoH(pImc)) {
    00386             AssociateInputContext(pWnd, pImc);
    00387             if (pWnd == pWndFocus)
    00388                 Status = AIC_FOCUSCONTEXTCHANGED;
    00389         }
    00390     }
    00391
    00392     return Status;
    00393 }
    00394
    00395
    00396 /**************************************************************************\
    00397 * xxxFocusSetInputContext
    00398 *
    00399 * Set active input context upon focus change.
    00400 *
    00401 * History:
    00402 * 21-Mar-1996 wkwok       Created
    00403 \**************************************************************************/
    00404
    00405 VOID xxxFocusSetInputContext(
    00406     IN PWND pWnd,
    00407     IN BOOL fActivate,
    00408     IN BOOL fQueueMsg)
    00409 {
    00410     PTHREADINFO pti;
    00411     PWND        pwndDefaultIme;
    00412     TL          tlpwndDefaultIme;
    00413
    00414     CheckLock(pWnd);
    00415
    00416     pti = GETPTI(pWnd);
    00417
    00418     /*
    00419      * CS_IME class or "IME" class windows can not be SetActivated to hImc.
    00420      * WinWord 6.0 US Help calls ShowWindow with the default IME window.
    00421      * HELPMACROS get the default IME window by calling GetNextWindow().
    00422      */
    00423     if (TestCF(pWnd, CFIME) ||
    00424             (pWnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]))
    00425         return;
    00426
    00427     /*
    00428      * Do nothing if the thread does not have default IME window.
    00429      */
    00430     if ((pwndDefaultIme = pti->spwndDefaultIme) == NULL)
    00431         return;
    00432
    00433     /*
    00434      * If the thread is going away or the default IME window is being vanished,
    00435      * then do nothing.
    00436      */
    00437     if (pti->TIF_flags & TIF_INCLEANUP)
    00438         return;
    00439
    00440     UserAssert(!TestWF(pwndDefaultIme, WFDESTROYED));
    00441
    00442     ThreadLockAlways(pwndDefaultIme, &tlpwndDefaultIme);
    00443
    00444     if (fQueueMsg) {
    00445         xxxSendMessageCallback(pwndDefaultIme, WM_IME_SYSTEM,
    00446                 fActivate ? IMS_ACTIVATECONTEXT : IMS_DEACTIVATECONTEXT,
    00447                 (LPARAM)HWq(pWnd), NULL, 1L, 0);
    00448     } else {
    00449         xxxSendMessage(pwndDefaultIme, WM_IME_SYSTEM,
    00450                 fActivate ? IMS_ACTIVATECONTEXT : IMS_DEACTIVATECONTEXT,
    00451                 (LPARAM)HWq(pWnd));
    00452     }
    00453
    00454 #if _DBG
    00455     if (pti->spwndDefaultIme != pwndDefaultIme) {
    00456         RIPMSG1(RIP_WARNING, "pti(%#p)->spwndDefaultIme got freed during the callback.", pti);
    00457     }
    00458 #endif
    00459
    00460     ThreadUnlock(&tlpwndDefaultIme);
    00461
    00462     return;
    00463 }
    00464
    00465
    00466 /**************************************************************************\
    00467 * BuildHimcList
    00468 *
    00469 * Retrieve the list of input context handles created by given thread.
    00470 *
    00471 * History:
    00472 * 21-Feb-1995 wkwok       Created
    00473 \**************************************************************************/
    00474
    00475 UINT BuildHimcList(
    00476     PTHREADINFO pti,
    00477     UINT cHimcMax,
    00478     HIMC *ccxphimcFirst)
    00479 {
    00480     PIMC pImcT;
    00481     UINT i = 0;
    00482
    00483     if (pti == NULL) {
    00484         /*
    00485          * Build the list which contains all IMCs created by calling process.
    00486          */
    00487         for (pti = PtiCurrent()->ppi->ptiList; pti != NULL; pti = pti->ptiSibling) {
    00488             pImcT = pti->spDefaultImc;
    00489             while (pImcT != NULL) {
    00490                 if (i < cHimcMax) {
    00491                     try {
    00492                         ccxphimcFirst[i] = (HIMC)PtoH(pImcT);
    00493                     } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
    00494                     }
    00495                 }
    00496                 i++;
    00497                 pImcT = pImcT->pImcNext;
    00498             }
    00499         }
    00500     }
    00501     else {
    00502         /*
    00503          * Build the list which contains all IMCs created by specified thread.
    00504          */
    00505         pImcT = pti->spDefaultImc;
    00506         while (pImcT != NULL) {
    00507             if (i < cHimcMax) {
    00508                 try {
    00509                     ccxphimcFirst[i] = (HIMC)PtoH(pImcT);
    00510                 } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
    00511                 }
    00512             }
    00513             i++;
    00514             pImcT = pImcT->pImcNext;
    00515         }
    00516     }
    00517
    00518     return i;
    00519 }
    00520
    00521
    00522 /**************************************************************************\
    00523 * xxxCreateDefaultImeWindow
    00524 *
    00525 * Create per-thread based default IME window.
    00526 *
    00527 * History:
    00528 * 21-Mar-1996 wkwok       Created
    00529 \**************************************************************************/
    00530
    00531 PWND xxxCreateDefaultImeWindow(
    00532     IN PWND pwnd,
    00533     IN ATOM atomT,
    00534     IN HANDLE hInst)
    00535 {
    00536     LARGE_STRING strWindowName;
    00537     PWND pwndDefaultIme;
    00538     TL tlpwnd;
    00539     PIMEUI pimeui;
    00540     PTHREADINFO ptiCurrent = PtiCurrentShared();
    00541     LPWSTR pwszDefaultIme;
    00542
    00543     UserAssert(ptiCurrent == GETPTI(pwnd) && ptiCurrent->spwndDefaultIme == NULL);
    00544
    00545     /*
    00546      * Those conditions should have been checked by WantImeWindow()
    00547      * before xxxCreateDefaultImeWindow gets called.
    00548      */
    00549     UserAssert(!(ptiCurrent->TIF_flags & TIF_DISABLEIME));
    00550     UserAssert(!TestWF(pwnd, WFSERVERSIDEPROC));
    00551
    00552     /*
    00553      * The first Winlogon thread starts without default input context.
    00554      * Create it now.
    00555      */
    00556     if (ptiCurrent->spDefaultImc == NULL &&
    00557             ptiCurrent->pEThread->Cid.UniqueProcess == gpidLogon)
    00558         CreateInputContext(0);
    00559
    00560     /*
    00561      * No default IME window for thread that doesn't have
    00562      * default input context
    00563      */
    00564     if (ptiCurrent->spDefaultImc == NULL)
    00565         return (PWND)NULL;
    00566
    00567     /*
    00568      * Avoid recursion
    00569      */
    00570     if (atomT == gpsi->atomSysClass[ICLS_IME] || TestCF(pwnd, CFIME))
    00571         return (PWND)NULL;
    00572
    00573     /*
    00574      * B#12165-win95b
    00575      * Yet MFC does another nice. We need to avoid to give an IME window
    00576      * to the child of desktop window which is in different process.
    00577      */
    00578     if (TestwndChild(pwnd) && GETPTI(pwnd->spwndParent)->ppi != ptiCurrent->ppi &&
    00579             !(pwnd->style & WS_VISIBLE))
    00580         return (PWND)NULL;
    00581
    00582     if (ptiCurrent->rpdesk->pheapDesktop == NULL)
    00583         return (PWND)NULL;
    00584
    00585     /*
    00586      * Allocate storage for L"Default IME" string from desktop heap
    00587      * so that it can be referenced from USER32.DLL in user mode.
    00588      */
    00589     pwszDefaultIme = (LPWSTR)DesktopAlloc(ptiCurrent->rpdesk,
    00590                                           sizeof(wszDefaultIme),
    00591                                           DTAG_IMETEXT);
    00592     if (pwszDefaultIme == NULL)
    00593         return (PWND)NULL;
    00594
    00595     RtlCopyMemory(pwszDefaultIme, wszDefaultIme, sizeof(wszDefaultIme));
    00596
    00597     RtlInitLargeUnicodeString((PLARGE_UNICODE_STRING)&strWindowName,
    00598                               pwszDefaultIme,
    00599                               (UINT)-1);
    00600
    00601     ThreadLock(pwnd, &tlpwnd);
    00602
    00603     pwndDefaultIme = xxxCreateWindowEx( (DWORD)0,
    00604                              (PLARGE_STRING)gpsi->atomSysClass[ICLS_IME],
    00605                              (PLARGE_STRING)&strWindowName,
    00606                              WS_POPUP | WS_DISABLED,
    00607                              0, 0, 0, 0,
    00608                              pwnd, (PMENU)NULL,
    00609                              hInst, NULL, VER40);
    00610
    00611
    00612     if (pwndDefaultIme != NULL) {
    00613         pimeui = ((PIMEWND)pwndDefaultIme)->pimeui;
    00614         UserAssert(pimeui != NULL && (LONG_PTR)pimeui != (LONG_PTR)-1);
    00615         try {
    00616             ProbeForWrite(pimeui, sizeof *pimeui, sizeof(DWORD));
    00617             pimeui->fDefault = TRUE;
    00618             if (TestwndChild(pwnd) && GETPTI(pwnd->spwndParent) != ptiCurrent) {
    00619                 pimeui->fChildThreadDef = TRUE;
    00620             }
    00621         } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
    00622         }
    00623     }
    00624
    00625     ThreadUnlock(&tlpwnd);
    00626
    00627     DesktopFree(ptiCurrent->rpdesk, pwszDefaultIme);
    00628
    00629     return pwndDefaultIme;
    00630 }
    00631
    00632
    00633 /**************************************************************************\
    00634 * xxxImmActivateThreadsLayout
    00635 *
    00636 * Activate keyboard layout for multiple threads.
    00637 *
    00638 * Return:
    00639 *     TRUE if at least one thread has changed its active keyboard layout.
    00640 *     FALSE otherwise
    00641 *
    00642 * History:
    00643 * 11-Apr-1996 wkwok       Created
    00644 \**************************************************************************/
    00645
    00646 BOOL xxxImmActivateThreadsLayout(
    00647     PTHREADINFO pti,
    00648     PTLBLOCK    ptlBlockPrev,
    00649     PKL         pkl)
    00650 {
    00651     TLBLOCK     tlBlock;
    00652     PTHREADINFO ptiCurrent, ptiT;
    00653     UINT        cThreads = 0;
    00654     INT         i;
    00655
    00656     CheckLock(pkl);
    00657
    00658     ptiCurrent = PtiCurrentShared();
    00659
    00660     /*
    00661      * Build a list of threads that we need to update their active layouts.
    00662      * We can't just walk the ptiT list while we're doing the work, because
    00663      * for IME based keyboard layout, we will do callback to client side
    00664      * and the ptiT could get deleted out while we leave the critical section.
    00665      */
    00666     for (ptiT = pti; ptiT != NULL; ptiT = ptiT->ptiSibling) {
    00667         /*
    00668          * Skip all the *do nothing* cases in xxxImmActivateLayout
    00669          * so as to minimize the # of TLBLOCK required.
    00670          */
    00671         if (ptiT->spklActive == pkl || (ptiT->TIF_flags & TIF_INCLEANUP))
    00672             continue;
    00673
    00674         UserAssert(ptiT->pClientInfo != NULL);
    00675         UserAssert(ptiT->ppi == PpiCurrent()); // can't access pClientInfo of other process
    00676
    00677         if (ptiT->spwndDefaultIme == NULL) {
    00678             /*
    00679              * Keyboard layout is being switched but there's no way to callback
    00680              * the client side to activate&initialize input context now.
    00681              * Let's do hkl switching only in the kernel side for this thread
    00682              * but remember the input context needs to be re-initialized
    00683              * when this GUI thread recreates the default IME window later.
    00684              */
    00685             ptiT->hklPrev = ptiT->spklActive->hkl;
    00686             Lock(&ptiT->spklActive, pkl);
    00687             if (ptiT->spDefaultImc) {
    00688                 ptiT->pClientInfo->CI_flags |= CI_INPUTCONTEXT_REINIT;
    00689                 RIPMSG1(RIP_VERBOSE, "xxxImmActivateThreadsLayout: ptiT(%08p) will be re-initialized.", ptiT);
    00690             }
    00691             UserAssert((ptiT->TIF_flags & TIF_INCLEANUP) == 0);
    00692             ptiT->pClientInfo->hKL = pkl->hkl;
    00693             ptiT->pClientInfo->CodePage = pkl->CodePage;
    00694             continue;
    00695         }
    00696
    00697         ThreadLockPti(ptiCurrent, ptiT, &tlBlock.list[cThreads].tlpti);
    00698         tlBlock.list[cThreads++].pti = ptiT;
    00699
    00700         if (cThreads == THREADS_PER_TLBLOCK)
    00701             break;
    00702     }
    00703
    00704     /*
    00705      * Return FALSE if all the threads already had the pkl active.
    00706      */
    00707     if (ptlBlockPrev == NULL && ptiT == NULL && cThreads == 0)
    00708         return FALSE;
    00709
    00710     /*
    00711      * If we can't service all the threads in this run,
    00712      * call ImmActivateThreadsLayout() again for a new TLBLOCK.
    00713      */
    00714     if (ptiT != NULL && ptiT->ptiSibling != NULL) {
    00715         tlBlock.ptlBlockPrev = ptlBlockPrev;
    00716         return xxxImmActivateThreadsLayout(ptiT->ptiSibling, &tlBlock, pkl);
    00717     }
    00718
    00719     /*
    00720      * Finally, we can do the actual keyboard layout activation
    00721      * starting from this run. Work on current TLBLOCK first.
    00722      * We walk the list backwards so that the pti unlocks will
    00723      * be done in the right order.
    00724      */
    00725
    00726     tlBlock.ptlBlockPrev = ptlBlockPrev;
    00727     ptlBlockPrev = &tlBlock;
    00728
    00729     while (ptlBlockPrev != NULL) {
    00730         for (i = cThreads - 1; i >= 0; --i) {
    00731             if ((ptlBlockPrev->list[i].pti->TIF_flags & TIF_INCLEANUP) == 0) {
    00732                 ptiT = ptlBlockPrev->list[i].pti;
    00733                 UserAssert(ptiT);
    00734                 xxxImmActivateLayout(ptiT, pkl);
    00735                 if ((ptiT->TIF_flags & TIF_INCLEANUP) == 0) {
    00736                     ptiT->pClientInfo->hKL = pkl->hkl;
    00737                     ptiT->pClientInfo->CodePage = pkl->CodePage;
    00738                 }
    00739             }
    00740             ThreadUnlockPti(ptiCurrent, &ptlBlockPrev->list[i].tlpti);
    00741         }
    00742         ptlBlockPrev = ptlBlockPrev->ptlBlockPrev;
    00743         cThreads = THREADS_PER_TLBLOCK;
    00744     }
    00745
    00746     return TRUE;
    00747 }
    00748
    00749 VOID xxxImmActivateAndUnloadThreadsLayout(
    00750     IN PTHREADINFO *ptiList,
    00751     IN UINT         nEntries,
    00752     IN PTLBLOCK     ptlBlockPrev,
    00753     PKL             pklCurrent,
    00754     DWORD           dwHklReplace)
    00755 {
    00756     TLBLOCK     tlBlock;
    00757     PTHREADINFO ptiCurrent;
    00758     int         i, cThreads;
    00759     enum { RUN_ACTIVATE = 1, RUN_UNLOAD = 2, RUN_FLAGS_MASK = RUN_ACTIVATE | RUN_UNLOAD, RUN_INVALID = 0xffff0000 };
    00760
    00761     CheckLock(pklCurrent);
    00762
    00763     ptiCurrent = PtiCurrentShared();
    00764
    00765     tlBlock.ptlBlockPrev = ptlBlockPrev;
    00766
    00767     /*
    00768      * Build a list of threads that we need to unload their IME DLL(s).
    00769      * We can't just walk the ptiList while we're doing the work, because
    00770      * for IME based keyboard layout, we will do callback to client side
    00771      * and the pti could get deleted out while we leave the critical section.
    00772      */
    00773     for (i = 0, cThreads = 0; i < (INT)nEntries; i++) {
    00774         DWORD dwFlags = 0;
    00775
    00776         /*
    00777          * Skip all the *do nothing* cases in xxxImmActivateLayout
    00778          * so as to minimize the # of TLBLOCKs required.
    00779          */
    00780         if (ptiList[i]->TIF_flags & TIF_INCLEANUP) {
    00781             dwFlags = RUN_INVALID;
    00782         }
    00783         else if (ptiList[i]->spklActive != pklCurrent) {
    00784             if (ptiList[i]->spwndDefaultIme == NULL) {
    00785                 BOOLEAN fAttached = FALSE;
    00786
    00787                 Lock(&ptiList[i]->spklActive, pklCurrent);
    00788                 if (ptiList[i]->pClientInfo != ptiCurrent->pClientInfo &&
    00789                         ptiList[i]->ppi != ptiCurrent->ppi) {
    00790                     /*
    00791                      * If the thread is in another process, attach
    00792                      * to that process so that we can access its ClientInfo.
    00793                      */
    00794                     KeAttachProcess(&ptiList[i]->ppi->Process->Pcb);
    00795                     fAttached = TRUE;
    00796                 }
    00797
    00798                 try {
    00799                     ptiList[i]->pClientInfo->CodePage = pklCurrent->CodePage;
    00800                     ptiList[i]->pClientInfo->hKL = pklCurrent->hkl;
    00801                 } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
    00802                       dwFlags = RUN_INVALID;
    00803                 }
    00804                 if (fAttached) {
    00805                     KeDetachProcess();
    00806                 }
    00807             } else {
    00808                 dwFlags = RUN_ACTIVATE;
    00809             }
    00810         }
    00811
    00812         /*
    00813          * Skip all the *do nothing* cases in xxxImmUnloadLayout()
    00814          * so as to minimize the # of TLBLOCK required.
    00815          * (#99321)
    00816          */
    00817         if (ptiList[i]->spwndDefaultIme != NULL &&
    00818                 ptiList[i]->spklActive != NULL &&
    00819                 (dwHklReplace != IFL_DEACTIVATEIME || IS_IME_KBDLAYOUT(ptiList[i]->spklActive->hkl)) &&
    00820                 dwFlags != RUN_INVALID) {
    00821             dwFlags |= RUN_UNLOAD;
    00822         }
    00823
    00824         if (dwFlags && dwFlags != RUN_INVALID) {
    00825             ThreadLockPti(ptiCurrent, ptiList[i], &tlBlock.list[cThreads].tlpti);
    00826 #if DBG
    00827             tlBlock.list[cThreads].dwUnlockedCount = 0;
    00828 #endif
    00829             tlBlock.list[cThreads].pti = ptiList[i];
    00830             tlBlock.list[cThreads++].dwFlags = dwFlags;
    00831
    00832             if (cThreads == THREADS_PER_TLBLOCK) {
    00833                 i++;   // 1 more before exit the loop.
    00834                 break;
    00835             }
    00836         }
    00837     }
    00838
    00839     /*
    00840      * If we can't service all the threads in this run,
    00841      * call xxxImmActivateAndUnloadThreadsLayout again for a new TLBLOCK.
    00842      */
    00843     if (i < (INT)nEntries) {
    00844         ptiList  += i;
    00845         nEntries -= i;
    00846         xxxImmActivateAndUnloadThreadsLayout(ptiList, nEntries, &tlBlock, pklCurrent, dwHklReplace);
    00847         return;
    00848     }
    00849
    00850     /*
    00851      * Finally, we can do the actual keyboard layout activation
    00852      * starting from this run. Work on current TLBLOCK first.
    00853      * We walk the list backwards so that the pti unlocks will
    00854      * be done in the right order.
    00855      */
    00856     i = cThreads - 1;
    00857     for (ptlBlockPrev = &tlBlock; ptlBlockPrev != NULL; ptlBlockPrev = ptlBlockPrev->ptlBlockPrev) {
    00858         for ( ; i >= 0; i--) {
    00859             if ((ptlBlockPrev->list[i].dwFlags & RUN_ACTIVATE) &&
    00860                     !(ptlBlockPrev->list[i].pti->TIF_flags & TIF_INCLEANUP)) {
    00861                 xxxImmActivateLayout(ptlBlockPrev->list[i].pti, pklCurrent);
    00862             }
    00863
    00864             // unlock the thread if the thread is only locked for the first run
    00865             if ((ptlBlockPrev->list[i].dwFlags & RUN_FLAGS_MASK) == RUN_ACTIVATE) {
    00866                 ThreadUnlockPti(ptiCurrent, &ptlBlockPrev->list[i].tlpti);
    00867 #if DBG
    00868                 ptlBlockPrev->list[i].dwUnlockedCount++;
    00869 #endif
    00870             }
    00871         }
    00872         i = THREADS_PER_TLBLOCK - 1;
    00873     }
    00874
    00875     i = cThreads - 1;
    00876     for (ptlBlockPrev = &tlBlock; ptlBlockPrev != NULL; ptlBlockPrev = ptlBlockPrev->ptlBlockPrev) {
    00877         for ( ; i >= 0; --i) {
    00878             if (ptlBlockPrev->list[i].dwFlags & RUN_UNLOAD) {
    00879                 if (!(ptlBlockPrev->list[i].pti->TIF_flags & TIF_INCLEANUP)) {
    00880                     xxxImmUnloadLayout(ptlBlockPrev->list[i].pti, dwHklReplace);
    00881                 }
    00882                 else {
    00883                     RIPMSG1(RIP_WARNING, "xxxImmActivateAndUnloadThreadsLayout: thread %#p is cleaned up.",
    00884                             ptlBlockPrev->list[i].pti);
    00885                 }
    00886                 // unlock the thread
    00887                 UserAssert((ptlBlockPrev->list[i].dwFlags & RUN_FLAGS_MASK) != RUN_ACTIVATE);
    00888                 UserAssert(ptlBlockPrev->list[i].dwUnlockedCount == 0);
    00889                 ThreadUnlockPti(ptiCurrent, &ptlBlockPrev->list[i].tlpti);
    00890 #if DBG
    00891                 ptlBlockPrev->list[i].dwUnlockedCount++;
    00892 #endif
    00893             }
    00894         }
    00895         i = THREADS_PER_TLBLOCK - 1;
    00896     }
    00897
    00898 #if DBG
    00899     // Check if all the locked thread is properly unlocked
    00900     i = cThreads - 1;
    00901     for (ptlBlockPrev = &tlBlock; ptlBlockPrev; ptlBlockPrev = ptlBlockPrev->ptlBlockPrev) {
    00902         for ( ; i >= 0; --i) {
    00903             UserAssert(ptlBlockPrev->list[i].dwUnlockedCount == 1);
    00904         }
    00905         i = THREADS_PER_TLBLOCK - 1;
    00906     }
    00907 #endif
    00908
    00909     return;
    00910 }
    00911
    00912 /**************************************************************************\
    00913 * xxxImmActivateLayout
    00914 *
    00915 * Activate IME based keyboard layout.
    00916 *
    00917 * History:
    00918 * 21-Mar-1996 wkwok       Created
    00919 \**************************************************************************/
    00920
    00921 VOID xxxImmActivateLayout(
    00922     IN PTHREADINFO pti,
    00923     IN PKL pkl)
    00924 {
    00925     TL tlpwndDefaultIme;
    00926     PTHREADINFO ptiCurrent;
    00927
    00928     CheckLock(pkl);
    00929
    00930     /*
    00931      * Do nothing if it's already been the current active layout.
    00932      */
    00933     if (pti->spklActive == pkl)
    00934         return;
    00935
    00936     if (pti->spwndDefaultIme == NULL) {
    00937         /*
    00938          * Only activate kernel side keyboard layout if this pti
    00939          * doesn't have the default IME window.
    00940          */
    00941         Lock(&pti->spklActive, pkl);
    00942         return;
    00943     }
    00944
    00945     ptiCurrent = PtiCurrentShared();
    00946
    00947     /*
    00948      * Activate client side IME based keyboard layout.
    00949      */
    00950     ThreadLockAlwaysWithPti(ptiCurrent, pti->spwndDefaultIme, &tlpwndDefaultIme);
    00951     xxxSendMessage(pti->spwndDefaultIme, WM_IME_SYSTEM,
    00952                 (WPARAM)IMS_ACTIVATETHREADLAYOUT, (LPARAM)pkl->hkl);
    00953     ThreadUnlock(&tlpwndDefaultIme);
    00954
    00955     Lock(&pti->spklActive, pkl);
    00956
    00957     return;
    00958 }
    00959
    00960
    00961 VOID xxxImmUnloadThreadsLayout(
    00962     IN PTHREADINFO *ptiList,
    00963     IN UINT         nEntries,
    00964     IN PTLBLOCK     ptlBlockPrev,
    00965     IN DWORD        dwFlag)
    00966 {
    00967     TLBLOCK     tlBlock;
    00968     PTHREADINFO ptiCurrent;
    00969     INT         i, cThreads;
    00970     BOOLEAN     fPerformUnlock;
    00971
    00972     ptiCurrent = PtiCurrentShared();
    00973     tlBlock.ptlBlockPrev = ptlBlockPrev;
    00974
    00975     /*
    00976      * Build a list of threads that we need to unload their IME DLL(s).
    00977      * We can't just walk the ptiList while we're doing the work, because
    00978      * for IME based keyboard layout, we will do callback to client side
    00979      * and the pti could get deleted out while we leave the critical section.
    00980      */
    00981     for (i = 0, cThreads = 0; i < (INT)nEntries; i++) {
    00982         /*
    00983          * Skip all the *do nothing* cases in xxxImmUnloadLayout()
    00984          * so as to minimize the # of TLBLOCK required.
    00985          */
    00986         if ((ptiList[i]->TIF_flags & TIF_INCLEANUP) || ptiList[i]->spwndDefaultIme == NULL)
    00987             continue;
    00988
    00989         if (ptiList[i]->spklActive == NULL)
    00990             continue;
    00991
    00992         if (dwFlag == IFL_DEACTIVATEIME &&
    00993                 !IS_IME_KBDLAYOUT(ptiList[i]->spklActive->hkl)) // #99321
    00994             continue;
    00995
    00996 #if DBG
    00997         tlBlock.list[cThreads].dwUnlockedCount = 0;
    00998 #endif
    00999         ThreadLockPti(ptiCurrent, ptiList[i], &tlBlock.list[cThreads].tlpti);
    01000         tlBlock.list[cThreads++].pti = ptiList[i];
    01001         if (cThreads == THREADS_PER_TLBLOCK) {
    01002             i++;   // 1 more before exit the loop.
    01003             break;
    01004         }
    01005     }
    01006
    01007     if (i < (INT)nEntries) {
    01008         ptiList  += i;
    01009         nEntries -= i;
    01010         xxxImmUnloadThreadsLayout(ptiList, nEntries, &tlBlock, dwFlag);
    01011         return;
    01012     }
    01013
    01014     UserAssert(dwFlag == IFL_UNLOADIME || dwFlag == IFL_DEACTIVATEIME);
    01015     if (dwFlag == IFL_UNLOADIME) {
    01016         dwFlag = IFL_DEACTIVATEIME;
    01017         fPerformUnlock = FALSE;
    01018     } else {
    01019         fPerformUnlock = TRUE;
    01020     }
    01021 RepeatForUnload:
    01022     /*
    01023      * Finally, we can unload the IME based keyboard layout
    01024      * starting from this run. Work on current TLBLOCK first.
    01025      * We walk the list backwards so that the pti unlocks will
    01026      * be done in the right order.
    01027      */
    01028     i = cThreads - 1;
    01029     for (ptlBlockPrev = &tlBlock; ptlBlockPrev; ptlBlockPrev = ptlBlockPrev->ptlBlockPrev) {
    01030         for ( ; i >= 0; --i) {
    01031             if (!(ptlBlockPrev->list[i].pti->TIF_flags & TIF_INCLEANUP)) {
    01032                 xxxImmUnloadLayout(ptlBlockPrev->list[i].pti, dwFlag);
    01033             }
    01034             else {
    01035                 RIPMSG2(RIP_WARNING, "Thread %#p is cleaned during the loop for %x!", ptlBlockPrev->list[i].pti, dwFlag);
    01036             }
    01037
    01038             if (fPerformUnlock) {
    01039 #if DBG
    01040                 ptlBlockPrev->list[i].dwUnlockedCount++;
    01041 #endif
    01042                 ThreadUnlockPti(ptiCurrent, &ptlBlockPrev->list[i].tlpti);
    01043             }
    01044         }
    01045         i = THREADS_PER_TLBLOCK - 1;
    01046     }
    01047
    01048     if (!fPerformUnlock) {
    01049         fPerformUnlock = TRUE;
    01050         dwFlag = IFL_UNLOADIME;
    01051         goto RepeatForUnload;
    01052     }
    01053
    01054 #if DBG
    01055     // Check if all the locked thread is properly unlocked
    01056     i = cThreads - 1;
    01057     for (ptlBlockPrev = &tlBlock; ptlBlockPrev; ptlBlockPrev = ptlBlockPrev->ptlBlockPrev) {
    01058         for ( ; i >= 0; --i) {
    01059             UserAssert(ptlBlockPrev->list[i].dwUnlockedCount == 1);
    01060         }
    01061         i = THREADS_PER_TLBLOCK - 1;
    01062     }
    01063 #endif
    01064
    01065     return;
    01066 }
    01067
    01068
    01069
    01070 VOID xxxImmUnloadLayout(
    01071     IN PTHREADINFO pti,
    01072     IN DWORD dwFlag)
    01073 {
    01074     TL tlpwndDefaultIme;
    01075     PTHREADINFO ptiCurrent;
    01076     ULONG_PTR dwResult;
    01077     LRESULT r;
    01078
    01079     /*
    01080      * Do nothing if the thread does not have default IME window.
    01081      */
    01082     if (pti->spwndDefaultIme == NULL)
    01083         return;
    01084
    01085     if (pti->spklActive == NULL)
    01086         return;
    01087
    01088     if (dwFlag == IFL_DEACTIVATEIME &&
    01089             !IS_IME_KBDLAYOUT(pti->spklActive->hkl))
    01090         return;
    01091
    01092     ptiCurrent = PtiCurrentShared();
    01093
    01094     ThreadLockAlwaysWithPti(ptiCurrent, pti->spwndDefaultIme, &tlpwndDefaultIme);
    01095     r = xxxSendMessageTimeout(pti->spwndDefaultIme, WM_IME_SYSTEM,
    01096                           IMS_UNLOADTHREADLAYOUT, (LONG)dwFlag,
    01097                           SMTO_NOTIMEOUTIFNOTHUNG, CMSHUNGAPPTIMEOUT, &dwResult);
    01098
    01099     if (!r) {
    01100         RIPMSG1(RIP_WARNING, "Timeout in xxxImmUnloadLayout: perhaps this thread (0x%x) is not pumping messages.",
    01101                 pti->pEThread->Cid.UniqueThread);
    01102     }
    01103
    01104     ThreadUnlock(&tlpwndDefaultIme);
    01105
    01106     return;
    01107 }
    01108
    01109 /**************************************************************************\
    01110 * xxxImmLoadLayout
    01111 *
    01112 * Retrieves extended IMEINFO for the given IME based keyboard layout.
    01113 *
    01114 * History:
    01115 * 21-Mar-1996 wkwok       Created
    01116 \**************************************************************************/
    01117
    01118 PIMEINFOEX xxxImmLoadLayout(
    01119     IN HKL hKL)
    01120 {
    01121     PIMEINFOEX  piiex;
    01122     PTHREADINFO ptiCurrent;
    01123     TL          tlPool;
    01124
    01125     /*
    01126      * No IMEINFOEX for non-IME based keyboard layout.
    01127      */
    01128     if (!IS_IME_KBDLAYOUT(hKL))
    01129         return (PIMEINFOEX)NULL;
    01130
    01131     piiex = (PIMEINFOEX)UserAllocPool(sizeof(IMEINFOEX), TAG_IME);
    01132
    01133     if (piiex == NULL) {
    01134         RIPMSG1(RIP_WARNING,
    01135               "xxxImmLoadLayout: failed to create piiex for hkl = %lx", hKL);
    01136         return (PIMEINFOEX)NULL;
    01137     }
    01138
    01139     ptiCurrent = PtiCurrent();
    01140
    01141     /*
    01142      * Lock this allocations since we are going to the client side
    01143      */
    01144     ThreadLockPool(ptiCurrent, piiex, &tlPool);
    01145
    01146     if (!ClientImmLoadLayout(hKL, piiex)) {
    01147         ThreadUnlockAndFreePool(ptiCurrent, &tlPool);
    01148         return (PIMEINFOEX)NULL;
    01149     }
    01150
    01151     ThreadUnlockPool(ptiCurrent, &tlPool);
    01152
    01153     return piiex;
    01154 }
    01155
    01156
    01157 /**************************************************************************\
    01158 * GetImeInfoEx
    01159 *
    01160 * Query extended IMEINFO.
    01161 *
    01162 * History:
    01163 * 21-Mar-1996 wkwok       Created
    01164 \**************************************************************************/
    01165
    01166 BOOL GetImeInfoEx(
    01167     PWINDOWSTATION pwinsta,
    01168     PIMEINFOEX piiex,
    01169     IMEINFOEXCLASS SearchType)
    01170 {
    01171     PKL pkl, pklFirst;
    01172
    01173     /*
    01174      * Note: this check was forced to insert due to winmm.dll who indirectly
    01175      * loads imm32.dll in CSRSS context. CSRSS is not always bound to
    01176      * specific window station, thus pwinsta could be NULL.
    01177      * This has been avoided by not loading imm32.dll.
    01178      * After winmm.dll gets removed from CSRSS, this if statement should be
    01179      * removed, or substituted as an assertion.
    01180      */
    01181     if (pwinsta == NULL) {
    01182         return FALSE;
    01183     }
    01184
    01185     /*
    01186      * Keyboard layer has not been initialized.
    01187      */
    01188     if (pwinsta->spklList == NULL)
    01189         return FALSE;
    01190
    01191     pkl = pklFirst = pwinsta->spklList;
    01192
    01193     switch (SearchType) {
    01194     case ImeInfoExKeyboardLayout:
    01195         do {
    01196             if (pkl->hkl == piiex->hkl) {
    01197
    01198                 if (pkl->piiex == NULL)
    01199                     break;
    01200
    01201                 RtlCopyMemory(piiex, pkl->piiex, sizeof(IMEINFOEX));
    01202                 return TRUE;
    01203             }
    01204             pkl = pkl->pklNext;
    01205         } while (pkl != pklFirst);
    01206         break;
    01207
    01208     case ImeInfoExImeFileName:
    01209         do {
    01210             if (pkl->piiex != NULL &&
    01211                 !_wcsnicmp(pkl->piiex->wszImeFile, piiex->wszImeFile, IM_FILE_SIZE)) {
    01212
    01213                 RtlCopyMemory(piiex, pkl->piiex, sizeof(IMEINFOEX));
    01214                 return TRUE;
    01215             }
    01216             pkl = pkl->pklNext;
    01217         } while (pkl != pklFirst);
    01218         break;
    01219
    01220     default:
    01221         break;
    01222     }
    01223
    01224     return FALSE;
    01225 }
    01226
    01227
    01228 /**************************************************************************\
    01229 * SetImeInfoEx
    01230 *
    01231 * Set extended IMEINFO.
    01232 *
    01233 * History:
    01234 * 21-Mar-1996 wkwok       Created
    01235 \**************************************************************************/
    01236
    01237 BOOL SetImeInfoEx(
    01238     PWINDOWSTATION pwinsta,
    01239     PIMEINFOEX piiex)
    01240 {
    01241     PKL pkl, pklFirst;
    01242
    01243     if (pwinsta == NULL) {
    01244         return FALSE;
    01245     }
    01246
    01247     UserAssert(pwinsta->spklList != NULL);
    01248
    01249     pkl = pklFirst = pwinsta->spklList;
    01250
    01251     do {
    01252         if (pkl->hkl == piiex->hkl) {
    01253
    01254             /*
    01255              * Error out for non-IME based keyboard layout.
    01256              */
    01257             if (pkl->piiex == NULL)
    01258                 return FALSE;
    01259
    01260             /*
    01261              * Update kernel side IMEINFOEX for this keyboard layout
    01262              * only if this is its first loading.
    01263              */
    01264             if (pkl->piiex->fLoadFlag == IMEF_NONLOAD) {
    01265                 RtlCopyMemory(pkl->piiex, piiex, sizeof(IMEINFOEX));
    01266             }
    01267
    01268             return TRUE;
    01269         }
    01270         pkl = pkl->pklNext;
    01271
    01272     } while (pkl != pklFirst);
    01273
    01274     return FALSE;
    01275 }
    01276
    01277
    01278 /***************************************************************************\
    01279 * xxxImmProcessKey
    01280 *
    01281 *
    01282 * History:
    01283 * 03-03-96 TakaoK             Created.
    01284 \***************************************************************************/
    01285
    01286 DWORD xxxImmProcessKey(
    01287     IN PQ   pq,
    01288     IN PWND pwnd,
    01289     IN UINT message,
    01290     IN WPARAM wParam,
    01291     IN LPARAM lParam)
    01292 {
    01293     UINT  uVKey;
    01294     PKL   pkl;
    01295     DWORD dwHotKeyID;
    01296     DWORD dwReturn = 0;
    01297     PIMC  pImc = NULL;
    01298     BOOL  fDBERoman = FALSE;
    01299     PIMEHOTKEYOBJ pImeHotKeyObj;
    01300     HKL hklTarget;
    01301
    01302     CheckLock(pwnd);
    01303
    01304     //
    01305     // we're interested in only keyboard messages.
    01306     //
    01307     if ( message != WM_KEYDOWN    &&
    01308          message != WM_SYSKEYDOWN &&
    01309          message != WM_KEYUP      &&
    01310          message != WM_SYSKEYUP ) {
    01311
    01312         return dwReturn;
    01313     }
    01314
    01315     //
    01316     // Check if it's IME hotkey. This must be done before checking
    01317     // the keyboard layout because IME hotkey handler should be
    01318     // called even if current keyboard layout is non-IME layout.
    01319     //
    01320     pkl = GETPTI(pwnd)->spklActive;
    01321     if ( pkl == NULL ) {
    01322         return dwReturn;
    01323     }
    01324
    01325     uVKey = (UINT)wParam & 0xff;
    01326
    01327     pImeHotKeyObj = CheckImeHotKey(pq, uVKey, lParam);
    01328     if (pImeHotKeyObj) {
    01329         dwHotKeyID = pImeHotKeyObj->hk.dwHotKeyID;
    01330         hklTarget = pImeHotKeyObj->hk.hKL;
    01331     }
    01332     else {
    01333         dwHotKeyID = IME_INVALID_HOTKEY;
    01334         hklTarget = (HKL)NULL;
    01335     }
    01336
    01337     //
    01338     // Handle Direct KL switching here.
    01339     //
    01340     if (dwHotKeyID >= IME_HOTKEY_DSWITCH_FIRST && dwHotKeyID <= IME_HOTKEY_DSWITCH_LAST) {
    01341         UserAssert(hklTarget != NULL);
    01342         if (pkl->hkl != hklTarget) {
    01343             //
    01344             // Post the message only if the new Keyboard Layout is different from
    01345             // the current Keyboard Layout.
    01346             //
    01347             _PostMessage(pwnd, WM_INPUTLANGCHANGEREQUEST,
    01348                          (pkl->dwFontSigs & gSystemFS) ? INPUTLANGCHANGE_SYSCHARSET : 0,
    01349                          (LPARAM)hklTarget);
    01350         }
    01351         if (GetAppImeCompatFlags(GETPTI(pwnd)) & IMECOMPAT_HYDRACLIENT) {
    01352             return 0;
    01353         }
    01354         return IPHK_HOTKEY;
    01355     }
    01356
    01357     if (!IS_IME_ENABLED()) {
    01358         //
    01359         // Since IMM is disabled, no need to process further.
    01360         // Just bail out.
    01361         //
    01362         return 0;
    01363     }
    01364
    01365     if ( dwHotKeyID != IME_INVALID_HOTKEY ) {
    01366         //
    01367         // if it's a valid hotkey, go straight and call back
    01368         // the IME in the client side.
    01369         //
    01370         goto ProcessKeyCallClient;
    01371     }
    01372
    01373     //
    01374     // if it's not a hotkey, we may want to check something
    01375     // before calling back.
    01376     //
    01377     if ( pkl->piiex == NULL ) {
    01378         return dwReturn;
    01379     }
    01380
    01381     //
    01382     // Check input context
    01383     //
    01384     pImc = HtoP(pwnd->hImc);
    01385     if ( pImc == NULL ) {
    01386         return dwReturn;
    01387     }
    01388
    01389 #ifdef LATER
    01390     //
    01391     // If there is an easy way to check the input context open/close status
    01392     // from the kernel side, IME_PROP_NO_KEYS_ON_CLOSE checking should be
    01393     // done here in kernel side.  [ 3/10/96 takaok]
    01394     //
    01395
    01396     //
    01397     // Check IME_PROP_NO_KEYS_ON_CLOSE bit
    01398     //
    01399     // if the current imc is not open and IME doesn't need
    01400     // keys when being closed, we don't pass any keyboard
    01401     // input to ime except hotkey and keys that change
    01402     // the keyboard status.
    01403     //
    01404     if ( (piix->ImeInfo.fdwProperty & IME_PROP_NO_KEYS_ON_CLOSE) &&
    01405          (!pimc->fdwState & IMC_OPEN)                            &&
    01406          uVKey != VK_SHIFT                                       &&  // 0x10
    01407          uVKey != VK_CONTROL                                     &&  // 0x11
    01408          uVKey != VK_CAPITAL                                     &&  // 0x14
    01409          uVKey != VK_KANA                                        &&  // 0x15
    01410          uVKey != VK_NUMLOCK                                     &&  // 0x90
    01411          uVKey != VK_SCROLL )                                        // 0x91
    01412     {
    01413       // Check if Korea Hanja conversion mode
    01414       if( !(pimc->fdwConvMode & IME_CMODE_HANJACONVERT) ) {
    01415           return dwReturn;
    01416       }
    01417     }
    01418 #endif
    01419
    01420     //
    01421     // if the IME doesn't need key up messages, we don't call ime.
    01422     //
    01423     if ( lParam & 0x80000000 && // set if key up, clear if key down
    01424          pkl->piiex->ImeInfo.fdwProperty & IME_PROP_IGNORE_UPKEYS )
    01425     {
    01426         return dwReturn;
    01427     }
    01428
    01429     //
    01430     // we don't want to handle sys keys since many functions for
    01431     // acceelerators won't work without this
    01432     //
    01433     fDBERoman = (BOOL)( (uVKey == VK_DBE_ROMAN)            ||
    01434                         (uVKey == VK_DBE_NOROMAN)          ||
    01435                         (uVKey == VK_DBE_HIRAGANA)         ||
    01436                         (uVKey == VK_DBE_KATAKANA)         ||
    01437                         (uVKey == VK_DBE_CODEINPUT)        ||
    01438                         (uVKey == VK_DBE_NOCODEINPUT)      ||
    01439                         (uVKey == VK_DBE_IME_WORDREGISTER) ||
    01440                         (uVKey == VK_DBE_IME_DIALOG) );
    01441
    01442     if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP ) {
    01443         //
    01444         // IME may be waiting for VK_MENU, VK_F10 or VK_DBE_xxx
    01445         //
    01446         if ( uVKey != VK_MENU && uVKey != VK_F10 && !fDBERoman ) {
    01447             return dwReturn;
    01448         }
    01449     }
    01450
    01451     //
    01452     // check if the IME doesn't need ALT key
    01453     //
    01454     if ( !(pkl->piiex->ImeInfo.fdwProperty & IME_PROP_NEED_ALTKEY) ) {
    01455         //
    01456         // IME doesn't need ALT key
    01457         //
    01458         // we don't pass the ALT and ALT+xxx except VK_DBE_xxx keys.
    01459         //
    01460         if ( ! fDBERoman &&
    01461              (uVKey == VK_MENU || (lParam & 0x20000000))  // KF_ALTDOWN
    01462            )
    01463         {
    01464             return dwReturn;
    01465         }
    01466     }
    01467
    01468     //
    01469     // finaly call back the client
    01470     //
    01471
    01472 ProcessKeyCallClient:
    01473
    01474     if ((uVKey & 0xff) == VK_PACKET) {
    01475         //
    01476         // need to retrieve UNICODE character from pti
    01477         //
    01478         uVKey = MAKELONG(wParam, PtiCurrent()->wchInjected);
    01479     }
    01480     dwReturn = ClientImmProcessKey( PtoH(pwnd),
    01481                                     pkl->hkl,
    01482                                     uVKey,
    01483                                     lParam,
    01484                                     dwHotKeyID);
    01485
    01486     //
    01487     // Hydra server wants to see the IME hotkeys.
    01488     //
    01489     if (GetAppImeCompatFlags(GETPTI(pwnd)) & IMECOMPAT_HYDRACLIENT) {
    01490         dwReturn &= ~IPHK_HOTKEY;
    01491     }
    01492     return dwReturn;
    01493 }
    01494
    01495
    01496 /**************************************************************************\
    01497 * ImeCanDestroyDefIME
    01498 *
    01499 * History:
    01500 * 02-Apr-1996 wkwok       Ported from FE Win95 (imeclass.c)
    01501 \**************************************************************************/
    01502
    01503 BOOL ImeCanDestroyDefIME(
    01504     PWND pwndDefaultIme,
    01505     PWND pwndDestroy)
    01506 {
    01507     PWND   pwnd;
    01508     PIMEUI pimeui;
    01509
    01510     pimeui = ((PIMEWND)pwndDefaultIme)->pimeui;
    01511
    01512     if (pimeui == NULL || (LONG_PTR)pimeui == (LONG_PTR)-1)
    01513         return FALSE;
    01514
    01515     try {
    01516         if (ProbeAndReadStructure(pimeui, IMEUI).fDestroy) {
    01517             return FALSE;
    01518         }
    01519     } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
    01520     }
    01521
    01522     /*
    01523      * If the pwndDestroy has no owner/ownee relationship with
    01524      * pwndDefaultIme, don't bother to change anything.
    01525      *
    01526      * If pwndDefaultIme->spwndOwner is NULL, this means we need
    01527      * to search for a new good owner window.
    01528      */
    01529     if ( pwndDefaultIme->spwndOwner != NULL ) {
    01530         for (pwnd = pwndDefaultIme->spwndOwner;
    01531              pwnd != pwndDestroy && pwnd != NULL; pwnd = pwnd->spwndOwner) ;
    01532
    01533         if (pwnd == NULL)
    01534             return FALSE;
    01535     }
    01536
    01537     /*
    01538      * If the destroying window is IME or UI window, do nothing
    01539      */
    01540     pwnd = pwndDestroy;
    01541
    01542     while (pwnd != NULL) {
    01543         if (TestCF(pwnd, CFIME) ||
    01544                 pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])
    01545             return FALSE;
    01546
    01547         pwnd = pwnd->spwndOwner;
    01548     }
    01549
    01550     ImeSetFutureOwner(pwndDefaultIme, pwndDestroy);
    01551
    01552     /*
    01553      * If new owner is lower z-order than IME class window,
    01554      * we need to check topmost to change z-order.
    01555      */
    01556     pwnd = pwndDefaultIme->spwndOwner;
    01557     while (pwnd != NULL && pwnd != pwndDefaultIme)
    01558         pwnd = pwnd->spwndNext;
    01559
    01560     if (pwnd == pwndDefaultIme)
    01561         ImeCheckTopmost(pwndDefaultIme);
    01562
    01563 #if DBG
    01564     CheckOwnerCirculate(pwndDefaultIme);
    01565 #endif
    01566
    01567     /*
    01568      * If ImeSetFutureOwner can not find the owner window any
    01569      * more, this IME window should be destroyed.
    01570      */
    01571     if (pwndDefaultIme->spwndOwner == NULL ||
    01572             pwndDestroy == pwndDefaultIme->spwndOwner) {
    01573
    01574 //        RIPMSG1(RIP_WARNING, "ImeCanDestroyDefIME: TRUE for pwnd=%#p", pwndDestroy);
    01575         Unlock(&pwndDefaultIme->spwndOwner);
    01576
    01577         /*
    01578          * Return TRUE! Please destroy me.
    01579          */
    01580         return TRUE;
    01581     }
    01582
    01583     return FALSE;
    01584 }
    01585
    01586
    01587 /**************************************************************************\
    01588 * IsChildSameThread (IsChildSameQ)
    01589 *
    01590 * History:
    01591 * 02-Apr-1996 wkwok       Ported from FE Win95 (imeclass.c)
    01592 \**************************************************************************/
    01593
    01594 BOOL IsChildSameThread(
    01595     PWND pwndParent,
    01596     PWND pwndChild)
    01597 {
    01598     PWND pwnd;
    01599     PTHREADINFO ptiChild = GETPTI(pwndChild);
    01600
    01601     for (pwnd = pwndParent->spwndChild; pwnd; pwnd = pwnd->spwndNext) {
    01602         /*
    01603          * If pwnd is not child window, we need to skip MENU window and
    01604          * IME related window.
    01605          */
    01606         if (!TestwndChild(pwnd)) {
    01607             PWND pwndOwner = pwnd;
    01608             BOOL fFoundOwner = FALSE;
    01609
    01610             /*
    01611              * Skip MENU window.
    01612              */
    01613             if (pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_MENU])
    01614                 continue;
    01615
    01616             while (pwndOwner != NULL) {
    01617                 /*
    01618                  * CS_IME class or "IME" class windows can not be the owner of
    01619                  * IME windows.
    01620                  */
    01621                 if (TestCF(pwndOwner, CFIME) ||
    01622                         pwndOwner->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]) {
    01623                     fFoundOwner = TRUE;
    01624                     break;
    01625                 }
    01626
    01627                 pwndOwner = pwndOwner->spwndOwner;
    01628             }
    01629
    01630             if (fFoundOwner)
    01631                 continue;
    01632         }
    01633
    01634         /*
    01635          * We need to skip pwndChild.
    01636          */
    01637         if (pwnd == pwndChild)
    01638             continue;
    01639
    01640         /*
    01641          * pwnd and pwndChild are on same thread?
    01642          */
    01643         if (GETPTI(pwnd) == ptiChild) {
    01644             PWND pwndT = pwnd;
    01645             BOOL fFoundImeWnd = FALSE;
    01646
    01647             /*
    01648              * Check again. If hwndT is children or ownee of
    01649              * IME related window, skip it.
    01650              */
    01651             if (TestwndChild(pwndT)) {
    01652
    01653                 for (; TestwndChild(pwndT) && GETPTI(pwndT) == ptiChild;
    01654                         pwndT = pwndT->spwndParent) {
    01655                     if (TestCF(pwndT, CFIME) ||
    01656                             pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])
    01657                         fFoundImeWnd = TRUE;
    01658                 }
    01659             }
    01660
    01661             if (!TestwndChild(pwndT)) {
    01662
    01663                 for (; pwndT != NULL && GETPTI(pwndT) == ptiChild;
    01664                         pwndT = pwndT->spwndOwner) {
    01665                     if (TestCF(pwndT, CFIME) ||
    01666                             pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])
    01667                         fFoundImeWnd = TRUE;
    01668                 }
    01669             }
    01670
    01671             if (!fFoundImeWnd)
    01672                 return TRUE;
    01673         }
    01674     }
    01675
    01676     return FALSE;
    01677 }
    01678
    01679
    01680 /**************************************************************************\
    01681 * ImeCanDestroyDefIMEforChild
    01682 *
    01683 * History:
    01684 * 02-Apr-1996 wkwok       Ported from FE Win95 (imeclass.c)
    01685 \**************************************************************************/
    01686
    01687 BOOL ImeCanDestroyDefIMEforChild(
    01688     PWND pwndDefaultIme,
    01689     PWND pwndDestroy)
    01690 {
    01691     PWND pwnd;
    01692     PIMEUI pimeui;
    01693
    01694     pimeui = ((PIMEWND)pwndDefaultIme)->pimeui;
    01695
    01696     /*
    01697      * If this window is not for Child Thread.....
    01698      */
    01699     if (pimeui == NULL || (LONG_PTR)pimeui == (LONG_PTR)-1)
    01700         return FALSE;
    01701
    01702     try {
    01703         if (!ProbeAndReadStructure(pimeui, IMEUI).fChildThreadDef) {
    01704             return FALSE;
    01705         }
    01706     } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
    01707     }
    01708
    01709     /*
    01710      * If parent belongs to different thread,
    01711      * we don't need to check any more...
    01712      */
    01713     if (pwndDestroy->spwndParent == NULL ||
    01714             GETPTI(pwndDestroy) == GETPTI(pwndDestroy->spwndParent))
    01715         return FALSE;
    01716
    01717     pwnd = pwndDestroy;
    01718
    01719     while (pwnd != NULL && pwnd != PWNDDESKTOP(pwnd)) {
    01720         if (IsChildSameThread(pwnd->spwndParent, pwndDestroy))
    01721             return FALSE;
    01722         pwnd = pwnd->spwndParent;
    01723     }
    01724
    01725     /*
    01726      * We could not find any other window created by GETPTI(pwndDestroy).
    01727      * Let's destroy the default IME window of this Q.
    01728      */
    01729     return TRUE;
    01730 }
    01731
    01732
    01733 /**************************************************************************\
    01734 * ImeCheckTopmost
    01735 *
    01736 * History:
    01737 * 02-Apr-1996 wkwok       Ported from FE Win95 (imeclass.c)
    01738 \**************************************************************************/
    01739
    01740 VOID ImeCheckTopmost(
    01741     PWND pwndIme)
    01742 {
    01743     if (pwndIme->spwndOwner) {
    01744         PWND pwndInsertBeforeThis;
    01745         /*
    01746          * The ime window have to be same topmost tyle with the owner window.
    01747          * If the Q of this window is not foreground Q, we don't need to
    01748          * forground the IME window.
    01749          * But the topmost attribute of owner was changed, this IME window
    01750          * should be re-calced.
    01751          */
    01752         if (GETPTI(pwndIme) == gptiForeground) {
    01753             pwndInsertBeforeThis = NULL;
    01754         } else {
    01755             pwndInsertBeforeThis = pwndIme->spwndOwner;
    01756         }
    01757
    01758         ImeSetTopmost(pwndIme, TestWF(pwndIme->spwndOwner, WEFTOPMOST) != 0, pwndInsertBeforeThis);
    01759     }
    01760 }
    01761
    01762
    01763 /**************************************************************************\
    01764 * ImeSetFutureOwner
    01765 *
    01766 * History:
    01767 * 02-Apr-1996 wkwok       Ported from FE Win95 (imeclass.c)
    01768 \**************************************************************************/
    01769
    01770 VOID ImeSetFutureOwner(
    01771     PWND pwndIme,
    01772     PWND pwndOrgOwner)
    01773 {
    01774     PWND pwnd, pwndOwner;
    01775     PTHREADINFO ptiImeWnd = GETPTI(pwndIme);
    01776
    01777     if (pwndOrgOwner == NULL || TestWF(pwndOrgOwner, WFCHILD))
    01778         return;
    01779
    01780     pwnd = pwndOrgOwner;
    01781
    01782     /*
    01783      * Get top of owner created by the same thread.
    01784      */
    01785     while ((pwndOwner = pwnd->spwndOwner) != NULL &&
    01786             GETPTI(pwndOwner) == ptiImeWnd)
    01787         pwnd = pwndOwner;
    01788
    01789     /*
    01790      * Bottom window can not be the owner of IME window easily...
    01791      */
    01792     if (TestWF(pwnd, WFBOTTOMMOST) && !TestWF(pwndOrgOwner, WFBOTTOMMOST))
    01793         pwnd = pwndOrgOwner;
    01794
    01795     /*
    01796      * CS_IME class or "IME" class windows can not be the owner of
    01797      * IME windows.
    01798      */
    01799     if (TestCF(pwnd, CFIME) ||
    01800             pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])
    01801         pwnd = pwndOrgOwner;
    01802
    01803     /*
    01804      * If hwndOrgOwner is a top of owner, we start to search
    01805      * another top owner window in same queue.
    01806      */
    01807     if (pwndOrgOwner == pwnd && pwnd->spwndParent != NULL) {
    01808         PWND pwndT;
    01809
    01810         for (pwndT = pwnd->spwndParent->spwndChild;
    01811                 pwndT != NULL; pwndT = pwndT->spwndNext) {
    01812
    01813             if (GETPTI(pwnd) != GETPTI(pwndT))
    01814                 continue;
    01815
    01816             if (pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_MENU])
    01817                 continue;
    01818
    01819             /*
    01820              * CS_IME class or "IME" class windows can not be the owner of
    01821              * IME windows.
    01822              */
    01823             if (TestCF(pwndT, CFIME) ||
    01824                     pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])
    01825                 continue;
    01826
    01827             // We don't like the window that is being destroyed.
    01828             if (TestWF(pwndT, WFINDESTROY))
    01829                 continue;
    01830
    01831             /*
    01832              * !!!!WARNING!!!!!
    01833              * Is hwndT a good owner of hIMEwnd??
    01834              *  1. Of cource, it should no CHILD window!
    01835              *  2. If it is hwnd,.. I know it and find next!
    01836              *  3. Does hwndT have owner in the same thread?
    01837              */
    01838             if (!TestWF(pwndT, WFCHILD) && pwnd != pwndT &&
    01839                     (pwndT->spwndOwner == NULL ||
    01840                      GETPTI(pwndT) != GETPTI(pwndT->spwndOwner))) {
    01841                 UserAssert(GETPTI(pwndIme) == GETPTI(pwndT));
    01842                 pwnd = pwndT;
    01843                 break;
    01844             }
    01845         }
    01846     }
    01847
    01848     UserAssert(!TestCF(pwnd, CFIME));
    01849     Lock(&pwndIme->spwndOwner, pwnd);
    01850
    01851     return;
    01852 }
    01853
    01854
    01855 /**************************************************************************\
    01856 * ImeSetTopmostChild
    01857 *
    01858 * History:
    01859 * 02-Apr-1996 wkwok       Ported from FE Win95 (imeclass.c)
    01860 \**************************************************************************/
    01861
    01862 VOID ImeSetTopmostChild(
    01863     PWND pwndParent,
    01864     BOOL fMakeTopmost)
    01865 {
    01866     PWND pwnd = pwndParent->spwndChild;
    01867
    01868     while (pwnd != NULL) {
    01869         if (fMakeTopmost)
    01870             SetWF(pwnd, WEFTOPMOST);
    01871         else
    01872             ClrWF(pwnd, WEFTOPMOST);
    01873
    01874         ImeSetTopmostChild(pwnd, fMakeTopmost);
    01875
    01876         pwnd = pwnd->spwndNext;
    01877     }
    01878
    01879     return;
    01880 }
    01881
    01882
    01883 /**************************************************************************\
    01884 *
    01885 *  GetLastTopMostWindowNoIME() -
    01886 *
    01887 *  Get the last topmost window which is not the ownee of pwndRoot (IME window).
    01888 *
    01889 \**************************************************************************/
    01890
    01891 PWND GetLastTopMostWindowNoIME(PWND pwndRoot)
    01892 {
    01893     PWND pwndT = _GetDesktopWindow();
    01894     PWND pwndRet = NULL;
    01895
    01896     /*
    01897      * pwndRoot should not be NULL, and should be IME window.
    01898      */
    01899     UserAssert(pwndRoot && pwndRoot->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]);
    01900
    01901     if (pwndT == NULL || pwndT->spwndChild == NULL) {
    01902 #if _DBG
    01903         if (pwndT == NULL) {
    01904             RIPMSG0(RIP_WARNING, "GetLastTopMostWindowNoIME: there's no desktop window !!");
    01905         }
    01906         else {
    01907             RIPMSG0(RIP_WARNING, "GetLastTopMostWindowNoIME: there is no toplevel window !!");
    01908         }
    01909 #endif
    01910         return NULL;
    01911     }
    01912
    01913     /*
    01914      * Get the first child of the desktop window.
    01915      */
    01916     pwndT = pwndT->spwndChild;
    01917
    01918     /*
    01919      * Loop through the toplevel windows while they are topmost.
    01920      */
    01921     while (TestWF(pwndT, WEFTOPMOST)) {
    01922         PWND pwndOwner = pwndT;
    01923         BOOL fOwned = FALSE;
    01924
    01925         /*
    01926          * If pwndT is a IME related window, track the owner. If pwndRoot is not
    01927          * pwndT's owner, remember pwndT as a candidate.
    01928          */
    01929         if (TestCF(pwndT,CFIME) || (pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])) {
    01930             while (pwndOwner != NULL) {
    01931                 if (pwndRoot == pwndOwner) {
    01932                     fOwned = TRUE;
    01933                     break;
    01934                 }
    01935                 pwndOwner = pwndOwner->spwndOwner;
    01936             }
    01937         }
    01938         if (!fOwned)
    01939             pwndRet = pwndT;
    01940
    01941         /*
    01942          * Next toplevel window.
    01943          */
    01944         pwndT = pwndT->spwndNext;
    01945         UserAssert(pwndT->spwndParent == _GetDesktopWindow());
    01946     }
    01947
    01948     return pwndRet;
    01949 }
    01950
    01951
    01952 #if DBG
    01953 void ImeCheckSetTopmostLink(PWND pwnd, PWND pwndInsFirst, PWND pwndIns)
    01954 {
    01955     PWND pwndDebT0 = pwndInsFirst;
    01956     BOOL fFound = FALSE;
    01957
    01958     if (pwndDebT0) {
    01959         while (pwndDebT0 && (pwndDebT0 != pwndIns)) {
    01960             if (pwndDebT0 == pwnd)
    01961                 fFound = TRUE;
    01962
    01963             pwndDebT0 = pwndDebT0->spwndNext;
    01964         }
    01965
    01966         if (pwndDebT0 == NULL) {
    01967             RIPMSG3(RIP_ERROR, "pwndIns(%#p) is upper that pwndInsFirst(%#p) pwnd is (%#p)", pwndIns, pwndInsFirst, pwnd);
    01968         } else if (fFound) {
    01969             RIPMSG3(RIP_ERROR, "pwnd(%#p) is between pwndInsFirst(%#p) and pwndIns(%#p)", pwnd, pwndInsFirst, pwndIns);
    01970         }
    01971     } else if (pwndIns) {
    01972         pwndDebT0 = pwnd->spwndParent->spwndChild;
    01973
    01974         while (pwndDebT0 && (pwndDebT0 != pwndIns)) {
    01975             if (pwndDebT0 == pwnd)
    01976                 fFound = TRUE;
    01977
    01978             pwndDebT0 = pwndDebT0->spwndNext;
    01979         }
    01980
    01981         if (fFound) {
    01982             RIPMSG3(RIP_ERROR, "pwnd(%#p) is between TOPLEVEL pwndInsFirst(%#p) and pwndIns(%#p)", pwnd, pwndInsFirst, pwndIns);
    01983         }
    01984     }
    01985 }
    01986 #endif
    01987
    01988 /**************************************************************************\
    01989 * ImeSetTopmost
    01990 *
    01991 * History:
    01992 * 02-Apr-1996 wkwok       Ported from FE Win95 (imeclass.c)
    01993 \**************************************************************************/
    01994
    01995 VOID ImeSetTopmost(
    01996     PWND pwndRootIme,
    01997     BOOL fMakeTopmost,
    01998     PWND pwndInsertBefore)
    01999 {
    02000     PWND pwndParent = pwndRootIme->spwndParent;
    02001     PWND pwndInsert = PWND_TOP; // pwnd which should be prior to pwndRootIme.
    02002     PWND pwnd, pwndT;
    02003     PWND pwndInsertFirst;
    02004     BOOLEAN fFound;
    02005
    02006     if (pwndParent == NULL)
    02007         return;
    02008
    02009     pwnd = pwndParent->spwndChild;
    02010
    02011     if (!fMakeTopmost) {
    02012         /*
    02013          * Get the last topmost window. This should be after unlink pwndRootIme
    02014          * because pwndRootIme may be the last topmost window.
    02015          */
    02016         pwndInsert = GetLastTopMostWindowNoIME(pwndRootIme);
    02017
    02018         if (pwndInsertBefore) {
    02019
    02020             fFound = FALSE;
    02021             pwndT = pwndInsert;
    02022
    02023             while (pwndT != NULL && pwndT->spwndNext != pwndInsertBefore) {
    02024                 if (pwndT == pwndRootIme)
    02025                     fFound = TRUE;
    02026                 pwndT = pwndT->spwndNext;
    02027             }
    02028
    02029             if (pwndT == NULL || fFound)
    02030                 return;
    02031
    02032             pwndInsert = pwndT;
    02033         }
    02034
    02035         if (TestWF(pwndRootIme->spwndOwner, WFBOTTOMMOST)) {
    02036             pwndT = pwndInsert;
    02037
    02038             while (pwndT != NULL && pwndT != pwndRootIme->spwndOwner) {
    02039                 if (!TestCF(pwndT, CFIME) &&
    02040                         pwndT->pcls->atomClassName != gpsi->atomSysClass[ICLS_IME]) {
    02041                     pwndInsert = pwndT;
    02042                 }
    02043                 pwndT = pwndT->spwndNext;
    02044             }
    02045         }
    02046     }
    02047
    02048     pwndInsertFirst = pwndInsert;
    02049
    02050     /*
    02051      * Enum the all toplevel windows and if the owner of the window is same as
    02052      * the owner of pwndRootIme, the window should be changed the position of
    02053      * window link.
    02054      */
    02055     while (pwnd != NULL) {
    02056         /*
    02057          * Get the next window before calling ImeSetTopmost.
    02058          * Because the next window will be changed in LinkWindow.
    02059          */
    02060         PWND pwndNext = pwnd->spwndNext;
    02061
    02062         /*
    02063          * the owner relation between IME and UI window is in same thread.
    02064          */
    02065         if (GETPTI(pwnd) != GETPTI(pwndRootIme))
    02066             goto ist_next;
    02067
    02068         /*
    02069          * pwnd have to be CS_IME class or "IME" class.
    02070          */
    02071         if (!TestCF(pwnd, CFIME) &&
    02072                 pwnd->pcls->atomClassName != gpsi->atomSysClass[ICLS_IME])
    02073             goto ist_next;
    02074
    02075         /*
    02076          * If pwnd is pwndInsert, we don't need to do anything...
    02077          */
    02078         if (pwnd == pwndInsert)
    02079             goto ist_next;
    02080
    02081         pwndT = pwnd;
    02082         while (pwndT != NULL) {
    02083             if (pwndT == pwndRootIme) {
    02084                 /*
    02085                  * Found!!
    02086                  * pwnd is the ownee of pwndRootIme.
    02087                  */
    02088
    02089                 UserAssert(GETPTI(pwnd) == GETPTI(pwndRootIme));
    02090                 UserAssert(TestCF(pwnd,CFIME) ||
    02091                             (pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]));
    02092                 UserAssert(pwnd != pwndInsert);
    02093
    02094                 UnlinkWindow(pwnd, pwndParent);
    02095
    02096                 if (fMakeTopmost) {
    02097                     if (pwndInsert != PWND_TOP)
    02098                         UserAssert(TestWF(pwndInsert, WEFTOPMOST));
    02099                     SetWF(pwnd, WEFTOPMOST);
    02100                 }
    02101                 else {
    02102                     if (pwndInsert == PWND_TOP) {
    02103                         /*
    02104                          * In rare cases, the first toplevel window could be the one we'll look next,
    02105                          * who may still have obscure topmost flag.
    02106                          */
    02107                         UserAssert(pwndParent->spwndChild == pwndNext || !TestWF(pwndParent->spwndChild, WEFTOPMOST));
    02108                     }
    02109                     else if (pwndInsert->spwndNext != NULL) {
    02110                         /*
    02111                          * In rare cases, pwndInsert->spwndNext could be the one we'll look next,
    02112                          * who may still have obscure topmost flag.
    02113                          */
    02114                         UserAssert(pwndInsert->spwndNext == pwndNext || !TestWF(pwndInsert->spwndNext, WEFTOPMOST));
    02115                     }
    02116                     ClrWF(pwnd, WEFTOPMOST);
    02117                 }
    02118
    02119                 LinkWindow(pwnd, pwndInsert, pwndParent);
    02120 #if 0   // Let's see what happens if we disable this
    02121                 ImeSetTopmostChild(pwnd, fMakeTopmost);
    02122 #endif
    02123
    02124                 pwndInsert = pwnd;
    02125                 break;  // goto ist_next;
    02126             }
    02127             pwndT = pwndT->spwndOwner;
    02128         }
    02129 ist_next:
    02130         pwnd = pwndNext;
    02131
    02132         /*
    02133          * Skip the windows that were inserted before.
    02134          */
    02135         if (pwnd != NULL && pwnd == pwndInsertFirst)
    02136             pwnd = pwndInsert->spwndNext;
    02137
    02138 #if DBG
    02139         if (pwnd)
    02140             ImeCheckSetTopmostLink(pwnd, pwndInsertFirst, pwndInsert);
    02141 #endif
    02142     }
    02143 }
    02144
    02145
    02146 /**************************************************************************\
    02147 * ProbeAndCaptureSoftKbdData
    02148 *
    02149 * Captures SoftKbdData that comes from user mode.
    02150 *
    02151 * 23-Apr-1996 wkwok     created
    02152 \**************************************************************************/
    02153
    02154 PSOFTKBDDATA ProbeAndCaptureSoftKbdData(
    02155     PSOFTKBDDATA Source)
    02156 {
    02157     PSOFTKBDDATA Destination = NULL;
    02158     DWORD        cbSize;
    02159     UINT         uCount;
    02160
    02161     try {
    02162         uCount = ProbeAndReadUlong((PULONG)Source);
    02163
    02164 #if defined(_X86_)
    02165         ProbeForReadBuffer(&Source->wCode, uCount, sizeof(BYTE));
    02166 #else
    02167         ProbeForReadBuffer(&Source->wCode, uCount, sizeof(WORD));
    02168 #endif
    02169
    02170         cbSize = FIELD_OFFSET(SOFTKBDDATA, wCode[0])
    02171                + uCount * sizeof(WORD) * 256;
    02172
    02173         Destination = (PSOFTKBDDATA)UserAllocPool(cbSize, TAG_IME);
    02174
    02175         if (Destination != NULL) {
    02176             RtlCopyMemory(Destination, Source, cbSize);
    02177         } else {
    02178             ExRaiseStatus(STATUS_NO_MEMORY);
    02179         }
    02180
    02181     } except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
    02182
    02183         if (Destination != NULL) {
    02184             UserFreePool(Destination);
    02185         }
    02186
    02187         return NULL;
    02188     }
    02189
    02190     return Destination;
    02191 }
    02192
    02193 //
    02194 // ported from Win95:ctxtman.c\SetConvMode()
    02195 //
    02196 VOID  SetConvMode( PTHREADINFO pti, DWORD dwConversion )
    02197 {
    02198     if ( pti->spklActive == NULL )
    02199         return;
    02200
    02201     switch ( PRIMARYLANGID(HandleToUlong(pti->spklActive->hkl)) ) {
    02202     case LANG_JAPANESE:
    02203
    02204         ClearKeyStateDown(pti->pq, VK_DBE_ALPHANUMERIC);
    02205         ClearKeyStateToggle(pti->pq, VK_DBE_ALPHANUMERIC);
    02206         ClearKeyStateDown(pti->pq, VK_DBE_KATAKANA);
    02207         ClearKeyStateToggle(pti->pq, VK_DBE_KATAKANA);
    02208         ClearKeyStateDown(pti->pq, VK_DBE_HIRAGANA);
    02209         ClearKeyStateToggle(pti->pq, VK_DBE_HIRAGANA);
    02210
    02211         if ( dwConversion & IME_CMODE_NATIVE ) {
    02212             if ( dwConversion & IME_CMODE_KATAKANA ) {
    02213                 SetKeyStateDown( pti->pq, VK_DBE_KATAKANA);
    02214                 SetKeyStateToggle( pti->pq, VK_DBE_KATAKANA);
    02215             } else {
    02216                 SetKeyStateDown( pti->pq, VK_DBE_HIRAGANA);
    02217                 SetKeyStateToggle( pti->pq, VK_DBE_HIRAGANA);
    02218             }
    02219         } else {
    02220             SetKeyStateDown( pti->pq, VK_DBE_ALPHANUMERIC);
    02221             SetKeyStateToggle( pti->pq, VK_DBE_ALPHANUMERIC);
    02222         }
    02223
    02224         if ( dwConversion & IME_CMODE_FULLSHAPE ) {
    02225             SetKeyStateDown( pti->pq, VK_DBE_DBCSCHAR);
    02226             SetKeyStateToggle( pti->pq, VK_DBE_DBCSCHAR);
    02227             ClearKeyStateDown(pti->pq, VK_DBE_SBCSCHAR);
    02228             ClearKeyStateToggle(pti->pq, VK_DBE_SBCSCHAR);
    02229         } else {
    02230             SetKeyStateDown( pti->pq, VK_DBE_SBCSCHAR);
    02231             SetKeyStateToggle( pti->pq, VK_DBE_SBCSCHAR);
    02232             ClearKeyStateDown(pti->pq, VK_DBE_DBCSCHAR);
    02233             ClearKeyStateToggle(pti->pq, VK_DBE_DBCSCHAR);
    02234         }
    02235
    02236         if ( dwConversion & IME_CMODE_ROMAN ) {
    02237             SetKeyStateDown( pti->pq, VK_DBE_ROMAN);
    02238             SetKeyStateToggle( pti->pq, VK_DBE_ROMAN);
    02239             ClearKeyStateDown(pti->pq, VK_DBE_NOROMAN);
    02240             ClearKeyStateToggle(pti->pq, VK_DBE_NOROMAN);
    02241         } else {
    02242             SetKeyStateDown( pti->pq, VK_DBE_NOROMAN);
    02243             SetKeyStateToggle( pti->pq, VK_DBE_NOROMAN);
    02244             ClearKeyStateDown(pti->pq, VK_DBE_ROMAN);
    02245             ClearKeyStateToggle(pti->pq, VK_DBE_ROMAN);
    02246         }
    02247
    02248         if ( dwConversion & IME_CMODE_CHARCODE ) {
    02249             SetKeyStateDown( pti->pq, VK_DBE_CODEINPUT);
    02250             SetKeyStateToggle( pti->pq, VK_DBE_CODEINPUT);
    02251             ClearKeyStateDown(pti->pq, VK_DBE_NOCODEINPUT);
    02252             ClearKeyStateToggle(pti->pq, VK_DBE_NOCODEINPUT);
    02253         } else {
    02254             SetKeyStateDown( pti->pq, VK_DBE_NOCODEINPUT);
    02255             SetKeyStateToggle( pti->pq, VK_DBE_NOCODEINPUT);
    02256             ClearKeyStateDown(pti->pq, VK_DBE_CODEINPUT);
    02257             ClearKeyStateToggle(pti->pq, VK_DBE_CODEINPUT);
    02258         }
    02259         break;
    02260
    02261     case LANG_KOREAN:
    02262         if ( dwConversion & IME_CMODE_NATIVE) {
    02263             SetKeyStateToggle( pti->pq, VK_HANGUL);
    02264         } else {
    02265             ClearKeyStateToggle( pti->pq, VK_HANGUL);
    02266         }
    02267
    02268         if ( dwConversion & IME_CMODE_FULLSHAPE ) {
    02269             SetKeyStateToggle( pti->pq, VK_JUNJA);
    02270         } else {
    02271             ClearKeyStateToggle( pti->pq, VK_JUNJA);
    02272         }
    02273
    02274         if ( dwConversion & IME_CMODE_HANJACONVERT ) {
    02275             SetKeyStateToggle( pti->pq, VK_HANJA);
    02276         } else {
    02277             ClearKeyStateToggle( pti->pq, VK_HANJA);
    02278         }
    02279         break;
    02280
    02281     default:
    02282         break;
    02283     }
    02284     return;
    02285 }
    02286
    02287 //
    02288 // called by IMM32 client when:
    02289 //
    02290 //      input focus is switched
    02291 //   or IME open status is changed
    02292 //   or IME conversion status is changed
    02293 //
    02294 //
    02295 VOID xxxNotifyIMEStatus(
    02296                        IN PWND pwnd,
    02297                        IN DWORD dwOpen,
    02298                        IN DWORD dwConversion )
    02299 {
    02300     PTHREADINFO pti;
    02301
    02302     CheckLock(pwnd);
    02303
    02304     if ( (pti = GETPTI(pwnd)) != NULL && gptiForeground != NULL ) {
    02305         if ( pti == gptiForeground || pti->pq == gptiForeground->pq ) {
    02306
    02307             if ( gHimcFocus != pwnd->hImc   ||
    02308                  gdwIMEOpenStatus != dwOpen ||
    02309                  gdwIMEConversionStatus != dwConversion ) {
    02310
    02311                 //
    02312                 // save the new status
    02313                 //
    02314                 gHimcFocus = pwnd->hImc;
    02315                 if ( gHimcFocus != (HIMC)NULL ) {
    02316
    02317                     RIPMSG2(RIP_VERBOSE, "xxxNotifyIMEStatus: newOpen=%x newConv=%x",
    02318                             dwOpen, dwConversion);
    02319                     gdwIMEOpenStatus = dwOpen;
    02320                     gdwIMEConversionStatus = dwConversion;
    02321
    02322                     //
    02323                     // set keyboard states that are related to IME conversion status
    02324                     //
    02325                     SetConvMode(pti, dwOpen ? dwConversion : 0);
    02326                 }
    02327
    02328                 //
    02329                 // notify shell the IME status change
    02330                 //
    02331                 // Implementation note: [takaok 9/5/96]
    02332                 //
    02333                 // Using HSHELL_LANGUAGE is not the best way to inform shell
    02334                 // IME status change because we didn't change the keyboard layout.
    02335                 // ( The spec says HSHELL_LANGUAGE is for keyboard layout change.
    02336                 //  Also passing window handle as WPARAM is not documented )
    02337                 //
    02338                 // This is same as what Win95 does. I won't change this for now
    02339                 // because in the future shell will be developed by a different
    02340                 // group in MS.
    02341                 //
    02342                 // Currently only Korean Windows is interested in getting
    02343                 // the conversion status change.
    02344                 //
    02345                 if (IsHooked(pti, WHF_SHELL)) {
    02346                     HKL hkl = NULL;
    02347
    02348                     if (pti->spklActive != NULL) {
    02349                         hkl = pti->spklActive->hkl;
    02350                     }
    02351                     xxxCallHook(HSHELL_LANGUAGE, (WPARAM)HWq(pwnd), (LPARAM)hkl, WH_SHELL);
    02352                 }
    02353
    02354                 //
    02355                 // notify keyboard driver
    02356                 //
    02357                 NlsKbdSendIMENotification(dwOpen,dwConversion);
    02358             }
    02359         }
    02360     }
    02361 }
    02362
    02363 //---------------------------------------------------------------------------
    02364 //
    02365 // xxxCheckImeShowStatus() -
    02366 //
    02367 // Only one Status Window should be shown in the System.
    02368 // This functsion enums all IME window and check the show status of them.
    02369 //
    02370 // If pti is NULL, check all toplevel windows regardless their owners.
    02371 // If pti is not NULL, check only windows belong to the thread.
    02372 //
    02373 //----------------------------------------------------------------------------
    02374
    02375 BOOL xxxCheckImeShowStatus(PWND pwndIme, PTHREADINFO pti)
    02376 {
    02377     PBWL pbwl;
    02378     PHWND phwnd;
    02379     BOOL fRet = FALSE;
    02380     PTHREADINFO ptiCurrent = PtiCurrent();
    02381
    02382     if (TestWF(pwndIme, WFINDESTROY)) {
    02383         return FALSE;
    02384     }
    02385
    02386     // Parent window of IME window should be the desktop window
    02387     UserAssert(pwndIme);
    02388     UserAssert(pwndIme->spwndParent == GETPTI(pwndIme)->pDeskInfo->spwnd);
    02389
    02390     pbwl = BuildHwndList(pwndIme->spwndParent->spwndChild, BWL_ENUMLIST, NULL);
    02391     if (pbwl != NULL) {
    02392         fRet = TRUE;
    02393         for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) {
    02394             PWND pwndT = RevalidateHwnd(*phwnd);
    02395
    02396             // If pwndT is the current active IME window, we should skip it
    02397             // since it's the only one window allowed to show status, and
    02398             // we've already taken care of it.
    02399             if (pwndT == NULL || (/*pwndIme && */pwndIme == pwndT)) {   // Can skip pwndIme != NULL test
    02400                 continue;
    02401             }
    02402
    02403             // We are going to touch IME windows only
    02404             if (pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME] &&
    02405                     !TestWF(pwndT, WFINDESTROY)) {
    02406
    02407                 PIMEUI pimeui = ((PIMEWND)pwndT)->pimeui;
    02408
    02409                 if (pimeui == NULL || pimeui == (PIMEUI)-1) {
    02410                     continue;
    02411                 }
    02412
    02413                 if (pti == NULL || pti == GETPTI(pwndT)) {
    02414                     BOOLEAN fAttached = FALSE;
    02415                     PWND    pwndIMC;
    02416
    02417                     // If pwndT is not a window of the current process, we have to138163
    02418                     // attach the process to get access to pimeui.
    02419                     if (GETPTI(pwndT)->ppi != ptiCurrent->ppi) {
    02420                         RIPMSG0(RIP_VERBOSE, "Attaching process in xxxCheckImeShowStatus");
    02421                         KeAttachProcess(&GETPTI(pwndT)->ppi->Process->Pcb);
    02422                         fAttached = TRUE;
    02423                     }
    02424
    02425                     try {
    02426                         if (ProbeAndReadStructure(pimeui, IMEUI).fShowStatus) {
    02427                             if (pti == NULL) {
    02428                                 RIPMSG0(RIP_VERBOSE, "xxxCheckImeShowStatus: the status window is shown !!");
    02429                             }
    02430                             if ((pwndIMC = RevalidateHwnd(pimeui->hwndIMC)) != NULL) {
    02431                                 pimeui->fShowStatus = FALSE;
    02432                             }
    02433                         } else {
    02434                             pwndIMC = NULL;
    02435                         }
    02436
    02437                     } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
    02438                           pwndIMC = NULL;
    02439                     }
    02440                     if (fAttached) {
    02441                         KeDetachProcess();
    02442                     }
    02443
    02444                     if (pwndIMC && GETPTI(pwndIMC) && !(GETPTI(pwndIMC)->TIF_flags & TIF_INCLEANUP)) {
    02445                         TL tl;
    02446
    02447                         ThreadLockAlways(pwndIMC, &tl);
    02448                         RIPMSG1(RIP_VERBOSE, "Sending WM_IME_NOTIFY to %#p, IMN_CLOSESTATUSWINDOW", pwndIMC);
    02449                         xxxSendMessage(pwndIMC, WM_IME_NOTIFY, IMN_CLOSESTATUSWINDOW, 0L);
    02450                         ThreadUnlock(&tl);
    02451                     }
    02452
    02453                 }
    02454             }
    02455         }
    02456         FreeHwndList(pbwl);
    02457     }
    02458     return fRet;
    02459 }
    02460
    02461 LRESULT xxxSendMessageToUI(
    02462     PTHREADINFO ptiIme,
    02463     PIMEUI pimeui,
    02464     UINT   message,
    02465     WPARAM wParam,
    02466     LPARAM lParam)
    02467 {
    02468     PWND  pwndUI;
    02469     LRESULT lRet = 0L;
    02470     TL    tl;
    02471     BOOL  fAttached = FALSE;
    02472
    02473     CheckCritIn();
    02474
    02475     if (ptiIme != PtiCurrent()) {
    02476         fAttached = TRUE;
    02477         KeAttachProcess(&ptiIme->ppi->Process->Pcb);
    02478     }
    02479
    02480     try {
    02481         pwndUI = RevalidateHwnd(ProbeAndReadStructure(pimeui, IMEUI).hwndUI);
    02482     } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
    02483         pwndUI = NULL;
    02484     }
    02485
    02486     if (pwndUI != NULL){
    02487         try {
    02488             ProbeAndReadUlong((PULONG)&pimeui->nCntInIMEProc);
    02489             InterlockedIncrement(&pimeui->nCntInIMEProc);   // Mark to avoid recursion.
    02490         } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
    02491               goto skip_it;
    02492         }
    02493         if (fAttached) {
    02494             KeDetachProcess();
    02495         }
    02496
    02497         ThreadLockAlways(pwndUI, &tl);
    02498         RIPMSG3(RIP_VERBOSE, "Sending message UI pwnd=%#p, msg:%x to wParam=%#p", pwndUI, message, wParam);
    02499         lRet = xxxSendMessage(pwndUI, message, wParam, lParam);
    02500         ThreadUnlock(&tl);
    02501
    02502         if (fAttached) {
    02503             KeAttachProcess(&ptiIme->ppi->Process->Pcb);
    02504         }
    02505         try {
    02506             InterlockedDecrement(&pimeui->nCntInIMEProc);   // Mark to avoid recursion.
    02507         } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
    02508         }
    02509     }
    02510 skip_it:
    02511     if (fAttached) {
    02512         KeDetachProcess();
    02513     }
    02514
    02515     return lRet;
    02516 }
    02517
    02518 VOID xxxSendOpenStatusNotify(
    02519     PTHREADINFO ptiIme,
    02520     PIMEUI pimeui,
    02521     PWND   pwndApp,
    02522     BOOL   fOpen)
    02523 {
    02524     WPARAM wParam = fOpen ? IMN_OPENSTATUSWINDOW : IMN_CLOSESTATUSWINDOW;
    02525
    02526     UserAssert(GETPTI(pwndApp));
    02527
    02528     if (GETPTI(pwndApp)->dwExpWinVer >= VER40 && pwndApp->hImc != NULL) {
    02529         TL tl;
    02530         ThreadLockAlways(pwndApp, &tl);
    02531         RIPMSG2(RIP_VERBOSE, "Sending %s to pwnd=%#p",
    02532                 fOpen ? "IMN_OPENSTATUSWINDOW" : "IMN_CLOSESTATUSWINDOW",
    02533                 pwndApp);
    02534         xxxSendMessage(pwndApp, WM_IME_NOTIFY, wParam, 0L);
    02535         ThreadUnlock(&tl);
    02536     }
    02537     else {
    02538         xxxSendMessageToUI(ptiIme, pimeui, WM_IME_NOTIFY, wParam, 0L);
    02539     }
    02540
    02541     return;
    02542 }
    02543
    02544 VOID xxxNotifyImeShowStatus(PWND pwndIme)
    02545 {
    02546     PIMEUI pimeui;
    02547     BOOL fShow;
    02548     PWND pwnd;
    02549     PTHREADINFO ptiIme, ptiCurrent;
    02550     BOOL fSendOpenStatusNotify = FALSE;
    02551
    02552     if (!IS_IME_ENABLED() || TestWF(pwndIme, WFINDESTROY)) {
    02553         RIPMSG0(RIP_WARNING, "IME is not enabled or in destroy.");
    02554         return;
    02555     }
    02556
    02557     ptiCurrent = PtiCurrent();
    02558     ptiIme = GETPTI(pwndIme);
    02559
    02560     if (ptiIme != ptiCurrent) {
    02561         RIPMSG1(RIP_VERBOSE, "Attaching pti=%#p", ptiIme);
    02562         KeAttachProcess(&GETPTI(pwndIme)->ppi->Process->Pcb);
    02563     }
    02564
    02565     try {
    02566         pimeui = ((PIMEWND)pwndIme)->pimeui;
    02567         fShow = gfIMEShowStatus && ProbeAndReadStructure(pimeui, IMEUI).fCtrlShowStatus;
    02568
    02569         pwnd = RevalidateHwnd(pimeui->hwndIMC);
    02570
    02571         if (pwnd != NULL || (pwnd = GETPTI(pwndIme)->pq->spwndFocus) != NULL) {
    02572             RIPMSG0(RIP_VERBOSE, "Setting new show status.");
    02573             fSendOpenStatusNotify = TRUE;
    02574             pimeui->fShowStatus = fShow;
    02575         }
    02576     } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
    02577           if (ptiIme != ptiCurrent) {
    02578               KeDetachProcess();
    02579           }
    02580           return;
    02581     }
    02582     if (ptiIme != ptiCurrent) {
    02583         KeDetachProcess();
    02584     }
    02585
    02586     if (fSendOpenStatusNotify) {
    02587         RIPMSG1(RIP_VERBOSE, "Sending OpenStatus fShow=%d", fShow);
    02588         xxxSendOpenStatusNotify(ptiIme, pimeui, pwnd, fShow);
    02589     }
    02590
    02591     // Check the show status of all IME windows in the system.
    02592     if (!TestWF(pwndIme, WFINDESTROY)) {
    02593         xxxCheckImeShowStatus(pwndIme, NULL);
    02594     }
    02595 }
    02596
    02597
    02598 /***************************************************************************\
    02599 * xxxSetIMEShowStatus() -
    02600 *
    02601 * Set IME Status windows' show status. Called from SystemParametersInfo()
    02602 * handler.
    02603 *
    02604 \***************************************************************************/
    02605
    02606 BOOL xxxSetIMEShowStatus(IN BOOL fShow)
    02607 {
    02608     CheckCritIn();
    02609
    02610     UserAssert(fShow == FALSE || fShow == TRUE);
    02611
    02612     if (gfIMEShowStatus == fShow) {
    02613         return TRUE;
    02614     }
    02615
    02616     if (gfIMEShowStatus == IMESHOWSTATUS_NOTINITIALIZED) {
    02617         /*
    02618          * Called for the first time after logon.
    02619          * No need to write the value to the registry.
    02620          */
    02621         gfIMEShowStatus = fShow;
    02622     }
    02623     else {
    02624         /*
    02625          * We need to save the new fShow status to the registry.
    02626          */
    02627         TL tlName;
    02628         PUNICODE_STRING pProfileUserName;
    02629         BOOL fOK = FALSE;
    02630
    02631         pProfileUserName = CreateProfileUserName(&tlName);
    02632         if (pProfileUserName) {
    02633             UserAssert(pProfileUserName != NULL);
    02634             fOK = UpdateWinIniInt(pProfileUserName, PMAP_INPUTMETHOD, STR_SHOWIMESTATUS, fShow);
    02635             FreeProfileUserName(pProfileUserName, &tlName);
    02636         }
    02637         if (!fOK) {
    02638             return FALSE;
    02639         }
    02640         gfIMEShowStatus = fShow;
    02641     }
    02642
    02643     /*
    02644      * If IME is not enabled, further processing is not needed
    02645      */
    02646     if (!IS_IME_ENABLED()) {
    02647         return TRUE;
    02648     }
    02649
    02650     /*
    02651      * Let the current active IME window know the change.
    02652      */
    02653     if (gpqForeground && gpqForeground->spwndFocus) {
    02654         PTHREADINFO ptiFocus = GETPTI(gpqForeground->spwndFocus);
    02655         TL tl;
    02656
    02657         UserAssert(ptiFocus);
    02658
    02659         if (ptiFocus->spwndDefaultIme && !(ptiFocus->TIF_flags & TIF_INCLEANUP)) {
    02660             ThreadLockAlways(ptiFocus->spwndDefaultIme, &tl);
    02661             xxxNotifyImeShowStatus(ptiFocus->spwndDefaultIme);
    02662             ThreadUnlock(&tl);
    02663         }
    02664     }
    02665
    02666     return TRUE;
    02667 }
    02668
    02669 /***************************************************************************\
    02670 * xxxBroadcastImeShowStatusChange() -
    02671 *
    02672 * Let all IME windows in the desktop, including myself  know about the
    02673 * status change.
    02674 * This routine does not touch the registry, assuming internat.exe updated
    02675 * the registry.
    02676 *
    02677 \***************************************************************************/
    02678
    02679 VOID xxxBroadcastImeShowStatusChange(PWND pwndIme, BOOL fShow)
    02680 {
    02681     CheckCritIn();
    02682
    02683     gfIMEShowStatus = !!fShow;
    02684     xxxNotifyImeShowStatus(pwndIme);
    02685 }
    02686
    02687 /***************************************************************************\
    02688 * xxxCheckImeShowStatusInThread() -
    02689 *
    02690 * Let all IME windows in the same thread know about the status change.
    02691 * Called from ImeSetContextHandler().
    02692 *
    02693 \***************************************************************************/
    02694 VOID xxxCheckImeShowStatusInThread(PWND pwndIme)
    02695 {
    02696     if (IS_IME_ENABLED()) {
    02697         UserAssert(pwndIme);
    02698
    02699         if (!TestWF(pwndIme, WFINDESTROY)) {
    02700             xxxCheckImeShowStatus(pwndIme, GETPTI(pwndIme));
    02701         }
    02702     }
    02703 }
    02704
    02705 BOOL _GetIMEShowStatus(VOID)
    02706 {
    02707     return gfIMEShowStatus != FALSE;
    02708 }
    02709