Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2794 lines
83 KiB

  1. /**************************************************************************\
  2. * Module Name: ntimm.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * This module contains IMM functionality
  7. *
  8. * History:
  9. * 21-Dec-1995 wkwok
  10. \**************************************************************************/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. static CONST WCHAR wszDefaultIme[] = L"Default IME";
  14. #if DBG
  15. BOOL CheckOwnerCirculate(PWND pwnd)
  16. {
  17. PWND pwndT = pwnd->spwndOwner;
  18. while (pwndT) {
  19. UserAssert(pwndT->spwndOwner != pwnd);
  20. pwndT = pwndT->spwndOwner;
  21. }
  22. return TRUE;
  23. }
  24. #endif
  25. /**************************************************************************\
  26. * CreateInputContext
  27. *
  28. * Create input context object.
  29. *
  30. * History:
  31. * 21-Dec-1995 wkwok Created
  32. \**************************************************************************/
  33. PIMC CreateInputContext(
  34. ULONG_PTR dwClientImcData)
  35. {
  36. PTHREADINFO ptiCurrent;
  37. PIMC pImc;
  38. PDESKTOP pdesk = NULL;
  39. ptiCurrent = PtiCurrentShared();
  40. /*
  41. * Only for thread that wants IME processing.
  42. */
  43. if ((ptiCurrent->TIF_flags & TIF_DISABLEIME) || !IS_IME_ENABLED()) {
  44. RIPMSG1(RIP_VERBOSE, "CreateInputContext: TIF_DISABLEIME or !IME Enabled. pti=%#p", ptiCurrent);
  45. return NULL;
  46. }
  47. /*
  48. * If pti->spDefaultImc is NULL (means this is the first instance)
  49. * but dwClientImcData is not 0, some bogus application like NtCrash
  50. * has tried to trick the kernel. Just bail out.
  51. */
  52. if (dwClientImcData != 0 && ptiCurrent->spDefaultImc == NULL) {
  53. RIPMSG2(RIP_WARNING, "CreateInputContext: bogus value(0x%08x) is passed. pti=%#p",
  54. dwClientImcData, ptiCurrent);
  55. return NULL;
  56. }
  57. /*
  58. * If the windowstation has been initialized, allocate from
  59. * the current desktop.
  60. */
  61. pdesk = ptiCurrent->rpdesk;
  62. #ifdef LATER
  63. RETURN_IF_ACCESS_DENIED(ptiCurrent->amdesk, DESKTOP_CREATEINPUTCONTEXT, NULL);
  64. #else
  65. if (ptiCurrent->rpdesk == NULL) {
  66. return NULL;
  67. }
  68. #endif
  69. pImc = HMAllocObject(ptiCurrent, pdesk, TYPE_INPUTCONTEXT, sizeof(IMC));
  70. if (pImc == NULL) {
  71. RIPMSG0(RIP_WARNING, "CreateInputContext: out of memory");
  72. return NULL;
  73. }
  74. if (dwClientImcData == 0) {
  75. /*
  76. * We are creating default input context for current thread.
  77. * Initialize the default input context as head of the
  78. * per-thread IMC list.
  79. */
  80. UserAssert(ptiCurrent->spDefaultImc == NULL);
  81. Lock(&ptiCurrent->spDefaultImc, pImc);
  82. pImc->pImcNext = NULL;
  83. }
  84. else {
  85. /*
  86. * Link it to the per-thread IMC list.
  87. */
  88. UserAssert(ptiCurrent->spDefaultImc != NULL);
  89. pImc->pImcNext = ptiCurrent->spDefaultImc->pImcNext;
  90. ptiCurrent->spDefaultImc->pImcNext = pImc;
  91. }
  92. pImc->dwClientImcData = dwClientImcData;
  93. return pImc;
  94. }
  95. /**************************************************************************\
  96. * DestroyInputContext
  97. *
  98. * Destroy the specified input context object.
  99. *
  100. * History:
  101. * 21-Dec-1995 wkwok Created
  102. \**************************************************************************/
  103. BOOL DestroyInputContext(
  104. IN PIMC pImc)
  105. {
  106. PTHREADINFO ptiImcOwner;
  107. PBWL pbwl;
  108. PWND pwnd;
  109. HWND *phwnd;
  110. PHE phe;
  111. ptiImcOwner = GETPTI(pImc);
  112. /*
  113. * Cannot destroy input context from other thread.
  114. */
  115. if (ptiImcOwner != PtiCurrent()) {
  116. RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING,
  117. "DestroyInputContext: pImc not of current pti");
  118. return FALSE;
  119. }
  120. /*
  121. * Cannot destroy default input context.
  122. */
  123. if (pImc == ptiImcOwner->spDefaultImc) {
  124. RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING,
  125. "DestroyInputContext: can't destroy default Imc");
  126. return FALSE;
  127. }
  128. /*
  129. * Cleanup destroyed input context from each associated window.
  130. */
  131. pbwl = BuildHwndList(ptiImcOwner->rpdesk->pDeskInfo->spwnd->spwndChild,
  132. BWL_ENUMLIST|BWL_ENUMCHILDREN, ptiImcOwner);
  133. if (pbwl != NULL) {
  134. for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) {
  135. /*
  136. * Make sure this hwnd is still around.
  137. */
  138. if ((pwnd = RevalidateHwnd(*phwnd)) == NULL)
  139. continue;
  140. /*
  141. * Cleanup by associating the default input context.
  142. */
  143. if (pwnd->hImc == (HIMC)PtoH(pImc))
  144. AssociateInputContext(pwnd, ptiImcOwner->spDefaultImc);
  145. }
  146. FreeHwndList(pbwl);
  147. }
  148. phe = HMPheFromObject(pImc);
  149. /*
  150. * Make sure this object isn't already marked to be destroyed - we'll
  151. * do no good if we try to destroy it now since it is locked.
  152. */
  153. if (!(phe->bFlags & HANDLEF_DESTROY))
  154. HMDestroyUnlockedObject(phe);
  155. return TRUE;
  156. }
  157. /**************************************************************************\
  158. * FreeInputContext
  159. *
  160. * Free up the specified input context object.
  161. *
  162. * History:
  163. * 21-Dec-1995 wkwok Created
  164. \**************************************************************************/
  165. VOID FreeInputContext(
  166. IN PIMC pImc)
  167. {
  168. PIMC pImcT;
  169. /*
  170. * Mark it for destruction. If it the object is locked it can't
  171. * be freed right now.
  172. */
  173. if (!HMMarkObjectDestroy((PVOID)pImc))
  174. return;
  175. /*
  176. * Unlink it.
  177. */
  178. pImcT = GETPTI(pImc)->spDefaultImc;
  179. while (pImcT != NULL && pImcT->pImcNext != pImc)
  180. pImcT = pImcT->pImcNext;
  181. if (pImcT != NULL)
  182. pImcT->pImcNext = pImc->pImcNext;
  183. /*
  184. * We're really going to free the input context.
  185. */
  186. HMFreeObject((PVOID)pImc);
  187. return;
  188. }
  189. /**************************************************************************\
  190. * UpdateInputContext
  191. *
  192. * Update the specified input context object according to UpdateType.
  193. *
  194. * History:
  195. * 21-Dec-1995 wkwok Created
  196. \**************************************************************************/
  197. BOOL UpdateInputContext(
  198. IN PIMC pImc,
  199. IN UPDATEINPUTCONTEXTCLASS UpdateType,
  200. IN ULONG_PTR UpdateValue)
  201. {
  202. PTHREADINFO ptiCurrent, ptiImcOwner;
  203. ptiCurrent = PtiCurrent();
  204. ptiImcOwner = GETPTI(pImc);
  205. /*
  206. * Cannot update input context from other process.
  207. */
  208. if (ptiImcOwner->ppi != ptiCurrent->ppi) {
  209. RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING, "UpdateInputContext: pImc not of current ppi");
  210. return FALSE;
  211. }
  212. switch (UpdateType) {
  213. case UpdateClientInputContext:
  214. if (pImc->dwClientImcData != 0) {
  215. RIPERR0(RIP_WARNING, RIP_WARNING, "UpdateInputContext: pImc->dwClientImcData != 0");
  216. return FALSE;
  217. }
  218. pImc->dwClientImcData = UpdateValue;
  219. break;
  220. case UpdateInUseImeWindow:
  221. pImc->hImeWnd = (HWND)UpdateValue;
  222. break;
  223. default:
  224. return FALSE;
  225. }
  226. return TRUE;
  227. }
  228. /**************************************************************************\
  229. * AssociateInputContext
  230. *
  231. * Associate input context object to the specified window.
  232. *
  233. * History:
  234. * 21-Dec-1995 wkwok Created
  235. \**************************************************************************/
  236. HIMC AssociateInputContext(
  237. IN PWND pWnd,
  238. IN PIMC pImc)
  239. {
  240. HIMC hImcRet = pWnd->hImc;
  241. pWnd->hImc = (HIMC)PtoH(pImc);
  242. return hImcRet;
  243. }
  244. AIC_STATUS AssociateInputContextEx(
  245. IN PWND pWnd,
  246. IN PIMC pImc,
  247. IN DWORD dwFlag)
  248. {
  249. PTHREADINFO ptiWnd = GETPTI(pWnd);
  250. PWND pWndFocus = ptiWnd->pq->spwndFocus;
  251. BOOL fIgnoreNoContext = (dwFlag & IACE_IGNORENOCONTEXT) == IACE_IGNORENOCONTEXT;
  252. AIC_STATUS Status = AIC_SUCCESS;
  253. if (dwFlag & IACE_DEFAULT) {
  254. /*
  255. * use default input context.
  256. */
  257. pImc = ptiWnd->spDefaultImc;
  258. } else if (pImc != NULL && GETPTI(pImc) != ptiWnd) {
  259. /*
  260. * Cannot associate input context to window created
  261. * by other thread.
  262. */
  263. RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING,
  264. "AssociateInputContextEx: pwnd not of Imc pti");
  265. return AIC_ERROR;
  266. }
  267. /*
  268. * Cannot do association under different process context.
  269. */
  270. if (GETPTI(pWnd)->ppi != PtiCurrent()->ppi) {
  271. RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING,
  272. "AssociateInputContextEx: pwnd not of current ppi");
  273. return AIC_ERROR;
  274. }
  275. /*
  276. * Finally, make sure they are on the same desktop.
  277. */
  278. if (pImc != NULL && pImc->head.rpdesk != pWnd->head.rpdesk) {
  279. RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING,
  280. "AssociateInputContextEx: no desktop access");
  281. return AIC_ERROR;
  282. }
  283. /*
  284. * If IACE_CHILDREN is specified, associate the input context
  285. * to the child windows of pWnd as well.
  286. */
  287. if ((dwFlag & IACE_CHILDREN) && pWnd->spwndChild != NULL) {
  288. PBWL pbwl;
  289. PWND pwndT;
  290. HWND *phwndT;
  291. pbwl = BuildHwndList(pWnd->spwndChild,
  292. BWL_ENUMLIST|BWL_ENUMCHILDREN, ptiWnd);
  293. if (pbwl != NULL) {
  294. for (phwndT = pbwl->rghwnd; *phwndT != (HWND)1; phwndT++) {
  295. /*
  296. * Make sure this hwnd is still around.
  297. */
  298. if ((pwndT = RevalidateHwnd(*phwndT)) == NULL)
  299. continue;
  300. if (pwndT->hImc == (HIMC)PtoH(pImc))
  301. continue;
  302. if (pwndT->hImc == NULL_HIMC && fIgnoreNoContext)
  303. continue;
  304. AssociateInputContext(pwndT, pImc);
  305. if (pwndT == pWndFocus)
  306. Status = AIC_FOCUSCONTEXTCHANGED;
  307. }
  308. FreeHwndList(pbwl);
  309. }
  310. }
  311. /*
  312. * Associate the input context to pWnd.
  313. */
  314. if (pWnd->hImc != NULL_HIMC || !fIgnoreNoContext) {
  315. if (pWnd->hImc != (HIMC)PtoH(pImc)) {
  316. AssociateInputContext(pWnd, pImc);
  317. if (pWnd == pWndFocus)
  318. Status = AIC_FOCUSCONTEXTCHANGED;
  319. }
  320. }
  321. return Status;
  322. }
  323. /**************************************************************************\
  324. * xxxFocusSetInputContext
  325. *
  326. * Set active input context upon focus change.
  327. *
  328. * History:
  329. * 21-Mar-1996 wkwok Created
  330. \**************************************************************************/
  331. VOID xxxFocusSetInputContext(
  332. IN PWND pWnd,
  333. IN BOOL fActivate,
  334. IN BOOL fQueueMsg)
  335. {
  336. PTHREADINFO pti;
  337. PWND pwndDefaultIme;
  338. TL tlpwndDefaultIme;
  339. CheckLock(pWnd);
  340. pti = GETPTI(pWnd);
  341. /*
  342. * CS_IME class or "IME" class windows can not be SetActivated to hImc.
  343. * WinWord 6.0 US Help calls ShowWindow with the default IME window.
  344. * HELPMACROS get the default IME window by calling GetNextWindow().
  345. */
  346. if (TestCF(pWnd, CFIME) ||
  347. (pWnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]))
  348. return;
  349. /*
  350. * Do nothing if the thread does not have default IME window.
  351. */
  352. if ((pwndDefaultIme = pti->spwndDefaultIme) == NULL)
  353. return;
  354. /*
  355. * If the thread is going away or the default IME window is being vanished,
  356. * then do nothing.
  357. */
  358. if (pti->TIF_flags & TIF_INCLEANUP)
  359. return;
  360. UserAssert(!TestWF(pwndDefaultIme, WFDESTROYED));
  361. ThreadLockAlways(pwndDefaultIme, &tlpwndDefaultIme);
  362. if (fQueueMsg) {
  363. xxxSendMessageCallback(pwndDefaultIme, WM_IME_SYSTEM,
  364. fActivate ? IMS_ACTIVATECONTEXT : IMS_DEACTIVATECONTEXT,
  365. (LPARAM)HWq(pWnd), NULL, 1L, 0);
  366. } else {
  367. xxxSendMessage(pwndDefaultIme, WM_IME_SYSTEM,
  368. fActivate ? IMS_ACTIVATECONTEXT : IMS_DEACTIVATECONTEXT,
  369. (LPARAM)HWq(pWnd));
  370. }
  371. #if _DBG
  372. if (pti->spwndDefaultIme != pwndDefaultIme) {
  373. RIPMSG1(RIP_WARNING, "pti(%#p)->spwndDefaultIme got freed during the callback.", pti);
  374. }
  375. #endif
  376. ThreadUnlock(&tlpwndDefaultIme);
  377. return;
  378. }
  379. /**************************************************************************\
  380. * BuildHimcList
  381. *
  382. * Retrieve the list of input context handles created by given thread.
  383. *
  384. * History:
  385. * 21-Feb-1995 wkwok Created
  386. \**************************************************************************/
  387. UINT BuildHimcList(
  388. PTHREADINFO pti,
  389. UINT cHimcMax,
  390. HIMC *ccxphimcFirst)
  391. {
  392. PIMC pImcT;
  393. UINT i = 0;
  394. if (pti == NULL) {
  395. /*
  396. * Build the list which contains all IMCs created by calling process.
  397. */
  398. for (pti = PtiCurrent()->ppi->ptiList; pti != NULL; pti = pti->ptiSibling) {
  399. pImcT = pti->spDefaultImc;
  400. while (pImcT != NULL) {
  401. if (i < cHimcMax) {
  402. try {
  403. ccxphimcFirst[i] = (HIMC)PtoH(pImcT);
  404. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  405. }
  406. }
  407. i++;
  408. pImcT = pImcT->pImcNext;
  409. }
  410. }
  411. }
  412. else {
  413. /*
  414. * Build the list which contains all IMCs created by specified thread.
  415. */
  416. pImcT = pti->spDefaultImc;
  417. while (pImcT != NULL) {
  418. if (i < cHimcMax) {
  419. try {
  420. ccxphimcFirst[i] = (HIMC)PtoH(pImcT);
  421. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  422. }
  423. }
  424. i++;
  425. pImcT = pImcT->pImcNext;
  426. }
  427. }
  428. return i;
  429. }
  430. /**************************************************************************\
  431. * xxxCreateDefaultImeWindow
  432. *
  433. * Create per-thread based default IME window.
  434. *
  435. * History:
  436. * 21-Mar-1996 wkwok Created
  437. \**************************************************************************/
  438. PWND xxxCreateDefaultImeWindow(
  439. IN PWND pwnd,
  440. IN ATOM atomT,
  441. IN HANDLE hInst)
  442. {
  443. LARGE_STRING strWindowName;
  444. PWND pwndDefaultIme;
  445. TL tlpwnd;
  446. PIMEUI pimeui;
  447. PTHREADINFO ptiCurrent = PtiCurrentShared();
  448. LPWSTR pwszDefaultIme;
  449. UserAssert(ptiCurrent == GETPTI(pwnd) && ptiCurrent->spwndDefaultIme == NULL);
  450. /*
  451. * Those conditions should have been checked by WantImeWindow()
  452. * before xxxCreateDefaultImeWindow gets called.
  453. */
  454. UserAssert(!(ptiCurrent->TIF_flags & TIF_DISABLEIME));
  455. UserAssert(!TestWF(pwnd, WFSERVERSIDEPROC));
  456. /*
  457. * The first Winlogon thread starts without default input context.
  458. * Create it now.
  459. */
  460. if (ptiCurrent->spDefaultImc == NULL &&
  461. PsGetThreadProcessId(ptiCurrent->pEThread) == gpidLogon)
  462. CreateInputContext(0);
  463. /*
  464. * No default IME window for thread that doesn't have
  465. * default input context
  466. */
  467. if (ptiCurrent->spDefaultImc == NULL)
  468. return (PWND)NULL;
  469. /*
  470. * Avoid recursion
  471. */
  472. if (atomT == gpsi->atomSysClass[ICLS_IME] || TestCF(pwnd, CFIME))
  473. return (PWND)NULL;
  474. /*
  475. * B#12165-win95b
  476. * Yet MFC does another nice. We need to avoid to give an IME window
  477. * to the child of desktop window which is in different process.
  478. */
  479. if (TestwndChild(pwnd) && GETPTI(pwnd->spwndParent)->ppi != ptiCurrent->ppi &&
  480. !(pwnd->style & WS_VISIBLE))
  481. return (PWND)NULL;
  482. if (ptiCurrent->rpdesk->pheapDesktop == NULL)
  483. return (PWND)NULL;
  484. /*
  485. * Allocate storage for L"Default IME" string from desktop heap
  486. * so that it can be referenced from USER32.DLL in user mode.
  487. */
  488. pwszDefaultIme = (LPWSTR)DesktopAlloc(ptiCurrent->rpdesk,
  489. sizeof(wszDefaultIme),
  490. DTAG_IMETEXT);
  491. if (pwszDefaultIme == NULL)
  492. return (PWND)NULL;
  493. RtlCopyMemory(pwszDefaultIme, wszDefaultIme, sizeof(wszDefaultIme));
  494. RtlInitLargeUnicodeString((PLARGE_UNICODE_STRING)&strWindowName,
  495. pwszDefaultIme,
  496. (UINT)-1);
  497. ThreadLock(pwnd, &tlpwnd);
  498. pwndDefaultIme = xxxNVCreateWindowEx( (DWORD)0,
  499. (PLARGE_STRING)gpsi->atomSysClass[ICLS_IME],
  500. (PLARGE_STRING)&strWindowName,
  501. WS_POPUP | WS_DISABLED,
  502. 0, 0, 0, 0,
  503. pwnd, (PMENU)NULL,
  504. hInst, NULL, VER40);
  505. if (pwndDefaultIme != NULL) {
  506. pimeui = ((PIMEWND)pwndDefaultIme)->pimeui;
  507. UserAssert(pimeui != NULL && (LONG_PTR)pimeui != (LONG_PTR)-1);
  508. try {
  509. ProbeForWrite(pimeui, sizeof *pimeui, sizeof(DWORD));
  510. pimeui->fDefault = TRUE;
  511. if (TestwndChild(pwnd) && GETPTI(pwnd->spwndParent) != ptiCurrent) {
  512. pimeui->fChildThreadDef = TRUE;
  513. }
  514. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  515. }
  516. }
  517. ThreadUnlock(&tlpwnd);
  518. DesktopFree(ptiCurrent->rpdesk, pwszDefaultIme);
  519. return pwndDefaultIme;
  520. }
  521. /**************************************************************************\
  522. * xxxImmActivateThreadsLayout
  523. *
  524. * Activate keyboard layout for multiple threads.
  525. *
  526. * Return:
  527. * TRUE if at least one thread has changed its active keyboard layout.
  528. * FALSE otherwise
  529. *
  530. * History:
  531. * 11-Apr-1996 wkwok Created
  532. \**************************************************************************/
  533. BOOL xxxImmActivateThreadsLayout(
  534. PTHREADINFO pti,
  535. PTLBLOCK ptlBlockPrev,
  536. PKL pkl)
  537. {
  538. TLBLOCK tlBlock;
  539. PTHREADINFO ptiCurrent, ptiT;
  540. UINT cThreads = 0;
  541. INT i;
  542. CheckLock(pkl);
  543. ptiCurrent = PtiCurrentShared();
  544. /*
  545. * Build a list of threads that we need to update their active layouts.
  546. * We can't just walk the ptiT list while we're doing the work, because
  547. * for IME based keyboard layout, we will do callback to client side
  548. * and the ptiT could get deleted out while we leave the critical section.
  549. */
  550. for (ptiT = pti; ptiT != NULL; ptiT = ptiT->ptiSibling) {
  551. /*
  552. * Skip all the *do nothing* cases in xxxImmActivateLayout
  553. * so as to minimize the # of TLBLOCK required.
  554. */
  555. if (ptiT->spklActive == pkl || (ptiT->TIF_flags & TIF_INCLEANUP))
  556. continue;
  557. UserAssert(ptiT->pClientInfo != NULL);
  558. UserAssert(ptiT->ppi == PpiCurrent()); // can't access pClientInfo of other process
  559. if (ptiT->spwndDefaultIme == NULL) {
  560. /*
  561. * Keyboard layout is being switched but there's no way to callback
  562. * the client side to activate&initialize input context now.
  563. * Let's do hkl switching only in the kernel side for this thread
  564. * but remember the input context needs to be re-initialized
  565. * when this GUI thread recreates the default IME window later.
  566. */
  567. ptiT->hklPrev = ptiT->spklActive->hkl;
  568. Lock(&ptiT->spklActive, pkl);
  569. if (ptiT->spDefaultImc) {
  570. try {
  571. ptiT->pClientInfo->CI_flags |= CI_INPUTCONTEXT_REINIT;
  572. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  573. continue;
  574. }
  575. RIPMSG1(RIP_VERBOSE, "xxxImmActivateThreadsLayout: ptiT(%08p) will be re-initialized.", ptiT);
  576. }
  577. UserAssert((ptiT->TIF_flags & TIF_INCLEANUP) == 0);
  578. try {
  579. ptiT->pClientInfo->hKL = pkl->hkl;
  580. ptiT->pClientInfo->CodePage = pkl->CodePage;
  581. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  582. }
  583. continue;
  584. }
  585. ThreadLockPti(ptiCurrent, ptiT, &tlBlock.list[cThreads].tlpti);
  586. tlBlock.list[cThreads++].pti = ptiT;
  587. if (cThreads == THREADS_PER_TLBLOCK)
  588. break;
  589. }
  590. /*
  591. * Return FALSE if all the threads already had the pkl active.
  592. */
  593. if (ptlBlockPrev == NULL && ptiT == NULL && cThreads == 0)
  594. return FALSE;
  595. /*
  596. * If we can't service all the threads in this run,
  597. * call ImmActivateThreadsLayout() again for a new TLBLOCK.
  598. */
  599. if (ptiT != NULL && ptiT->ptiSibling != NULL) {
  600. tlBlock.ptlBlockPrev = ptlBlockPrev;
  601. return xxxImmActivateThreadsLayout(ptiT->ptiSibling, &tlBlock, pkl);
  602. }
  603. /*
  604. * Finally, we can do the actual keyboard layout activation
  605. * starting from this run. Work on current TLBLOCK first.
  606. * We walk the list backwards so that the pti unlocks will
  607. * be done in the right order.
  608. */
  609. tlBlock.ptlBlockPrev = ptlBlockPrev;
  610. ptlBlockPrev = &tlBlock;
  611. while (ptlBlockPrev != NULL) {
  612. for (i = cThreads - 1; i >= 0; --i) {
  613. if ((ptlBlockPrev->list[i].pti->TIF_flags & TIF_INCLEANUP) == 0) {
  614. ptiT = ptlBlockPrev->list[i].pti;
  615. UserAssert(ptiT);
  616. xxxImmActivateLayout(ptiT, pkl);
  617. if ((ptiT->TIF_flags & TIF_INCLEANUP) == 0) {
  618. try {
  619. ptiT->pClientInfo->hKL = pkl->hkl;
  620. ptiT->pClientInfo->CodePage = pkl->CodePage;
  621. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  622. }
  623. }
  624. }
  625. ThreadUnlockPti(ptiCurrent, &ptlBlockPrev->list[i].tlpti);
  626. }
  627. ptlBlockPrev = ptlBlockPrev->ptlBlockPrev;
  628. cThreads = THREADS_PER_TLBLOCK;
  629. }
  630. return TRUE;
  631. }
  632. VOID xxxImmActivateAndUnloadThreadsLayout(
  633. IN PTHREADINFO *ptiList,
  634. IN UINT nEntries,
  635. IN PTLBLOCK ptlBlockPrev,
  636. PKL pklCurrent,
  637. DWORD dwHklReplace)
  638. {
  639. TLBLOCK tlBlock;
  640. PTHREADINFO ptiCurrent;
  641. int i, cThreads;
  642. enum { RUN_ACTIVATE = 1, RUN_UNLOAD = 2, RUN_FLAGS_MASK = RUN_ACTIVATE | RUN_UNLOAD, RUN_INVALID = 0xffff0000 };
  643. CheckLock(pklCurrent);
  644. ptiCurrent = PtiCurrentShared();
  645. tlBlock.ptlBlockPrev = ptlBlockPrev;
  646. /*
  647. * Build a list of threads that we need to unload their IME DLL(s).
  648. * We can't just walk the ptiList while we're doing the work, because
  649. * for IME based keyboard layout, we will do callback to client side
  650. * and the pti could get deleted out while we leave the critical section.
  651. */
  652. for (i = 0, cThreads = 0; i < (INT)nEntries; i++) {
  653. DWORD dwFlags = 0;
  654. /*
  655. * Skip all the *do nothing* cases in xxxImmActivateLayout
  656. * so as to minimize the # of TLBLOCKs required.
  657. */
  658. if (ptiList[i]->TIF_flags & TIF_INCLEANUP) {
  659. dwFlags = RUN_INVALID;
  660. }
  661. else if (ptiList[i]->spklActive != pklCurrent) {
  662. if (ptiList[i]->spwndDefaultIme == NULL) {
  663. BOOLEAN fAttached = FALSE;
  664. Lock(&ptiList[i]->spklActive, pklCurrent);
  665. if (ptiList[i]->pClientInfo != ptiCurrent->pClientInfo &&
  666. ptiList[i]->ppi != ptiCurrent->ppi) {
  667. /*
  668. * If the thread is in another process, attach
  669. * to that process so that we can access its ClientInfo.
  670. */
  671. KeAttachProcess(PsGetProcessPcb(ptiList[i]->ppi->Process));
  672. fAttached = TRUE;
  673. }
  674. try {
  675. ptiList[i]->pClientInfo->CodePage = pklCurrent->CodePage;
  676. ptiList[i]->pClientInfo->hKL = pklCurrent->hkl;
  677. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  678. dwFlags = RUN_INVALID;
  679. }
  680. if (fAttached) {
  681. KeDetachProcess();
  682. }
  683. } else {
  684. dwFlags = RUN_ACTIVATE;
  685. }
  686. }
  687. /*
  688. * Skip all the *do nothing* cases in xxxImmUnloadLayout()
  689. * so as to minimize the # of TLBLOCK required.
  690. * (#99321)
  691. */
  692. if (ptiList[i]->spwndDefaultIme != NULL &&
  693. ptiList[i]->spklActive != NULL &&
  694. (dwHklReplace != IFL_DEACTIVATEIME ||
  695. IS_IME_KBDLAYOUT(ptiList[i]->spklActive->hkl)
  696. #ifdef CUAS_ENABLE
  697. ||
  698. IS_CICERO_ENABLED_AND_NOT16BIT()
  699. #endif // CUAS_ENABLE
  700. ) &&
  701. dwFlags != RUN_INVALID) {
  702. dwFlags |= RUN_UNLOAD;
  703. }
  704. if (dwFlags && dwFlags != RUN_INVALID) {
  705. ThreadLockPti(ptiCurrent, ptiList[i], &tlBlock.list[cThreads].tlpti);
  706. #if DBG
  707. tlBlock.list[cThreads].dwUnlockedCount = 0;
  708. #endif
  709. tlBlock.list[cThreads].pti = ptiList[i];
  710. tlBlock.list[cThreads++].dwFlags = dwFlags;
  711. if (cThreads == THREADS_PER_TLBLOCK) {
  712. i++; // 1 more before exit the loop.
  713. break;
  714. }
  715. }
  716. }
  717. /*
  718. * If we can't service all the threads in this run,
  719. * call xxxImmActivateAndUnloadThreadsLayout again for a new TLBLOCK.
  720. */
  721. if (i < (INT)nEntries) {
  722. ptiList += i;
  723. nEntries -= i;
  724. xxxImmActivateAndUnloadThreadsLayout(ptiList, nEntries, &tlBlock, pklCurrent, dwHklReplace);
  725. return;
  726. }
  727. /*
  728. * Finally, we can do the actual keyboard layout activation
  729. * starting from this run. Work on current TLBLOCK first.
  730. * We walk the list backwards so that the pti unlocks will
  731. * be done in the right order.
  732. */
  733. i = cThreads - 1;
  734. for (ptlBlockPrev = &tlBlock; ptlBlockPrev != NULL; ptlBlockPrev = ptlBlockPrev->ptlBlockPrev) {
  735. for ( ; i >= 0; i--) {
  736. if ((ptlBlockPrev->list[i].dwFlags & RUN_ACTIVATE) &&
  737. !(ptlBlockPrev->list[i].pti->TIF_flags & TIF_INCLEANUP)) {
  738. xxxImmActivateLayout(ptlBlockPrev->list[i].pti, pklCurrent);
  739. }
  740. // unlock the thread if the thread is only locked for the first run
  741. if ((ptlBlockPrev->list[i].dwFlags & RUN_FLAGS_MASK) == RUN_ACTIVATE) {
  742. ThreadUnlockPti(ptiCurrent, &ptlBlockPrev->list[i].tlpti);
  743. #if DBG
  744. ptlBlockPrev->list[i].dwUnlockedCount++;
  745. #endif
  746. }
  747. }
  748. i = THREADS_PER_TLBLOCK - 1;
  749. }
  750. i = cThreads - 1;
  751. for (ptlBlockPrev = &tlBlock; ptlBlockPrev != NULL; ptlBlockPrev = ptlBlockPrev->ptlBlockPrev) {
  752. for ( ; i >= 0; --i) {
  753. if (ptlBlockPrev->list[i].dwFlags & RUN_UNLOAD) {
  754. if (!(ptlBlockPrev->list[i].pti->TIF_flags & TIF_INCLEANUP)) {
  755. xxxImmUnloadLayout(ptlBlockPrev->list[i].pti, dwHklReplace);
  756. }
  757. else {
  758. RIPMSG1(RIP_WARNING, "xxxImmActivateAndUnloadThreadsLayout: thread %#p is cleaned up.",
  759. ptlBlockPrev->list[i].pti);
  760. }
  761. // unlock the thread
  762. UserAssert((ptlBlockPrev->list[i].dwFlags & RUN_FLAGS_MASK) != RUN_ACTIVATE);
  763. UserAssert(ptlBlockPrev->list[i].dwUnlockedCount == 0);
  764. ThreadUnlockPti(ptiCurrent, &ptlBlockPrev->list[i].tlpti);
  765. #if DBG
  766. ptlBlockPrev->list[i].dwUnlockedCount++;
  767. #endif
  768. }
  769. }
  770. i = THREADS_PER_TLBLOCK - 1;
  771. }
  772. #if DBG
  773. // Check if all the locked thread is properly unlocked
  774. i = cThreads - 1;
  775. for (ptlBlockPrev = &tlBlock; ptlBlockPrev; ptlBlockPrev = ptlBlockPrev->ptlBlockPrev) {
  776. for ( ; i >= 0; --i) {
  777. UserAssert(ptlBlockPrev->list[i].dwUnlockedCount == 1);
  778. }
  779. i = THREADS_PER_TLBLOCK - 1;
  780. }
  781. #endif
  782. return;
  783. }
  784. /**************************************************************************\
  785. * xxxImmActivateLayout
  786. *
  787. * Activate IME based keyboard layout.
  788. *
  789. * History:
  790. * 21-Mar-1996 wkwok Created
  791. \**************************************************************************/
  792. VOID xxxImmActivateLayout(
  793. IN PTHREADINFO pti,
  794. IN PKL pkl)
  795. {
  796. TL tlpwndDefaultIme;
  797. PTHREADINFO ptiCurrent;
  798. CheckLock(pkl);
  799. /*
  800. * Do nothing if it's already been the current active layout.
  801. */
  802. if (pti->spklActive == pkl)
  803. return;
  804. if (pti->spwndDefaultIme == NULL) {
  805. /*
  806. * Only activate kernel side keyboard layout if this pti
  807. * doesn't have the default IME window.
  808. */
  809. Lock(&pti->spklActive, pkl);
  810. return;
  811. }
  812. ptiCurrent = PtiCurrentShared();
  813. /*
  814. * Activate client side IME based keyboard layout.
  815. */
  816. ThreadLockAlwaysWithPti(ptiCurrent, pti->spwndDefaultIme, &tlpwndDefaultIme);
  817. xxxSendMessage(pti->spwndDefaultIme, WM_IME_SYSTEM,
  818. (WPARAM)IMS_ACTIVATETHREADLAYOUT, (LPARAM)pkl->hkl);
  819. ThreadUnlock(&tlpwndDefaultIme);
  820. Lock(&pti->spklActive, pkl);
  821. return;
  822. }
  823. VOID xxxImmUnloadThreadsLayout(
  824. IN PTHREADINFO *ptiList,
  825. IN UINT nEntries,
  826. IN PTLBLOCK ptlBlockPrev,
  827. IN DWORD dwFlag)
  828. {
  829. TLBLOCK tlBlock;
  830. PTHREADINFO ptiCurrent;
  831. INT i, cThreads;
  832. BOOLEAN fPerformUnlock;
  833. ptiCurrent = PtiCurrentShared();
  834. tlBlock.ptlBlockPrev = ptlBlockPrev;
  835. /*
  836. * Build a list of threads that we need to unload their IME DLL(s).
  837. * We can't just walk the ptiList while we're doing the work, because
  838. * for IME based keyboard layout, we will do callback to client side
  839. * and the pti could get deleted out while we leave the critical section.
  840. */
  841. for (i = 0, cThreads = 0; i < (INT)nEntries; i++) {
  842. /*
  843. * Skip all the *do nothing* cases in xxxImmUnloadLayout()
  844. * so as to minimize the # of TLBLOCK required.
  845. */
  846. if ((ptiList[i]->TIF_flags & TIF_INCLEANUP) || ptiList[i]->spwndDefaultIme == NULL)
  847. continue;
  848. if (ptiList[i]->spklActive == NULL)
  849. continue;
  850. #if !defined(CUAS_ENABLE)
  851. if (dwFlag == IFL_DEACTIVATEIME &&
  852. !IS_IME_KBDLAYOUT(ptiList[i]->spklActive->hkl)) // #99321
  853. continue;
  854. #else
  855. if (dwFlag == IFL_DEACTIVATEIME &&
  856. ((! IS_CICERO_ENABLED() && ! IS_IME_KBDLAYOUT(ptiList[i]->spklActive->hkl)) ||
  857. ( IS_CICERO_ENABLED() && (PtiCurrent()->TIF_flags & TIF_16BIT)))
  858. ) // #99321
  859. continue;
  860. #endif
  861. #if DBG
  862. tlBlock.list[cThreads].dwUnlockedCount = 0;
  863. #endif
  864. ThreadLockPti(ptiCurrent, ptiList[i], &tlBlock.list[cThreads].tlpti);
  865. tlBlock.list[cThreads++].pti = ptiList[i];
  866. if (cThreads == THREADS_PER_TLBLOCK) {
  867. i++; // 1 more before exit the loop.
  868. break;
  869. }
  870. }
  871. if (i < (INT)nEntries) {
  872. ptiList += i;
  873. nEntries -= i;
  874. xxxImmUnloadThreadsLayout(ptiList, nEntries, &tlBlock, dwFlag);
  875. return;
  876. }
  877. UserAssert(dwFlag == IFL_UNLOADIME || dwFlag == IFL_DEACTIVATEIME);
  878. if (dwFlag == IFL_UNLOADIME) {
  879. dwFlag = IFL_DEACTIVATEIME;
  880. fPerformUnlock = FALSE;
  881. } else {
  882. fPerformUnlock = TRUE;
  883. }
  884. RepeatForUnload:
  885. /*
  886. * Finally, we can unload the IME based keyboard layout
  887. * starting from this run. Work on current TLBLOCK first.
  888. * We walk the list backwards so that the pti unlocks will
  889. * be done in the right order.
  890. */
  891. i = cThreads - 1;
  892. for (ptlBlockPrev = &tlBlock; ptlBlockPrev; ptlBlockPrev = ptlBlockPrev->ptlBlockPrev) {
  893. for ( ; i >= 0; --i) {
  894. if (!(ptlBlockPrev->list[i].pti->TIF_flags & TIF_INCLEANUP)) {
  895. xxxImmUnloadLayout(ptlBlockPrev->list[i].pti, dwFlag);
  896. }
  897. else {
  898. RIPMSG2(RIP_WARNING, "Thread %#p is cleaned during the loop for %x!", ptlBlockPrev->list[i].pti, dwFlag);
  899. }
  900. if (fPerformUnlock) {
  901. #if DBG
  902. ptlBlockPrev->list[i].dwUnlockedCount++;
  903. #endif
  904. ThreadUnlockPti(ptiCurrent, &ptlBlockPrev->list[i].tlpti);
  905. }
  906. }
  907. i = THREADS_PER_TLBLOCK - 1;
  908. }
  909. if (!fPerformUnlock) {
  910. fPerformUnlock = TRUE;
  911. dwFlag = IFL_UNLOADIME;
  912. goto RepeatForUnload;
  913. }
  914. #if DBG
  915. // Check if all the locked thread is properly unlocked
  916. i = cThreads - 1;
  917. for (ptlBlockPrev = &tlBlock; ptlBlockPrev; ptlBlockPrev = ptlBlockPrev->ptlBlockPrev) {
  918. for ( ; i >= 0; --i) {
  919. UserAssert(ptlBlockPrev->list[i].dwUnlockedCount == 1);
  920. }
  921. i = THREADS_PER_TLBLOCK - 1;
  922. }
  923. #endif
  924. return;
  925. }
  926. VOID xxxImmUnloadLayout(
  927. IN PTHREADINFO pti,
  928. IN DWORD dwFlag)
  929. {
  930. TL tlpwndDefaultIme;
  931. PTHREADINFO ptiCurrent;
  932. ULONG_PTR dwResult;
  933. LRESULT r;
  934. /*
  935. * Do nothing if the thread does not have default IME window.
  936. */
  937. if (pti->spwndDefaultIme == NULL)
  938. return;
  939. if (pti->spklActive == NULL)
  940. return;
  941. #if !defined(CUAS_ENABLE)
  942. if (dwFlag == IFL_DEACTIVATEIME &&
  943. !IS_IME_KBDLAYOUT(pti->spklActive->hkl))
  944. return;
  945. #else
  946. if (dwFlag == IFL_DEACTIVATEIME &&
  947. ((! IS_CICERO_ENABLED() && !IS_IME_KBDLAYOUT(pti->spklActive->hkl)) ||
  948. ( IS_CICERO_ENABLED() && (PtiCurrent()->TIF_flags & TIF_16BIT)))
  949. )
  950. return;
  951. #endif
  952. ptiCurrent = PtiCurrentShared();
  953. ThreadLockAlwaysWithPti(ptiCurrent, pti->spwndDefaultIme, &tlpwndDefaultIme);
  954. r = xxxSendMessageTimeout(pti->spwndDefaultIme, WM_IME_SYSTEM,
  955. IMS_UNLOADTHREADLAYOUT, (LONG)dwFlag,
  956. SMTO_NOTIMEOUTIFNOTHUNG, CMSHUNGAPPTIMEOUT, &dwResult);
  957. if (!r) {
  958. RIPMSG1(RIP_WARNING, "Timeout in xxxImmUnloadLayout: perhaps this thread (0x%x) is not pumping messages.",
  959. GETPTIID(pti));
  960. }
  961. ThreadUnlock(&tlpwndDefaultIme);
  962. return;
  963. }
  964. /**************************************************************************\
  965. * xxxImmLoadLayout
  966. *
  967. * Retrieves extended IMEINFO for the given IME based keyboard layout.
  968. *
  969. * History:
  970. * 21-Mar-1996 wkwok Created
  971. \**************************************************************************/
  972. PIMEINFOEX xxxImmLoadLayout(
  973. IN HKL hKL)
  974. {
  975. PIMEINFOEX piiex;
  976. PTHREADINFO ptiCurrent;
  977. TL tlPool;
  978. /*
  979. * No IMEINFOEX for non-IME based keyboard layout.
  980. */
  981. #if !defined(CUAS_ENABLE)
  982. if (!IS_IME_KBDLAYOUT(hKL))
  983. return (PIMEINFOEX)NULL;
  984. #else
  985. if ((! IS_CICERO_ENABLED() && !IS_IME_KBDLAYOUT(hKL)) ||
  986. ( IS_CICERO_ENABLED() && (PtiCurrent()->TIF_flags & TIF_16BIT))
  987. )
  988. return (PIMEINFOEX)NULL;
  989. #endif
  990. piiex = (PIMEINFOEX)UserAllocPool(sizeof(IMEINFOEX), TAG_IME);
  991. if (piiex == NULL) {
  992. RIPMSG1(RIP_WARNING,
  993. "xxxImmLoadLayout: failed to create piiex for hkl = %lx", hKL);
  994. return (PIMEINFOEX)NULL;
  995. }
  996. ptiCurrent = PtiCurrent();
  997. /*
  998. * Lock this allocations since we are going to the client side
  999. */
  1000. ThreadLockPool(ptiCurrent, piiex, &tlPool);
  1001. if (!ClientImmLoadLayout(hKL, piiex)) {
  1002. ThreadUnlockAndFreePool(ptiCurrent, &tlPool);
  1003. return (PIMEINFOEX)NULL;
  1004. }
  1005. ThreadUnlockPool(ptiCurrent, &tlPool);
  1006. return piiex;
  1007. }
  1008. /**************************************************************************\
  1009. * GetImeInfoEx
  1010. *
  1011. * Query extended IMEINFO.
  1012. *
  1013. * History:
  1014. * 21-Mar-1996 wkwok Created
  1015. \**************************************************************************/
  1016. BOOL GetImeInfoEx(
  1017. PWINDOWSTATION pwinsta,
  1018. PIMEINFOEX piiex,
  1019. IMEINFOEXCLASS SearchType)
  1020. {
  1021. PKL pkl, pklFirst;
  1022. /*
  1023. * Note: this check was forced to insert due to winmm.dll who indirectly
  1024. * loads imm32.dll in CSRSS context. CSRSS is not always bound to
  1025. * specific window station, thus pwinsta could be NULL.
  1026. * This has been avoided by not loading imm32.dll.
  1027. * After winmm.dll gets removed from CSRSS, this if statement should be
  1028. * removed, or substituted as an assertion.
  1029. */
  1030. if (pwinsta == NULL) {
  1031. return FALSE;
  1032. }
  1033. /*
  1034. * Keyboard layer has not been initialized.
  1035. */
  1036. if (pwinsta->spklList == NULL)
  1037. return FALSE;
  1038. pkl = pklFirst = pwinsta->spklList;
  1039. switch (SearchType) {
  1040. case ImeInfoExKeyboardLayout:
  1041. do {
  1042. if (pkl->hkl == piiex->hkl) {
  1043. if (pkl->piiex == NULL)
  1044. break;
  1045. RtlCopyMemory(piiex, pkl->piiex, sizeof(IMEINFOEX));
  1046. return TRUE;
  1047. }
  1048. pkl = pkl->pklNext;
  1049. } while (pkl != pklFirst);
  1050. break;
  1051. case ImeInfoExImeFileName:
  1052. do {
  1053. if (pkl->piiex != NULL &&
  1054. !_wcsnicmp(pkl->piiex->wszImeFile, piiex->wszImeFile, IM_FILE_SIZE)) {
  1055. RtlCopyMemory(piiex, pkl->piiex, sizeof(IMEINFOEX));
  1056. return TRUE;
  1057. }
  1058. pkl = pkl->pklNext;
  1059. } while (pkl != pklFirst);
  1060. break;
  1061. default:
  1062. break;
  1063. }
  1064. return FALSE;
  1065. }
  1066. /**************************************************************************\
  1067. * SetImeInfoEx
  1068. *
  1069. * Set extended IMEINFO.
  1070. *
  1071. * History:
  1072. * 21-Mar-1996 wkwok Created
  1073. \**************************************************************************/
  1074. BOOL SetImeInfoEx(
  1075. PWINDOWSTATION pwinsta,
  1076. PIMEINFOEX piiex)
  1077. {
  1078. PKL pkl, pklFirst;
  1079. if (pwinsta == NULL) {
  1080. return FALSE;
  1081. }
  1082. UserAssert(pwinsta->spklList != NULL);
  1083. pkl = pklFirst = pwinsta->spklList;
  1084. do {
  1085. if (pkl->hkl == piiex->hkl) {
  1086. /*
  1087. * Error out for non-IME based keyboard layout.
  1088. */
  1089. if (pkl->piiex == NULL)
  1090. return FALSE;
  1091. /*
  1092. * Update kernel side IMEINFOEX for this keyboard layout
  1093. * only if this is its first loading.
  1094. */
  1095. if (pkl->piiex->fLoadFlag == IMEF_NONLOAD) {
  1096. RtlCopyMemory(pkl->piiex, piiex, sizeof(IMEINFOEX));
  1097. }
  1098. return TRUE;
  1099. }
  1100. pkl = pkl->pklNext;
  1101. } while (pkl != pklFirst);
  1102. return FALSE;
  1103. }
  1104. /***************************************************************************\
  1105. * xxxImmProcessKey
  1106. *
  1107. *
  1108. * History:
  1109. * 03-03-96 TakaoK Created.
  1110. \***************************************************************************/
  1111. DWORD xxxImmProcessKey(
  1112. IN PQ pq,
  1113. IN PWND pwnd,
  1114. IN UINT message,
  1115. IN WPARAM wParam,
  1116. IN LPARAM lParam)
  1117. {
  1118. UINT uVKey;
  1119. PKL pkl;
  1120. DWORD dwHotKeyID;
  1121. DWORD dwReturn = 0;
  1122. PIMC pImc = NULL;
  1123. BOOL fDBERoman = FALSE;
  1124. PIMEHOTKEYOBJ pImeHotKeyObj;
  1125. HKL hklTarget;
  1126. CheckLock(pwnd);
  1127. //
  1128. // we're interested in only keyboard messages.
  1129. //
  1130. if ( message != WM_KEYDOWN &&
  1131. message != WM_SYSKEYDOWN &&
  1132. message != WM_KEYUP &&
  1133. message != WM_SYSKEYUP ) {
  1134. return dwReturn;
  1135. }
  1136. //
  1137. // Check if it's IME hotkey. This must be done before checking
  1138. // the keyboard layout because IME hotkey handler should be
  1139. // called even if current keyboard layout is non-IME layout.
  1140. //
  1141. pkl = GETPTI(pwnd)->spklActive;
  1142. if ( pkl == NULL ) {
  1143. return dwReturn;
  1144. }
  1145. uVKey = (UINT)wParam & 0xff;
  1146. pImeHotKeyObj = CheckImeHotKey(pq, uVKey, lParam);
  1147. if (pImeHotKeyObj) {
  1148. dwHotKeyID = pImeHotKeyObj->hk.dwHotKeyID;
  1149. hklTarget = pImeHotKeyObj->hk.hKL;
  1150. }
  1151. else {
  1152. dwHotKeyID = IME_INVALID_HOTKEY;
  1153. hklTarget = (HKL)NULL;
  1154. }
  1155. //
  1156. // Handle Direct KL switching here.
  1157. //
  1158. if (dwHotKeyID >= IME_HOTKEY_DSWITCH_FIRST && dwHotKeyID <= IME_HOTKEY_DSWITCH_LAST) {
  1159. UserAssert(hklTarget != NULL);
  1160. if (pkl->hkl != hklTarget) {
  1161. //
  1162. // Post the message only if the new Keyboard Layout is different from
  1163. // the current Keyboard Layout.
  1164. //
  1165. _PostMessage(pwnd, WM_INPUTLANGCHANGEREQUEST,
  1166. (pkl->dwFontSigs & gSystemFS) ? INPUTLANGCHANGE_SYSCHARSET : 0,
  1167. (LPARAM)hklTarget);
  1168. }
  1169. if (GetAppImeCompatFlags(GETPTI(pwnd)) & IMECOMPAT_HYDRACLIENT) {
  1170. return 0;
  1171. }
  1172. return IPHK_HOTKEY;
  1173. }
  1174. if (!IS_IME_ENABLED()) {
  1175. //
  1176. // Since IMM is disabled, no need to process further.
  1177. // Just bail out.
  1178. //
  1179. return 0;
  1180. }
  1181. if ( dwHotKeyID != IME_INVALID_HOTKEY ) {
  1182. //
  1183. // if it's a valid hotkey, go straight and call back
  1184. // the IME in the client side.
  1185. //
  1186. goto ProcessKeyCallClient;
  1187. }
  1188. //
  1189. // if it's not a hotkey, we may want to check something
  1190. // before calling back.
  1191. //
  1192. if ( pkl->piiex == NULL ) {
  1193. return dwReturn;
  1194. }
  1195. //
  1196. // Check input context
  1197. //
  1198. pImc = HtoP(pwnd->hImc);
  1199. if ( pImc == NULL ) {
  1200. return dwReturn;
  1201. }
  1202. #ifdef LATER
  1203. //
  1204. // If there is an easy way to check the input context open/close status
  1205. // from the kernel side, IME_PROP_NO_KEYS_ON_CLOSE checking should be
  1206. // done here in kernel side. [ 3/10/96 takaok]
  1207. //
  1208. //
  1209. // Check IME_PROP_NO_KEYS_ON_CLOSE bit
  1210. //
  1211. // if the current imc is not open and IME doesn't need
  1212. // keys when being closed, we don't pass any keyboard
  1213. // input to ime except hotkey and keys that change
  1214. // the keyboard status.
  1215. //
  1216. if ( (piix->ImeInfo.fdwProperty & IME_PROP_NO_KEYS_ON_CLOSE) &&
  1217. (!pimc->fdwState & IMC_OPEN) &&
  1218. uVKey != VK_SHIFT && // 0x10
  1219. uVKey != VK_CONTROL && // 0x11
  1220. uVKey != VK_CAPITAL && // 0x14
  1221. uVKey != VK_KANA && // 0x15
  1222. uVKey != VK_NUMLOCK && // 0x90
  1223. uVKey != VK_SCROLL ) // 0x91
  1224. {
  1225. // Check if Korea Hanja conversion mode
  1226. if( !(pimc->fdwConvMode & IME_CMODE_HANJACONVERT) ) {
  1227. return dwReturn;
  1228. }
  1229. }
  1230. #endif
  1231. //
  1232. // if the IME doesn't need key up messages, we don't call ime.
  1233. //
  1234. if ( lParam & 0x80000000 && // set if key up, clear if key down
  1235. pkl->piiex->ImeInfo.fdwProperty & IME_PROP_IGNORE_UPKEYS )
  1236. {
  1237. return dwReturn;
  1238. }
  1239. //
  1240. // we don't want to handle sys keys since many functions for
  1241. // acceelerators won't work without this
  1242. //
  1243. fDBERoman = (BOOL)( (uVKey == VK_DBE_ROMAN) ||
  1244. (uVKey == VK_DBE_NOROMAN) ||
  1245. (uVKey == VK_DBE_HIRAGANA) ||
  1246. (uVKey == VK_DBE_KATAKANA) ||
  1247. (uVKey == VK_DBE_CODEINPUT) ||
  1248. (uVKey == VK_DBE_NOCODEINPUT) ||
  1249. (uVKey == VK_DBE_IME_WORDREGISTER) ||
  1250. (uVKey == VK_DBE_IME_DIALOG) );
  1251. if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP ) {
  1252. //
  1253. // IME may be waiting for VK_MENU, VK_F10 or VK_DBE_xxx
  1254. //
  1255. if ( uVKey != VK_MENU && uVKey != VK_F10 && !fDBERoman ) {
  1256. return dwReturn;
  1257. }
  1258. }
  1259. //
  1260. // check if the IME doesn't need ALT key
  1261. //
  1262. if ( !(pkl->piiex->ImeInfo.fdwProperty & IME_PROP_NEED_ALTKEY) ) {
  1263. //
  1264. // IME doesn't need ALT key
  1265. //
  1266. // we don't pass the ALT and ALT+xxx except VK_DBE_xxx keys.
  1267. //
  1268. if ( ! fDBERoman &&
  1269. (uVKey == VK_MENU || (lParam & 0x20000000)) // KF_ALTDOWN
  1270. )
  1271. {
  1272. return dwReturn;
  1273. }
  1274. }
  1275. //
  1276. // finaly call back the client
  1277. //
  1278. ProcessKeyCallClient:
  1279. if ((uVKey & 0xff) == VK_PACKET) {
  1280. //
  1281. // need to retrieve UNICODE character from pti
  1282. //
  1283. uVKey = MAKELONG(wParam, PtiCurrent()->wchInjected);
  1284. }
  1285. dwReturn = ClientImmProcessKey( PtoH(pwnd),
  1286. pkl->hkl,
  1287. uVKey,
  1288. lParam,
  1289. dwHotKeyID);
  1290. //
  1291. // Hydra server wants to see the IME hotkeys.
  1292. //
  1293. if (GetAppImeCompatFlags(GETPTI(pwnd)) & IMECOMPAT_HYDRACLIENT) {
  1294. dwReturn &= ~IPHK_HOTKEY;
  1295. }
  1296. return dwReturn;
  1297. }
  1298. /**************************************************************************\
  1299. * ImeCanDestroyDefIME
  1300. *
  1301. * History:
  1302. * 02-Apr-1996 wkwok Ported from FE Win95 (imeclass.c)
  1303. \**************************************************************************/
  1304. BOOL ImeCanDestroyDefIME(
  1305. PWND pwndDefaultIme,
  1306. PWND pwndDestroy)
  1307. {
  1308. PWND pwnd;
  1309. PIMEUI pimeui;
  1310. pimeui = ((PIMEWND)pwndDefaultIme)->pimeui;
  1311. if (pimeui == NULL || (LONG_PTR)pimeui == (LONG_PTR)-1)
  1312. return FALSE;
  1313. try {
  1314. if (ProbeAndReadStructure(pimeui, IMEUI).fDestroy) {
  1315. return FALSE;
  1316. }
  1317. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  1318. }
  1319. /*
  1320. * If the pwndDestroy has no owner/ownee relationship with
  1321. * pwndDefaultIme, don't bother to change anything.
  1322. *
  1323. * If pwndDefaultIme->spwndOwner is NULL, this means we need
  1324. * to search for a new good owner window.
  1325. */
  1326. if ( pwndDefaultIme->spwndOwner != NULL ) {
  1327. for (pwnd = pwndDefaultIme->spwndOwner;
  1328. pwnd != pwndDestroy && pwnd != NULL; pwnd = pwnd->spwndOwner) ;
  1329. if (pwnd == NULL)
  1330. return FALSE;
  1331. }
  1332. /*
  1333. * If the destroying window is IME or UI window, do nothing
  1334. */
  1335. pwnd = pwndDestroy;
  1336. while (pwnd != NULL) {
  1337. if (TestCF(pwnd, CFIME) ||
  1338. pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])
  1339. return FALSE;
  1340. pwnd = pwnd->spwndOwner;
  1341. }
  1342. ImeSetFutureOwner(pwndDefaultIme, pwndDestroy);
  1343. /*
  1344. * If new owner is lower z-order than IME class window,
  1345. * we need to check topmost to change z-order.
  1346. */
  1347. pwnd = pwndDefaultIme->spwndOwner;
  1348. while (pwnd != NULL && pwnd != pwndDefaultIme)
  1349. pwnd = pwnd->spwndNext;
  1350. if (pwnd == pwndDefaultIme)
  1351. ImeCheckTopmost(pwndDefaultIme);
  1352. #if DBG
  1353. CheckOwnerCirculate(pwndDefaultIme);
  1354. #endif
  1355. /*
  1356. * If ImeSetFutureOwner can not find the owner window any
  1357. * more, this IME window should be destroyed.
  1358. */
  1359. if (pwndDefaultIme->spwndOwner == NULL ||
  1360. pwndDestroy == pwndDefaultIme->spwndOwner) {
  1361. // RIPMSG1(RIP_WARNING, "ImeCanDestroyDefIME: TRUE for pwnd=%#p", pwndDestroy);
  1362. Unlock(&pwndDefaultIme->spwndOwner);
  1363. /*
  1364. * Return TRUE! Please destroy me.
  1365. */
  1366. return TRUE;
  1367. }
  1368. return FALSE;
  1369. }
  1370. /**************************************************************************\
  1371. * IsChildSameThread (IsChildSameQ)
  1372. *
  1373. * History:
  1374. * 02-Apr-1996 wkwok Ported from FE Win95 (imeclass.c)
  1375. \**************************************************************************/
  1376. BOOL IsChildSameThread(
  1377. PWND pwndParent,
  1378. PWND pwndChild)
  1379. {
  1380. PWND pwnd;
  1381. PTHREADINFO ptiChild = GETPTI(pwndChild);
  1382. for (pwnd = pwndParent->spwndChild; pwnd; pwnd = pwnd->spwndNext) {
  1383. /*
  1384. * If pwnd is not child window, we need to skip MENU window and
  1385. * IME related window.
  1386. */
  1387. if (!TestwndChild(pwnd)) {
  1388. PWND pwndOwner = pwnd;
  1389. BOOL fFoundOwner = FALSE;
  1390. /*
  1391. * Skip MENU window.
  1392. */
  1393. if (pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_MENU])
  1394. continue;
  1395. while (pwndOwner != NULL) {
  1396. /*
  1397. * CS_IME class or "IME" class windows can not be the owner of
  1398. * IME windows.
  1399. */
  1400. if (TestCF(pwndOwner, CFIME) ||
  1401. pwndOwner->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]) {
  1402. fFoundOwner = TRUE;
  1403. break;
  1404. }
  1405. pwndOwner = pwndOwner->spwndOwner;
  1406. }
  1407. if (fFoundOwner)
  1408. continue;
  1409. }
  1410. /*
  1411. * We need to skip pwndChild.
  1412. */
  1413. if (pwnd == pwndChild)
  1414. continue;
  1415. /*
  1416. * pwnd and pwndChild are on same thread?
  1417. */
  1418. if (GETPTI(pwnd) == ptiChild) {
  1419. PWND pwndT = pwnd;
  1420. BOOL fFoundImeWnd = FALSE;
  1421. /*
  1422. * Check again. If hwndT is children or ownee of
  1423. * IME related window, skip it.
  1424. */
  1425. if (TestwndChild(pwndT)) {
  1426. for (; TestwndChild(pwndT) && GETPTI(pwndT) == ptiChild;
  1427. pwndT = pwndT->spwndParent) {
  1428. if (TestCF(pwndT, CFIME) ||
  1429. pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])
  1430. fFoundImeWnd = TRUE;
  1431. }
  1432. }
  1433. if (!TestwndChild(pwndT)) {
  1434. for (; pwndT != NULL && GETPTI(pwndT) == ptiChild;
  1435. pwndT = pwndT->spwndOwner) {
  1436. if (TestCF(pwndT, CFIME) ||
  1437. pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])
  1438. fFoundImeWnd = TRUE;
  1439. }
  1440. }
  1441. if (!fFoundImeWnd)
  1442. return TRUE;
  1443. }
  1444. }
  1445. return FALSE;
  1446. }
  1447. /**************************************************************************\
  1448. * ImeCanDestroyDefIMEforChild
  1449. *
  1450. * History:
  1451. * 02-Apr-1996 wkwok Ported from FE Win95 (imeclass.c)
  1452. \**************************************************************************/
  1453. BOOL ImeCanDestroyDefIMEforChild(
  1454. PWND pwndDefaultIme,
  1455. PWND pwndDestroy)
  1456. {
  1457. PWND pwnd;
  1458. PIMEUI pimeui;
  1459. pimeui = ((PIMEWND)pwndDefaultIme)->pimeui;
  1460. /*
  1461. * If this window is not for Child Thread.....
  1462. */
  1463. if (pimeui == NULL || (LONG_PTR)pimeui == (LONG_PTR)-1)
  1464. return FALSE;
  1465. try {
  1466. if (!ProbeAndReadStructure(pimeui, IMEUI).fChildThreadDef) {
  1467. return FALSE;
  1468. }
  1469. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  1470. }
  1471. /*
  1472. * If parent belongs to different thread,
  1473. * we don't need to check any more...
  1474. */
  1475. if (pwndDestroy->spwndParent == NULL ||
  1476. GETPTI(pwndDestroy) == GETPTI(pwndDestroy->spwndParent))
  1477. return FALSE;
  1478. pwnd = pwndDestroy;
  1479. while (pwnd != NULL && pwnd != PWNDDESKTOP(pwnd)) {
  1480. if (IsChildSameThread(pwnd->spwndParent, pwndDestroy))
  1481. return FALSE;
  1482. pwnd = pwnd->spwndParent;
  1483. }
  1484. /*
  1485. * We could not find any other window created by GETPTI(pwndDestroy).
  1486. * Let's destroy the default IME window of this Q.
  1487. */
  1488. return TRUE;
  1489. }
  1490. /**************************************************************************\
  1491. * ImeCheckTopmost
  1492. *
  1493. * History:
  1494. * 02-Apr-1996 wkwok Ported from FE Win95 (imeclass.c)
  1495. \**************************************************************************/
  1496. VOID ImeCheckTopmost(
  1497. PWND pwndIme)
  1498. {
  1499. if (pwndIme->spwndOwner) {
  1500. PWND pwndInsertBeforeThis;
  1501. /*
  1502. * The ime window have to be same topmost tyle with the owner window.
  1503. * If the Q of this window is not foreground Q, we don't need to
  1504. * forground the IME window.
  1505. * But the topmost attribute of owner was changed, this IME window
  1506. * should be re-calced.
  1507. */
  1508. if (GETPTI(pwndIme) == gptiForeground) {
  1509. pwndInsertBeforeThis = NULL;
  1510. } else {
  1511. pwndInsertBeforeThis = pwndIme->spwndOwner;
  1512. }
  1513. ImeSetTopmost(pwndIme, TestWF(pwndIme->spwndOwner, WEFTOPMOST) != 0, pwndInsertBeforeThis);
  1514. }
  1515. }
  1516. /**************************************************************************\
  1517. * ImeSetOwnerWindow
  1518. *
  1519. * Before re-owning the IME window, several checks must be done on the new
  1520. * owner. Of great importance, the new owner cannot be an IME window itself,
  1521. * must be a top level window and there can never be an ownership cycle or
  1522. * it would throw win32k into an un-recoverable spin.
  1523. *
  1524. * History:
  1525. * 17-July-2001 Mohamed Created.
  1526. \**************************************************************************/
  1527. VOID ImeSetOwnerWindow(
  1528. IN PWND pwndIme,
  1529. IN PWND pwndNewOwner)
  1530. {
  1531. PWND pwndTopLevel;
  1532. PWND pwndT;
  1533. if (TestCF(pwndNewOwner, CFIME) || pwndNewOwner->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]) {
  1534. RIPMSG1(RIP_WARNING, "New owner window (pwnd=%p) should not be an IME/UI window!!", pwndNewOwner);
  1535. return;
  1536. }
  1537. /*
  1538. * Child window cannot be an owner window. Therefore, we get that window's top
  1539. * level parent.
  1540. */
  1541. pwndTopLevel = pwndT = GetTopLevelWindow(pwndNewOwner);
  1542. /*
  1543. * To prevent an IME window from becoming the owner of another IME window.
  1544. */
  1545. while (pwndT != NULL) {
  1546. if (pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]) {
  1547. RIPMSG1(RIP_WARNING, "The new owner (pwnd=%p) of an IME window should not itself be an IME window!!", pwndT);
  1548. pwndTopLevel = NULL;
  1549. break;
  1550. }
  1551. pwndT = pwndT->spwndOwner;
  1552. }
  1553. UserAssert(pwndIme->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]);
  1554. UserAssert(pwndTopLevel == NULL || !TestCF(pwndTopLevel, CFIME));
  1555. Lock(&pwndIme->spwndOwner, pwndTopLevel);
  1556. }
  1557. /**************************************************************************\
  1558. * ImeSetFutureOwner
  1559. *
  1560. * History:
  1561. * 02-Apr-1996 wkwok Ported from FE Win95 (imeclass.c)
  1562. \**************************************************************************/
  1563. VOID ImeSetFutureOwner(
  1564. PWND pwndIme,
  1565. PWND pwndOrgOwner)
  1566. {
  1567. PWND pwnd, pwndOwner;
  1568. PTHREADINFO ptiImeWnd = GETPTI(pwndIme);
  1569. if (pwndOrgOwner == NULL || TestWF(pwndOrgOwner, WFCHILD))
  1570. return;
  1571. pwnd = pwndOrgOwner;
  1572. /*
  1573. * Get top of owner created by the same thread.
  1574. */
  1575. while ((pwndOwner = pwnd->spwndOwner) != NULL &&
  1576. GETPTI(pwndOwner) == ptiImeWnd)
  1577. pwnd = pwndOwner;
  1578. /*
  1579. * Bottom window can not be the owner of IME window easily...
  1580. */
  1581. if (TestWF(pwnd, WFBOTTOMMOST) && !TestWF(pwndOrgOwner, WFBOTTOMMOST))
  1582. pwnd = pwndOrgOwner;
  1583. /*
  1584. * CS_IME class or "IME" class windows can not be the owner of
  1585. * IME windows.
  1586. */
  1587. if (TestCF(pwnd, CFIME) ||
  1588. pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])
  1589. pwnd = pwndOrgOwner;
  1590. /*
  1591. * If hwndOrgOwner is a top of owner, we start to search
  1592. * another top owner window in same queue.
  1593. */
  1594. if (pwndOrgOwner == pwnd && pwnd->spwndParent != NULL) {
  1595. PWND pwndT;
  1596. for (pwndT = pwnd->spwndParent->spwndChild;
  1597. pwndT != NULL; pwndT = pwndT->spwndNext) {
  1598. if (GETPTI(pwnd) != GETPTI(pwndT))
  1599. continue;
  1600. if (pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_MENU])
  1601. continue;
  1602. /*
  1603. * CS_IME class or "IME" class windows can not be the owner of
  1604. * IME windows.
  1605. */
  1606. if (TestCF(pwndT, CFIME) ||
  1607. pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])
  1608. continue;
  1609. // We don't like the window that is being destroyed.
  1610. if (TestWF(pwndT, WFINDESTROY))
  1611. continue;
  1612. /*
  1613. * !!!!WARNING!!!!!
  1614. * Is hwndT a good owner of hIMEwnd??
  1615. * 1. Of cource, it should no CHILD window!
  1616. * 2. If it is hwnd,.. I know it and find next!
  1617. * 3. Does hwndT have owner in the same thread?
  1618. */
  1619. if (!TestWF(pwndT, WFCHILD) && pwnd != pwndT &&
  1620. (pwndT->spwndOwner == NULL ||
  1621. GETPTI(pwndT) != GETPTI(pwndT->spwndOwner))) {
  1622. UserAssert(GETPTI(pwndIme) == GETPTI(pwndT));
  1623. pwnd = pwndT;
  1624. break;
  1625. }
  1626. }
  1627. }
  1628. UserAssert(!TestCF(pwnd, CFIME));
  1629. Lock(&pwndIme->spwndOwner, pwnd);
  1630. return;
  1631. }
  1632. /**************************************************************************\
  1633. * ImeSetTopmostChild
  1634. *
  1635. * History:
  1636. * 02-Apr-1996 wkwok Ported from FE Win95 (imeclass.c)
  1637. \**************************************************************************/
  1638. VOID ImeSetTopmostChild(
  1639. PWND pwndParent,
  1640. BOOL fMakeTopmost)
  1641. {
  1642. PWND pwnd = pwndParent->spwndChild;
  1643. while (pwnd != NULL) {
  1644. if (fMakeTopmost)
  1645. SetWF(pwnd, WEFTOPMOST);
  1646. else
  1647. ClrWF(pwnd, WEFTOPMOST);
  1648. ImeSetTopmostChild(pwnd, fMakeTopmost);
  1649. pwnd = pwnd->spwndNext;
  1650. }
  1651. return;
  1652. }
  1653. /**************************************************************************\
  1654. *
  1655. * GetLastTopMostWindowNoIME() -
  1656. *
  1657. * Get the last topmost window which is not the ownee of pwndRoot (IME window).
  1658. *
  1659. \**************************************************************************/
  1660. PWND GetLastTopMostWindowNoIME(PWND pwndRoot)
  1661. {
  1662. PWND pwndT = _GetDesktopWindow();
  1663. PWND pwndRet = NULL;
  1664. /*
  1665. * pwndRoot should not be NULL, and should be IME window.
  1666. */
  1667. UserAssert(pwndRoot && pwndRoot->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]);
  1668. if (pwndT == NULL || pwndT->spwndChild == NULL) {
  1669. #if _DBG
  1670. if (pwndT == NULL) {
  1671. RIPMSG0(RIP_WARNING, "GetLastTopMostWindowNoIME: there's no desktop window !!");
  1672. }
  1673. else {
  1674. RIPMSG0(RIP_WARNING, "GetLastTopMostWindowNoIME: there is no toplevel window !!");
  1675. }
  1676. #endif
  1677. return NULL;
  1678. }
  1679. /*
  1680. * Get the first child of the desktop window.
  1681. */
  1682. pwndT = pwndT->spwndChild;
  1683. /*
  1684. * Loop through the toplevel windows while they are topmost.
  1685. */
  1686. while (TestWF(pwndT, WEFTOPMOST)) {
  1687. PWND pwndOwner = pwndT;
  1688. BOOL fOwned = FALSE;
  1689. /*
  1690. * If pwndT is a IME related window, track the owner. If pwndRoot is not
  1691. * pwndT's owner, remember pwndT as a candidate.
  1692. */
  1693. if (TestCF(pwndT,CFIME) || (pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])) {
  1694. while (pwndOwner != NULL) {
  1695. if (pwndRoot == pwndOwner) {
  1696. fOwned = TRUE;
  1697. break;
  1698. }
  1699. pwndOwner = pwndOwner->spwndOwner;
  1700. }
  1701. }
  1702. if (!fOwned)
  1703. pwndRet = pwndT;
  1704. /*
  1705. * Next toplevel window.
  1706. */
  1707. pwndT = pwndT->spwndNext;
  1708. UserAssert(pwndT->spwndParent == _GetDesktopWindow());
  1709. }
  1710. return pwndRet;
  1711. }
  1712. #if DBG
  1713. void ImeCheckSetTopmostLink(PWND pwnd, PWND pwndInsFirst, PWND pwndIns)
  1714. {
  1715. PWND pwndDebT0 = pwndInsFirst;
  1716. BOOL fFound = FALSE;
  1717. if (pwndDebT0) {
  1718. while (pwndDebT0 && (pwndDebT0 != pwndIns)) {
  1719. if (pwndDebT0 == pwnd)
  1720. fFound = TRUE;
  1721. pwndDebT0 = pwndDebT0->spwndNext;
  1722. }
  1723. if (pwndDebT0 == NULL) {
  1724. RIPMSG3(RIP_ERROR, "pwndIns(%#p) is upper that pwndInsFirst(%#p) pwnd is (%#p)", pwndIns, pwndInsFirst, pwnd);
  1725. } else if (fFound) {
  1726. RIPMSG3(RIP_ERROR, "pwnd(%#p) is between pwndInsFirst(%#p) and pwndIns(%#p)", pwnd, pwndInsFirst, pwndIns);
  1727. }
  1728. } else if (pwndIns) {
  1729. pwndDebT0 = pwnd->spwndParent->spwndChild;
  1730. while (pwndDebT0 && (pwndDebT0 != pwndIns)) {
  1731. if (pwndDebT0 == pwnd)
  1732. fFound = TRUE;
  1733. pwndDebT0 = pwndDebT0->spwndNext;
  1734. }
  1735. if (fFound) {
  1736. RIPMSG3(RIP_ERROR, "pwnd(%#p) is between TOPLEVEL pwndInsFirst(%#p) and pwndIns(%#p)", pwnd, pwndInsFirst, pwndIns);
  1737. }
  1738. }
  1739. }
  1740. #endif
  1741. /**************************************************************************\
  1742. * ImeSetTopmost
  1743. *
  1744. * History:
  1745. * 02-Apr-1996 wkwok Ported from FE Win95 (imeclass.c)
  1746. \**************************************************************************/
  1747. VOID ImeSetTopmost(
  1748. PWND pwndRootIme,
  1749. BOOL fMakeTopmost,
  1750. PWND pwndInsertBefore)
  1751. {
  1752. PWND pwndParent = pwndRootIme->spwndParent;
  1753. PWND pwndInsert = PWND_TOP; // pwnd which should be prior to pwndRootIme.
  1754. PWND pwnd, pwndT;
  1755. PWND pwndInsertFirst;
  1756. BOOLEAN fFound;
  1757. if (pwndParent == NULL)
  1758. return;
  1759. pwnd = pwndParent->spwndChild;
  1760. if (!fMakeTopmost) {
  1761. /*
  1762. * Get the last topmost window. This should be after unlink pwndRootIme
  1763. * because pwndRootIme may be the last topmost window.
  1764. */
  1765. pwndInsert = GetLastTopMostWindowNoIME(pwndRootIme);
  1766. if (pwndInsertBefore) {
  1767. fFound = FALSE;
  1768. pwndT = pwndInsert;
  1769. while (pwndT != NULL && pwndT->spwndNext != pwndInsertBefore) {
  1770. if (pwndT == pwndRootIme)
  1771. fFound = TRUE;
  1772. pwndT = pwndT->spwndNext;
  1773. }
  1774. if (pwndT == NULL || fFound)
  1775. return;
  1776. pwndInsert = pwndT;
  1777. }
  1778. if (TestWF(pwndRootIme->spwndOwner, WFBOTTOMMOST)) {
  1779. pwndT = pwndInsert;
  1780. while (pwndT != NULL && pwndT != pwndRootIme->spwndOwner) {
  1781. if (!TestCF(pwndT, CFIME) &&
  1782. pwndT->pcls->atomClassName != gpsi->atomSysClass[ICLS_IME]) {
  1783. pwndInsert = pwndT;
  1784. }
  1785. pwndT = pwndT->spwndNext;
  1786. }
  1787. }
  1788. }
  1789. pwndInsertFirst = pwndInsert;
  1790. /*
  1791. * Enum the all toplevel windows and if the owner of the window is same as
  1792. * the owner of pwndRootIme, the window should be changed the position of
  1793. * window link.
  1794. */
  1795. while (pwnd != NULL) {
  1796. /*
  1797. * Get the next window before calling ImeSetTopmost.
  1798. * Because the next window will be changed in LinkWindow.
  1799. */
  1800. PWND pwndNext = pwnd->spwndNext;
  1801. /*
  1802. * the owner relation between IME and UI window is in same thread.
  1803. */
  1804. if (GETPTI(pwnd) != GETPTI(pwndRootIme))
  1805. goto ist_next;
  1806. /*
  1807. * pwnd have to be CS_IME class or "IME" class.
  1808. */
  1809. if (!TestCF(pwnd, CFIME) &&
  1810. pwnd->pcls->atomClassName != gpsi->atomSysClass[ICLS_IME])
  1811. goto ist_next;
  1812. /*
  1813. * If pwnd is pwndInsert, we don't need to do anything...
  1814. */
  1815. if (pwnd == pwndInsert)
  1816. goto ist_next;
  1817. pwndT = pwnd;
  1818. while (pwndT != NULL) {
  1819. if (pwndT == pwndRootIme) {
  1820. /*
  1821. * Found!!
  1822. * pwnd is the ownee of pwndRootIme.
  1823. */
  1824. UserAssert(GETPTI(pwnd) == GETPTI(pwndRootIme));
  1825. UserAssert(TestCF(pwnd,CFIME) ||
  1826. (pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]));
  1827. UserAssert(pwnd != pwndInsert);
  1828. UnlinkWindow(pwnd, pwndParent);
  1829. if (fMakeTopmost) {
  1830. if (pwndInsert != PWND_TOP)
  1831. UserAssert(TestWF(pwndInsert, WEFTOPMOST));
  1832. SetWF(pwnd, WEFTOPMOST);
  1833. }
  1834. else {
  1835. if (pwndInsert == PWND_TOP) {
  1836. /*
  1837. * In rare cases, the first toplevel window could be the one we'll look next,
  1838. * who may still have obscure topmost flag.
  1839. */
  1840. UserAssert(pwndParent->spwndChild == pwndNext || !TestWF(pwndParent->spwndChild, WEFTOPMOST));
  1841. }
  1842. else if (pwndInsert->spwndNext != NULL) {
  1843. /*
  1844. * In rare cases, pwndInsert->spwndNext could be the one we'll look next,
  1845. * who may still have obscure topmost flag.
  1846. */
  1847. UserAssert(pwndInsert->spwndNext == pwndNext || !TestWF(pwndInsert->spwndNext, WEFTOPMOST));
  1848. }
  1849. ClrWF(pwnd, WEFTOPMOST);
  1850. }
  1851. LinkWindow(pwnd, pwndInsert, pwndParent);
  1852. #if 0 // Let's see what happens if we disable this
  1853. ImeSetTopmostChild(pwnd, fMakeTopmost);
  1854. #endif
  1855. pwndInsert = pwnd;
  1856. break; // goto ist_next;
  1857. }
  1858. pwndT = pwndT->spwndOwner;
  1859. }
  1860. ist_next:
  1861. pwnd = pwndNext;
  1862. /*
  1863. * Skip the windows that were inserted before.
  1864. */
  1865. if (pwnd != NULL && pwnd == pwndInsertFirst)
  1866. pwnd = pwndInsert->spwndNext;
  1867. #if DBG
  1868. if (pwnd)
  1869. ImeCheckSetTopmostLink(pwnd, pwndInsertFirst, pwndInsert);
  1870. #endif
  1871. }
  1872. }
  1873. /**************************************************************************\
  1874. * ProbeAndCaptureSoftKbdData
  1875. *
  1876. * Captures SoftKbdData that comes from user mode.
  1877. *
  1878. * 23-Apr-1996 wkwok created
  1879. \**************************************************************************/
  1880. PSOFTKBDDATA ProbeAndCaptureSoftKbdData(
  1881. PSOFTKBDDATA Source)
  1882. {
  1883. PSOFTKBDDATA Destination = NULL;
  1884. DWORD cbSize;
  1885. UINT uCount;
  1886. try {
  1887. uCount = ProbeAndReadUlong((PULONG)Source);
  1888. #if defined(_X86_)
  1889. ProbeForReadBuffer(&Source->wCode, uCount, sizeof(BYTE));
  1890. #else
  1891. ProbeForReadBuffer(&Source->wCode, uCount, sizeof(WORD));
  1892. #endif
  1893. cbSize = FIELD_OFFSET(SOFTKBDDATA, wCode[0])
  1894. + uCount * sizeof(WORD) * 256;
  1895. Destination = (PSOFTKBDDATA)UserAllocPool(cbSize, TAG_IME);
  1896. if (Destination != NULL) {
  1897. RtlCopyMemory(Destination, Source, cbSize);
  1898. } else {
  1899. ExRaiseStatus(STATUS_NO_MEMORY);
  1900. }
  1901. } except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
  1902. if (Destination != NULL) {
  1903. UserFreePool(Destination);
  1904. }
  1905. return NULL;
  1906. }
  1907. return Destination;
  1908. }
  1909. //
  1910. // ported from Win95:ctxtman.c\SetConvMode()
  1911. //
  1912. VOID SetConvMode( PTHREADINFO pti, DWORD dwConversion )
  1913. {
  1914. if ( pti->spklActive == NULL )
  1915. return;
  1916. switch ( PRIMARYLANGID(HandleToUlong(pti->spklActive->hkl)) ) {
  1917. case LANG_JAPANESE:
  1918. ClearKeyStateDown(pti->pq, VK_DBE_ALPHANUMERIC);
  1919. ClearKeyStateToggle(pti->pq, VK_DBE_ALPHANUMERIC);
  1920. ClearKeyStateDown(pti->pq, VK_DBE_KATAKANA);
  1921. ClearKeyStateToggle(pti->pq, VK_DBE_KATAKANA);
  1922. ClearKeyStateDown(pti->pq, VK_DBE_HIRAGANA);
  1923. ClearKeyStateToggle(pti->pq, VK_DBE_HIRAGANA);
  1924. if ( dwConversion & IME_CMODE_NATIVE ) {
  1925. if ( dwConversion & IME_CMODE_KATAKANA ) {
  1926. SetKeyStateDown( pti->pq, VK_DBE_KATAKANA);
  1927. SetKeyStateToggle( pti->pq, VK_DBE_KATAKANA);
  1928. } else {
  1929. SetKeyStateDown( pti->pq, VK_DBE_HIRAGANA);
  1930. SetKeyStateToggle( pti->pq, VK_DBE_HIRAGANA);
  1931. }
  1932. } else {
  1933. SetKeyStateDown( pti->pq, VK_DBE_ALPHANUMERIC);
  1934. SetKeyStateToggle( pti->pq, VK_DBE_ALPHANUMERIC);
  1935. }
  1936. if ( dwConversion & IME_CMODE_FULLSHAPE ) {
  1937. SetKeyStateDown( pti->pq, VK_DBE_DBCSCHAR);
  1938. SetKeyStateToggle( pti->pq, VK_DBE_DBCSCHAR);
  1939. ClearKeyStateDown(pti->pq, VK_DBE_SBCSCHAR);
  1940. ClearKeyStateToggle(pti->pq, VK_DBE_SBCSCHAR);
  1941. } else {
  1942. SetKeyStateDown( pti->pq, VK_DBE_SBCSCHAR);
  1943. SetKeyStateToggle( pti->pq, VK_DBE_SBCSCHAR);
  1944. ClearKeyStateDown(pti->pq, VK_DBE_DBCSCHAR);
  1945. ClearKeyStateToggle(pti->pq, VK_DBE_DBCSCHAR);
  1946. }
  1947. if ( dwConversion & IME_CMODE_ROMAN ) {
  1948. SetKeyStateDown( pti->pq, VK_DBE_ROMAN);
  1949. SetKeyStateToggle( pti->pq, VK_DBE_ROMAN);
  1950. ClearKeyStateDown(pti->pq, VK_DBE_NOROMAN);
  1951. ClearKeyStateToggle(pti->pq, VK_DBE_NOROMAN);
  1952. } else {
  1953. SetKeyStateDown( pti->pq, VK_DBE_NOROMAN);
  1954. SetKeyStateToggle( pti->pq, VK_DBE_NOROMAN);
  1955. ClearKeyStateDown(pti->pq, VK_DBE_ROMAN);
  1956. ClearKeyStateToggle(pti->pq, VK_DBE_ROMAN);
  1957. }
  1958. if ( dwConversion & IME_CMODE_CHARCODE ) {
  1959. SetKeyStateDown( pti->pq, VK_DBE_CODEINPUT);
  1960. SetKeyStateToggle( pti->pq, VK_DBE_CODEINPUT);
  1961. ClearKeyStateDown(pti->pq, VK_DBE_NOCODEINPUT);
  1962. ClearKeyStateToggle(pti->pq, VK_DBE_NOCODEINPUT);
  1963. } else {
  1964. SetKeyStateDown( pti->pq, VK_DBE_NOCODEINPUT);
  1965. SetKeyStateToggle( pti->pq, VK_DBE_NOCODEINPUT);
  1966. ClearKeyStateDown(pti->pq, VK_DBE_CODEINPUT);
  1967. ClearKeyStateToggle(pti->pq, VK_DBE_CODEINPUT);
  1968. }
  1969. break;
  1970. case LANG_KOREAN:
  1971. if ( dwConversion & IME_CMODE_NATIVE) {
  1972. SetKeyStateToggle( pti->pq, VK_HANGUL);
  1973. } else {
  1974. ClearKeyStateToggle( pti->pq, VK_HANGUL);
  1975. }
  1976. if ( dwConversion & IME_CMODE_FULLSHAPE ) {
  1977. SetKeyStateToggle( pti->pq, VK_JUNJA);
  1978. } else {
  1979. ClearKeyStateToggle( pti->pq, VK_JUNJA);
  1980. }
  1981. if ( dwConversion & IME_CMODE_HANJACONVERT ) {
  1982. SetKeyStateToggle( pti->pq, VK_HANJA);
  1983. } else {
  1984. ClearKeyStateToggle( pti->pq, VK_HANJA);
  1985. }
  1986. break;
  1987. default:
  1988. break;
  1989. }
  1990. return;
  1991. }
  1992. //
  1993. // called by IMM32 client when:
  1994. //
  1995. // input focus is switched
  1996. // or IME open status is changed
  1997. // or IME conversion status is changed
  1998. //
  1999. //
  2000. VOID xxxNotifyIMEStatus(
  2001. IN PWND pwnd,
  2002. IN DWORD dwOpen,
  2003. IN DWORD dwConversion )
  2004. {
  2005. PTHREADINFO pti;
  2006. CheckLock(pwnd);
  2007. if ( (pti = GETPTI(pwnd)) != NULL && gptiForeground != NULL ) {
  2008. if ( pti == gptiForeground || pti->pq == gptiForeground->pq ) {
  2009. if ( gHimcFocus != pwnd->hImc ||
  2010. gdwIMEOpenStatus != dwOpen ||
  2011. gdwIMEConversionStatus != dwConversion ) {
  2012. //
  2013. // save the new status
  2014. //
  2015. gHimcFocus = pwnd->hImc;
  2016. if ( gHimcFocus != (HIMC)NULL ) {
  2017. RIPMSG2(RIP_VERBOSE, "xxxNotifyIMEStatus: newOpen=%x newConv=%x",
  2018. dwOpen, dwConversion);
  2019. gdwIMEOpenStatus = dwOpen;
  2020. gdwIMEConversionStatus = dwConversion;
  2021. //
  2022. // set keyboard states that are related to IME conversion status
  2023. //
  2024. SetConvMode(pti, dwOpen ? dwConversion : 0);
  2025. }
  2026. //
  2027. // notify shell the IME status change
  2028. //
  2029. // Implementation note: [takaok 9/5/96]
  2030. //
  2031. // Using HSHELL_LANGUAGE is not the best way to inform shell
  2032. // IME status change because we didn't change the keyboard layout.
  2033. // ( The spec says HSHELL_LANGUAGE is for keyboard layout change.
  2034. // Also passing window handle as WPARAM is not documented )
  2035. //
  2036. // This is same as what Win95 does. I won't change this for now
  2037. // because in the future shell will be developed by a different
  2038. // group in MS.
  2039. //
  2040. // Currently only Korean Windows is interested in getting
  2041. // the conversion status change.
  2042. //
  2043. if (IsHooked(pti, WHF_SHELL)) {
  2044. HKL hkl = NULL;
  2045. if (pti->spklActive != NULL) {
  2046. hkl = pti->spklActive->hkl;
  2047. }
  2048. xxxCallHook(HSHELL_LANGUAGE, (WPARAM)HWq(pwnd), (LPARAM)hkl, WH_SHELL);
  2049. }
  2050. //
  2051. // notify keyboard driver
  2052. //
  2053. NlsKbdSendIMENotification(dwOpen,dwConversion);
  2054. }
  2055. }
  2056. }
  2057. }
  2058. //---------------------------------------------------------------------------
  2059. //
  2060. // xxxCheckImeShowStatus() -
  2061. //
  2062. // Only one Status Window should be shown in the System.
  2063. // This functsion enums all IME window and check the show status of them.
  2064. //
  2065. // If pti is NULL, check all toplevel windows regardless their owners.
  2066. // If pti is not NULL, check only windows belong to the thread.
  2067. //
  2068. //----------------------------------------------------------------------------
  2069. BOOL xxxCheckImeShowStatus(PWND pwndIme, PTHREADINFO pti)
  2070. {
  2071. PBWL pbwl;
  2072. PHWND phwnd;
  2073. BOOL fRet = FALSE;
  2074. PTHREADINFO ptiCurrent = PtiCurrent();
  2075. if (TestWF(pwndIme, WFINDESTROY)) {
  2076. return FALSE;
  2077. }
  2078. // Parent window of IME window should be the desktop window
  2079. UserAssert(pwndIme);
  2080. UserAssert(pwndIme->spwndParent == GETPTI(pwndIme)->pDeskInfo->spwnd);
  2081. pbwl = BuildHwndList(pwndIme->spwndParent->spwndChild, BWL_ENUMLIST, NULL);
  2082. if (pbwl != NULL) {
  2083. fRet = TRUE;
  2084. for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) {
  2085. PWND pwndT = RevalidateHwnd(*phwnd);
  2086. // If pwndT is the current active IME window, we should skip it
  2087. // since it's the only one window allowed to show status, and
  2088. // we've already taken care of it.
  2089. if (pwndT == NULL || (/*pwndIme && */pwndIme == pwndT)) { // Can skip pwndIme != NULL test
  2090. continue;
  2091. }
  2092. // We are going to touch IME windows only
  2093. if (pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME] &&
  2094. !TestWF(pwndT, WFINDESTROY)) {
  2095. PIMEUI pimeui = ((PIMEWND)pwndT)->pimeui;
  2096. if (pimeui == NULL || pimeui == (PIMEUI)-1) {
  2097. continue;
  2098. }
  2099. if (pti == NULL || pti == GETPTI(pwndT)) {
  2100. BOOLEAN fAttached = FALSE;
  2101. PWND pwndIMC;
  2102. // If pwndT is not a window of the current process, we have to138163
  2103. // attach the process to get access to pimeui.
  2104. if (GETPTI(pwndT)->ppi != ptiCurrent->ppi) {
  2105. RIPMSG0(RIP_VERBOSE, "Attaching process in xxxCheckImeShowStatus");
  2106. KeAttachProcess(PsGetProcessPcb(GETPTI(pwndT)->ppi->Process));
  2107. fAttached = TRUE;
  2108. }
  2109. try {
  2110. if (ProbeAndReadStructure(pimeui, IMEUI).fShowStatus) {
  2111. if (pti == NULL) {
  2112. RIPMSG0(RIP_VERBOSE, "xxxCheckImeShowStatus: the status window is shown !!");
  2113. }
  2114. if ((pwndIMC = RevalidateHwnd(pimeui->hwndIMC)) != NULL) {
  2115. pimeui->fShowStatus = FALSE;
  2116. }
  2117. } else {
  2118. pwndIMC = NULL;
  2119. }
  2120. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  2121. pwndIMC = NULL;
  2122. }
  2123. if (fAttached) {
  2124. KeDetachProcess();
  2125. }
  2126. if (pwndIMC && GETPTI(pwndIMC) && !(GETPTI(pwndIMC)->TIF_flags & TIF_INCLEANUP)) {
  2127. TL tl;
  2128. ThreadLockAlways(pwndIMC, &tl);
  2129. RIPMSG1(RIP_VERBOSE, "Sending WM_IME_NOTIFY to %#p, IMN_CLOSESTATUSWINDOW", pwndIMC);
  2130. xxxSendMessage(pwndIMC, WM_IME_NOTIFY, IMN_CLOSESTATUSWINDOW, 0L);
  2131. ThreadUnlock(&tl);
  2132. }
  2133. }
  2134. }
  2135. }
  2136. FreeHwndList(pbwl);
  2137. }
  2138. return fRet;
  2139. }
  2140. LRESULT xxxSendMessageToUI(
  2141. PTHREADINFO ptiIme,
  2142. PIMEUI pimeui,
  2143. UINT message,
  2144. WPARAM wParam,
  2145. LPARAM lParam)
  2146. {
  2147. PWND pwndUI;
  2148. LRESULT lRet = 0L;
  2149. TL tl;
  2150. BOOL fAttached = FALSE;
  2151. CheckCritIn();
  2152. if (ptiIme != PtiCurrent()) {
  2153. fAttached = TRUE;
  2154. KeAttachProcess(PsGetProcessPcb(ptiIme->ppi->Process));
  2155. }
  2156. try {
  2157. pwndUI = RevalidateHwnd(ProbeAndReadStructure(pimeui, IMEUI).hwndUI);
  2158. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  2159. pwndUI = NULL;
  2160. }
  2161. if (pwndUI != NULL){
  2162. try {
  2163. ProbeAndReadUlong((PULONG)&pimeui->nCntInIMEProc);
  2164. InterlockedIncrement(&pimeui->nCntInIMEProc); // Mark to avoid recursion.
  2165. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  2166. goto skip_it;
  2167. }
  2168. if (fAttached) {
  2169. KeDetachProcess();
  2170. }
  2171. ThreadLockAlways(pwndUI, &tl);
  2172. RIPMSG3(RIP_VERBOSE, "Sending message UI pwnd=%#p, msg:%x to wParam=%#p", pwndUI, message, wParam);
  2173. lRet = xxxSendMessage(pwndUI, message, wParam, lParam);
  2174. ThreadUnlock(&tl);
  2175. if (fAttached) {
  2176. KeAttachProcess(PsGetProcessPcb(ptiIme->ppi->Process));
  2177. }
  2178. try {
  2179. InterlockedDecrement(&pimeui->nCntInIMEProc); // Mark to avoid recursion.
  2180. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  2181. }
  2182. }
  2183. skip_it:
  2184. if (fAttached) {
  2185. KeDetachProcess();
  2186. }
  2187. return lRet;
  2188. }
  2189. VOID xxxSendOpenStatusNotify(
  2190. PTHREADINFO ptiIme,
  2191. PIMEUI pimeui,
  2192. PWND pwndApp,
  2193. BOOL fOpen)
  2194. {
  2195. WPARAM wParam = fOpen ? IMN_OPENSTATUSWINDOW : IMN_CLOSESTATUSWINDOW;
  2196. UserAssert(GETPTI(pwndApp));
  2197. if (LOWORD(GETPTI(pwndApp)->dwExpWinVer >= VER40) && pwndApp->hImc != NULL) {
  2198. TL tl;
  2199. ThreadLockAlways(pwndApp, &tl);
  2200. RIPMSG2(RIP_VERBOSE, "Sending %s to pwnd=%#p",
  2201. fOpen ? "IMN_OPENSTATUSWINDOW" : "IMN_CLOSESTATUSWINDOW",
  2202. pwndApp);
  2203. xxxSendMessage(pwndApp, WM_IME_NOTIFY, wParam, 0L);
  2204. ThreadUnlock(&tl);
  2205. }
  2206. else {
  2207. xxxSendMessageToUI(ptiIme, pimeui, WM_IME_NOTIFY, wParam, 0L);
  2208. }
  2209. return;
  2210. }
  2211. VOID xxxNotifyImeShowStatus(PWND pwndIme)
  2212. {
  2213. PIMEUI pimeui;
  2214. BOOL fShow;
  2215. PWND pwnd;
  2216. PTHREADINFO ptiIme, ptiCurrent;
  2217. BOOL fSendOpenStatusNotify = FALSE;
  2218. if (!IS_IME_ENABLED() || TestWF(pwndIme, WFINDESTROY)) {
  2219. RIPMSG0(RIP_WARNING, "IME is not enabled or in destroy.");
  2220. return;
  2221. }
  2222. ptiCurrent = PtiCurrent();
  2223. ptiIme = GETPTI(pwndIme);
  2224. if (ptiIme != ptiCurrent) {
  2225. RIPMSG1(RIP_VERBOSE, "Attaching pti=%#p", ptiIme);
  2226. KeAttachProcess(PsGetProcessPcb(GETPTI(pwndIme)->ppi->Process));
  2227. }
  2228. try {
  2229. pimeui = ((PIMEWND)pwndIme)->pimeui;
  2230. fShow = gfIMEShowStatus && ProbeAndReadStructure(pimeui, IMEUI).fCtrlShowStatus;
  2231. pwnd = RevalidateHwnd(pimeui->hwndIMC);
  2232. if (pwnd != NULL || (pwnd = GETPTI(pwndIme)->pq->spwndFocus) != NULL) {
  2233. RIPMSG0(RIP_VERBOSE, "Setting new show status.");
  2234. fSendOpenStatusNotify = TRUE;
  2235. pimeui->fShowStatus = fShow;
  2236. }
  2237. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  2238. if (ptiIme != ptiCurrent) {
  2239. KeDetachProcess();
  2240. }
  2241. return;
  2242. }
  2243. if (ptiIme != ptiCurrent) {
  2244. KeDetachProcess();
  2245. }
  2246. if (fSendOpenStatusNotify) {
  2247. RIPMSG1(RIP_VERBOSE, "Sending OpenStatus fShow=%d", fShow);
  2248. xxxSendOpenStatusNotify(ptiIme, pimeui, pwnd, fShow);
  2249. }
  2250. // Check the show status of all IME windows in the system.
  2251. if (!TestWF(pwndIme, WFINDESTROY)) {
  2252. xxxCheckImeShowStatus(pwndIme, NULL);
  2253. }
  2254. }
  2255. /***************************************************************************\
  2256. * xxxSetIMEShowStatus() -
  2257. *
  2258. * Set IME Status windows' show status. Called from SystemParametersInfo()
  2259. * handler.
  2260. *
  2261. \***************************************************************************/
  2262. BOOL xxxSetIMEShowStatus(IN BOOL fShow)
  2263. {
  2264. CheckCritIn();
  2265. UserAssert(fShow == FALSE || fShow == TRUE);
  2266. if (gfIMEShowStatus == fShow) {
  2267. return TRUE;
  2268. }
  2269. if (gfIMEShowStatus == IMESHOWSTATUS_NOTINITIALIZED) {
  2270. /*
  2271. * Called for the first time after logon.
  2272. * No need to write the value to the registry.
  2273. */
  2274. gfIMEShowStatus = fShow;
  2275. }
  2276. else {
  2277. /*
  2278. * We need to save the new fShow status to the registry.
  2279. */
  2280. TL tlName;
  2281. PUNICODE_STRING pProfileUserName;
  2282. BOOL fOK = FALSE;
  2283. pProfileUserName = CreateProfileUserName(&tlName);
  2284. if (pProfileUserName) {
  2285. UserAssert(pProfileUserName != NULL);
  2286. fOK = UpdateWinIniInt(pProfileUserName, PMAP_INPUTMETHOD, STR_SHOWIMESTATUS, fShow);
  2287. FreeProfileUserName(pProfileUserName, &tlName);
  2288. }
  2289. if (!fOK) {
  2290. return FALSE;
  2291. }
  2292. gfIMEShowStatus = fShow;
  2293. }
  2294. /*
  2295. * If IME is not enabled, further processing is not needed
  2296. */
  2297. if (!IS_IME_ENABLED()) {
  2298. return TRUE;
  2299. }
  2300. /*
  2301. * Let the current active IME window know the change.
  2302. */
  2303. if (gpqForeground && gpqForeground->spwndFocus) {
  2304. PTHREADINFO ptiFocus = GETPTI(gpqForeground->spwndFocus);
  2305. TL tl;
  2306. UserAssert(ptiFocus);
  2307. if (ptiFocus->spwndDefaultIme && !(ptiFocus->TIF_flags & TIF_INCLEANUP)) {
  2308. ThreadLockAlways(ptiFocus->spwndDefaultIme, &tl);
  2309. xxxNotifyImeShowStatus(ptiFocus->spwndDefaultIme);
  2310. ThreadUnlock(&tl);
  2311. }
  2312. }
  2313. return TRUE;
  2314. }
  2315. /***************************************************************************\
  2316. * xxxBroadcastImeShowStatusChange() -
  2317. *
  2318. * Let all IME windows in the desktop, including myself know about the
  2319. * status change.
  2320. * This routine does not touch the registry, assuming internat.exe updated
  2321. * the registry.
  2322. *
  2323. \***************************************************************************/
  2324. VOID xxxBroadcastImeShowStatusChange(PWND pwndIme, BOOL fShow)
  2325. {
  2326. CheckCritIn();
  2327. gfIMEShowStatus = !!fShow;
  2328. xxxNotifyImeShowStatus(pwndIme);
  2329. }
  2330. /***************************************************************************\
  2331. * xxxCheckImeShowStatusInThread() -
  2332. *
  2333. * Let all IME windows in the same thread know about the status change.
  2334. * Called from ImeSetContextHandler().
  2335. *
  2336. \***************************************************************************/
  2337. VOID xxxCheckImeShowStatusInThread(PWND pwndIme)
  2338. {
  2339. if (IS_IME_ENABLED()) {
  2340. UserAssert(pwndIme);
  2341. if (!TestWF(pwndIme, WFINDESTROY)) {
  2342. xxxCheckImeShowStatus(pwndIme, GETPTI(pwndIme));
  2343. }
  2344. }
  2345. }
  2346. BOOL _GetIMEShowStatus(VOID)
  2347. {
  2348. return gfIMEShowStatus != FALSE;
  2349. }