xref: /reactos/win32ss/user/ntuser/ime.c (revision 9d3c3a75)
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 DBG_DEFAULT_CHANNEL(UserMisc);
12 
13 #define INVALID_THREAD_ID  ((ULONG)-1)
14 #define INVALID_HOTKEY     ((UINT)-1)
15 #define MOD_KEYS           (MOD_CONTROL | MOD_SHIFT | MOD_ALT | MOD_WIN)
16 #define MOD_LEFT_RIGHT     (MOD_LEFT | MOD_RIGHT)
17 
18 #define LANGID_CHINESE_SIMPLIFIED   MAKELANGID(LANG_CHINESE,  SUBLANG_CHINESE_SIMPLIFIED)
19 #define LANGID_JAPANESE             MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT)
20 #define LANGID_KOREAN               MAKELANGID(LANG_KOREAN,   SUBLANG_KOREAN)
21 #define LANGID_CHINESE_TRADITIONAL  MAKELANGID(LANG_CHINESE,  SUBLANG_CHINESE_TRADITIONAL)
22 #define LANGID_NEUTRAL              MAKELANGID(LANG_NEUTRAL,  SUBLANG_NEUTRAL)
23 
24 #define IS_WND_IMELIKE(pwnd) \
25     (((pwnd)->pcls->style & CS_IME) || \
26      ((pwnd)->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]))
27 
28 // The special virtual keys for Japanese: Used for key states.
29 // https://www.kthree.co.jp/kihelp/index.html?page=app/vkey&type=html
30 #define VK_DBE_ALPHANUMERIC 0xF0
31 #define VK_DBE_KATAKANA 0xF1
32 #define VK_DBE_HIRAGANA 0xF2
33 #define VK_DBE_SBCSCHAR 0xF3
34 #define VK_DBE_DBCSCHAR 0xF4
35 #define VK_DBE_ROMAN 0xF5
36 #define VK_DBE_NOROMAN 0xF6
37 #define VK_DBE_ENTERWORDREGISTERMODE 0xF7
38 #define VK_DBE_ENTERCONFIGMODE 0xF8
39 #define VK_DBE_FLUSHSTRING 0xF9
40 #define VK_DBE_CODEINPUT 0xFA
41 #define VK_DBE_NOCODEINPUT 0xFB
42 #define VK_DBE_DETERINESTRING 0xFC
43 #define VK_DBE_ENTERDLGCONVERSIONMODE 0xFD
44 
45 HIMC ghIMC = NULL;
46 BOOL gfImeOpen = (BOOL)-1;
47 DWORD gdwImeConversion = (DWORD)-1;
48 
49 typedef struct tagIMEHOTKEY
50 {
51     struct tagIMEHOTKEY *pNext;
52     DWORD  dwHotKeyId;
53     UINT   uVirtualKey;
54     UINT   uModifiers;
55     HKL    hKL;
56 } IMEHOTKEY, *PIMEHOTKEY;
57 
58 PIMEHOTKEY gpImeHotKeyList = NULL;
59 LCID glcid = 0;
60 
61 DWORD FASTCALL IntGetImeCompatFlags(PTHREADINFO pti)
62 {
63     if (!pti)
64         pti = PsGetCurrentThreadWin32Thread();
65 
66     return pti->ppi->dwImeCompatFlags;
67 }
68 
69 UINT FASTCALL IntGetImeHotKeyLanguageScore(HKL hKL, LANGID HotKeyLangId)
70 {
71     LCID lcid;
72 
73     if (HotKeyLangId == LANGID_NEUTRAL || HotKeyLangId == LOWORD(hKL))
74         return 3;
75 
76     _SEH2_TRY
77     {
78         lcid = NtCurrentTeb()->CurrentLocale;
79     }
80     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
81     {
82         lcid = MAKELCID(LANGID_NEUTRAL, SORT_DEFAULT);
83     }
84     _SEH2_END;
85 
86     if (HotKeyLangId == LANGIDFROMLCID(lcid))
87         return 2;
88 
89     if (glcid == 0)
90         ZwQueryDefaultLocale(FALSE, &glcid);
91 
92     if (HotKeyLangId == LANGIDFROMLCID(glcid))
93         return 1;
94 
95     return 0;
96 }
97 
98 HKL FASTCALL IntGetActiveKeyboardLayout(VOID)
99 {
100     PTHREADINFO pti;
101 
102     if (gpqForeground && gpqForeground->spwndActive)
103     {
104         pti = gpqForeground->spwndActive->head.pti;
105         if (pti && pti->KeyboardLayout)
106             return pti->KeyboardLayout->hkl;
107     }
108 
109     return UserGetKeyboardLayout(0);
110 }
111 
112 static LANGID FASTCALL IntGetImeHotKeyLangId(DWORD dwHotKeyId)
113 {
114 #define IME_CHOTKEY 0x10
115 #define IME_JHOTKEY 0x30
116 #define IME_KHOTKEY 0x50
117 #define IME_THOTKEY 0x70
118 #define IME_XHOTKEY 0x90
119     static const LANGID s_array[] =
120     {
121         /* 0x00 */ (WORD)-1,
122         /* 0x10 */ LANGID_CHINESE_SIMPLIFIED,
123         /* 0x20 */ LANGID_CHINESE_SIMPLIFIED,
124         /* 0x30 */ LANGID_JAPANESE,
125         /* 0x40 */ LANGID_JAPANESE,
126         /* 0x50 */ LANGID_KOREAN,
127         /* 0x60 */ LANGID_KOREAN,
128         /* 0x70 */ LANGID_CHINESE_TRADITIONAL,
129         /* 0x80 */ LANGID_CHINESE_TRADITIONAL
130     };
131 
132     if (IME_CHOTKEY <= dwHotKeyId && dwHotKeyId < IME_XHOTKEY)
133         return s_array[(dwHotKeyId & 0xF0) >> 4];
134     return LANGID_NEUTRAL;
135 }
136 
137 static VOID FASTCALL IntAddImeHotKey(PIMEHOTKEY *ppList, PIMEHOTKEY pHotKey)
138 {
139     PIMEHOTKEY pNode;
140 
141     if (!*ppList)
142     {
143         *ppList = pHotKey;
144         return;
145     }
146 
147     for (pNode = *ppList; pNode; pNode = pNode->pNext)
148     {
149         if (!pNode->pNext)
150         {
151             pNode->pNext = pHotKey;
152             return;
153         }
154     }
155 }
156 
157 static PIMEHOTKEY FASTCALL IntGetImeHotKeyById(PIMEHOTKEY pList, DWORD dwHotKeyId)
158 {
159     PIMEHOTKEY pNode;
160     for (pNode = pList; pNode; pNode = pNode->pNext)
161     {
162         if (pNode->dwHotKeyId == dwHotKeyId)
163             return pNode;
164     }
165     return NULL;
166 }
167 
168 static PIMEHOTKEY APIENTRY
169 IntGetImeHotKeyByKeyAndLang(PIMEHOTKEY pList, UINT uModKeys, UINT uLeftRight,
170                             UINT uVirtualKey, LANGID TargetLangId)
171 {
172     PIMEHOTKEY pNode;
173     LANGID LangID;
174     UINT uModifiers;
175 
176     for (pNode = pList; pNode; pNode = pNode->pNext)
177     {
178         if (pNode->uVirtualKey != uVirtualKey)
179             continue;
180 
181         LangID = IntGetImeHotKeyLangId(pNode->dwHotKeyId);
182         if (LangID != TargetLangId)
183             continue;
184 
185         uModifiers = pNode->uModifiers;
186         if (uModifiers & MOD_IGNORE_ALL_MODIFIER)
187             return pNode;
188 
189         if ((uModifiers & MOD_KEYS) != uModKeys)
190             continue;
191 
192         if ((uModifiers & uLeftRight) || (uModifiers & MOD_LEFT_RIGHT) == uLeftRight)
193             return pNode;
194     }
195 
196     return NULL;
197 }
198 
199 static VOID FASTCALL IntDeleteImeHotKey(PIMEHOTKEY *ppList, PIMEHOTKEY pHotKey)
200 {
201     PIMEHOTKEY pNode;
202 
203     if (*ppList == pHotKey)
204     {
205         *ppList = pHotKey->pNext;
206         ExFreePoolWithTag(pHotKey, USERTAG_IMEHOTKEY);
207         return;
208     }
209 
210     for (pNode = *ppList; pNode; pNode = pNode->pNext)
211     {
212         if (pNode->pNext == pHotKey)
213         {
214             pNode->pNext = pHotKey->pNext;
215             ExFreePoolWithTag(pHotKey, USERTAG_IMEHOTKEY);
216             return;
217         }
218     }
219 }
220 
221 PIMEHOTKEY
222 IntGetImeHotKeyByKey(PIMEHOTKEY pList, UINT uModKeys, UINT uLeftRight, UINT uVirtualKey)
223 {
224     PIMEHOTKEY pNode, ret = NULL;
225     PTHREADINFO pti = GetW32ThreadInfo();
226     LANGID LangId;
227     HKL hKL = IntGetActiveKeyboardLayout();
228     BOOL fKorean = (PRIMARYLANGID(LOWORD(hKL)) == LANG_KOREAN);
229     UINT nScore, nMaxScore = 0;
230 
231     for (pNode = pList; pNode; pNode = pNode->pNext)
232     {
233         if (pNode->uVirtualKey != uVirtualKey)
234             continue;
235 
236         if ((pNode->uModifiers & MOD_IGNORE_ALL_MODIFIER))
237         {
238             ;
239         }
240         else if ((pNode->uModifiers & MOD_KEYS) != uModKeys)
241         {
242             continue;
243         }
244         else if ((pNode->uModifiers & uLeftRight) ||
245                  (pNode->uModifiers & MOD_LEFT_RIGHT) == uLeftRight)
246         {
247             ;
248         }
249         else
250         {
251             continue;
252         }
253 
254         LangId = IntGetImeHotKeyLangId(pNode->dwHotKeyId);
255         nScore = IntGetImeHotKeyLanguageScore(hKL, LangId);
256         if (nScore >= 3)
257             return pNode;
258 
259         if (fKorean)
260             continue;
261 
262         if (nScore == 0)
263         {
264             if (pNode->dwHotKeyId == IME_CHOTKEY_IME_NONIME_TOGGLE ||
265                 pNode->dwHotKeyId == IME_THOTKEY_IME_NONIME_TOGGLE)
266             {
267                 if (LOWORD(pti->hklPrev) == LangId)
268                     return pNode;
269             }
270         }
271 
272         if (nMaxScore < nScore)
273         {
274             nMaxScore = nScore;
275             ret = pNode;
276         }
277     }
278 
279     return ret;
280 }
281 
282 PIMEHOTKEY IntCheckImeHotKey(PUSER_MESSAGE_QUEUE MessageQueue, UINT uVirtualKey, LPARAM lParam)
283 {
284     PIMEHOTKEY pHotKey;
285     UINT uModifiers;
286     BOOL bKeyUp = (lParam & 0x80000000);
287     const BYTE *KeyState = MessageQueue->afKeyState;
288     static UINT s_uKeyUpVKey = 0;
289 
290     if (bKeyUp)
291     {
292         if (s_uKeyUpVKey != uVirtualKey)
293         {
294             s_uKeyUpVKey = 0;
295             return NULL;
296         }
297 
298         s_uKeyUpVKey = 0;
299     }
300 
301     uModifiers = 0;
302     if (IS_KEY_DOWN(KeyState, VK_LSHIFT))   uModifiers |= (MOD_SHIFT | MOD_LEFT);
303     if (IS_KEY_DOWN(KeyState, VK_RSHIFT))   uModifiers |= (MOD_SHIFT | MOD_RIGHT);
304     if (IS_KEY_DOWN(KeyState, VK_LCONTROL)) uModifiers |= (MOD_CONTROL | MOD_LEFT);
305     if (IS_KEY_DOWN(KeyState, VK_RCONTROL)) uModifiers |= (MOD_CONTROL | MOD_RIGHT);
306     if (IS_KEY_DOWN(KeyState, VK_LMENU))    uModifiers |= (MOD_ALT | MOD_LEFT);
307     if (IS_KEY_DOWN(KeyState, VK_RMENU))    uModifiers |= (MOD_ALT | MOD_RIGHT);
308 
309     pHotKey = IntGetImeHotKeyByKey(gpImeHotKeyList,
310                                    (uModifiers & MOD_KEYS),
311                                    (uModifiers & MOD_LEFT_RIGHT),
312                                    uVirtualKey);
313     if (pHotKey)
314     {
315         if (bKeyUp)
316         {
317             if (pHotKey->uModifiers & MOD_ON_KEYUP)
318                 return pHotKey;
319         }
320         else
321         {
322             if (pHotKey->uModifiers & MOD_ON_KEYUP)
323                 s_uKeyUpVKey = uVirtualKey;
324             else
325                 return pHotKey;
326         }
327     }
328 
329     return NULL;
330 }
331 
332 VOID FASTCALL IntFreeImeHotKeys(VOID)
333 {
334     PIMEHOTKEY pNode, pNext;
335     for (pNode = gpImeHotKeyList; pNode; pNode = pNext)
336     {
337         pNext = pNode->pNext;
338         ExFreePoolWithTag(pNode, USERTAG_IMEHOTKEY);
339     }
340     gpImeHotKeyList = NULL;
341 }
342 
343 static BOOL APIENTRY
344 IntSetImeHotKey(DWORD dwHotKeyId, UINT uModifiers, UINT uVirtualKey, HKL hKL, DWORD dwAction)
345 {
346     PIMEHOTKEY pNode;
347     LANGID LangId;
348 
349     switch (dwAction)
350     {
351         case SETIMEHOTKEY_DELETE:
352             pNode = IntGetImeHotKeyById(gpImeHotKeyList, dwHotKeyId);
353             if (!pNode)
354             {
355                 ERR("dwHotKeyId: 0x%lX\n", dwHotKeyId);
356                 return FALSE;
357             }
358 
359             IntDeleteImeHotKey(&gpImeHotKeyList, pNode);
360             return TRUE;
361 
362         case SETIMEHOTKEY_ADD:
363             if (uVirtualKey == VK_PACKET)
364                 return FALSE;
365 
366             LangId = IntGetImeHotKeyLangId(dwHotKeyId);
367             if (LangId == LANGID_KOREAN)
368                 return FALSE;
369 
370             pNode = IntGetImeHotKeyByKeyAndLang(gpImeHotKeyList,
371                                                 (uModifiers & MOD_KEYS),
372                                                 (uModifiers & MOD_LEFT_RIGHT),
373                                                 uVirtualKey, LangId);
374             if (!pNode)
375                 pNode = IntGetImeHotKeyById(gpImeHotKeyList, dwHotKeyId);
376 
377             if (pNode)
378             {
379                 pNode->uModifiers = uModifiers;
380                 pNode->uVirtualKey = uVirtualKey;
381                 pNode->hKL = hKL;
382                 return TRUE;
383             }
384 
385             pNode = ExAllocatePoolWithTag(PagedPool, sizeof(IMEHOTKEY), USERTAG_IMEHOTKEY);
386             if (!pNode)
387                 return FALSE;
388 
389             pNode->pNext = NULL;
390             pNode->dwHotKeyId = dwHotKeyId;
391             pNode->uModifiers = uModifiers;
392             pNode->uVirtualKey = uVirtualKey;
393             pNode->hKL = hKL;
394             IntAddImeHotKey(&gpImeHotKeyList, pNode);
395             return TRUE;
396 
397         case SETIMEHOTKEY_DELETEALL:
398             IntFreeImeHotKeys();
399             return TRUE;
400 
401         default:
402             return FALSE;
403     }
404 }
405 
406 BOOL NTAPI
407 NtUserGetImeHotKey(DWORD dwHotKeyId, LPUINT lpuModifiers, LPUINT lpuVirtualKey, LPHKL lphKL)
408 {
409     PIMEHOTKEY pNode = NULL;
410 
411     UserEnterExclusive();
412 
413     _SEH2_TRY
414     {
415         ProbeForWrite(lpuModifiers, sizeof(UINT), 1);
416         ProbeForWrite(lpuVirtualKey, sizeof(UINT), 1);
417         if (lphKL)
418             ProbeForWrite(lphKL, sizeof(HKL), 1);
419     }
420     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
421     {
422         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         pNode = NULL;
440     }
441     _SEH2_END;
442 
443 Quit:
444     UserLeave();
445     return !!pNode;
446 }
447 
448 BOOL
449 NTAPI
450 NtUserSetImeHotKey(
451     DWORD  dwHotKeyId,
452     UINT   uModifiers,
453     UINT   uVirtualKey,
454     HKL    hKL,
455     DWORD  dwAction)
456 {
457     BOOL ret;
458     UserEnterExclusive();
459     ret = IntSetImeHotKey(dwHotKeyId, uModifiers, uVirtualKey, hKL, dwAction);
460     UserLeave();
461     return ret;
462 }
463 
464 DWORD
465 NTAPI
466 NtUserCheckImeHotKey(UINT uVirtualKey, LPARAM lParam)
467 {
468     PIMEHOTKEY pNode;
469     DWORD ret = INVALID_HOTKEY;
470 
471     UserEnterExclusive();
472 
473     if (!gpqForeground || !IS_IMM_MODE())
474         goto Quit;
475 
476     pNode = IntCheckImeHotKey(gpqForeground, uVirtualKey, lParam);
477     if (pNode)
478         ret = pNode->dwHotKeyId;
479 
480 Quit:
481     UserLeave();
482     return ret;
483 }
484 
485 PWND FASTCALL IntGetTopLevelWindow(PWND pwnd)
486 {
487     if (!pwnd)
488         return NULL;
489 
490     while (pwnd->style & WS_CHILD)
491         pwnd = pwnd->spwndParent;
492 
493     return pwnd;
494 }
495 
496 HIMC FASTCALL IntAssociateInputContext(PWND pWnd, PIMC pImc)
497 {
498     HIMC hOldImc = pWnd->hImc;
499     pWnd->hImc = (pImc ? UserHMGetHandle(pImc) : NULL);
500     return hOldImc;
501 }
502 
503 DWORD
504 NTAPI
505 NtUserSetThreadLayoutHandles(HKL hNewKL, HKL hOldKL)
506 {
507     PTHREADINFO pti;
508     PKL pOldKL, pNewKL;
509 
510     UserEnterExclusive();
511 
512     pti = GetW32ThreadInfo();
513     pOldKL = pti->KeyboardLayout;
514     if (pOldKL && pOldKL->hkl != hOldKL)
515         goto Quit;
516 
517     pNewKL = UserHklToKbl(hNewKL);
518     if (!pNewKL)
519         goto Quit;
520 
521     if (IS_IME_HKL(hNewKL) != IS_IME_HKL(hOldKL))
522         pti->hklPrev = hOldKL;
523 
524     pti->KeyboardLayout = pNewKL;
525 
526 Quit:
527     UserLeave();
528     return 0;
529 }
530 
531 DWORD FASTCALL UserBuildHimcList(PTHREADINFO pti, DWORD dwCount, HIMC *phList)
532 {
533     PIMC pIMC;
534     DWORD dwRealCount = 0;
535 
536     if (pti)
537     {
538         for (pIMC = pti->spDefaultImc; pIMC; pIMC = pIMC->pImcNext)
539         {
540             if (dwRealCount < dwCount)
541                 phList[dwRealCount] = UserHMGetHandle(pIMC);
542 
543             ++dwRealCount;
544         }
545     }
546     else
547     {
548         for (pti = GetW32ThreadInfo()->ppi->ptiList; pti; pti = pti->ptiSibling)
549         {
550             for (pIMC = pti->spDefaultImc; pIMC; pIMC = pIMC->pImcNext)
551             {
552                 if (dwRealCount < dwCount)
553                     phList[dwRealCount] = UserHMGetHandle(pIMC);
554 
555                 ++dwRealCount;
556             }
557         }
558     }
559 
560     return dwRealCount;
561 }
562 
563 UINT FASTCALL
564 IntImmProcessKey(PUSER_MESSAGE_QUEUE MessageQueue, PWND pWnd, UINT uMsg,
565                  WPARAM wParam, LPARAM lParam)
566 {
567     UINT uVirtualKey, ret = 0;
568     DWORD dwHotKeyId;
569     PKL pKL;
570     PIMC pIMC = NULL;
571     PIMEHOTKEY pImeHotKey;
572     HKL hKL;
573     HWND hWnd;
574 
575     ASSERT_REFS_CO(pWnd);
576 
577     switch (uMsg)
578     {
579         case WM_KEYDOWN:
580         case WM_KEYUP:
581         case WM_SYSKEYDOWN:
582         case WM_SYSKEYUP:
583             break;
584 
585         default:
586             return 0;
587     }
588 
589     hWnd = UserHMGetHandle(pWnd);
590     pKL = pWnd->head.pti->KeyboardLayout;
591     if (!pKL)
592         return 0;
593 
594     uVirtualKey = LOBYTE(wParam);
595     pImeHotKey = IntCheckImeHotKey(MessageQueue, uVirtualKey, lParam);
596     if (pImeHotKey)
597     {
598         dwHotKeyId = pImeHotKey->dwHotKeyId;
599         hKL = pImeHotKey->hKL;
600     }
601     else
602     {
603         dwHotKeyId = INVALID_HOTKEY;
604         hKL = NULL;
605     }
606 
607     if (IME_HOTKEY_DSWITCH_FIRST <= dwHotKeyId && dwHotKeyId <= IME_HOTKEY_DSWITCH_LAST)
608     {
609         if (pKL->hkl != hKL)
610         {
611             UserPostMessage(hWnd, WM_INPUTLANGCHANGEREQUEST,
612                             ((pKL->dwFontSigs & gSystemFS) ? INPUTLANGCHANGE_SYSCHARSET : 0),
613                             (LPARAM)hKL);
614         }
615 
616         if (IntGetImeCompatFlags(pWnd->head.pti) & 0x800000)
617             return 0;
618 
619         return IPHK_HOTKEY;
620     }
621 
622     if (!IS_IMM_MODE())
623         return 0;
624 
625     if (dwHotKeyId == INVALID_HOTKEY)
626     {
627         if (!pKL->piiex)
628             return 0;
629 
630         if (pWnd->hImc)
631             pIMC = UserGetObject(gHandleTable, pWnd->hImc, TYPE_INPUTCONTEXT);
632         if (!pIMC)
633             return 0;
634 
635         if ((lParam & 0x80000000) &&
636             (pKL->piiex->ImeInfo.fdwProperty & IME_PROP_IGNORE_UPKEYS))
637         {
638             return 0;
639         }
640 
641         switch (uVirtualKey)
642         {
643             case VK_DBE_CODEINPUT:
644             case VK_DBE_ENTERCONFIGMODE:
645             case VK_DBE_ENTERWORDREGISTERMODE:
646             case VK_DBE_HIRAGANA:
647             case VK_DBE_KATAKANA:
648             case VK_DBE_NOCODEINPUT:
649             case VK_DBE_NOROMAN:
650             case VK_DBE_ROMAN:
651                 break;
652 
653             default:
654             {
655                 if (uMsg == WM_SYSKEYDOWN || uMsg == WM_SYSKEYUP)
656                 {
657                     if (uVirtualKey != VK_MENU && uVirtualKey != VK_F10)
658                         return 0;
659                 }
660 
661                 if (!(pKL->piiex->ImeInfo.fdwProperty & IME_PROP_NEED_ALTKEY))
662                 {
663                     if (uVirtualKey == VK_MENU || (lParam & 0x20000000))
664                         return 0;
665                 }
666                 break;
667             }
668         }
669     }
670 
671     if (LOBYTE(uVirtualKey) == VK_PACKET)
672         uVirtualKey = MAKELONG(wParam, GetW32ThreadInfo()->wchInjected);
673 
674     ret = co_IntImmProcessKey(hWnd, pKL->hkl, uVirtualKey, lParam, dwHotKeyId);
675 
676     if (IntGetImeCompatFlags(pWnd->head.pti) & 0x800000)
677         ret &= ~IPHK_HOTKEY;
678 
679     return ret;
680 }
681 
682 NTSTATUS
683 NTAPI
684 NtUserBuildHimcList(DWORD dwThreadId, DWORD dwCount, HIMC *phList, LPDWORD pdwCount)
685 {
686     NTSTATUS ret = STATUS_UNSUCCESSFUL;
687     DWORD dwRealCount;
688     PTHREADINFO pti;
689 
690     UserEnterExclusive();
691 
692     if (!IS_IMM_MODE())
693     {
694         ERR("!IS_IMM_MODE()\n");
695         EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
696         goto Quit;
697     }
698 
699     if (dwThreadId == 0)
700     {
701         pti = GetW32ThreadInfo();
702     }
703     else if (dwThreadId == INVALID_THREAD_ID)
704     {
705         pti = NULL;
706     }
707     else
708     {
709         pti = IntTID2PTI(UlongToHandle(dwThreadId));
710         if (!pti || !pti->rpdesk)
711             goto Quit;
712     }
713 
714     _SEH2_TRY
715     {
716         ProbeForWrite(phList, dwCount * sizeof(HIMC), 1);
717         ProbeForWrite(pdwCount, sizeof(DWORD), 1);
718         *pdwCount = dwRealCount = UserBuildHimcList(pti, dwCount, phList);
719     }
720     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
721     {
722         goto Quit;
723     }
724     _SEH2_END;
725 
726     if (dwCount < dwRealCount)
727         ret = STATUS_BUFFER_TOO_SMALL;
728     else
729         ret = STATUS_SUCCESS;
730 
731 Quit:
732     UserLeave();
733     return ret;
734 }
735 
736 static VOID FASTCALL UserSetImeConversionKeyState(PTHREADINFO pti, DWORD dwConversion)
737 {
738     HKL hKL;
739     LANGID LangID;
740     LPBYTE KeyState;
741     BOOL bAlphaNumeric, bKatakana, bHiragana, bFullShape, bRoman, bCharCode;
742 
743     if (!pti->KeyboardLayout)
744         return;
745 
746     hKL = pti->KeyboardLayout->hkl;
747     LangID = LOWORD(hKL);
748     KeyState = pti->MessageQueue->afKeyState;
749 
750     switch (PRIMARYLANGID(LangID))
751     {
752         case LANG_JAPANESE:
753             bAlphaNumeric = !(dwConversion & IME_CMODE_NATIVE);
754             bKatakana = !bAlphaNumeric && (dwConversion & IME_CMODE_KATAKANA);
755             bHiragana = !bAlphaNumeric && !(dwConversion & IME_CMODE_KATAKANA);
756             SET_KEY_DOWN(KeyState, VK_DBE_ALPHANUMERIC, bAlphaNumeric);
757             SET_KEY_LOCKED(KeyState, VK_DBE_ALPHANUMERIC, bAlphaNumeric);
758             SET_KEY_DOWN(KeyState, VK_DBE_HIRAGANA, bHiragana);
759             SET_KEY_LOCKED(KeyState, VK_DBE_HIRAGANA, bHiragana);
760             SET_KEY_DOWN(KeyState, VK_DBE_KATAKANA, bKatakana);
761             SET_KEY_LOCKED(KeyState, VK_DBE_KATAKANA, bKatakana);
762 
763             bFullShape = (dwConversion & IME_CMODE_FULLSHAPE);
764             SET_KEY_DOWN(KeyState, VK_DBE_DBCSCHAR, bFullShape);
765             SET_KEY_LOCKED(KeyState, VK_DBE_DBCSCHAR, bFullShape);
766             SET_KEY_DOWN(KeyState, VK_DBE_SBCSCHAR, !bFullShape);
767             SET_KEY_LOCKED(KeyState, VK_DBE_SBCSCHAR, !bFullShape);
768 
769             bRoman = (dwConversion & IME_CMODE_ROMAN);
770             SET_KEY_DOWN(KeyState, VK_DBE_ROMAN, bRoman);
771             SET_KEY_LOCKED(KeyState, VK_DBE_ROMAN, bRoman);
772             SET_KEY_DOWN(KeyState, VK_DBE_NOROMAN, !bRoman);
773             SET_KEY_LOCKED(KeyState, VK_DBE_NOROMAN, !bRoman);
774 
775             bCharCode = (dwConversion & IME_CMODE_CHARCODE);
776             SET_KEY_DOWN(KeyState, VK_DBE_CODEINPUT, bCharCode);
777             SET_KEY_LOCKED(KeyState, VK_DBE_CODEINPUT, bCharCode);
778             SET_KEY_DOWN(KeyState, VK_DBE_NOCODEINPUT, !bCharCode);
779             SET_KEY_LOCKED(KeyState, VK_DBE_NOCODEINPUT, !bCharCode);
780             break;
781 
782         case LANG_KOREAN:
783             SET_KEY_LOCKED(KeyState, VK_HANGUL, (dwConversion & IME_CMODE_NATIVE));
784             SET_KEY_LOCKED(KeyState, VK_JUNJA, (dwConversion & IME_CMODE_FULLSHAPE));
785             SET_KEY_LOCKED(KeyState, VK_HANJA, (dwConversion & IME_CMODE_HANJACONVERT));
786             break;
787 
788         default:
789             break;
790     }
791 }
792 
793 DWORD
794 NTAPI
795 NtUserNotifyIMEStatus(HWND hwnd, BOOL fOpen, DWORD dwConversion)
796 {
797     PWND pwnd;
798     PTHREADINFO pti;
799     HKL hKL;
800 
801     UserEnterExclusive();
802 
803     if (!IS_IMM_MODE())
804     {
805         ERR("!IS_IMM_MODE()\n");
806         goto Quit;
807     }
808 
809     pwnd = ValidateHwndNoErr(hwnd);
810     if (!pwnd)
811         goto Quit;
812 
813     pti = pwnd->head.pti;
814     if (!pti || !gptiForeground)
815         goto Quit;
816     if (pti != gptiForeground && pti->MessageQueue != gptiForeground->MessageQueue)
817         goto Quit;
818     if (ghIMC == pwnd->hImc && gfImeOpen == !!fOpen && gdwImeConversion == dwConversion)
819         goto Quit;
820 
821     ghIMC = pwnd->hImc;
822     if (ghIMC)
823     {
824         gfImeOpen = !!fOpen;
825         gdwImeConversion = dwConversion;
826         UserSetImeConversionKeyState(pti, (fOpen ? dwConversion : IME_CMODE_ALPHANUMERIC));
827     }
828 
829     if (ISITHOOKED(WH_SHELL))
830     {
831         hKL = (pti->KeyboardLayout ? pti->KeyboardLayout->hkl : NULL);
832         co_HOOK_CallHooks(WH_SHELL, HSHELL_LANGUAGE, (WPARAM)hwnd, (LPARAM)hKL);
833     }
834 
835     // TODO:
836 
837 Quit:
838     UserLeave();
839     return 0;
840 }
841 
842 BOOL
843 NTAPI
844 NtUserDisableThreadIme(
845     DWORD dwThreadID)
846 {
847     PTHREADINFO pti, ptiCurrent;
848     PPROCESSINFO ppi;
849     BOOL ret = FALSE;
850 
851     UserEnterExclusive();
852 
853     if (!IS_IMM_MODE())
854     {
855         ERR("!IS_IMM_MODE()\n");
856         EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
857         goto Quit;
858     }
859 
860     ptiCurrent = GetW32ThreadInfo();
861 
862     if (dwThreadID == INVALID_THREAD_ID)
863     {
864         ppi = ptiCurrent->ppi;
865         ppi->W32PF_flags |= W32PF_DISABLEIME;
866 
867 Retry:
868         for (pti = ppi->ptiList; pti; pti = pti->ptiSibling)
869         {
870             pti->TIF_flags |= TIF_DISABLEIME;
871 
872             if (pti->spwndDefaultIme)
873             {
874                 co_UserDestroyWindow(pti->spwndDefaultIme);
875                 pti->spwndDefaultIme = NULL;
876                 goto Retry; /* The contents of ppi->ptiList may be changed. */
877             }
878         }
879     }
880     else
881     {
882         if (dwThreadID == 0)
883         {
884             pti = ptiCurrent;
885         }
886         else
887         {
888             pti = IntTID2PTI(UlongToHandle(dwThreadID));
889 
890             /* The thread needs to reside in the current process. */
891             if (!pti || pti->ppi != ptiCurrent->ppi)
892                 goto Quit;
893         }
894 
895         pti->TIF_flags |= TIF_DISABLEIME;
896 
897         if (pti->spwndDefaultIme)
898         {
899             co_UserDestroyWindow(pti->spwndDefaultIme);
900             pti->spwndDefaultIme = NULL;
901         }
902     }
903 
904     ret = TRUE;
905 
906 Quit:
907     UserLeave();
908     return ret;
909 }
910 
911 DWORD
912 NTAPI
913 NtUserGetAppImeLevel(HWND hWnd)
914 {
915     DWORD ret = 0;
916     PWND pWnd;
917     PTHREADINFO pti;
918 
919     UserEnterShared();
920 
921     pWnd = ValidateHwndNoErr(hWnd);
922     if (!pWnd)
923         goto Quit;
924 
925     if (!IS_IMM_MODE())
926     {
927         ERR("!IS_IMM_MODE()\n");
928         EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
929         goto Quit;
930     }
931 
932     pti = PsGetCurrentThreadWin32Thread();
933     if (pWnd->head.pti->ppi == pti->ppi)
934         ret = (DWORD)(ULONG_PTR)UserGetProp(pWnd, AtomImeLevel, TRUE);
935 
936 Quit:
937     UserLeave();
938     return ret;
939 }
940 
941 BOOL FASTCALL UserGetImeInfoEx(LPVOID pUnknown, PIMEINFOEX pInfoEx, IMEINFOEXCLASS SearchType)
942 {
943     PKL pkl, pklHead;
944 
945     if (!gspklBaseLayout)
946         return FALSE;
947 
948     pkl = pklHead = gspklBaseLayout;
949 
950     /* Find the matching entry from the list and get info */
951     if (SearchType == ImeInfoExKeyboardLayout)
952     {
953         do
954         {
955             if (pInfoEx->hkl == pkl->hkl)
956             {
957                 if (!pkl->piiex)
958                     break;
959 
960                 *pInfoEx = *pkl->piiex;
961                 return TRUE;
962             }
963 
964             pkl = pkl->pklNext;
965         } while (pkl != pklHead);
966     }
967     else if (SearchType == ImeInfoExImeFileName)
968     {
969         do
970         {
971             if (pkl->piiex &&
972                 _wcsnicmp(pkl->piiex->wszImeFile, pInfoEx->wszImeFile,
973                           RTL_NUMBER_OF(pkl->piiex->wszImeFile)) == 0)
974             {
975                 *pInfoEx = *pkl->piiex;
976                 return TRUE;
977             }
978 
979             pkl = pkl->pklNext;
980         } while (pkl != pklHead);
981     }
982     else
983     {
984         /* Do nothing */
985     }
986 
987     return FALSE;
988 }
989 
990 BOOL
991 NTAPI
992 NtUserGetImeInfoEx(
993     PIMEINFOEX pImeInfoEx,
994     IMEINFOEXCLASS SearchType)
995 {
996     IMEINFOEX ImeInfoEx;
997     BOOL ret = FALSE;
998 
999     UserEnterShared();
1000 
1001     if (!IS_IMM_MODE())
1002     {
1003         ERR("!IS_IMM_MODE()\n");
1004         goto Quit;
1005     }
1006 
1007     _SEH2_TRY
1008     {
1009         ProbeForWrite(pImeInfoEx, sizeof(*pImeInfoEx), 1);
1010         ImeInfoEx = *pImeInfoEx;
1011     }
1012     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1013     {
1014         goto Quit;
1015     }
1016     _SEH2_END;
1017 
1018     ret = UserGetImeInfoEx(NULL, &ImeInfoEx, SearchType);
1019     if (!ret)
1020         goto Quit;
1021 
1022     _SEH2_TRY
1023     {
1024         *pImeInfoEx = ImeInfoEx;
1025     }
1026     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1027     {
1028         ret = FALSE;
1029     }
1030     _SEH2_END;
1031 
1032 Quit:
1033     UserLeave();
1034     return ret;
1035 }
1036 
1037 BOOL
1038 NTAPI
1039 NtUserSetAppImeLevel(HWND hWnd, DWORD dwLevel)
1040 {
1041     BOOL ret = FALSE;
1042     PWND pWnd;
1043     PTHREADINFO pti;
1044 
1045     UserEnterExclusive();
1046 
1047     if (!IS_IMM_MODE())
1048     {
1049         ERR("!IS_IMM_MODE()\n");
1050         EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1051         goto Quit;
1052     }
1053 
1054     pWnd = ValidateHwndNoErr(hWnd);
1055     if (!pWnd)
1056         goto Quit;
1057 
1058     pti = PsGetCurrentThreadWin32Thread();
1059     if (pWnd->head.pti->ppi == pti->ppi)
1060         ret = UserSetProp(pWnd, AtomImeLevel, (HANDLE)(ULONG_PTR)dwLevel, TRUE);
1061 
1062 Quit:
1063     UserLeave();
1064     return ret;
1065 }
1066 
1067 BOOL FASTCALL UserSetImeInfoEx(LPVOID pUnknown, PIMEINFOEX pImeInfoEx)
1068 {
1069     PKL pklHead, pkl;
1070 
1071     pkl = pklHead = gspklBaseLayout;
1072 
1073     do
1074     {
1075         if (pkl->hkl != pImeInfoEx->hkl)
1076         {
1077             pkl = pkl->pklNext;
1078             continue;
1079         }
1080 
1081         if (!pkl->piiex)
1082             return FALSE;
1083 
1084         if (!pkl->piiex->fLoadFlag)
1085             *pkl->piiex = *pImeInfoEx;
1086 
1087         return TRUE;
1088     } while (pkl != pklHead);
1089 
1090     return FALSE;
1091 }
1092 
1093 BOOL
1094 NTAPI
1095 NtUserSetImeInfoEx(PIMEINFOEX pImeInfoEx)
1096 {
1097     BOOL ret = FALSE;
1098     IMEINFOEX ImeInfoEx;
1099 
1100     UserEnterExclusive();
1101 
1102     if (!IS_IMM_MODE())
1103     {
1104         ERR("!IS_IMM_MODE()\n");
1105         goto Quit;
1106     }
1107 
1108     _SEH2_TRY
1109     {
1110         ProbeForRead(pImeInfoEx, sizeof(*pImeInfoEx), 1);
1111         ImeInfoEx = *pImeInfoEx;
1112     }
1113     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1114     {
1115         goto Quit;
1116     }
1117     _SEH2_END;
1118 
1119     ret = UserSetImeInfoEx(NULL, &ImeInfoEx);
1120 
1121 Quit:
1122     UserLeave();
1123     return ret;
1124 }
1125 
1126 BOOL NTAPI
1127 NtUserSetImeOwnerWindow(HWND hImeWnd, HWND hwndFocus)
1128 {
1129     BOOL ret = FALSE;
1130     PWND pImeWnd, pwndFocus, pwndTopLevel, pwnd, pwndActive;
1131     PTHREADINFO ptiIme;
1132 
1133     UserEnterExclusive();
1134 
1135     if (!IS_IMM_MODE())
1136     {
1137         ERR("!IS_IMM_MODE()\n");
1138         goto Quit;
1139     }
1140 
1141     pImeWnd = ValidateHwndNoErr(hImeWnd);
1142     if (!pImeWnd || pImeWnd->fnid != FNID_IME)
1143         goto Quit;
1144 
1145     pwndFocus = ValidateHwndNoErr(hwndFocus);
1146     if (pwndFocus)
1147     {
1148         if (IS_WND_IMELIKE(pwndFocus))
1149             goto Quit;
1150 
1151         pwndTopLevel = IntGetTopLevelWindow(pwndFocus);
1152 
1153         for (pwnd = pwndTopLevel; pwnd; pwnd = pwnd->spwndOwner)
1154         {
1155             if (pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])
1156             {
1157                 pwndTopLevel = NULL;
1158                 break;
1159             }
1160         }
1161 
1162         pImeWnd->spwndOwner = pwndTopLevel;
1163         // TODO:
1164     }
1165     else
1166     {
1167         ptiIme = pImeWnd->head.pti;
1168         pwndActive = ptiIme->MessageQueue->spwndActive;
1169 
1170         if (!pwndActive || pwndActive != pImeWnd->spwndOwner)
1171         {
1172             if (pwndActive && ptiIme == pwndActive->head.pti && !IS_WND_IMELIKE(pwndActive))
1173             {
1174                 pImeWnd->spwndOwner = pwndActive;
1175             }
1176             else
1177             {
1178                 // TODO:
1179             }
1180 
1181             // TODO:
1182         }
1183     }
1184 
1185     ret = TRUE;
1186 
1187 Quit:
1188     UserLeave();
1189     return ret;
1190 }
1191 
1192 PVOID
1193 AllocInputContextObject(PDESKTOP pDesk,
1194                         PTHREADINFO pti,
1195                         SIZE_T Size,
1196                         PVOID* HandleOwner)
1197 {
1198     PTHRDESKHEAD ObjHead;
1199 
1200     ASSERT(Size > sizeof(*ObjHead));
1201     ASSERT(pti != NULL);
1202 
1203     if (!pDesk)
1204         pDesk = pti->rpdesk;
1205 
1206     ObjHead = DesktopHeapAlloc(pDesk, Size);
1207     if (!ObjHead)
1208         return NULL;
1209 
1210     RtlZeroMemory(ObjHead, Size);
1211 
1212     ObjHead->pSelf = ObjHead;
1213     ObjHead->rpdesk = pDesk;
1214     ObjHead->pti = pti;
1215     IntReferenceThreadInfo(pti);
1216     *HandleOwner = pti;
1217     pti->ppi->UserHandleCount++;
1218 
1219     return ObjHead;
1220 }
1221 
1222 VOID UserFreeInputContext(PVOID Object)
1223 {
1224     PTHRDESKHEAD ObjHead = Object;
1225     PDESKTOP pDesk = ObjHead->rpdesk;
1226     PIMC pIMC = Object, *ppIMC;
1227     PTHREADINFO pti;
1228 
1229     if (!pIMC)
1230         return;
1231 
1232     /* Find the IMC in the list and remove it */
1233     pti = pIMC->head.pti;
1234     for (ppIMC = &pti->spDefaultImc; *ppIMC; ppIMC = &(*ppIMC)->pImcNext)
1235     {
1236         if (*ppIMC == pIMC)
1237         {
1238             *ppIMC = pIMC->pImcNext;
1239             break;
1240         }
1241     }
1242 
1243     DesktopHeapFree(pDesk, Object);
1244 
1245     pti->ppi->UserHandleCount--;
1246     IntDereferenceThreadInfo(pti);
1247 }
1248 
1249 BOOLEAN UserDestroyInputContext(PVOID Object)
1250 {
1251     PIMC pIMC = Object;
1252 
1253     if (!pIMC)
1254         return TRUE;
1255 
1256     UserMarkObjectDestroy(pIMC);
1257 
1258     return UserDeleteObject(UserHMGetHandle(pIMC), TYPE_INPUTCONTEXT);
1259 }
1260 
1261 BOOL NTAPI NtUserDestroyInputContext(HIMC hIMC)
1262 {
1263     PIMC pIMC;
1264     BOOL ret = FALSE;
1265     HWND *phwnd;
1266     PWND pWnd;
1267     PWINDOWLIST pwl;
1268     PTHREADINFO pti;
1269 
1270     UserEnterExclusive();
1271 
1272     if (!IS_IMM_MODE())
1273     {
1274         ERR("!IS_IMM_MODE()\n");
1275         EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1276         goto Quit;
1277     }
1278 
1279     pIMC = UserGetObjectNoErr(gHandleTable, hIMC, TYPE_INPUTCONTEXT);
1280     if (!pIMC)
1281         goto Quit;
1282 
1283     pti = pIMC->head.pti;
1284     if (pti != GetW32ThreadInfo() || pIMC == pti->spDefaultImc)
1285         goto Quit;
1286 
1287     UserMarkObjectDestroy(pIMC);
1288 
1289     pwl = IntBuildHwndList(pti->rpdesk->pDeskInfo->spwnd->spwndChild,
1290                            IACE_CHILDREN | IACE_LIST, pti);
1291     if (pwl)
1292     {
1293         for (phwnd = pwl->ahwnd; *phwnd != HWND_TERMINATOR; ++phwnd)
1294         {
1295             pWnd = ValidateHwndNoErr(*phwnd);
1296             if (!pWnd)
1297                 continue;
1298 
1299             if (pWnd->hImc == hIMC)
1300                 IntAssociateInputContext(pWnd, pti->spDefaultImc);
1301         }
1302 
1303         IntFreeHwndList(pwl);
1304     }
1305 
1306     ret = UserDeleteObject(hIMC, TYPE_INPUTCONTEXT);
1307 
1308 Quit:
1309     UserLeave();
1310     return ret;
1311 }
1312 
1313 PIMC FASTCALL UserCreateInputContext(ULONG_PTR dwClientImcData)
1314 {
1315     PIMC pIMC;
1316     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1317     PDESKTOP pdesk = pti->rpdesk;
1318 
1319     if (!IS_IMM_MODE() || (pti->TIF_flags & TIF_DISABLEIME)) // Disabled?
1320     {
1321         ERR("IME is disabled\n");
1322         return NULL;
1323     }
1324 
1325     if (!pdesk) // No desktop?
1326         return NULL;
1327 
1328     // pti->spDefaultImc should be already set if non-first time.
1329     if (dwClientImcData && !pti->spDefaultImc)
1330         return NULL;
1331 
1332     // Create an input context user object.
1333     pIMC = UserCreateObject(gHandleTable, pdesk, pti, NULL, TYPE_INPUTCONTEXT, sizeof(IMC));
1334     if (!pIMC)
1335         return NULL;
1336 
1337     // Release the extra reference (UserCreateObject added 2 references).
1338     UserDereferenceObject(pIMC);
1339 
1340     if (dwClientImcData) // Non-first time.
1341     {
1342         // Insert pIMC to the second position (non-default) of the list.
1343         pIMC->pImcNext = pti->spDefaultImc->pImcNext;
1344         pti->spDefaultImc->pImcNext = pIMC;
1345     }
1346     else // First time. It's the default IMC.
1347     {
1348         // Add the first one (default) to the list.
1349         pti->spDefaultImc = pIMC;
1350         pIMC->pImcNext = NULL;
1351     }
1352 
1353     pIMC->dwClientImcData = dwClientImcData; // Set it.
1354     return pIMC;
1355 }
1356 
1357 HIMC
1358 NTAPI
1359 NtUserCreateInputContext(ULONG_PTR dwClientImcData)
1360 {
1361     PIMC pIMC;
1362     HIMC ret = NULL;
1363 
1364     if (!dwClientImcData)
1365         return NULL;
1366 
1367     UserEnterExclusive();
1368 
1369     if (!IS_IMM_MODE())
1370     {
1371         ERR("!IS_IMM_MODE()\n");
1372         goto Quit;
1373     }
1374 
1375     pIMC = UserCreateInputContext(dwClientImcData);
1376     if (pIMC)
1377         ret = UserHMGetHandle(pIMC);
1378 
1379 Quit:
1380     UserLeave();
1381     return ret;
1382 }
1383 
1384 DWORD FASTCALL IntAssociateInputContextEx(PWND pWnd, PIMC pIMC, DWORD dwFlags)
1385 {
1386     DWORD ret = 0;
1387     PWINDOWLIST pwl;
1388     BOOL bIgnoreNullImc = (dwFlags & IACE_IGNORENOCONTEXT);
1389     PTHREADINFO pti = pWnd->head.pti;
1390     PWND pwndTarget, pwndFocus = pti->MessageQueue->spwndFocus;
1391     HWND *phwnd;
1392     HIMC hIMC;
1393 
1394     if (dwFlags & IACE_DEFAULT)
1395     {
1396         pIMC = pti->spDefaultImc;
1397     }
1398     else
1399     {
1400         if (pIMC && pti != pIMC->head.pti)
1401             return 2;
1402     }
1403 
1404     if (pWnd->head.pti->ppi != GetW32ThreadInfo()->ppi ||
1405         (pIMC && pIMC->head.rpdesk != pWnd->head.rpdesk))
1406     {
1407         return 2;
1408     }
1409 
1410     if ((dwFlags & IACE_CHILDREN) && pWnd->spwndChild)
1411     {
1412         pwl = IntBuildHwndList(pWnd->spwndChild, IACE_CHILDREN | IACE_LIST, pti);
1413         if (pwl)
1414         {
1415             for (phwnd = pwl->ahwnd; *phwnd != HWND_TERMINATOR; ++phwnd)
1416             {
1417                 pwndTarget = ValidateHwndNoErr(*phwnd);
1418                 if (!pwndTarget)
1419                     continue;
1420 
1421                 hIMC = (pIMC ? UserHMGetHandle(pIMC) : NULL);
1422                 if (pwndTarget->hImc == hIMC || (bIgnoreNullImc && !pwndTarget->hImc))
1423                     continue;
1424 
1425                 IntAssociateInputContext(pwndTarget, pIMC);
1426                 if (pwndTarget == pwndFocus)
1427                     ret = 1;
1428             }
1429 
1430             IntFreeHwndList(pwl);
1431         }
1432     }
1433 
1434     if (!bIgnoreNullImc || pWnd->hImc)
1435     {
1436         hIMC = (pIMC ? UserHMGetHandle(pIMC) : NULL);
1437         if (pWnd->hImc != hIMC)
1438         {
1439             IntAssociateInputContext(pWnd, pIMC);
1440             if (pWnd == pwndFocus)
1441                 ret = 1;
1442         }
1443     }
1444 
1445     return ret;
1446 }
1447 
1448 DWORD
1449 NTAPI
1450 NtUserAssociateInputContext(HWND hWnd, HIMC hIMC, DWORD dwFlags)
1451 {
1452     DWORD ret = 2;
1453     PWND pWnd;
1454     PIMC pIMC;
1455 
1456     UserEnterExclusive();
1457 
1458     if (!IS_IMM_MODE())
1459     {
1460         ERR("!IS_IMM_MODE()\n");
1461         goto Quit;
1462     }
1463 
1464     pWnd = ValidateHwndNoErr(hWnd);
1465     if (!pWnd)
1466         goto Quit;
1467 
1468     pIMC = (hIMC ? UserGetObjectNoErr(gHandleTable, hIMC, TYPE_INPUTCONTEXT) : NULL);
1469     ret = IntAssociateInputContextEx(pWnd, pIMC, dwFlags);
1470 
1471 Quit:
1472     UserLeave();
1473     return ret;
1474 }
1475 
1476 BOOL FASTCALL UserUpdateInputContext(PIMC pIMC, DWORD dwType, DWORD_PTR dwValue)
1477 {
1478     PTHREADINFO pti = GetW32ThreadInfo();
1479     PTHREADINFO ptiIMC = pIMC->head.pti;
1480 
1481     if (pti->ppi != ptiIMC->ppi) // Different process?
1482         return FALSE;
1483 
1484     switch (dwType)
1485     {
1486         case UIC_CLIENTIMCDATA:
1487             if (pIMC->dwClientImcData)
1488                 return FALSE; // Already set
1489 
1490             pIMC->dwClientImcData = dwValue;
1491             break;
1492 
1493         case UIC_IMEWINDOW:
1494             if (!ValidateHwndNoErr((HWND)dwValue))
1495                 return FALSE; // Invalid HWND
1496 
1497             pIMC->hImeWnd = (HWND)dwValue;
1498             break;
1499 
1500         default:
1501             return FALSE;
1502     }
1503 
1504     return TRUE;
1505 }
1506 
1507 BOOL
1508 NTAPI
1509 NtUserUpdateInputContext(
1510     HIMC hIMC,
1511     DWORD dwType,
1512     DWORD_PTR dwValue)
1513 {
1514     PIMC pIMC;
1515     BOOL ret = FALSE;
1516 
1517     UserEnterExclusive();
1518 
1519     if (!IS_IMM_MODE())
1520     {
1521         ERR("!IS_IMM_MODE()\n");
1522         goto Quit;
1523     }
1524 
1525     pIMC = UserGetObject(gHandleTable, hIMC, TYPE_INPUTCONTEXT);
1526     if (!pIMC)
1527         goto Quit;
1528 
1529     ret = UserUpdateInputContext(pIMC, dwType, dwValue);
1530 
1531 Quit:
1532     UserLeave();
1533     return ret;
1534 }
1535 
1536 DWORD_PTR
1537 NTAPI
1538 NtUserQueryInputContext(HIMC hIMC, DWORD dwType)
1539 {
1540     PIMC pIMC;
1541     PTHREADINFO ptiIMC;
1542     DWORD_PTR ret = 0;
1543 
1544     UserEnterExclusive();
1545 
1546     if (!IS_IMM_MODE())
1547     {
1548         ERR("!IS_IMM_MODE()\n");
1549         goto Quit;
1550     }
1551 
1552     pIMC = UserGetObject(gHandleTable, hIMC, TYPE_INPUTCONTEXT);
1553     if (!pIMC)
1554         goto Quit;
1555 
1556     ptiIMC = pIMC->head.pti;
1557 
1558     switch (dwType)
1559     {
1560         case QIC_INPUTPROCESSID:
1561             ret = (DWORD_PTR)PsGetThreadProcessId(ptiIMC->pEThread);
1562             break;
1563 
1564         case QIC_INPUTTHREADID:
1565             ret = (DWORD_PTR)PsGetThreadId(ptiIMC->pEThread);
1566             break;
1567 
1568         case QIC_DEFAULTWINDOWIME:
1569             if (ptiIMC->spwndDefaultIme)
1570                 ret = (DWORD_PTR)UserHMGetHandle(ptiIMC->spwndDefaultIme);
1571             break;
1572 
1573         case QIC_DEFAULTIMC:
1574             if (ptiIMC->spDefaultImc)
1575                 ret = (DWORD_PTR)UserHMGetHandle(ptiIMC->spDefaultImc);
1576             break;
1577     }
1578 
1579 Quit:
1580     UserLeave();
1581     return ret;
1582 }
1583 
1584 /* EOF */
1585