xref: /reactos/win32ss/user/ntuser/ime.c (revision f7d612f3)
1 /*
2  * COPYRIGHT:        See COPYING in the top level directory
3  * PROJECT:          ReactOS Win32k subsystem
4  * PURPOSE:          Input Method Editor and Input Method Manager support
5  * FILE:             win32ss/user/ntuser/ime.c
6  * PROGRAMERS:       Casper S. Hornstrup (chorns@users.sourceforge.net)
7  *                   Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
8  */
9 
10 #include <win32k.h>
11 #include <jpnvkeys.h>
12 
13 DBG_DEFAULT_CHANNEL(UserMisc);
14 
15 #define INVALID_THREAD_ID  ((ULONG)-1)
16 #define INVALID_HOTKEY     ((UINT)-1)
17 #define MOD_KEYS           (MOD_CONTROL | MOD_SHIFT | MOD_ALT | MOD_WIN)
18 #define MOD_LEFT_RIGHT     (MOD_LEFT | MOD_RIGHT)
19 
20 #define LANGID_CHINESE_SIMPLIFIED   MAKELANGID(LANG_CHINESE,  SUBLANG_CHINESE_SIMPLIFIED)
21 #define LANGID_JAPANESE             MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT)
22 #define LANGID_KOREAN               MAKELANGID(LANG_KOREAN,   SUBLANG_KOREAN)
23 #define LANGID_CHINESE_TRADITIONAL  MAKELANGID(LANG_CHINESE,  SUBLANG_CHINESE_TRADITIONAL)
24 #define LANGID_NEUTRAL              MAKELANGID(LANG_NEUTRAL,  SUBLANG_NEUTRAL)
25 
26 HIMC ghIMC = NULL;
27 BOOL gfImeOpen = (BOOL)-1;
28 DWORD gdwImeConversion = (DWORD)-1;
29 BOOL gfIMEShowStatus = (BOOL)-1;
30 
31 typedef struct tagIMEHOTKEY
32 {
33     struct tagIMEHOTKEY *pNext;
34     DWORD  dwHotKeyId;
35     UINT   uVirtualKey;
36     UINT   uModifiers;
37     HKL    hKL;
38 } IMEHOTKEY, *PIMEHOTKEY;
39 
40 PIMEHOTKEY gpImeHotKeyList = NULL; // Win: gpImeHotKeyListHeader
41 LCID glcidSystem = 0; // Win: glcidSystem
42 
43 // Win: GetAppImeCompatFlags
44 DWORD FASTCALL IntGetImeCompatFlags(PTHREADINFO pti)
45 {
46     if (!pti)
47         pti = PsGetCurrentThreadWin32Thread();
48 
49     return pti->ppi->dwImeCompatFlags;
50 }
51 
52 // Win: GetLangIdMatchLevel
53 UINT FASTCALL IntGetImeHotKeyLanguageScore(HKL hKL, LANGID HotKeyLangId)
54 {
55     LCID lcid;
56 
57     if (HotKeyLangId == LANGID_NEUTRAL || HotKeyLangId == LOWORD(hKL))
58         return 3;
59 
60     _SEH2_TRY
61     {
62         lcid = NtCurrentTeb()->CurrentLocale;
63     }
64     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
65     {
66         ERR("%p\n", NtCurrentTeb());
67         lcid = MAKELCID(LANGID_NEUTRAL, SORT_DEFAULT);
68     }
69     _SEH2_END;
70 
71     if (HotKeyLangId == LANGIDFROMLCID(lcid))
72         return 2;
73 
74     if (glcidSystem == 0)
75         ZwQueryDefaultLocale(FALSE, &glcidSystem);
76 
77     if (HotKeyLangId == LANGIDFROMLCID(glcidSystem))
78         return 1;
79 
80     return 0;
81 }
82 
83 // Win: GetActiveHKL
84 HKL FASTCALL IntGetActiveKeyboardLayout(VOID)
85 {
86     PTHREADINFO pti;
87 
88     if (gpqForeground && gpqForeground->spwndActive)
89     {
90         pti = gpqForeground->spwndActive->head.pti;
91         if (pti && pti->KeyboardLayout)
92             return pti->KeyboardLayout->hkl;
93     }
94 
95     return UserGetKeyboardLayout(0);
96 }
97 
98 // Win: GetHotKeyLangID
99 static LANGID FASTCALL IntGetImeHotKeyLangId(DWORD dwHotKeyId)
100 {
101 #define IME_CHOTKEY 0x10
102 #define IME_JHOTKEY 0x30
103 #define IME_KHOTKEY 0x50
104 #define IME_THOTKEY 0x70
105 #define IME_XHOTKEY 0x90
106     static const LANGID s_array[] =
107     {
108         /* 0x00 */ (WORD)-1,
109         /* 0x10 */ LANGID_CHINESE_SIMPLIFIED,
110         /* 0x20 */ LANGID_CHINESE_SIMPLIFIED,
111         /* 0x30 */ LANGID_JAPANESE,
112         /* 0x40 */ LANGID_JAPANESE,
113         /* 0x50 */ LANGID_KOREAN,
114         /* 0x60 */ LANGID_KOREAN,
115         /* 0x70 */ LANGID_CHINESE_TRADITIONAL,
116         /* 0x80 */ LANGID_CHINESE_TRADITIONAL
117     };
118 
119     if (IME_CHOTKEY <= dwHotKeyId && dwHotKeyId < IME_XHOTKEY)
120         return s_array[(dwHotKeyId & 0xF0) >> 4];
121     return LANGID_NEUTRAL;
122 }
123 
124 // Win: AddImeHotKey
125 static VOID FASTCALL IntAddImeHotKey(PIMEHOTKEY *ppList, PIMEHOTKEY pHotKey)
126 {
127     PIMEHOTKEY pNode;
128 
129     if (!*ppList)
130     {
131         *ppList = pHotKey;
132         return;
133     }
134 
135     for (pNode = *ppList; pNode; pNode = pNode->pNext)
136     {
137         if (!pNode->pNext)
138         {
139             pNode->pNext = pHotKey;
140             return;
141         }
142     }
143 }
144 
145 // Win: FindImeHotKeyByID
146 static PIMEHOTKEY FASTCALL IntGetImeHotKeyById(PIMEHOTKEY pList, DWORD dwHotKeyId)
147 {
148     PIMEHOTKEY pNode;
149     for (pNode = pList; pNode; pNode = pNode->pNext)
150     {
151         if (pNode->dwHotKeyId == dwHotKeyId)
152             return pNode;
153     }
154     return NULL;
155 }
156 
157 // Win: FindImeHotKeyByKeyWithLang
158 static PIMEHOTKEY APIENTRY
159 IntGetImeHotKeyByKeyAndLang(PIMEHOTKEY pList, UINT uModKeys, UINT uLeftRight,
160                             UINT uVirtualKey, LANGID TargetLangId)
161 {
162     PIMEHOTKEY pNode;
163     LANGID LangID;
164     UINT uModifiers;
165 
166     for (pNode = pList; pNode; pNode = pNode->pNext)
167     {
168         if (pNode->uVirtualKey != uVirtualKey)
169             continue;
170 
171         LangID = IntGetImeHotKeyLangId(pNode->dwHotKeyId);
172         if (LangID != TargetLangId && LangID != 0)
173             continue;
174 
175         uModifiers = pNode->uModifiers;
176         if (uModifiers & MOD_IGNORE_ALL_MODIFIER)
177             return pNode;
178 
179         if ((uModifiers & MOD_KEYS) != uModKeys)
180             continue;
181 
182         if ((uModifiers & uLeftRight) || (uModifiers & MOD_LEFT_RIGHT) == uLeftRight)
183             return pNode;
184     }
185 
186     return NULL;
187 }
188 
189 // Win: DeleteImeHotKey
190 static VOID FASTCALL IntDeleteImeHotKey(PIMEHOTKEY *ppList, PIMEHOTKEY pHotKey)
191 {
192     PIMEHOTKEY pNode;
193 
194     if (*ppList == pHotKey)
195     {
196         *ppList = pHotKey->pNext;
197         ExFreePoolWithTag(pHotKey, USERTAG_IMEHOTKEY);
198         return;
199     }
200 
201     for (pNode = *ppList; pNode; pNode = pNode->pNext)
202     {
203         if (pNode->pNext == pHotKey)
204         {
205             pNode->pNext = pHotKey->pNext;
206             ExFreePoolWithTag(pHotKey, USERTAG_IMEHOTKEY);
207             return;
208         }
209     }
210 }
211 
212 // Win: FindImeHotKeyByKey
213 PIMEHOTKEY
214 IntGetImeHotKeyByKey(PIMEHOTKEY pList, UINT uModKeys, UINT uLeftRight, UINT uVirtualKey)
215 {
216     PIMEHOTKEY pNode, ret = NULL;
217     PTHREADINFO pti = GetW32ThreadInfo();
218     LANGID LangId;
219     HKL hKL = IntGetActiveKeyboardLayout();
220     BOOL fKorean = (PRIMARYLANGID(LOWORD(hKL)) == LANG_KOREAN);
221     UINT nScore, nMaxScore = 0;
222 
223     for (pNode = pList; pNode; pNode = pNode->pNext)
224     {
225         if (pNode->uVirtualKey != uVirtualKey)
226             continue;
227 
228         if ((pNode->uModifiers & MOD_IGNORE_ALL_MODIFIER))
229         {
230             ;
231         }
232         else if ((pNode->uModifiers & MOD_KEYS) != uModKeys)
233         {
234             continue;
235         }
236         else if ((pNode->uModifiers & uLeftRight) ||
237                  (pNode->uModifiers & MOD_LEFT_RIGHT) == uLeftRight)
238         {
239             ;
240         }
241         else
242         {
243             continue;
244         }
245 
246         LangId = IntGetImeHotKeyLangId(pNode->dwHotKeyId);
247         nScore = IntGetImeHotKeyLanguageScore(hKL, LangId);
248         if (nScore >= 3)
249             return pNode;
250 
251         if (fKorean)
252             continue;
253 
254         if (nScore == 0)
255         {
256             if (pNode->dwHotKeyId == IME_CHOTKEY_IME_NONIME_TOGGLE ||
257                 pNode->dwHotKeyId == IME_THOTKEY_IME_NONIME_TOGGLE)
258             {
259                 if (LOWORD(pti->hklPrev) == LangId)
260                     return pNode;
261             }
262         }
263 
264         if (nMaxScore < nScore)
265         {
266             nMaxScore = nScore;
267             ret = pNode;
268         }
269     }
270 
271     return ret;
272 }
273 
274 // Win: CheckImeHotKey
275 PIMEHOTKEY IntCheckImeHotKey(PUSER_MESSAGE_QUEUE MessageQueue, UINT uVirtualKey, LPARAM lParam)
276 {
277     PIMEHOTKEY pHotKey;
278     UINT uModifiers;
279     BOOL bKeyUp = (lParam & 0x80000000);
280     const BYTE *KeyState = MessageQueue->afKeyState;
281     static UINT s_uKeyUpVKey = 0;
282 
283     if (bKeyUp)
284     {
285         if (s_uKeyUpVKey != uVirtualKey)
286         {
287             s_uKeyUpVKey = 0;
288             return NULL;
289         }
290 
291         s_uKeyUpVKey = 0;
292     }
293 
294     uModifiers = 0;
295     if (IS_KEY_DOWN(KeyState, VK_LSHIFT))   uModifiers |= (MOD_SHIFT | MOD_LEFT);
296     if (IS_KEY_DOWN(KeyState, VK_RSHIFT))   uModifiers |= (MOD_SHIFT | MOD_RIGHT);
297     if (IS_KEY_DOWN(KeyState, VK_LCONTROL)) uModifiers |= (MOD_CONTROL | MOD_LEFT);
298     if (IS_KEY_DOWN(KeyState, VK_RCONTROL)) uModifiers |= (MOD_CONTROL | MOD_RIGHT);
299     if (IS_KEY_DOWN(KeyState, VK_LMENU))    uModifiers |= (MOD_ALT | MOD_LEFT);
300     if (IS_KEY_DOWN(KeyState, VK_RMENU))    uModifiers |= (MOD_ALT | MOD_RIGHT);
301 
302     pHotKey = IntGetImeHotKeyByKey(gpImeHotKeyList,
303                                    (uModifiers & MOD_KEYS),
304                                    (uModifiers & MOD_LEFT_RIGHT),
305                                    uVirtualKey);
306     if (pHotKey)
307     {
308         if (bKeyUp)
309         {
310             if (pHotKey->uModifiers & MOD_ON_KEYUP)
311                 return pHotKey;
312         }
313         else
314         {
315             if (pHotKey->uModifiers & MOD_ON_KEYUP)
316                 s_uKeyUpVKey = uVirtualKey;
317             else
318                 return pHotKey;
319         }
320     }
321 
322     return NULL;
323 }
324 
325 // Win: FreeImeHotKeys
326 VOID FASTCALL IntFreeImeHotKeys(VOID)
327 {
328     PIMEHOTKEY pNode, pNext;
329     for (pNode = gpImeHotKeyList; pNode; pNode = pNext)
330     {
331         pNext = pNode->pNext;
332         ExFreePoolWithTag(pNode, USERTAG_IMEHOTKEY);
333     }
334     gpImeHotKeyList = NULL;
335 }
336 
337 // Win: SetImeHotKey
338 static BOOL APIENTRY
339 IntSetImeHotKey(DWORD dwHotKeyId, UINT uModifiers, UINT uVirtualKey, HKL hKL, DWORD dwAction)
340 {
341     PIMEHOTKEY pNode;
342     LANGID LangId;
343 
344     switch (dwAction)
345     {
346         case SETIMEHOTKEY_DELETE:
347             pNode = IntGetImeHotKeyById(gpImeHotKeyList, dwHotKeyId); /* Find hotkey by ID */
348             if (!pNode)
349             {
350                 ERR("dwHotKeyId: 0x%lX\n", dwHotKeyId);
351                 return FALSE;
352             }
353 
354             IntDeleteImeHotKey(&gpImeHotKeyList, pNode); /* Delete it */
355             return TRUE;
356 
357         case SETIMEHOTKEY_ADD:
358             if (LOWORD(uVirtualKey) == VK_PACKET) /* In case of VK_PACKET */
359                 return FALSE;
360 
361             LangId = IntGetImeHotKeyLangId(dwHotKeyId);
362             if (LangId == LANGID_KOREAN)
363                 return FALSE; /* Korean can't add IME hotkeys */
364 
365             /* Find hotkey by key and language */
366             pNode = IntGetImeHotKeyByKeyAndLang(gpImeHotKeyList,
367                                                 (uModifiers & MOD_KEYS),
368                                                 (uModifiers & MOD_LEFT_RIGHT),
369                                                 uVirtualKey, LangId);
370             if (pNode == NULL) /* If not found */
371                 pNode = IntGetImeHotKeyById(gpImeHotKeyList, dwHotKeyId); /* Find by ID */
372 
373             if (pNode) /* Already exists */
374             {
375                 pNode->uModifiers = uModifiers;
376                 pNode->uVirtualKey = uVirtualKey;
377                 pNode->hKL = hKL;
378                 return TRUE;
379             }
380 
381             /* Allocate new hotkey */
382             pNode = ExAllocatePoolWithTag(PagedPool, sizeof(IMEHOTKEY), USERTAG_IMEHOTKEY);
383             if (!pNode)
384                 return FALSE;
385 
386             /* Populate */
387             pNode->pNext = NULL;
388             pNode->dwHotKeyId = dwHotKeyId;
389             pNode->uModifiers = uModifiers;
390             pNode->uVirtualKey = uVirtualKey;
391             pNode->hKL = hKL;
392             IntAddImeHotKey(&gpImeHotKeyList, pNode); /* Add it */
393             return TRUE;
394 
395         case SETIMEHOTKEY_INITIALIZE:
396             IntFreeImeHotKeys(); /* Delete all the IME hotkeys */
397             return TRUE;
398 
399         default:
400             ERR("0x%lX\n", dwAction);
401             return FALSE;
402     }
403 }
404 
405 BOOL NTAPI
406 NtUserGetImeHotKey(DWORD dwHotKeyId, LPUINT lpuModifiers, LPUINT lpuVirtualKey, LPHKL lphKL)
407 {
408     PIMEHOTKEY pNode = NULL;
409 
410     UserEnterExclusive();
411 
412     _SEH2_TRY
413     {
414         ProbeForWrite(lpuModifiers, sizeof(UINT), 1);
415         ProbeForWrite(lpuVirtualKey, sizeof(UINT), 1);
416         if (lphKL)
417             ProbeForWrite(lphKL, sizeof(HKL), 1);
418     }
419     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
420     {
421         ERR("%p, %p, %p\n", lpuModifiers, lpuVirtualKey, lphKL);
422         _SEH2_YIELD(goto Quit);
423     }
424     _SEH2_END;
425 
426     pNode = IntGetImeHotKeyById(gpImeHotKeyList, dwHotKeyId);
427     if (!pNode)
428         goto Quit;
429 
430     _SEH2_TRY
431     {
432         *lpuModifiers = pNode->uModifiers;
433         *lpuVirtualKey = pNode->uVirtualKey;
434         if (lphKL)
435             *lphKL = pNode->hKL;
436     }
437     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
438     {
439         ERR("%p, %p, %p, %p\n", pNode, lpuModifiers, lpuVirtualKey, lphKL);
440         pNode = NULL;
441     }
442     _SEH2_END;
443 
444 Quit:
445     UserLeave();
446     return !!pNode;
447 }
448 
449 BOOL
450 NTAPI
451 NtUserSetImeHotKey(
452     DWORD  dwHotKeyId,
453     UINT   uModifiers,
454     UINT   uVirtualKey,
455     HKL    hKL,
456     DWORD  dwAction)
457 {
458     BOOL ret;
459     UserEnterExclusive();
460     ret = IntSetImeHotKey(dwHotKeyId, uModifiers, uVirtualKey, hKL, dwAction);
461     UserLeave();
462     return ret;
463 }
464 
465 DWORD
466 NTAPI
467 NtUserCheckImeHotKey(UINT uVirtualKey, LPARAM lParam)
468 {
469     PIMEHOTKEY pNode;
470     DWORD ret = INVALID_HOTKEY;
471 
472     UserEnterExclusive();
473 
474     if (!gpqForeground || !IS_IMM_MODE())
475         goto Quit;
476 
477     pNode = IntCheckImeHotKey(gpqForeground, uVirtualKey, lParam);
478     if (pNode)
479         ret = pNode->dwHotKeyId;
480 
481 Quit:
482     UserLeave();
483     return ret;
484 }
485 
486 // Win: GetTopLevelWindow
487 PWND FASTCALL IntGetTopLevelWindow(PWND pwnd)
488 {
489     if (!pwnd)
490         return NULL;
491 
492     while (pwnd->style & WS_CHILD)
493         pwnd = pwnd->spwndParent;
494 
495     return pwnd;
496 }
497 
498 // Win: AssociateInputContext
499 HIMC FASTCALL IntAssociateInputContext(PWND pWnd, PIMC pImc)
500 {
501     HIMC hOldImc = pWnd->hImc;
502     pWnd->hImc = (pImc ? UserHMGetHandle(pImc) : NULL);
503     return hOldImc;
504 }
505 
506 DWORD
507 NTAPI
508 NtUserSetThreadLayoutHandles(HKL hNewKL, HKL hOldKL)
509 {
510     PTHREADINFO pti;
511     PKL pOldKL, pNewKL;
512 
513     UserEnterExclusive();
514 
515     pti = GetW32ThreadInfo();
516     pOldKL = pti->KeyboardLayout;
517     if (pOldKL && pOldKL->hkl != hOldKL)
518         goto Quit;
519 
520     pNewKL = UserHklToKbl(hNewKL);
521     if (!pNewKL)
522         goto Quit;
523 
524     if (IS_IME_HKL(hNewKL) != IS_IME_HKL(hOldKL))
525         pti->hklPrev = hOldKL;
526 
527     UserAssignmentLock((PVOID*)&pti->KeyboardLayout, pNewKL);
528     pti->pClientInfo->hKL = pNewKL->hkl;
529 
530 Quit:
531     UserLeave();
532     return 0;
533 }
534 
535 // Win: BuildHimcList
536 DWORD FASTCALL UserBuildHimcList(PTHREADINFO pti, DWORD dwCount, HIMC *phList)
537 {
538     PIMC pIMC;
539     DWORD dwRealCount = 0;
540 
541     if (pti)
542     {
543         for (pIMC = pti->spDefaultImc; pIMC; pIMC = pIMC->pImcNext)
544         {
545             if (dwRealCount < dwCount)
546                 phList[dwRealCount] = UserHMGetHandle(pIMC);
547 
548             ++dwRealCount;
549         }
550     }
551     else
552     {
553         for (pti = gptiCurrent->ppi->ptiList; pti; pti = pti->ptiSibling)
554         {
555             for (pIMC = pti->spDefaultImc; pIMC; pIMC = pIMC->pImcNext)
556             {
557                 if (dwRealCount < dwCount)
558                     phList[dwRealCount] = UserHMGetHandle(pIMC);
559 
560                 ++dwRealCount;
561             }
562         }
563     }
564 
565     return dwRealCount;
566 }
567 
568 // Win: xxxImmProcessKey
569 UINT FASTCALL
570 IntImmProcessKey(PUSER_MESSAGE_QUEUE MessageQueue, PWND pWnd, UINT uMsg,
571                  WPARAM wParam, LPARAM lParam)
572 {
573     UINT uVirtualKey, ret = 0;
574     DWORD dwHotKeyId;
575     PKL pKL;
576     PIMC pIMC = NULL;
577     PIMEHOTKEY pImeHotKey;
578     HKL hKL;
579     HWND hWnd;
580 
581     ASSERT_REFS_CO(pWnd);
582 
583     switch (uMsg)
584     {
585         case WM_KEYDOWN:
586         case WM_KEYUP:
587         case WM_SYSKEYDOWN:
588         case WM_SYSKEYUP:
589             break;
590 
591         default:
592             return 0;
593     }
594 
595     hWnd = UserHMGetHandle(pWnd);
596     pKL = pWnd->head.pti->KeyboardLayout;
597     if (!pKL)
598         return 0;
599 
600     uVirtualKey = LOBYTE(wParam);
601     pImeHotKey = IntCheckImeHotKey(MessageQueue, uVirtualKey, lParam);
602     if (pImeHotKey)
603     {
604         dwHotKeyId = pImeHotKey->dwHotKeyId;
605         hKL = pImeHotKey->hKL;
606     }
607     else
608     {
609         dwHotKeyId = INVALID_HOTKEY;
610         hKL = NULL;
611     }
612 
613     if (IME_HOTKEY_DSWITCH_FIRST <= dwHotKeyId && dwHotKeyId <= IME_HOTKEY_DSWITCH_LAST)
614     {
615         if (pKL->hkl != hKL)
616         {
617             UserPostMessage(hWnd, WM_INPUTLANGCHANGEREQUEST,
618                             ((pKL->dwFontSigs & gSystemFS) ? INPUTLANGCHANGE_SYSCHARSET : 0),
619                             (LPARAM)hKL);
620         }
621 
622         if (IntGetImeCompatFlags(pWnd->head.pti) & 0x800000)
623             return 0;
624 
625         return IPHK_HOTKEY;
626     }
627 
628     if (!IS_IMM_MODE())
629         return 0;
630 
631     if (dwHotKeyId == INVALID_HOTKEY)
632     {
633         if (!pKL->piiex)
634             return 0;
635 
636         if (pWnd->hImc)
637             pIMC = UserGetObject(gHandleTable, pWnd->hImc, TYPE_INPUTCONTEXT);
638         if (!pIMC)
639             return 0;
640 
641         if ((lParam & 0x80000000) &&
642             (pKL->piiex->ImeInfo.fdwProperty & IME_PROP_IGNORE_UPKEYS))
643         {
644             return 0;
645         }
646 
647         switch (uVirtualKey)
648         {
649             case VK_DBE_CODEINPUT:
650             case VK_DBE_ENTERCONFIGMODE:
651             case VK_DBE_ENTERWORDREGISTERMODE:
652             case VK_DBE_HIRAGANA:
653             case VK_DBE_KATAKANA:
654             case VK_DBE_NOCODEINPUT:
655             case VK_DBE_NOROMAN:
656             case VK_DBE_ROMAN:
657                 break;
658 
659             default:
660             {
661                 if (uMsg == WM_SYSKEYDOWN || uMsg == WM_SYSKEYUP)
662                 {
663                     if (uVirtualKey != VK_MENU && uVirtualKey != VK_F10)
664                         return 0;
665                 }
666 
667                 if (!(pKL->piiex->ImeInfo.fdwProperty & IME_PROP_NEED_ALTKEY))
668                 {
669                     if (uVirtualKey == VK_MENU || (lParam & 0x20000000))
670                         return 0;
671                 }
672                 break;
673             }
674         }
675     }
676 
677     if (LOBYTE(uVirtualKey) == VK_PACKET)
678         uVirtualKey = MAKELONG(wParam, GetW32ThreadInfo()->wchInjected);
679 
680     ret = co_IntImmProcessKey(hWnd, pKL->hkl, uVirtualKey, lParam, dwHotKeyId);
681 
682     if (IntGetImeCompatFlags(pWnd->head.pti) & 0x800000)
683         ret &= ~IPHK_HOTKEY;
684 
685     return ret;
686 }
687 
688 NTSTATUS
689 NTAPI
690 NtUserBuildHimcList(DWORD dwThreadId, DWORD dwCount, HIMC *phList, LPDWORD pdwCount)
691 {
692     NTSTATUS ret = STATUS_UNSUCCESSFUL;
693     DWORD dwRealCount;
694     PTHREADINFO pti;
695 
696     UserEnterExclusive();
697 
698     if (!IS_IMM_MODE())
699     {
700         ERR("!IS_IMM_MODE()\n");
701         EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
702         goto Quit;
703     }
704 
705     if (dwThreadId == 0)
706     {
707         pti = gptiCurrent;
708     }
709     else if (dwThreadId == INVALID_THREAD_ID)
710     {
711         pti = NULL;
712     }
713     else
714     {
715         pti = IntTID2PTI(UlongToHandle(dwThreadId));
716         if (!pti || !pti->rpdesk)
717             goto Quit;
718     }
719 
720     _SEH2_TRY
721     {
722         ProbeForWrite(phList, dwCount * sizeof(HIMC), 1);
723         ProbeForWrite(pdwCount, sizeof(DWORD), 1);
724         *pdwCount = dwRealCount = UserBuildHimcList(pti, dwCount, phList);
725     }
726     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
727     {
728         ERR("%p, %p\n", phList, pdwCount);
729         _SEH2_YIELD(goto Quit);
730     }
731     _SEH2_END;
732 
733     if (dwCount < dwRealCount)
734         ret = STATUS_BUFFER_TOO_SMALL;
735     else
736         ret = STATUS_SUCCESS;
737 
738 Quit:
739     UserLeave();
740     return ret;
741 }
742 
743 // Win: SetConvMode
744 static VOID FASTCALL UserSetImeConversionKeyState(PTHREADINFO pti, DWORD dwConversion)
745 {
746     HKL hKL;
747     LANGID LangID;
748     LPBYTE KeyState;
749     BOOL bAlphaNumeric, bKatakana, bHiragana, bFullShape, bRoman, bCharCode;
750 
751     if (!pti->KeyboardLayout)
752         return;
753 
754     hKL = pti->KeyboardLayout->hkl;
755     LangID = LOWORD(hKL);
756     KeyState = pti->MessageQueue->afKeyState;
757 
758     switch (PRIMARYLANGID(LangID))
759     {
760         case LANG_JAPANESE:
761             bAlphaNumeric = !(dwConversion & IME_CMODE_NATIVE);
762             bKatakana = !bAlphaNumeric && (dwConversion & IME_CMODE_KATAKANA);
763             bHiragana = !bAlphaNumeric && !(dwConversion & IME_CMODE_KATAKANA);
764             SET_KEY_DOWN(KeyState, VK_DBE_ALPHANUMERIC, bAlphaNumeric);
765             SET_KEY_LOCKED(KeyState, VK_DBE_ALPHANUMERIC, bAlphaNumeric);
766             SET_KEY_DOWN(KeyState, VK_DBE_HIRAGANA, bHiragana);
767             SET_KEY_LOCKED(KeyState, VK_DBE_HIRAGANA, bHiragana);
768             SET_KEY_DOWN(KeyState, VK_DBE_KATAKANA, bKatakana);
769             SET_KEY_LOCKED(KeyState, VK_DBE_KATAKANA, bKatakana);
770 
771             bFullShape = (dwConversion & IME_CMODE_FULLSHAPE);
772             SET_KEY_DOWN(KeyState, VK_DBE_DBCSCHAR, bFullShape);
773             SET_KEY_LOCKED(KeyState, VK_DBE_DBCSCHAR, bFullShape);
774             SET_KEY_DOWN(KeyState, VK_DBE_SBCSCHAR, !bFullShape);
775             SET_KEY_LOCKED(KeyState, VK_DBE_SBCSCHAR, !bFullShape);
776 
777             bRoman = (dwConversion & IME_CMODE_ROMAN);
778             SET_KEY_DOWN(KeyState, VK_DBE_ROMAN, bRoman);
779             SET_KEY_LOCKED(KeyState, VK_DBE_ROMAN, bRoman);
780             SET_KEY_DOWN(KeyState, VK_DBE_NOROMAN, !bRoman);
781             SET_KEY_LOCKED(KeyState, VK_DBE_NOROMAN, !bRoman);
782 
783             bCharCode = (dwConversion & IME_CMODE_CHARCODE);
784             SET_KEY_DOWN(KeyState, VK_DBE_CODEINPUT, bCharCode);
785             SET_KEY_LOCKED(KeyState, VK_DBE_CODEINPUT, bCharCode);
786             SET_KEY_DOWN(KeyState, VK_DBE_NOCODEINPUT, !bCharCode);
787             SET_KEY_LOCKED(KeyState, VK_DBE_NOCODEINPUT, !bCharCode);
788             break;
789 
790         case LANG_KOREAN:
791             SET_KEY_LOCKED(KeyState, VK_HANGUL, (dwConversion & IME_CMODE_NATIVE));
792             SET_KEY_LOCKED(KeyState, VK_JUNJA, (dwConversion & IME_CMODE_FULLSHAPE));
793             SET_KEY_LOCKED(KeyState, VK_HANJA, (dwConversion & IME_CMODE_HANJACONVERT));
794             break;
795 
796         default:
797             break;
798     }
799 }
800 
801 DWORD
802 NTAPI
803 NtUserNotifyIMEStatus(HWND hwnd, BOOL fOpen, DWORD dwConversion)
804 {
805     PWND pwnd;
806     PTHREADINFO pti;
807     HKL hKL;
808 
809     UserEnterExclusive();
810 
811     if (!IS_IMM_MODE())
812     {
813         ERR("!IS_IMM_MODE()\n");
814         goto Quit;
815     }
816 
817     pwnd = ValidateHwndNoErr(hwnd);
818     if (!pwnd)
819         goto Quit;
820 
821     pti = pwnd->head.pti;
822     if (!pti || !gptiForeground)
823         goto Quit;
824     if (pti != gptiForeground && pti->MessageQueue != gptiForeground->MessageQueue)
825         goto Quit;
826     if (ghIMC == pwnd->hImc && gfImeOpen == !!fOpen && gdwImeConversion == dwConversion)
827         goto Quit;
828 
829     ghIMC = pwnd->hImc;
830     if (ghIMC)
831     {
832         gfImeOpen = !!fOpen;
833         gdwImeConversion = dwConversion;
834         UserSetImeConversionKeyState(pti, (fOpen ? dwConversion : IME_CMODE_ALPHANUMERIC));
835     }
836 
837     if (ISITHOOKED(WH_SHELL))
838     {
839         hKL = (pti->KeyboardLayout ? pti->KeyboardLayout->hkl : NULL);
840         co_HOOK_CallHooks(WH_SHELL, HSHELL_LANGUAGE, (WPARAM)hwnd, (LPARAM)hKL);
841     }
842 
843     // TODO:
844 
845 Quit:
846     UserLeave();
847     return 0;
848 }
849 
850 BOOL
851 NTAPI
852 NtUserDisableThreadIme(
853     DWORD dwThreadID)
854 {
855     PTHREADINFO pti, ptiCurrent;
856     PPROCESSINFO ppi;
857     BOOL ret = FALSE;
858 
859     UserEnterExclusive();
860 
861     if (!IS_IMM_MODE())
862     {
863         ERR("!IS_IMM_MODE()\n");
864         EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
865         goto Quit;
866     }
867 
868     ptiCurrent = GetW32ThreadInfo();
869 
870     if (dwThreadID == INVALID_THREAD_ID)
871     {
872         ppi = ptiCurrent->ppi;
873         ppi->W32PF_flags |= W32PF_DISABLEIME;
874 
875 Retry:
876         for (pti = ppi->ptiList; pti; pti = pti->ptiSibling)
877         {
878             pti->TIF_flags |= TIF_DISABLEIME;
879 
880             if (pti->spwndDefaultIme)
881             {
882                 co_UserDestroyWindow(pti->spwndDefaultIme);
883                 pti->spwndDefaultIme = NULL;
884                 goto Retry; /* The contents of ppi->ptiList may be changed. */
885             }
886         }
887     }
888     else
889     {
890         if (dwThreadID == 0)
891         {
892             pti = ptiCurrent;
893         }
894         else
895         {
896             pti = IntTID2PTI(UlongToHandle(dwThreadID));
897 
898             /* The thread needs to reside in the current process. */
899             if (!pti || pti->ppi != ptiCurrent->ppi)
900                 goto Quit;
901         }
902 
903         pti->TIF_flags |= TIF_DISABLEIME;
904 
905         if (pti->spwndDefaultIme)
906         {
907             co_UserDestroyWindow(pti->spwndDefaultIme);
908             pti->spwndDefaultIme = NULL;
909         }
910     }
911 
912     ret = TRUE;
913 
914 Quit:
915     UserLeave();
916     return ret;
917 }
918 
919 DWORD
920 NTAPI
921 NtUserGetAppImeLevel(HWND hWnd)
922 {
923     DWORD ret = 0;
924     PWND pWnd;
925     PTHREADINFO pti;
926 
927     UserEnterShared();
928 
929     pWnd = ValidateHwndNoErr(hWnd);
930     if (!pWnd)
931         goto Quit;
932 
933     if (!IS_IMM_MODE())
934     {
935         ERR("!IS_IMM_MODE()\n");
936         EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
937         goto Quit;
938     }
939 
940     pti = PsGetCurrentThreadWin32Thread();
941     if (pWnd->head.pti->ppi == pti->ppi)
942         ret = (DWORD)(ULONG_PTR)UserGetProp(pWnd, AtomImeLevel, TRUE);
943 
944 Quit:
945     UserLeave();
946     return ret;
947 }
948 
949 // Win: GetImeInfoEx
950 BOOL FASTCALL
951 UserGetImeInfoEx(
952     _Inout_ PWINSTATION_OBJECT pWinSta,
953     _Inout_ PIMEINFOEX pInfoEx,
954     _In_ IMEINFOEXCLASS SearchType)
955 {
956     PKL pkl, pklHead;
957 
958     if (!pWinSta || !gspklBaseLayout)
959         return FALSE;
960 
961     pkl = pklHead = gspklBaseLayout;
962 
963     /* Find the matching entry from the list and get info */
964     if (SearchType == ImeInfoExKeyboardLayout)
965     {
966         do
967         {
968             if (pInfoEx->hkl == pkl->hkl)
969             {
970                 if (!pkl->piiex)
971                 {
972                     ERR("!pkl->piiex at %p\n", pkl->hkl);
973                     break;
974                 }
975 
976                 *pInfoEx = *pkl->piiex;
977                 return TRUE;
978             }
979 
980             pkl = pkl->pklNext;
981         } while (pkl != pklHead);
982     }
983     else if (SearchType == ImeInfoExImeFileName)
984     {
985         do
986         {
987             if (pkl->piiex &&
988                 _wcsnicmp(pkl->piiex->wszImeFile, pInfoEx->wszImeFile,
989                           RTL_NUMBER_OF(pkl->piiex->wszImeFile)) == 0)
990             {
991                 *pInfoEx = *pkl->piiex;
992                 return TRUE;
993             }
994 
995             pkl = pkl->pklNext;
996         } while (pkl != pklHead);
997     }
998     else
999     {
1000         ERR("SearchType: %d\n", SearchType);
1001     }
1002 
1003     return FALSE;
1004 }
1005 
1006 BOOL
1007 NTAPI
1008 NtUserGetImeInfoEx(
1009     PIMEINFOEX pImeInfoEx,
1010     IMEINFOEXCLASS SearchType)
1011 {
1012     IMEINFOEX ImeInfoEx;
1013     BOOL ret = FALSE;
1014     PWINSTATION_OBJECT pWinSta;
1015 
1016     UserEnterShared();
1017 
1018     if (!IS_IMM_MODE())
1019     {
1020         ERR("!IS_IMM_MODE()\n");
1021         goto Quit;
1022     }
1023 
1024     _SEH2_TRY
1025     {
1026         ProbeForRead(pImeInfoEx, sizeof(*pImeInfoEx), 1);
1027         ImeInfoEx = *pImeInfoEx;
1028     }
1029     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1030     {
1031         ERR("%p\n", pImeInfoEx);
1032         _SEH2_YIELD(goto Quit);
1033     }
1034     _SEH2_END;
1035 
1036     pWinSta = IntGetProcessWindowStation(NULL);
1037     ret = UserGetImeInfoEx(pWinSta, &ImeInfoEx, SearchType);
1038     if (!ret)
1039         goto Quit;
1040 
1041     _SEH2_TRY
1042     {
1043         ProbeForWrite(pImeInfoEx, sizeof(*pImeInfoEx), 1);
1044         *pImeInfoEx = ImeInfoEx;
1045     }
1046     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1047     {
1048         ERR("%p\n", pImeInfoEx);
1049         ret = FALSE;
1050     }
1051     _SEH2_END;
1052 
1053 Quit:
1054     UserLeave();
1055     return ret;
1056 }
1057 
1058 BOOL
1059 NTAPI
1060 NtUserSetAppImeLevel(HWND hWnd, DWORD dwLevel)
1061 {
1062     BOOL ret = FALSE;
1063     PWND pWnd;
1064     PTHREADINFO pti;
1065 
1066     UserEnterExclusive();
1067 
1068     if (!IS_IMM_MODE())
1069     {
1070         ERR("!IS_IMM_MODE()\n");
1071         EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1072         goto Quit;
1073     }
1074 
1075     pWnd = ValidateHwndNoErr(hWnd);
1076     if (!pWnd)
1077         goto Quit;
1078 
1079     pti = PsGetCurrentThreadWin32Thread();
1080     if (pWnd->head.pti->ppi == pti->ppi)
1081         ret = UserSetProp(pWnd, AtomImeLevel, (HANDLE)(ULONG_PTR)dwLevel, TRUE);
1082 
1083 Quit:
1084     UserLeave();
1085     return ret;
1086 }
1087 
1088 // Win: SetImeInfoEx
1089 BOOL FASTCALL
1090 UserSetImeInfoEx(
1091     _Inout_ PWINSTATION_OBJECT pWinSta,
1092     _Inout_ PIMEINFOEX pImeInfoEx)
1093 {
1094     PKL pklHead, pkl;
1095 
1096     if (!pWinSta || !gspklBaseLayout)
1097         return FALSE;
1098 
1099     pkl = pklHead = gspklBaseLayout;
1100 
1101     do
1102     {
1103         if (pkl->hkl != pImeInfoEx->hkl)
1104         {
1105             pkl = pkl->pklNext;
1106             continue;
1107         }
1108 
1109         if (!pkl->piiex)
1110         {
1111             ERR("!pkl->piiex at %p\n", pkl->hkl);
1112             return FALSE;
1113         }
1114 
1115         if (!pkl->piiex->fLoadFlag)
1116             *pkl->piiex = *pImeInfoEx;
1117 
1118         return TRUE;
1119     } while (pkl != pklHead);
1120 
1121     return FALSE;
1122 }
1123 
1124 BOOL
1125 NTAPI
1126 NtUserSetImeInfoEx(PIMEINFOEX pImeInfoEx)
1127 {
1128     BOOL ret = FALSE;
1129     IMEINFOEX ImeInfoEx;
1130     PWINSTATION_OBJECT pWinSta;
1131 
1132     UserEnterExclusive();
1133 
1134     if (!IS_IMM_MODE())
1135     {
1136         ERR("!IS_IMM_MODE()\n");
1137         goto Quit;
1138     }
1139 
1140     _SEH2_TRY
1141     {
1142         ProbeForRead(pImeInfoEx, sizeof(*pImeInfoEx), 1);
1143         ImeInfoEx = *pImeInfoEx;
1144     }
1145     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1146     {
1147         ERR("%p\n", pImeInfoEx);
1148         _SEH2_YIELD(goto Quit);
1149     }
1150     _SEH2_END;
1151 
1152     pWinSta = IntGetProcessWindowStation(NULL);
1153     ret = UserSetImeInfoEx(pWinSta, &ImeInfoEx);
1154 
1155 Quit:
1156     UserLeave();
1157     return ret;
1158 }
1159 
1160 // Choose the preferred owner of the IME window.
1161 // Win: ImeSetFutureOwner
1162 VOID FASTCALL IntImeSetFutureOwner(PWND pImeWnd, PWND pwndOwner)
1163 {
1164     PWND pwndNode, pwndNextOwner, pwndParent, pwndSibling;
1165     PTHREADINFO pti = pImeWnd->head.pti;
1166 
1167     if (!pwndOwner || (pwndOwner->style & WS_CHILD)) // invalid owner
1168         return;
1169 
1170     // Get the top-level owner of the same thread
1171     for (pwndNode = pwndOwner; ; pwndNode = pwndNextOwner)
1172     {
1173         pwndNextOwner = pwndNode->spwndOwner;
1174         if (!pwndNextOwner || pwndNextOwner->head.pti != pti)
1175             break;
1176     }
1177 
1178     // Don't choose the IME-like windows and the bottom-most windows unless necessary.
1179     if (IS_WND_IMELIKE(pwndNode) ||
1180         ((pwndNode->state2 & WNDS2_BOTTOMMOST) && !(pwndOwner->state2 & WNDS2_BOTTOMMOST)))
1181     {
1182         pwndNode = pwndOwner;
1183     }
1184 
1185     pwndParent = pwndNode->spwndParent;
1186     if (!pwndParent || pwndOwner != pwndNode)
1187     {
1188         WndSetOwner(pImeWnd, pwndNode);
1189         return;
1190     }
1191 
1192     for (pwndSibling = pwndParent->spwndChild; pwndSibling; pwndSibling = pwndSibling->spwndNext)
1193     {
1194         if (pwndNode->head.pti != pwndSibling->head.pti)
1195             continue;
1196 
1197         if (IS_WND_MENU(pwndSibling) || IS_WND_IMELIKE(pwndSibling))
1198             continue;
1199 
1200         if (pwndSibling->state2 & WNDS2_INDESTROY)
1201             continue;
1202 
1203         if (pwndNode == pwndSibling || (pwndSibling->style & WS_CHILD))
1204             continue;
1205 
1206         if (pwndSibling->spwndOwner == NULL ||
1207             pwndSibling->head.pti != pwndSibling->spwndOwner->head.pti)
1208         {
1209             pwndNode = pwndSibling;
1210             break;
1211         }
1212     }
1213 
1214     WndSetOwner(pImeWnd, pwndNode);
1215 }
1216 
1217 // Get the last non-IME-like top-most window on the desktop.
1218 // Win: GetLastTopMostWindowNoIME
1219 PWND FASTCALL IntGetLastTopMostWindowNoIME(PWND pImeWnd)
1220 {
1221     PWND pwndNode, pwndOwner, pwndLastTopMost = NULL;
1222     BOOL bFound;
1223 
1224     pwndNode = UserGetDesktopWindow();
1225     if (!pwndNode || pwndNode->spwndChild == NULL)
1226         return NULL;
1227 
1228     for (pwndNode = pwndNode->spwndChild;
1229          pwndNode && (pwndNode->ExStyle & WS_EX_TOPMOST);
1230          pwndNode = pwndNode->spwndNext)
1231     {
1232         bFound = FALSE;
1233 
1234         if (IS_WND_IMELIKE(pwndNode)) // An IME-like window
1235         {
1236             // Search the IME window from owners
1237             for (pwndOwner = pwndNode; pwndOwner; pwndOwner = pwndOwner->spwndOwner)
1238             {
1239                 if (pImeWnd == pwndOwner)
1240                 {
1241                     bFound = TRUE;
1242                     break;
1243                 }
1244             }
1245         }
1246 
1247         if (!bFound)
1248             pwndLastTopMost = pwndNode;
1249     }
1250 
1251     return pwndLastTopMost;
1252 }
1253 
1254 // Adjust the ordering of the windows around the IME window.
1255 // Win: ImeSetTopMost
1256 VOID FASTCALL IntImeSetTopMost(PWND pImeWnd, BOOL bTopMost, PWND pwndInsertBefore)
1257 {
1258     PWND pwndParent, pwndChild, pwndNode, pwndNext, pwndInsertAfter = NULL;
1259     PWND pwndInsertAfterSave;
1260 
1261     pwndParent = pImeWnd->spwndParent;
1262     if (!pwndParent)
1263         return;
1264 
1265     pwndChild = pwndParent->spwndChild;
1266 
1267     if (!bTopMost)
1268     {
1269         // Calculate pwndInsertAfter
1270         pwndInsertAfter = IntGetLastTopMostWindowNoIME(pImeWnd);
1271         if (pwndInsertBefore)
1272         {
1273             for (pwndNode = pwndInsertAfter; pwndNode; pwndNode = pwndNode->spwndNext)
1274             {
1275                 if (pwndNode->spwndNext == pwndInsertBefore)
1276                     break;
1277 
1278                 if (pwndNode == pImeWnd)
1279                     return;
1280             }
1281 
1282             if (!pwndNode)
1283                 return;
1284 
1285             pwndInsertAfter = pwndNode;
1286         }
1287 
1288         // Adjust pwndInsertAfter if the owner is bottom-most
1289         if (pImeWnd->spwndOwner->state2 & WNDS2_BOTTOMMOST)
1290         {
1291             for (pwndNode = pwndInsertAfter; pwndNode; pwndNode = pwndNode->spwndNext)
1292             {
1293                 if (pwndNode == pImeWnd->spwndOwner)
1294                     break;
1295 
1296                 if (!IS_WND_IMELIKE(pwndNode))
1297                     pwndInsertAfter = pwndNode;
1298             }
1299         }
1300     }
1301 
1302     pwndInsertAfterSave = pwndInsertAfter;
1303 
1304     while (pwndChild)
1305     {
1306         pwndNext = pwndChild->spwndNext;
1307 
1308         // If pwndChild is a good IME-like window, ...
1309         if (IS_WND_IMELIKE(pwndChild) && pwndChild != pwndInsertAfter &&
1310             pwndChild->head.pti == pImeWnd->head.pti)
1311         {
1312             // Find pImeWnd from the owners
1313             for (pwndNode = pwndChild; pwndNode; pwndNode = pwndNode->spwndOwner)
1314             {
1315                 if (pwndNode != pImeWnd)
1316                     continue;
1317 
1318                 // Adjust the ordering and the linking
1319                 IntUnlinkWindow(pwndChild);
1320 
1321                 if (bTopMost)
1322                     pwndChild->ExStyle |= WS_EX_TOPMOST;
1323                 else
1324                     pwndChild->ExStyle &= ~WS_EX_TOPMOST;
1325 
1326                 if (!pwndInsertAfter)
1327                     IntLinkHwnd(pwndChild, HWND_TOP);
1328                 else
1329                     IntLinkHwnd(pwndChild, UserHMGetHandle(pwndInsertAfter));
1330 
1331                 // Update the preferred position
1332                 pwndInsertAfter = pwndChild;
1333                 break;
1334             }
1335         }
1336 
1337         // Get the next child, with ignoring pwndInsertAfterSave
1338         pwndChild = pwndNext;
1339         if (pwndChild && pwndChild == pwndInsertAfterSave && pwndInsertAfter)
1340             pwndChild = pwndInsertAfter->spwndNext;
1341     }
1342 }
1343 
1344 // Make the IME window top-most if necessary.
1345 // Win: ImeCheckTopmost
1346 VOID FASTCALL IntImeCheckTopmost(PWND pImeWnd)
1347 {
1348     BOOL bTopMost;
1349     PWND pwndOwner = pImeWnd->spwndOwner, pwndInsertBefore = NULL;
1350 
1351     if (!pwndOwner)
1352         return;
1353 
1354     if (pImeWnd->head.pti != gptiForeground)
1355         pwndInsertBefore = pwndOwner;
1356 
1357     bTopMost = !!(pwndOwner->ExStyle & WS_EX_TOPMOST);
1358     IntImeSetTopMost(pImeWnd, bTopMost, pwndInsertBefore);
1359 }
1360 
1361 BOOL NTAPI
1362 NtUserSetImeOwnerWindow(HWND hImeWnd, HWND hwndFocus)
1363 {
1364     BOOL ret = FALSE;
1365     PWND pImeWnd, pwndFocus, pwndTopLevel, pwndNode, pwndActive;
1366     PTHREADINFO ptiIme;
1367 
1368     UserEnterExclusive();
1369 
1370     if (!IS_IMM_MODE())
1371     {
1372         ERR("!IS_IMM_MODE()\n");
1373         goto Quit;
1374     }
1375 
1376     pImeWnd = ValidateHwndNoErr(hImeWnd);
1377     if (!pImeWnd || pImeWnd->fnid != FNID_IME)
1378         goto Quit;
1379 
1380     pwndFocus = ValidateHwndNoErr(hwndFocus);
1381     if (pwndFocus)
1382     {
1383         if (IS_WND_IMELIKE(pwndFocus))
1384             goto Quit;
1385 
1386         pwndTopLevel = IntGetTopLevelWindow(pwndFocus);
1387 
1388         for (pwndNode = pwndTopLevel; pwndNode; pwndNode = pwndNode->spwndOwner)
1389         {
1390             if (pwndNode->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])
1391             {
1392                 pwndTopLevel = NULL;
1393                 break;
1394             }
1395         }
1396 
1397         WndSetOwner(pImeWnd, pwndTopLevel);
1398         IntImeCheckTopmost(pImeWnd);
1399     }
1400     else
1401     {
1402         ptiIme = pImeWnd->head.pti;
1403         pwndActive = ptiIme->MessageQueue->spwndActive;
1404 
1405         if (!pwndActive || pwndActive != pImeWnd->spwndOwner)
1406         {
1407             if (pwndActive && ptiIme == pwndActive->head.pti && !IS_WND_IMELIKE(pwndActive))
1408             {
1409                 WndSetOwner(pImeWnd, pwndActive);
1410             }
1411             else
1412             {
1413                 IntImeSetFutureOwner(pImeWnd, pImeWnd->spwndOwner);
1414             }
1415 
1416             IntImeCheckTopmost(pImeWnd);
1417         }
1418     }
1419 
1420     ret = TRUE;
1421 
1422 Quit:
1423     UserLeave();
1424     return ret;
1425 }
1426 
1427 PVOID
1428 AllocInputContextObject(PDESKTOP pDesk,
1429                         PTHREADINFO pti,
1430                         SIZE_T Size,
1431                         PVOID* HandleOwner)
1432 {
1433     PTHRDESKHEAD ObjHead;
1434 
1435     ASSERT(Size > sizeof(*ObjHead));
1436     ASSERT(pti != NULL);
1437 
1438     if (!pDesk)
1439         pDesk = pti->rpdesk;
1440 
1441     ObjHead = DesktopHeapAlloc(pDesk, Size);
1442     if (!ObjHead)
1443         return NULL;
1444 
1445     RtlZeroMemory(ObjHead, Size);
1446 
1447     ObjHead->pSelf = ObjHead;
1448     ObjHead->rpdesk = pDesk;
1449     ObjHead->pti = pti;
1450     IntReferenceThreadInfo(pti);
1451     *HandleOwner = pti;
1452     pti->ppi->UserHandleCount++;
1453 
1454     return ObjHead;
1455 }
1456 
1457 VOID UserFreeInputContext(PVOID Object)
1458 {
1459     PTHRDESKHEAD ObjHead = Object;
1460     PDESKTOP pDesk = ObjHead->rpdesk;
1461     PIMC pNode, pIMC = Object;
1462     PTHREADINFO pti;
1463 
1464     if (!pIMC)
1465         return;
1466 
1467     // Remove pIMC from the list except spDefaultImc
1468     pti = pIMC->head.pti;
1469     for (pNode = pti->spDefaultImc; pNode; pNode = pNode->pImcNext)
1470     {
1471         if (pNode->pImcNext == pIMC)
1472         {
1473             pNode->pImcNext = pIMC->pImcNext;
1474             break;
1475         }
1476     }
1477 
1478     DesktopHeapFree(pDesk, Object);
1479 
1480     pti->ppi->UserHandleCount--;
1481     IntDereferenceThreadInfo(pti);
1482 }
1483 
1484 BOOLEAN UserDestroyInputContext(PVOID Object)
1485 {
1486     PIMC pIMC = Object;
1487     if (!pIMC)
1488         return TRUE;
1489 
1490     UserMarkObjectDestroy(pIMC);
1491     UserDeleteObject(UserHMGetHandle(pIMC), TYPE_INPUTCONTEXT);
1492     return TRUE;
1493 }
1494 
1495 // Win: DestroyInputContext
1496 BOOL IntDestroyInputContext(PIMC pIMC)
1497 {
1498     HIMC hIMC = UserHMGetHandle(pIMC);
1499     PTHREADINFO pti = pIMC->head.pti;
1500     PWND pwndChild;
1501     PWINDOWLIST pwl;
1502     HWND *phwnd;
1503     PWND pWnd;
1504 
1505     if (pti != gptiCurrent)
1506     {
1507         EngSetLastError(ERROR_ACCESS_DENIED);
1508         return FALSE;
1509     }
1510 
1511     if (pIMC == pti->spDefaultImc)
1512     {
1513         EngSetLastError(ERROR_INVALID_PARAMETER);
1514         return FALSE;
1515     }
1516 
1517     pwndChild = pti->rpdesk->pDeskInfo->spwnd->spwndChild;
1518     pwl = IntBuildHwndList(pwndChild, IACE_LIST | IACE_CHILDREN, pti);
1519     if (pwl)
1520     {
1521         for (phwnd = pwl->ahwnd; *phwnd != HWND_TERMINATOR; ++phwnd)
1522         {
1523             pWnd = UserGetObjectNoErr(gHandleTable, *phwnd, TYPE_WINDOW);
1524             if (pWnd && pWnd->hImc == hIMC)
1525                 IntAssociateInputContext(pWnd, pti->spDefaultImc);
1526         }
1527 
1528         IntFreeHwndList(pwl);
1529     }
1530 
1531     UserDeleteObject(hIMC, TYPE_INPUTCONTEXT);
1532     return TRUE;
1533 }
1534 
1535 BOOL NTAPI NtUserDestroyInputContext(HIMC hIMC)
1536 {
1537     BOOL ret = FALSE;
1538     PIMC pIMC;
1539 
1540     UserEnterExclusive();
1541 
1542     if (!IS_IMM_MODE())
1543     {
1544         EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1545         goto Quit;
1546     }
1547 
1548     pIMC = UserGetObjectNoErr(gHandleTable, hIMC, TYPE_INPUTCONTEXT);
1549     if (pIMC)
1550         ret = IntDestroyInputContext(pIMC);
1551 
1552 Quit:
1553     UserLeave();
1554     return ret;
1555 }
1556 
1557 // Win: CreateInputContext
1558 PIMC FASTCALL UserCreateInputContext(ULONG_PTR dwClientImcData)
1559 {
1560     PIMC pIMC;
1561     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1562     PDESKTOP pdesk = pti->rpdesk;
1563 
1564     if (!IS_IMM_MODE() || (pti->TIF_flags & TIF_DISABLEIME)) // Disabled?
1565     {
1566         ERR("IME is disabled\n");
1567         return NULL;
1568     }
1569 
1570     if (!pdesk) // No desktop?
1571         return NULL;
1572 
1573     // pti->spDefaultImc should be already set if non-first time.
1574     if (dwClientImcData && !pti->spDefaultImc)
1575         return NULL;
1576 
1577     // Create an input context user object.
1578     pIMC = UserCreateObject(gHandleTable, pdesk, pti, NULL, TYPE_INPUTCONTEXT, sizeof(IMC));
1579     if (!pIMC)
1580         return NULL;
1581 
1582     // Release the extra reference (UserCreateObject added 2 references).
1583     UserDereferenceObject(pIMC);
1584     ASSERT(pIMC->head.cLockObj == 1);
1585 
1586     if (dwClientImcData) // Non-first time.
1587     {
1588         // Insert pIMC to the second position (non-default) of the list.
1589         pIMC->pImcNext = pti->spDefaultImc->pImcNext;
1590         pti->spDefaultImc->pImcNext = pIMC;
1591     }
1592     else // First time. It's the default IMC.
1593     {
1594         // Add the first one (default) to the list.
1595         UserAssignmentLock((PVOID*)&pti->spDefaultImc, pIMC);
1596         pIMC->pImcNext = NULL;
1597         ASSERT(pIMC->head.cLockObj == 2); // UserAssignmentUnlock'ed at ExitThreadCallback
1598     }
1599 
1600     pIMC->dwClientImcData = dwClientImcData; // Set it.
1601     return pIMC;
1602 }
1603 
1604 HIMC
1605 NTAPI
1606 NtUserCreateInputContext(ULONG_PTR dwClientImcData)
1607 {
1608     PIMC pIMC;
1609     HIMC ret = NULL;
1610 
1611     UserEnterExclusive();
1612 
1613     if (!IS_IMM_MODE())
1614     {
1615         ERR("!IS_IMM_MODE()\n");
1616         EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1617         goto Quit;
1618     }
1619 
1620     if (!dwClientImcData)
1621     {
1622         EngSetLastError(ERROR_INVALID_PARAMETER);
1623         goto Quit;
1624     }
1625 
1626     pIMC = UserCreateInputContext(dwClientImcData);
1627     if (pIMC)
1628         ret = UserHMGetHandle(pIMC);
1629 
1630 Quit:
1631     UserLeave();
1632     return ret;
1633 }
1634 
1635 // Win: AssociateInputContextEx
1636 DWORD FASTCALL IntAssociateInputContextEx(PWND pWnd, PIMC pIMC, DWORD dwFlags)
1637 {
1638     DWORD ret = 0;
1639     PWINDOWLIST pwl;
1640     BOOL bIgnoreNullImc = (dwFlags & IACE_IGNORENOCONTEXT);
1641     PTHREADINFO pti = pWnd->head.pti;
1642     PWND pwndTarget, pwndFocus = pti->MessageQueue->spwndFocus;
1643     HWND *phwnd;
1644     HIMC hIMC;
1645 
1646     if (dwFlags & IACE_DEFAULT)
1647     {
1648         pIMC = pti->spDefaultImc;
1649     }
1650     else
1651     {
1652         if (pIMC && pti != pIMC->head.pti)
1653             return 2;
1654     }
1655 
1656     if (pWnd->head.pti->ppi != GetW32ThreadInfo()->ppi ||
1657         (pIMC && pIMC->head.rpdesk != pWnd->head.rpdesk))
1658     {
1659         return 2;
1660     }
1661 
1662     if ((dwFlags & IACE_CHILDREN) && pWnd->spwndChild)
1663     {
1664         pwl = IntBuildHwndList(pWnd->spwndChild, IACE_CHILDREN | IACE_LIST, pti);
1665         if (pwl)
1666         {
1667             for (phwnd = pwl->ahwnd; *phwnd != HWND_TERMINATOR; ++phwnd)
1668             {
1669                 pwndTarget = ValidateHwndNoErr(*phwnd);
1670                 if (!pwndTarget)
1671                     continue;
1672 
1673                 hIMC = (pIMC ? UserHMGetHandle(pIMC) : NULL);
1674                 if (pwndTarget->hImc == hIMC || (bIgnoreNullImc && !pwndTarget->hImc))
1675                     continue;
1676 
1677                 IntAssociateInputContext(pwndTarget, pIMC);
1678                 if (pwndTarget == pwndFocus)
1679                     ret = 1;
1680             }
1681 
1682             IntFreeHwndList(pwl);
1683         }
1684     }
1685 
1686     if (!bIgnoreNullImc || pWnd->hImc)
1687     {
1688         hIMC = (pIMC ? UserHMGetHandle(pIMC) : NULL);
1689         if (pWnd->hImc != hIMC)
1690         {
1691             IntAssociateInputContext(pWnd, pIMC);
1692             if (pWnd == pwndFocus)
1693                 ret = 1;
1694         }
1695     }
1696 
1697     return ret;
1698 }
1699 
1700 DWORD
1701 NTAPI
1702 NtUserAssociateInputContext(HWND hWnd, HIMC hIMC, DWORD dwFlags)
1703 {
1704     DWORD ret = 2;
1705     PWND pWnd;
1706     PIMC pIMC;
1707 
1708     UserEnterExclusive();
1709 
1710     if (!IS_IMM_MODE())
1711     {
1712         ERR("!IS_IMM_MODE()\n");
1713         goto Quit;
1714     }
1715 
1716     pWnd = ValidateHwndNoErr(hWnd);
1717     if (!pWnd)
1718         goto Quit;
1719 
1720     pIMC = (hIMC ? UserGetObjectNoErr(gHandleTable, hIMC, TYPE_INPUTCONTEXT) : NULL);
1721     ret = IntAssociateInputContextEx(pWnd, pIMC, dwFlags);
1722 
1723 Quit:
1724     UserLeave();
1725     return ret;
1726 }
1727 
1728 // Win: UpdateInputContext
1729 BOOL FASTCALL UserUpdateInputContext(PIMC pIMC, DWORD dwType, DWORD_PTR dwValue)
1730 {
1731     PTHREADINFO pti = GetW32ThreadInfo();
1732     PTHREADINFO ptiIMC = pIMC->head.pti;
1733 
1734     if (pti->ppi != ptiIMC->ppi) // Different process?
1735         return FALSE;
1736 
1737     switch (dwType)
1738     {
1739         case UIC_CLIENTIMCDATA:
1740             if (pIMC->dwClientImcData)
1741                 return FALSE; // Already set
1742 
1743             pIMC->dwClientImcData = dwValue;
1744             break;
1745 
1746         case UIC_IMEWINDOW:
1747             if (!ValidateHwndNoErr((HWND)dwValue))
1748                 return FALSE; // Invalid HWND
1749 
1750             pIMC->hImeWnd = (HWND)dwValue;
1751             break;
1752 
1753         default:
1754             return FALSE;
1755     }
1756 
1757     return TRUE;
1758 }
1759 
1760 BOOL
1761 NTAPI
1762 NtUserUpdateInputContext(
1763     HIMC hIMC,
1764     DWORD dwType,
1765     DWORD_PTR dwValue)
1766 {
1767     PIMC pIMC;
1768     BOOL ret = FALSE;
1769 
1770     UserEnterExclusive();
1771 
1772     if (!IS_IMM_MODE())
1773     {
1774         ERR("!IS_IMM_MODE()\n");
1775         goto Quit;
1776     }
1777 
1778     pIMC = UserGetObject(gHandleTable, hIMC, TYPE_INPUTCONTEXT);
1779     if (!pIMC)
1780         goto Quit;
1781 
1782     ret = UserUpdateInputContext(pIMC, dwType, dwValue);
1783 
1784 Quit:
1785     UserLeave();
1786     return ret;
1787 }
1788 
1789 DWORD_PTR
1790 NTAPI
1791 NtUserQueryInputContext(HIMC hIMC, DWORD dwType)
1792 {
1793     PIMC pIMC;
1794     PTHREADINFO ptiIMC;
1795     DWORD_PTR ret = 0;
1796 
1797     UserEnterExclusive();
1798 
1799     if (!IS_IMM_MODE())
1800     {
1801         ERR("!IS_IMM_MODE()\n");
1802         goto Quit;
1803     }
1804 
1805     pIMC = UserGetObject(gHandleTable, hIMC, TYPE_INPUTCONTEXT);
1806     if (!pIMC)
1807         goto Quit;
1808 
1809     ptiIMC = pIMC->head.pti;
1810 
1811     switch (dwType)
1812     {
1813         case QIC_INPUTPROCESSID:
1814             ret = (DWORD_PTR)PsGetThreadProcessId(ptiIMC->pEThread);
1815             break;
1816 
1817         case QIC_INPUTTHREADID:
1818             ret = (DWORD_PTR)PsGetThreadId(ptiIMC->pEThread);
1819             break;
1820 
1821         case QIC_DEFAULTWINDOWIME:
1822             if (ptiIMC->spwndDefaultIme)
1823                 ret = (DWORD_PTR)UserHMGetHandle(ptiIMC->spwndDefaultIme);
1824             break;
1825 
1826         case QIC_DEFAULTIMC:
1827             if (ptiIMC->spDefaultImc)
1828                 ret = (DWORD_PTR)UserHMGetHandle(ptiIMC->spDefaultImc);
1829             break;
1830     }
1831 
1832 Quit:
1833     UserLeave();
1834     return ret;
1835 }
1836 
1837 // Searchs a non-IME-related window of the same thread of pwndTarget,
1838 // other than pwndTarget, around pwndParent. Returns TRUE if found.
1839 //
1840 // Win: IsChildSameThread
1841 BOOL IntFindNonImeRelatedWndOfSameThread(PWND pwndParent, PWND pwndTarget)
1842 {
1843     PWND pwnd, pwndOwner, pwndNode;
1844     PTHREADINFO ptiTarget = pwndTarget->head.pti;
1845 
1846     // For all the children of pwndParent, ...
1847     for (pwnd = pwndParent->spwndChild; pwnd; pwnd = pwnd->spwndNext)
1848     {
1849         if (pwnd == pwndTarget || pwnd->head.pti != ptiTarget || IS_WND_MENU(pwnd))
1850             continue;
1851 
1852         if (!IS_WND_CHILD(pwnd))
1853         {
1854             // Check if any IME-like owner.
1855             BOOL bFound1 = FALSE;
1856             for (pwndOwner = pwnd; pwndOwner; pwndOwner = pwndOwner->spwndOwner)
1857             {
1858                 if (IS_WND_IMELIKE(pwndOwner))
1859                 {
1860                     bFound1 = TRUE;
1861                     break;
1862                 }
1863             }
1864             if (bFound1)
1865                 continue; // Skip if any IME-like owner.
1866         }
1867 
1868         pwndNode = pwnd;
1869 
1870         if (IS_WND_CHILD(pwndNode))
1871         {
1872             // Check if any same-thread IME-like ancestor.
1873             BOOL bFound2 = FALSE;
1874             for (; IS_WND_CHILD(pwndNode); pwndNode = pwndNode->spwndParent)
1875             {
1876                 if (pwndNode->head.pti != ptiTarget)
1877                     break;
1878 
1879                 if (IS_WND_IMELIKE(pwndNode))
1880                 {
1881                     bFound2 = TRUE;
1882                     break;
1883                 }
1884             }
1885             if (bFound2)
1886                 continue;
1887             // Now, pwndNode is non-child or non-same-thread window.
1888         }
1889 
1890         if (!IS_WND_CHILD(pwndNode)) // pwndNode is non-child
1891         {
1892             // Check if any same-thread IME-like owner.
1893             BOOL bFound3 = FALSE;
1894             for (; pwndNode; pwndNode = pwndNode->spwndOwner)
1895             {
1896                 if (pwndNode->head.pti != ptiTarget)
1897                     break;
1898 
1899                 if (IS_WND_IMELIKE(pwndNode))
1900                 {
1901                     bFound3 = TRUE;
1902                     break;
1903                 }
1904             }
1905             if (bFound3)
1906                 continue;
1907         }
1908 
1909         return TRUE;
1910     }
1911 
1912     return FALSE;
1913 }
1914 
1915 // Determines whether the target window needs the IME window.
1916 // Win: WantImeWindow(pwndParent, pwndTarget)
1917 BOOL FASTCALL IntWantImeWindow(PWND pwndTarget)
1918 {
1919     PDESKTOP rpdesk;
1920     PWINSTATION_OBJECT rpwinstaParent;
1921     PWND pwndNode, pwndParent = pwndTarget->spwndParent;
1922 
1923     if (gptiCurrent->TIF_flags & TIF_DISABLEIME)
1924         return FALSE;
1925 
1926     if (IS_WND_IMELIKE(pwndTarget))
1927         return FALSE;
1928 
1929     if (pwndTarget->fnid == FNID_DESKTOP || pwndTarget->fnid == FNID_MESSAGEWND)
1930         return FALSE;
1931 
1932     if (pwndTarget->state & WNDS_SERVERSIDEWINDOWPROC)
1933         return FALSE;
1934 
1935     rpdesk = pwndTarget->head.rpdesk;
1936     if (!rpdesk)
1937         return FALSE;
1938 
1939     rpwinstaParent = rpdesk->rpwinstaParent;
1940     if (!rpwinstaParent || (rpwinstaParent->Flags & WSS_NOIO))
1941         return FALSE;
1942 
1943     for (pwndNode = pwndParent; pwndNode; pwndNode = pwndNode->spwndParent)
1944     {
1945         if (rpdesk != pwndNode->head.rpdesk)
1946             break;
1947 
1948         if (pwndNode == rpdesk->spwndMessage)
1949             return FALSE;
1950     }
1951 
1952     return TRUE;
1953 }
1954 
1955 // Create the default IME window for the target window.
1956 // Win: xxxCreateDefaultImeWindow(pwndTarget, ATOM, hInst)
1957 PWND FASTCALL co_IntCreateDefaultImeWindow(PWND pwndTarget, HINSTANCE hInst)
1958 {
1959     LARGE_UNICODE_STRING WindowName;
1960     UNICODE_STRING ClassName;
1961     PWND pImeWnd;
1962     PIMEUI pimeui;
1963     CREATESTRUCTW Cs;
1964     USER_REFERENCE_ENTRY Ref;
1965     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1966     HANDLE pid = PsGetThreadProcessId(pti->pEThread);
1967 
1968     if (!(pti->spDefaultImc) && pid == gpidLogon)
1969         UserCreateInputContext(0);
1970 
1971     if (!(pti->spDefaultImc) || IS_WND_IMELIKE(pwndTarget) || !(pti->rpdesk->pheapDesktop))
1972         return NULL;
1973 
1974     if (IS_WND_CHILD(pwndTarget) && !(pwndTarget->style & WS_VISIBLE) &&
1975         pwndTarget->spwndParent->head.pti->ppi != pti->ppi)
1976     {
1977         return NULL;
1978     }
1979 
1980     RtlInitLargeUnicodeString(&WindowName, L"Default IME", 0);
1981 
1982     ClassName.Buffer = (PWCH)(ULONG_PTR)gpsi->atomSysClass[ICLS_IME];
1983     ClassName.Length = 0;
1984     ClassName.MaximumLength = 0;
1985 
1986     UserRefObjectCo(pwndTarget, &Ref);
1987 
1988     RtlZeroMemory(&Cs, sizeof(Cs));
1989     Cs.style = WS_POPUP | WS_DISABLED;
1990     Cs.hInstance = hInst;
1991     Cs.hwndParent = UserHMGetHandle(pwndTarget);
1992     Cs.lpszName = WindowName.Buffer;
1993     Cs.lpszClass = ClassName.Buffer;
1994 
1995     // NOTE: LARGE_UNICODE_STRING is compatible to LARGE_STRING.
1996     pImeWnd = co_UserCreateWindowEx(&Cs, &ClassName, (PLARGE_STRING)&WindowName, NULL, WINVER);
1997     if (pImeWnd)
1998     {
1999         pimeui = ((PIMEWND)pImeWnd)->pimeui;
2000         _SEH2_TRY
2001         {
2002             ProbeForWrite(pimeui, sizeof(IMEUI), 1);
2003             pimeui->fDefault = TRUE;
2004             if (IS_WND_CHILD(pwndTarget) && pwndTarget->spwndParent->head.pti != pti)
2005                 pimeui->fChildThreadDef = TRUE;
2006         }
2007         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2008         {
2009             ERR("%p\n", pimeui);
2010         }
2011         _SEH2_END;
2012     }
2013 
2014     UserDerefObjectCo(pwndTarget);
2015     return pImeWnd;
2016 }
2017 
2018 // Determines whether the system can destroy the default IME window for the target child window.
2019 // Win: ImeCanDestroyDefIMEforChild
2020 BOOL FASTCALL IntImeCanDestroyDefIMEforChild(PWND pImeWnd, PWND pwndTarget)
2021 {
2022     PWND pwndNode;
2023     PIMEUI pimeui;
2024     IMEUI SafeImeUI;
2025 
2026     pimeui = ((PIMEWND)pImeWnd)->pimeui;
2027     if (!pimeui || (LONG_PTR)pimeui == (LONG_PTR)-1)
2028         return FALSE;
2029 
2030     // Check IMEUI.fChildThreadDef
2031     _SEH2_TRY
2032     {
2033         ProbeForRead(pimeui, sizeof(IMEUI), 1);
2034         SafeImeUI = *pimeui;
2035         if (!SafeImeUI.fChildThreadDef)
2036             return FALSE;
2037     }
2038     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2039     {
2040         ERR("%p\n", pimeui);
2041     }
2042     _SEH2_END;
2043 
2044     // The parent of pwndTarget is NULL or of the same thread of pwndTarget?
2045     if (pwndTarget->spwndParent == NULL ||
2046         pwndTarget->head.pti == pwndTarget->spwndParent->head.pti)
2047     {
2048         return FALSE;
2049     }
2050 
2051     for (pwndNode = pwndTarget; pwndNode; pwndNode = pwndNode->spwndParent)
2052     {
2053         if (pwndNode == pwndNode->head.rpdesk->pDeskInfo->spwnd)
2054             break;
2055 
2056         if (IntFindNonImeRelatedWndOfSameThread(pwndNode->spwndParent, pwndTarget))
2057             return FALSE;
2058     }
2059 
2060     return TRUE;
2061 }
2062 
2063 // Determines whether the system can destroy the default IME window for the non-child target window.
2064 // Win: ImeCanDestroyDefIME
2065 BOOL FASTCALL IntImeCanDestroyDefIME(PWND pImeWnd, PWND pwndTarget)
2066 {
2067     PWND pwndNode;
2068     PIMEUI pimeui;
2069     IMEUI SafeImeUI;
2070 
2071     pimeui = ((PIMEWND)pImeWnd)->pimeui;
2072     if (!pimeui || (LONG_PTR)pimeui == (LONG_PTR)-1)
2073         return FALSE;
2074 
2075     // Check IMEUI.fDestroy
2076     _SEH2_TRY
2077     {
2078         ProbeForRead(pimeui, sizeof(IMEUI), 1);
2079         SafeImeUI = *pimeui;
2080         if (SafeImeUI.fDestroy)
2081             return FALSE;
2082     }
2083     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2084     {
2085         ERR("%p\n", pimeui);
2086     }
2087     _SEH2_END;
2088 
2089     // Any ancestor of pImeWnd is pwndTarget?
2090     if (pImeWnd->spwndOwner)
2091     {
2092         for (pwndNode = pImeWnd->spwndOwner; pwndNode; pwndNode = pwndNode->spwndOwner)
2093         {
2094             if (pwndNode == pwndTarget)
2095                 break;
2096         }
2097 
2098         if (!pwndNode)
2099             return FALSE;
2100     }
2101 
2102     // Any ancestor of pwndTarget is IME-like?
2103     for (pwndNode = pwndTarget; pwndNode; pwndNode = pwndNode->spwndOwner)
2104     {
2105         if (IS_WND_IMELIKE(pwndNode))
2106             return FALSE;
2107     }
2108 
2109     // Adjust the ordering and top-mode status
2110     IntImeSetFutureOwner(pImeWnd, pwndTarget);
2111     for (pwndNode = pImeWnd->spwndOwner; pwndNode; pwndNode = pwndNode->spwndNext)
2112     {
2113         if (pwndNode == pImeWnd)
2114             break;
2115     }
2116     if (pwndNode == pImeWnd)
2117         IntImeCheckTopmost(pImeWnd);
2118 
2119     // Is the owner of pImeWnd NULL or pwndTarget?
2120     if (pImeWnd->spwndOwner && pwndTarget != pImeWnd->spwndOwner)
2121         return FALSE;
2122 
2123     WndSetOwner(pImeWnd, NULL);
2124     return TRUE;
2125 }
2126 
2127 // Update IMEUI.fShowStatus flags and Send the WM_IME_NOTIFY messages.
2128 // Win: xxxCheckImeShowStatus
2129 BOOL FASTCALL IntCheckImeShowStatus(PWND pwndIme, PTHREADINFO pti)
2130 {
2131     BOOL ret = FALSE, bDifferent;
2132     PWINDOWLIST pwl;
2133     HWND *phwnd;
2134     PWND pwndNode, pwndIMC;
2135     PTHREADINFO ptiCurrent = GetW32ThreadInfo();
2136     PIMEUI pimeui;
2137     IMEUI SafeImeUI;
2138 
2139     if (pwndIme->state2 & WNDS2_INDESTROY)
2140         return FALSE;
2141 
2142     // Build a window list
2143     pwl = IntBuildHwndList(pwndIme->spwndParent->spwndChild, IACE_LIST, NULL);
2144     if (!pwl)
2145         return FALSE;
2146 
2147     ret = TRUE;
2148     for (phwnd = pwl->ahwnd; *phwnd != HWND_TERMINATOR; ++phwnd)
2149     {
2150         pwndNode = ValidateHwndNoErr(*phwnd);
2151 
2152         if (!pwndNode || pwndIme == pwndNode)
2153             continue;
2154 
2155         if (pwndNode->pcls->atomClassName != gpsi->atomSysClass[ICLS_IME] ||
2156             (pwndNode->state2 & WNDS2_INDESTROY))
2157         {
2158             continue;
2159         }
2160 
2161         pimeui = ((PIMEWND)pwndNode)->pimeui;
2162         if (!pimeui || pimeui == (PIMEUI)-1)
2163             continue;
2164 
2165         if (pti && pti != pwndNode->head.pti)
2166             continue;
2167 
2168         // Attach to the process if necessary
2169         bDifferent = FALSE;
2170         if (pwndNode->head.pti->ppi != ptiCurrent->ppi)
2171         {
2172             KeAttachProcess(&(pwndNode->head.pti->ppi->peProcess->Pcb));
2173             bDifferent = TRUE;
2174         }
2175 
2176         // Get pwndIMC and update IMEUI.fShowStatus flag
2177         _SEH2_TRY
2178         {
2179             ProbeForWrite(pimeui, sizeof(IMEUI), 1);
2180             SafeImeUI = *pimeui;
2181             if (SafeImeUI.fShowStatus)
2182             {
2183                 pwndIMC = ValidateHwndNoErr(pimeui->hwndIMC);
2184                 if (pwndIMC)
2185                     pimeui->fShowStatus = FALSE;
2186             }
2187             else
2188             {
2189                 pwndIMC = NULL;
2190             }
2191         }
2192         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2193         {
2194             ERR("%p\n", pimeui);
2195             pwndIMC = NULL;
2196         }
2197         _SEH2_END;
2198 
2199         // Detach from the process if necessary
2200         if (bDifferent)
2201             KeDetachProcess();
2202 
2203         // Send the WM_IME_NOTIFY message
2204         if (pwndIMC && pwndIMC->head.pti && !(pwndIMC->head.pti->TIF_flags & TIF_INCLEANUP))
2205         {
2206             HWND hImeWnd;
2207             USER_REFERENCE_ENTRY Ref;
2208 
2209             UserRefObjectCo(pwndIMC, &Ref);
2210 
2211             hImeWnd = UserHMGetHandle(pwndIMC);
2212             co_IntSendMessage(hImeWnd, WM_IME_NOTIFY, IMN_CLOSESTATUSWINDOW, 0);
2213 
2214             UserDerefObjectCo(pwndIMC);
2215         }
2216     }
2217 
2218     // Free the window list
2219     IntFreeHwndList(pwl);
2220     return ret;
2221 }
2222 
2223 // Send a UI message.
2224 LRESULT FASTCALL
2225 IntSendMessageToUI(PTHREADINFO ptiIME, PIMEUI pimeui, UINT uMsg, WPARAM wParam, LPARAM lParam)
2226 {
2227     PWND pwndUI;
2228     LRESULT ret = 0;
2229     IMEUI SafeImeUI;
2230     BOOL bDifferent = FALSE;
2231     USER_REFERENCE_ENTRY Ref;
2232 
2233     // Attach to the process if necessary
2234     if (ptiIME != GetW32ThreadInfo())
2235     {
2236         bDifferent = TRUE;
2237         KeAttachProcess(&(ptiIME->ppi->peProcess->Pcb));
2238     }
2239 
2240     // Get the pwndUI
2241     _SEH2_TRY
2242     {
2243         ProbeForRead(pimeui, sizeof(IMEUI), 1);
2244         SafeImeUI = *pimeui;
2245         pwndUI = ValidateHwndNoErr(SafeImeUI.hwndUI);
2246     }
2247     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2248     {
2249         ERR("%p\n", pimeui);
2250         pwndUI = NULL;
2251     }
2252     _SEH2_END;
2253 
2254     if (!pwndUI)
2255         goto Quit;
2256 
2257     // Increment the recursion count of the IME procedure.
2258     // See also ImeWndProc_common of user32.
2259     _SEH2_TRY
2260     {
2261         ProbeForWrite(&pimeui->nCntInIMEProc, sizeof(LONG), 1);
2262         InterlockedIncrement(&pimeui->nCntInIMEProc);
2263     }
2264     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2265     {
2266         ERR("%p\n", pimeui);
2267         _SEH2_YIELD(goto Quit);
2268     }
2269     _SEH2_END;
2270 
2271     // Detach from the process if necessary
2272     if (bDifferent)
2273         KeDetachProcess();
2274 
2275     UserRefObjectCo(pwndUI, &Ref);
2276     ret = co_IntSendMessage(UserHMGetHandle(pwndUI), uMsg, wParam, lParam);
2277     UserDerefObjectCo(pwndUI);
2278 
2279     // Attach to the process if necessary
2280     if (bDifferent)
2281         KeAttachProcess(&(ptiIME->ppi->peProcess->Pcb));
2282 
2283     // Decrement the recursion count of the IME procedure
2284     _SEH2_TRY
2285     {
2286         ProbeForWrite(&pimeui->nCntInIMEProc, sizeof(LONG), 1);
2287         InterlockedDecrement(&pimeui->nCntInIMEProc);
2288     }
2289     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2290     {
2291         ERR("%p\n", pimeui);
2292         _SEH2_YIELD(goto Quit);
2293     }
2294     _SEH2_END;
2295 
2296 Quit:
2297     // Detach from the process if necessary
2298     if (bDifferent)
2299         KeDetachProcess();
2300 
2301     return ret;
2302 }
2303 
2304 // Send the open status notification.
2305 // Win: xxxSendOpenStatusNotify
2306 VOID FASTCALL
2307 IntSendOpenStatusNotify(PTHREADINFO ptiIME, PIMEUI pimeui, PWND pWnd, BOOL bOpen)
2308 {
2309     WPARAM wParam = (bOpen ? IMN_OPENSTATUSWINDOW : IMN_CLOSESTATUSWINDOW);
2310     PTHREADINFO ptiWnd = pWnd->head.pti;
2311     USER_REFERENCE_ENTRY Ref;
2312 
2313     if (ptiWnd->dwExpWinVer >= WINVER_WINNT4 && pWnd->hImc)
2314     {
2315         UserRefObjectCo(pWnd, &Ref);
2316         co_IntSendMessage(UserHMGetHandle(pWnd), WM_IME_NOTIFY, wParam, 0);
2317         UserDerefObjectCo(pWnd);
2318     }
2319     else
2320     {
2321         IntSendMessageToUI(ptiIME, pimeui, WM_IME_NOTIFY, wParam, 0);
2322     }
2323 }
2324 
2325 // Update the IME status and send a notification.
2326 VOID FASTCALL IntNotifyImeShowStatus(PWND pImeWnd)
2327 {
2328     PIMEUI pimeui;
2329     PWND pWnd;
2330     PTHREADINFO pti, ptiIME;
2331     BOOL bShow, bSendNotify = FALSE;
2332     IMEUI SafeImeUI;
2333 
2334     if (!IS_IMM_MODE() || (pImeWnd->state2 & WNDS2_INDESTROY))
2335         return;
2336 
2337     pti = PsGetCurrentThreadWin32Thread();
2338     ptiIME = pImeWnd->head.pti;
2339 
2340     // Attach to the process if necessary
2341     if (pti != ptiIME)
2342         KeAttachProcess(&(ptiIME->ppi->peProcess->Pcb));
2343 
2344     // Get an IMEUI and check whether hwndIMC is valid and update fShowStatus
2345     _SEH2_TRY
2346     {
2347         ProbeForWrite(pImeWnd, sizeof(IMEWND), 1);
2348         pimeui = ((PIMEWND)pImeWnd)->pimeui;
2349         SafeImeUI = *pimeui;
2350 
2351         bShow = (gfIMEShowStatus == TRUE) && SafeImeUI.fCtrlShowStatus;
2352 
2353         pWnd = ValidateHwndNoErr(SafeImeUI.hwndIMC);
2354         if (!pWnd)
2355             pWnd = ptiIME->MessageQueue->spwndFocus;
2356 
2357         if (pWnd)
2358         {
2359             bSendNotify = TRUE;
2360             pimeui->fShowStatus = bShow;
2361         }
2362     }
2363     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2364     {
2365         ERR("%p, %p\n", pImeWnd, pimeui);
2366 
2367         if (pti != ptiIME)
2368             KeDetachProcess();
2369 
2370         _SEH2_YIELD(return);
2371     }
2372     _SEH2_END;
2373 
2374     // Detach from the process if necessary
2375     if (pti != ptiIME)
2376         KeDetachProcess();
2377 
2378     if (bSendNotify)
2379         IntSendOpenStatusNotify(ptiIME, &SafeImeUI, pWnd, bShow);
2380 
2381     if (!(pImeWnd->state2 & WNDS2_INDESTROY))
2382         IntCheckImeShowStatus(pImeWnd, NULL);
2383 }
2384 
2385 // Win: xxxBroadcastImeShowStatusChange
2386 BOOL FASTCALL IntBroadcastImeShowStatusChange(PWND pImeWnd, BOOL bShow)
2387 {
2388     if (gfIMEShowStatus == bShow || !IS_IMM_MODE())
2389         return TRUE;
2390 
2391     gfIMEShowStatus = bShow;
2392     IntNotifyImeShowStatus(pImeWnd);
2393     return TRUE;
2394 }
2395 
2396 /* Win: xxxCheckImeShowStatusInThread */
2397 VOID FASTCALL IntCheckImeShowStatusInThread(PWND pImeWnd)
2398 {
2399     if (IS_IMM_MODE() && !(pImeWnd->state2 & WNDS2_INDESTROY))
2400         IntCheckImeShowStatus(pImeWnd, pImeWnd->head.pti);
2401 }
2402 
2403 /* EOF */
2404