xref: /reactos/win32ss/user/ntuser/ime.c (revision 57b9c6b2)
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 // Win: AddImeHotKey
138 static VOID FASTCALL IntAddImeHotKey(PIMEHOTKEY *ppList, PIMEHOTKEY pHotKey)
139 {
140     PIMEHOTKEY pNode;
141 
142     if (!*ppList)
143     {
144         *ppList = pHotKey;
145         return;
146     }
147 
148     for (pNode = *ppList; pNode; pNode = pNode->pNext)
149     {
150         if (!pNode->pNext)
151         {
152             pNode->pNext = pHotKey;
153             return;
154         }
155     }
156 }
157 
158 static PIMEHOTKEY FASTCALL IntGetImeHotKeyById(PIMEHOTKEY pList, DWORD dwHotKeyId)
159 {
160     PIMEHOTKEY pNode;
161     for (pNode = pList; pNode; pNode = pNode->pNext)
162     {
163         if (pNode->dwHotKeyId == dwHotKeyId)
164             return pNode;
165     }
166     return NULL;
167 }
168 
169 static PIMEHOTKEY APIENTRY
170 IntGetImeHotKeyByKeyAndLang(PIMEHOTKEY pList, UINT uModKeys, UINT uLeftRight,
171                             UINT uVirtualKey, LANGID TargetLangId)
172 {
173     PIMEHOTKEY pNode;
174     LANGID LangID;
175     UINT uModifiers;
176 
177     for (pNode = pList; pNode; pNode = pNode->pNext)
178     {
179         if (pNode->uVirtualKey != uVirtualKey)
180             continue;
181 
182         LangID = IntGetImeHotKeyLangId(pNode->dwHotKeyId);
183         if (LangID != TargetLangId)
184             continue;
185 
186         uModifiers = pNode->uModifiers;
187         if (uModifiers & MOD_IGNORE_ALL_MODIFIER)
188             return pNode;
189 
190         if ((uModifiers & MOD_KEYS) != uModKeys)
191             continue;
192 
193         if ((uModifiers & uLeftRight) || (uModifiers & MOD_LEFT_RIGHT) == uLeftRight)
194             return pNode;
195     }
196 
197     return NULL;
198 }
199 
200 static VOID FASTCALL IntDeleteImeHotKey(PIMEHOTKEY *ppList, PIMEHOTKEY pHotKey)
201 {
202     PIMEHOTKEY pNode;
203 
204     if (*ppList == pHotKey)
205     {
206         *ppList = pHotKey->pNext;
207         ExFreePoolWithTag(pHotKey, USERTAG_IMEHOTKEY);
208         return;
209     }
210 
211     for (pNode = *ppList; pNode; pNode = pNode->pNext)
212     {
213         if (pNode->pNext == pHotKey)
214         {
215             pNode->pNext = pHotKey->pNext;
216             ExFreePoolWithTag(pHotKey, USERTAG_IMEHOTKEY);
217             return;
218         }
219     }
220 }
221 
222 PIMEHOTKEY
223 IntGetImeHotKeyByKey(PIMEHOTKEY pList, UINT uModKeys, UINT uLeftRight, UINT uVirtualKey)
224 {
225     PIMEHOTKEY pNode, ret = NULL;
226     PTHREADINFO pti = GetW32ThreadInfo();
227     LANGID LangId;
228     HKL hKL = IntGetActiveKeyboardLayout();
229     BOOL fKorean = (PRIMARYLANGID(LOWORD(hKL)) == LANG_KOREAN);
230     UINT nScore, nMaxScore = 0;
231 
232     for (pNode = pList; pNode; pNode = pNode->pNext)
233     {
234         if (pNode->uVirtualKey != uVirtualKey)
235             continue;
236 
237         if ((pNode->uModifiers & MOD_IGNORE_ALL_MODIFIER))
238         {
239             ;
240         }
241         else if ((pNode->uModifiers & MOD_KEYS) != uModKeys)
242         {
243             continue;
244         }
245         else if ((pNode->uModifiers & uLeftRight) ||
246                  (pNode->uModifiers & MOD_LEFT_RIGHT) == uLeftRight)
247         {
248             ;
249         }
250         else
251         {
252             continue;
253         }
254 
255         LangId = IntGetImeHotKeyLangId(pNode->dwHotKeyId);
256         nScore = IntGetImeHotKeyLanguageScore(hKL, LangId);
257         if (nScore >= 3)
258             return pNode;
259 
260         if (fKorean)
261             continue;
262 
263         if (nScore == 0)
264         {
265             if (pNode->dwHotKeyId == IME_CHOTKEY_IME_NONIME_TOGGLE ||
266                 pNode->dwHotKeyId == IME_THOTKEY_IME_NONIME_TOGGLE)
267             {
268                 if (LOWORD(pti->hklPrev) == LangId)
269                     return pNode;
270             }
271         }
272 
273         if (nMaxScore < nScore)
274         {
275             nMaxScore = nScore;
276             ret = pNode;
277         }
278     }
279 
280     return ret;
281 }
282 
283 // Win: CheckImeHotKey
284 PIMEHOTKEY IntCheckImeHotKey(PUSER_MESSAGE_QUEUE MessageQueue, UINT uVirtualKey, LPARAM lParam)
285 {
286     PIMEHOTKEY pHotKey;
287     UINT uModifiers;
288     BOOL bKeyUp = (lParam & 0x80000000);
289     const BYTE *KeyState = MessageQueue->afKeyState;
290     static UINT s_uKeyUpVKey = 0;
291 
292     if (bKeyUp)
293     {
294         if (s_uKeyUpVKey != uVirtualKey)
295         {
296             s_uKeyUpVKey = 0;
297             return NULL;
298         }
299 
300         s_uKeyUpVKey = 0;
301     }
302 
303     uModifiers = 0;
304     if (IS_KEY_DOWN(KeyState, VK_LSHIFT))   uModifiers |= (MOD_SHIFT | MOD_LEFT);
305     if (IS_KEY_DOWN(KeyState, VK_RSHIFT))   uModifiers |= (MOD_SHIFT | MOD_RIGHT);
306     if (IS_KEY_DOWN(KeyState, VK_LCONTROL)) uModifiers |= (MOD_CONTROL | MOD_LEFT);
307     if (IS_KEY_DOWN(KeyState, VK_RCONTROL)) uModifiers |= (MOD_CONTROL | MOD_RIGHT);
308     if (IS_KEY_DOWN(KeyState, VK_LMENU))    uModifiers |= (MOD_ALT | MOD_LEFT);
309     if (IS_KEY_DOWN(KeyState, VK_RMENU))    uModifiers |= (MOD_ALT | MOD_RIGHT);
310 
311     pHotKey = IntGetImeHotKeyByKey(gpImeHotKeyList,
312                                    (uModifiers & MOD_KEYS),
313                                    (uModifiers & MOD_LEFT_RIGHT),
314                                    uVirtualKey);
315     if (pHotKey)
316     {
317         if (bKeyUp)
318         {
319             if (pHotKey->uModifiers & MOD_ON_KEYUP)
320                 return pHotKey;
321         }
322         else
323         {
324             if (pHotKey->uModifiers & MOD_ON_KEYUP)
325                 s_uKeyUpVKey = uVirtualKey;
326             else
327                 return pHotKey;
328         }
329     }
330 
331     return NULL;
332 }
333 
334 // Win: FreeImeHotKeys
335 VOID FASTCALL IntFreeImeHotKeys(VOID)
336 {
337     PIMEHOTKEY pNode, pNext;
338     for (pNode = gpImeHotKeyList; pNode; pNode = pNext)
339     {
340         pNext = pNode->pNext;
341         ExFreePoolWithTag(pNode, USERTAG_IMEHOTKEY);
342     }
343     gpImeHotKeyList = NULL;
344 }
345 
346 // Win: SetImeHotKey
347 static BOOL APIENTRY
348 IntSetImeHotKey(DWORD dwHotKeyId, UINT uModifiers, UINT uVirtualKey, HKL hKL, DWORD dwAction)
349 {
350     PIMEHOTKEY pNode;
351     LANGID LangId;
352 
353     switch (dwAction)
354     {
355         case SETIMEHOTKEY_DELETE:
356             pNode = IntGetImeHotKeyById(gpImeHotKeyList, dwHotKeyId);
357             if (!pNode)
358             {
359                 ERR("dwHotKeyId: 0x%lX\n", dwHotKeyId);
360                 return FALSE;
361             }
362 
363             IntDeleteImeHotKey(&gpImeHotKeyList, pNode);
364             return TRUE;
365 
366         case SETIMEHOTKEY_ADD:
367             if (uVirtualKey == VK_PACKET)
368                 return FALSE;
369 
370             LangId = IntGetImeHotKeyLangId(dwHotKeyId);
371             if (LangId == LANGID_KOREAN)
372                 return FALSE;
373 
374             pNode = IntGetImeHotKeyByKeyAndLang(gpImeHotKeyList,
375                                                 (uModifiers & MOD_KEYS),
376                                                 (uModifiers & MOD_LEFT_RIGHT),
377                                                 uVirtualKey, LangId);
378             if (!pNode)
379                 pNode = IntGetImeHotKeyById(gpImeHotKeyList, dwHotKeyId);
380 
381             if (pNode)
382             {
383                 pNode->uModifiers = uModifiers;
384                 pNode->uVirtualKey = uVirtualKey;
385                 pNode->hKL = hKL;
386                 return TRUE;
387             }
388 
389             pNode = ExAllocatePoolWithTag(PagedPool, sizeof(IMEHOTKEY), USERTAG_IMEHOTKEY);
390             if (!pNode)
391                 return FALSE;
392 
393             pNode->pNext = NULL;
394             pNode->dwHotKeyId = dwHotKeyId;
395             pNode->uModifiers = uModifiers;
396             pNode->uVirtualKey = uVirtualKey;
397             pNode->hKL = hKL;
398             IntAddImeHotKey(&gpImeHotKeyList, pNode);
399             return TRUE;
400 
401         case SETIMEHOTKEY_DELETEALL:
402             IntFreeImeHotKeys();
403             return TRUE;
404 
405         default:
406             return FALSE;
407     }
408 }
409 
410 BOOL NTAPI
411 NtUserGetImeHotKey(DWORD dwHotKeyId, LPUINT lpuModifiers, LPUINT lpuVirtualKey, LPHKL lphKL)
412 {
413     PIMEHOTKEY pNode = NULL;
414 
415     UserEnterExclusive();
416 
417     _SEH2_TRY
418     {
419         ProbeForWrite(lpuModifiers, sizeof(UINT), 1);
420         ProbeForWrite(lpuVirtualKey, sizeof(UINT), 1);
421         if (lphKL)
422             ProbeForWrite(lphKL, sizeof(HKL), 1);
423     }
424     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
425     {
426         goto Quit;
427     }
428     _SEH2_END;
429 
430     pNode = IntGetImeHotKeyById(gpImeHotKeyList, dwHotKeyId);
431     if (!pNode)
432         goto Quit;
433 
434     _SEH2_TRY
435     {
436         *lpuModifiers = pNode->uModifiers;
437         *lpuVirtualKey = pNode->uVirtualKey;
438         if (lphKL)
439             *lphKL = pNode->hKL;
440     }
441     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
442     {
443         pNode = NULL;
444     }
445     _SEH2_END;
446 
447 Quit:
448     UserLeave();
449     return !!pNode;
450 }
451 
452 BOOL
453 NTAPI
454 NtUserSetImeHotKey(
455     DWORD  dwHotKeyId,
456     UINT   uModifiers,
457     UINT   uVirtualKey,
458     HKL    hKL,
459     DWORD  dwAction)
460 {
461     BOOL ret;
462     UserEnterExclusive();
463     ret = IntSetImeHotKey(dwHotKeyId, uModifiers, uVirtualKey, hKL, dwAction);
464     UserLeave();
465     return ret;
466 }
467 
468 DWORD
469 NTAPI
470 NtUserCheckImeHotKey(UINT uVirtualKey, LPARAM lParam)
471 {
472     PIMEHOTKEY pNode;
473     DWORD ret = INVALID_HOTKEY;
474 
475     UserEnterExclusive();
476 
477     if (!gpqForeground || !IS_IMM_MODE())
478         goto Quit;
479 
480     pNode = IntCheckImeHotKey(gpqForeground, uVirtualKey, lParam);
481     if (pNode)
482         ret = pNode->dwHotKeyId;
483 
484 Quit:
485     UserLeave();
486     return ret;
487 }
488 
489 PWND FASTCALL IntGetTopLevelWindow(PWND pwnd)
490 {
491     if (!pwnd)
492         return NULL;
493 
494     while (pwnd->style & WS_CHILD)
495         pwnd = pwnd->spwndParent;
496 
497     return pwnd;
498 }
499 
500 // Win: AssociateInputContext
501 HIMC FASTCALL IntAssociateInputContext(PWND pWnd, PIMC pImc)
502 {
503     HIMC hOldImc = pWnd->hImc;
504     pWnd->hImc = (pImc ? UserHMGetHandle(pImc) : NULL);
505     return hOldImc;
506 }
507 
508 DWORD
509 NTAPI
510 NtUserSetThreadLayoutHandles(HKL hNewKL, HKL hOldKL)
511 {
512     PTHREADINFO pti;
513     PKL pOldKL, pNewKL;
514 
515     UserEnterExclusive();
516 
517     pti = GetW32ThreadInfo();
518     pOldKL = pti->KeyboardLayout;
519     if (pOldKL && pOldKL->hkl != hOldKL)
520         goto Quit;
521 
522     pNewKL = UserHklToKbl(hNewKL);
523     if (!pNewKL)
524         goto Quit;
525 
526     if (IS_IME_HKL(hNewKL) != IS_IME_HKL(hOldKL))
527         pti->hklPrev = hOldKL;
528 
529     pti->KeyboardLayout = pNewKL;
530 
531 Quit:
532     UserLeave();
533     return 0;
534 }
535 
536 // Win: BuildHimcList
537 DWORD FASTCALL UserBuildHimcList(PTHREADINFO pti, DWORD dwCount, HIMC *phList)
538 {
539     PIMC pIMC;
540     DWORD dwRealCount = 0;
541 
542     if (pti)
543     {
544         for (pIMC = pti->spDefaultImc; pIMC; pIMC = pIMC->pImcNext)
545         {
546             if (dwRealCount < dwCount)
547                 phList[dwRealCount] = UserHMGetHandle(pIMC);
548 
549             ++dwRealCount;
550         }
551     }
552     else
553     {
554         for (pti = GetW32ThreadInfo()->ppi->ptiList; pti; pti = pti->ptiSibling)
555         {
556             for (pIMC = pti->spDefaultImc; pIMC; pIMC = pIMC->pImcNext)
557             {
558                 if (dwRealCount < dwCount)
559                     phList[dwRealCount] = UserHMGetHandle(pIMC);
560 
561                 ++dwRealCount;
562             }
563         }
564     }
565 
566     return dwRealCount;
567 }
568 
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 = GetW32ThreadInfo();
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         goto Quit;
729     }
730     _SEH2_END;
731 
732     if (dwCount < dwRealCount)
733         ret = STATUS_BUFFER_TOO_SMALL;
734     else
735         ret = STATUS_SUCCESS;
736 
737 Quit:
738     UserLeave();
739     return ret;
740 }
741 
742 static VOID FASTCALL UserSetImeConversionKeyState(PTHREADINFO pti, DWORD dwConversion)
743 {
744     HKL hKL;
745     LANGID LangID;
746     LPBYTE KeyState;
747     BOOL bAlphaNumeric, bKatakana, bHiragana, bFullShape, bRoman, bCharCode;
748 
749     if (!pti->KeyboardLayout)
750         return;
751 
752     hKL = pti->KeyboardLayout->hkl;
753     LangID = LOWORD(hKL);
754     KeyState = pti->MessageQueue->afKeyState;
755 
756     switch (PRIMARYLANGID(LangID))
757     {
758         case LANG_JAPANESE:
759             bAlphaNumeric = !(dwConversion & IME_CMODE_NATIVE);
760             bKatakana = !bAlphaNumeric && (dwConversion & IME_CMODE_KATAKANA);
761             bHiragana = !bAlphaNumeric && !(dwConversion & IME_CMODE_KATAKANA);
762             SET_KEY_DOWN(KeyState, VK_DBE_ALPHANUMERIC, bAlphaNumeric);
763             SET_KEY_LOCKED(KeyState, VK_DBE_ALPHANUMERIC, bAlphaNumeric);
764             SET_KEY_DOWN(KeyState, VK_DBE_HIRAGANA, bHiragana);
765             SET_KEY_LOCKED(KeyState, VK_DBE_HIRAGANA, bHiragana);
766             SET_KEY_DOWN(KeyState, VK_DBE_KATAKANA, bKatakana);
767             SET_KEY_LOCKED(KeyState, VK_DBE_KATAKANA, bKatakana);
768 
769             bFullShape = (dwConversion & IME_CMODE_FULLSHAPE);
770             SET_KEY_DOWN(KeyState, VK_DBE_DBCSCHAR, bFullShape);
771             SET_KEY_LOCKED(KeyState, VK_DBE_DBCSCHAR, bFullShape);
772             SET_KEY_DOWN(KeyState, VK_DBE_SBCSCHAR, !bFullShape);
773             SET_KEY_LOCKED(KeyState, VK_DBE_SBCSCHAR, !bFullShape);
774 
775             bRoman = (dwConversion & IME_CMODE_ROMAN);
776             SET_KEY_DOWN(KeyState, VK_DBE_ROMAN, bRoman);
777             SET_KEY_LOCKED(KeyState, VK_DBE_ROMAN, bRoman);
778             SET_KEY_DOWN(KeyState, VK_DBE_NOROMAN, !bRoman);
779             SET_KEY_LOCKED(KeyState, VK_DBE_NOROMAN, !bRoman);
780 
781             bCharCode = (dwConversion & IME_CMODE_CHARCODE);
782             SET_KEY_DOWN(KeyState, VK_DBE_CODEINPUT, bCharCode);
783             SET_KEY_LOCKED(KeyState, VK_DBE_CODEINPUT, bCharCode);
784             SET_KEY_DOWN(KeyState, VK_DBE_NOCODEINPUT, !bCharCode);
785             SET_KEY_LOCKED(KeyState, VK_DBE_NOCODEINPUT, !bCharCode);
786             break;
787 
788         case LANG_KOREAN:
789             SET_KEY_LOCKED(KeyState, VK_HANGUL, (dwConversion & IME_CMODE_NATIVE));
790             SET_KEY_LOCKED(KeyState, VK_JUNJA, (dwConversion & IME_CMODE_FULLSHAPE));
791             SET_KEY_LOCKED(KeyState, VK_HANJA, (dwConversion & IME_CMODE_HANJACONVERT));
792             break;
793 
794         default:
795             break;
796     }
797 }
798 
799 DWORD
800 NTAPI
801 NtUserNotifyIMEStatus(HWND hwnd, BOOL fOpen, DWORD dwConversion)
802 {
803     PWND pwnd;
804     PTHREADINFO pti;
805     HKL hKL;
806 
807     UserEnterExclusive();
808 
809     if (!IS_IMM_MODE())
810     {
811         ERR("!IS_IMM_MODE()\n");
812         goto Quit;
813     }
814 
815     pwnd = ValidateHwndNoErr(hwnd);
816     if (!pwnd)
817         goto Quit;
818 
819     pti = pwnd->head.pti;
820     if (!pti || !gptiForeground)
821         goto Quit;
822     if (pti != gptiForeground && pti->MessageQueue != gptiForeground->MessageQueue)
823         goto Quit;
824     if (ghIMC == pwnd->hImc && gfImeOpen == !!fOpen && gdwImeConversion == dwConversion)
825         goto Quit;
826 
827     ghIMC = pwnd->hImc;
828     if (ghIMC)
829     {
830         gfImeOpen = !!fOpen;
831         gdwImeConversion = dwConversion;
832         UserSetImeConversionKeyState(pti, (fOpen ? dwConversion : IME_CMODE_ALPHANUMERIC));
833     }
834 
835     if (ISITHOOKED(WH_SHELL))
836     {
837         hKL = (pti->KeyboardLayout ? pti->KeyboardLayout->hkl : NULL);
838         co_HOOK_CallHooks(WH_SHELL, HSHELL_LANGUAGE, (WPARAM)hwnd, (LPARAM)hKL);
839     }
840 
841     // TODO:
842 
843 Quit:
844     UserLeave();
845     return 0;
846 }
847 
848 BOOL
849 NTAPI
850 NtUserDisableThreadIme(
851     DWORD dwThreadID)
852 {
853     PTHREADINFO pti, ptiCurrent;
854     PPROCESSINFO ppi;
855     BOOL ret = FALSE;
856 
857     UserEnterExclusive();
858 
859     if (!IS_IMM_MODE())
860     {
861         ERR("!IS_IMM_MODE()\n");
862         EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
863         goto Quit;
864     }
865 
866     ptiCurrent = GetW32ThreadInfo();
867 
868     if (dwThreadID == INVALID_THREAD_ID)
869     {
870         ppi = ptiCurrent->ppi;
871         ppi->W32PF_flags |= W32PF_DISABLEIME;
872 
873 Retry:
874         for (pti = ppi->ptiList; pti; pti = pti->ptiSibling)
875         {
876             pti->TIF_flags |= TIF_DISABLEIME;
877 
878             if (pti->spwndDefaultIme)
879             {
880                 co_UserDestroyWindow(pti->spwndDefaultIme);
881                 pti->spwndDefaultIme = NULL;
882                 goto Retry; /* The contents of ppi->ptiList may be changed. */
883             }
884         }
885     }
886     else
887     {
888         if (dwThreadID == 0)
889         {
890             pti = ptiCurrent;
891         }
892         else
893         {
894             pti = IntTID2PTI(UlongToHandle(dwThreadID));
895 
896             /* The thread needs to reside in the current process. */
897             if (!pti || pti->ppi != ptiCurrent->ppi)
898                 goto Quit;
899         }
900 
901         pti->TIF_flags |= TIF_DISABLEIME;
902 
903         if (pti->spwndDefaultIme)
904         {
905             co_UserDestroyWindow(pti->spwndDefaultIme);
906             pti->spwndDefaultIme = NULL;
907         }
908     }
909 
910     ret = TRUE;
911 
912 Quit:
913     UserLeave();
914     return ret;
915 }
916 
917 DWORD
918 NTAPI
919 NtUserGetAppImeLevel(HWND hWnd)
920 {
921     DWORD ret = 0;
922     PWND pWnd;
923     PTHREADINFO pti;
924 
925     UserEnterShared();
926 
927     pWnd = ValidateHwndNoErr(hWnd);
928     if (!pWnd)
929         goto Quit;
930 
931     if (!IS_IMM_MODE())
932     {
933         ERR("!IS_IMM_MODE()\n");
934         EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
935         goto Quit;
936     }
937 
938     pti = PsGetCurrentThreadWin32Thread();
939     if (pWnd->head.pti->ppi == pti->ppi)
940         ret = (DWORD)(ULONG_PTR)UserGetProp(pWnd, AtomImeLevel, TRUE);
941 
942 Quit:
943     UserLeave();
944     return ret;
945 }
946 
947 // Win: GetImeInfoEx
948 BOOL FASTCALL UserGetImeInfoEx(LPVOID pUnknown, PIMEINFOEX pInfoEx, IMEINFOEXCLASS SearchType)
949 {
950     PKL pkl, pklHead;
951 
952     if (!gspklBaseLayout)
953         return FALSE;
954 
955     pkl = pklHead = gspklBaseLayout;
956 
957     /* Find the matching entry from the list and get info */
958     if (SearchType == ImeInfoExKeyboardLayout)
959     {
960         do
961         {
962             if (pInfoEx->hkl == pkl->hkl)
963             {
964                 if (!pkl->piiex)
965                     break;
966 
967                 *pInfoEx = *pkl->piiex;
968                 return TRUE;
969             }
970 
971             pkl = pkl->pklNext;
972         } while (pkl != pklHead);
973     }
974     else if (SearchType == ImeInfoExImeFileName)
975     {
976         do
977         {
978             if (pkl->piiex &&
979                 _wcsnicmp(pkl->piiex->wszImeFile, pInfoEx->wszImeFile,
980                           RTL_NUMBER_OF(pkl->piiex->wszImeFile)) == 0)
981             {
982                 *pInfoEx = *pkl->piiex;
983                 return TRUE;
984             }
985 
986             pkl = pkl->pklNext;
987         } while (pkl != pklHead);
988     }
989     else
990     {
991         /* Do nothing */
992     }
993 
994     return FALSE;
995 }
996 
997 BOOL
998 NTAPI
999 NtUserGetImeInfoEx(
1000     PIMEINFOEX pImeInfoEx,
1001     IMEINFOEXCLASS SearchType)
1002 {
1003     IMEINFOEX ImeInfoEx;
1004     BOOL ret = FALSE;
1005 
1006     UserEnterShared();
1007 
1008     if (!IS_IMM_MODE())
1009     {
1010         ERR("!IS_IMM_MODE()\n");
1011         goto Quit;
1012     }
1013 
1014     _SEH2_TRY
1015     {
1016         ProbeForWrite(pImeInfoEx, sizeof(*pImeInfoEx), 1);
1017         ImeInfoEx = *pImeInfoEx;
1018     }
1019     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1020     {
1021         goto Quit;
1022     }
1023     _SEH2_END;
1024 
1025     ret = UserGetImeInfoEx(NULL, &ImeInfoEx, SearchType);
1026     if (!ret)
1027         goto Quit;
1028 
1029     _SEH2_TRY
1030     {
1031         *pImeInfoEx = ImeInfoEx;
1032     }
1033     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1034     {
1035         ret = FALSE;
1036     }
1037     _SEH2_END;
1038 
1039 Quit:
1040     UserLeave();
1041     return ret;
1042 }
1043 
1044 BOOL
1045 NTAPI
1046 NtUserSetAppImeLevel(HWND hWnd, DWORD dwLevel)
1047 {
1048     BOOL ret = FALSE;
1049     PWND pWnd;
1050     PTHREADINFO pti;
1051 
1052     UserEnterExclusive();
1053 
1054     if (!IS_IMM_MODE())
1055     {
1056         ERR("!IS_IMM_MODE()\n");
1057         EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1058         goto Quit;
1059     }
1060 
1061     pWnd = ValidateHwndNoErr(hWnd);
1062     if (!pWnd)
1063         goto Quit;
1064 
1065     pti = PsGetCurrentThreadWin32Thread();
1066     if (pWnd->head.pti->ppi == pti->ppi)
1067         ret = UserSetProp(pWnd, AtomImeLevel, (HANDLE)(ULONG_PTR)dwLevel, TRUE);
1068 
1069 Quit:
1070     UserLeave();
1071     return ret;
1072 }
1073 
1074 // Win: SetImeInfoEx
1075 BOOL FASTCALL UserSetImeInfoEx(LPVOID pUnknown, PIMEINFOEX pImeInfoEx)
1076 {
1077     PKL pklHead, pkl;
1078 
1079     pkl = pklHead = gspklBaseLayout;
1080 
1081     do
1082     {
1083         if (pkl->hkl != pImeInfoEx->hkl)
1084         {
1085             pkl = pkl->pklNext;
1086             continue;
1087         }
1088 
1089         if (!pkl->piiex)
1090             return FALSE;
1091 
1092         if (!pkl->piiex->fLoadFlag)
1093             *pkl->piiex = *pImeInfoEx;
1094 
1095         return TRUE;
1096     } while (pkl != pklHead);
1097 
1098     return FALSE;
1099 }
1100 
1101 BOOL
1102 NTAPI
1103 NtUserSetImeInfoEx(PIMEINFOEX pImeInfoEx)
1104 {
1105     BOOL ret = FALSE;
1106     IMEINFOEX ImeInfoEx;
1107 
1108     UserEnterExclusive();
1109 
1110     if (!IS_IMM_MODE())
1111     {
1112         ERR("!IS_IMM_MODE()\n");
1113         goto Quit;
1114     }
1115 
1116     _SEH2_TRY
1117     {
1118         ProbeForRead(pImeInfoEx, sizeof(*pImeInfoEx), 1);
1119         ImeInfoEx = *pImeInfoEx;
1120     }
1121     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1122     {
1123         goto Quit;
1124     }
1125     _SEH2_END;
1126 
1127     ret = UserSetImeInfoEx(NULL, &ImeInfoEx);
1128 
1129 Quit:
1130     UserLeave();
1131     return ret;
1132 }
1133 
1134 BOOL NTAPI
1135 NtUserSetImeOwnerWindow(HWND hImeWnd, HWND hwndFocus)
1136 {
1137     BOOL ret = FALSE;
1138     PWND pImeWnd, pwndFocus, pwndTopLevel, pwnd, pwndActive;
1139     PTHREADINFO ptiIme;
1140 
1141     UserEnterExclusive();
1142 
1143     if (!IS_IMM_MODE())
1144     {
1145         ERR("!IS_IMM_MODE()\n");
1146         goto Quit;
1147     }
1148 
1149     pImeWnd = ValidateHwndNoErr(hImeWnd);
1150     if (!pImeWnd || pImeWnd->fnid != FNID_IME)
1151         goto Quit;
1152 
1153     pwndFocus = ValidateHwndNoErr(hwndFocus);
1154     if (pwndFocus)
1155     {
1156         if (IS_WND_IMELIKE(pwndFocus))
1157             goto Quit;
1158 
1159         pwndTopLevel = IntGetTopLevelWindow(pwndFocus);
1160 
1161         for (pwnd = pwndTopLevel; pwnd; pwnd = pwnd->spwndOwner)
1162         {
1163             if (pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])
1164             {
1165                 pwndTopLevel = NULL;
1166                 break;
1167             }
1168         }
1169 
1170         pImeWnd->spwndOwner = pwndTopLevel;
1171         // TODO:
1172     }
1173     else
1174     {
1175         ptiIme = pImeWnd->head.pti;
1176         pwndActive = ptiIme->MessageQueue->spwndActive;
1177 
1178         if (!pwndActive || pwndActive != pImeWnd->spwndOwner)
1179         {
1180             if (pwndActive && ptiIme == pwndActive->head.pti && !IS_WND_IMELIKE(pwndActive))
1181             {
1182                 pImeWnd->spwndOwner = pwndActive;
1183             }
1184             else
1185             {
1186                 // TODO:
1187             }
1188 
1189             // TODO:
1190         }
1191     }
1192 
1193     ret = TRUE;
1194 
1195 Quit:
1196     UserLeave();
1197     return ret;
1198 }
1199 
1200 PVOID
1201 AllocInputContextObject(PDESKTOP pDesk,
1202                         PTHREADINFO pti,
1203                         SIZE_T Size,
1204                         PVOID* HandleOwner)
1205 {
1206     PTHRDESKHEAD ObjHead;
1207 
1208     ASSERT(Size > sizeof(*ObjHead));
1209     ASSERT(pti != NULL);
1210 
1211     if (!pDesk)
1212         pDesk = pti->rpdesk;
1213 
1214     ObjHead = DesktopHeapAlloc(pDesk, Size);
1215     if (!ObjHead)
1216         return NULL;
1217 
1218     RtlZeroMemory(ObjHead, Size);
1219 
1220     ObjHead->pSelf = ObjHead;
1221     ObjHead->rpdesk = pDesk;
1222     ObjHead->pti = pti;
1223     IntReferenceThreadInfo(pti);
1224     *HandleOwner = pti;
1225     pti->ppi->UserHandleCount++;
1226 
1227     return ObjHead;
1228 }
1229 
1230 VOID UserFreeInputContext(PVOID Object)
1231 {
1232     PTHRDESKHEAD ObjHead = Object;
1233     PDESKTOP pDesk = ObjHead->rpdesk;
1234     PIMC pIMC = Object, *ppIMC;
1235     PTHREADINFO pti;
1236 
1237     if (!pIMC)
1238         return;
1239 
1240     /* Find the IMC in the list and remove it */
1241     pti = pIMC->head.pti;
1242     for (ppIMC = &pti->spDefaultImc; *ppIMC; ppIMC = &(*ppIMC)->pImcNext)
1243     {
1244         if (*ppIMC == pIMC)
1245         {
1246             *ppIMC = pIMC->pImcNext;
1247             break;
1248         }
1249     }
1250 
1251     DesktopHeapFree(pDesk, Object);
1252 
1253     pti->ppi->UserHandleCount--;
1254     IntDereferenceThreadInfo(pti);
1255 }
1256 
1257 BOOLEAN UserDestroyInputContext(PVOID Object)
1258 {
1259     PIMC pIMC = Object;
1260 
1261     if (!pIMC)
1262         return TRUE;
1263 
1264     UserMarkObjectDestroy(pIMC);
1265 
1266     return UserDeleteObject(UserHMGetHandle(pIMC), TYPE_INPUTCONTEXT);
1267 }
1268 
1269 BOOL NTAPI NtUserDestroyInputContext(HIMC hIMC)
1270 {
1271     PIMC pIMC;
1272     BOOL ret = FALSE;
1273     HWND *phwnd;
1274     PWND pWnd;
1275     PWINDOWLIST pwl;
1276     PTHREADINFO pti;
1277 
1278     UserEnterExclusive();
1279 
1280     if (!IS_IMM_MODE())
1281     {
1282         ERR("!IS_IMM_MODE()\n");
1283         EngSetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1284         goto Quit;
1285     }
1286 
1287     pIMC = UserGetObjectNoErr(gHandleTable, hIMC, TYPE_INPUTCONTEXT);
1288     if (!pIMC)
1289         goto Quit;
1290 
1291     pti = pIMC->head.pti;
1292     if (pti != GetW32ThreadInfo() || pIMC == pti->spDefaultImc)
1293         goto Quit;
1294 
1295     UserMarkObjectDestroy(pIMC);
1296 
1297     pwl = IntBuildHwndList(pti->rpdesk->pDeskInfo->spwnd->spwndChild,
1298                            IACE_CHILDREN | IACE_LIST, pti);
1299     if (pwl)
1300     {
1301         for (phwnd = pwl->ahwnd; *phwnd != HWND_TERMINATOR; ++phwnd)
1302         {
1303             pWnd = ValidateHwndNoErr(*phwnd);
1304             if (!pWnd)
1305                 continue;
1306 
1307             if (pWnd->hImc == hIMC)
1308                 IntAssociateInputContext(pWnd, pti->spDefaultImc);
1309         }
1310 
1311         IntFreeHwndList(pwl);
1312     }
1313 
1314     ret = UserDeleteObject(hIMC, TYPE_INPUTCONTEXT);
1315 
1316 Quit:
1317     UserLeave();
1318     return ret;
1319 }
1320 
1321 // Win: CreateInputContext
1322 PIMC FASTCALL UserCreateInputContext(ULONG_PTR dwClientImcData)
1323 {
1324     PIMC pIMC;
1325     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1326     PDESKTOP pdesk = pti->rpdesk;
1327 
1328     if (!IS_IMM_MODE() || (pti->TIF_flags & TIF_DISABLEIME)) // Disabled?
1329     {
1330         ERR("IME is disabled\n");
1331         return NULL;
1332     }
1333 
1334     if (!pdesk) // No desktop?
1335         return NULL;
1336 
1337     // pti->spDefaultImc should be already set if non-first time.
1338     if (dwClientImcData && !pti->spDefaultImc)
1339         return NULL;
1340 
1341     // Create an input context user object.
1342     pIMC = UserCreateObject(gHandleTable, pdesk, pti, NULL, TYPE_INPUTCONTEXT, sizeof(IMC));
1343     if (!pIMC)
1344         return NULL;
1345 
1346     // Release the extra reference (UserCreateObject added 2 references).
1347     UserDereferenceObject(pIMC);
1348 
1349     if (dwClientImcData) // Non-first time.
1350     {
1351         // Insert pIMC to the second position (non-default) of the list.
1352         pIMC->pImcNext = pti->spDefaultImc->pImcNext;
1353         pti->spDefaultImc->pImcNext = pIMC;
1354     }
1355     else // First time. It's the default IMC.
1356     {
1357         // Add the first one (default) to the list.
1358         pti->spDefaultImc = pIMC;
1359         pIMC->pImcNext = NULL;
1360     }
1361 
1362     pIMC->dwClientImcData = dwClientImcData; // Set it.
1363     return pIMC;
1364 }
1365 
1366 HIMC
1367 NTAPI
1368 NtUserCreateInputContext(ULONG_PTR dwClientImcData)
1369 {
1370     PIMC pIMC;
1371     HIMC ret = NULL;
1372 
1373     if (!dwClientImcData)
1374         return NULL;
1375 
1376     UserEnterExclusive();
1377 
1378     if (!IS_IMM_MODE())
1379     {
1380         ERR("!IS_IMM_MODE()\n");
1381         goto Quit;
1382     }
1383 
1384     pIMC = UserCreateInputContext(dwClientImcData);
1385     if (pIMC)
1386         ret = UserHMGetHandle(pIMC);
1387 
1388 Quit:
1389     UserLeave();
1390     return ret;
1391 }
1392 
1393 // Win: AssociateInputContextEx
1394 DWORD FASTCALL IntAssociateInputContextEx(PWND pWnd, PIMC pIMC, DWORD dwFlags)
1395 {
1396     DWORD ret = 0;
1397     PWINDOWLIST pwl;
1398     BOOL bIgnoreNullImc = (dwFlags & IACE_IGNORENOCONTEXT);
1399     PTHREADINFO pti = pWnd->head.pti;
1400     PWND pwndTarget, pwndFocus = pti->MessageQueue->spwndFocus;
1401     HWND *phwnd;
1402     HIMC hIMC;
1403 
1404     if (dwFlags & IACE_DEFAULT)
1405     {
1406         pIMC = pti->spDefaultImc;
1407     }
1408     else
1409     {
1410         if (pIMC && pti != pIMC->head.pti)
1411             return 2;
1412     }
1413 
1414     if (pWnd->head.pti->ppi != GetW32ThreadInfo()->ppi ||
1415         (pIMC && pIMC->head.rpdesk != pWnd->head.rpdesk))
1416     {
1417         return 2;
1418     }
1419 
1420     if ((dwFlags & IACE_CHILDREN) && pWnd->spwndChild)
1421     {
1422         pwl = IntBuildHwndList(pWnd->spwndChild, IACE_CHILDREN | IACE_LIST, pti);
1423         if (pwl)
1424         {
1425             for (phwnd = pwl->ahwnd; *phwnd != HWND_TERMINATOR; ++phwnd)
1426             {
1427                 pwndTarget = ValidateHwndNoErr(*phwnd);
1428                 if (!pwndTarget)
1429                     continue;
1430 
1431                 hIMC = (pIMC ? UserHMGetHandle(pIMC) : NULL);
1432                 if (pwndTarget->hImc == hIMC || (bIgnoreNullImc && !pwndTarget->hImc))
1433                     continue;
1434 
1435                 IntAssociateInputContext(pwndTarget, pIMC);
1436                 if (pwndTarget == pwndFocus)
1437                     ret = 1;
1438             }
1439 
1440             IntFreeHwndList(pwl);
1441         }
1442     }
1443 
1444     if (!bIgnoreNullImc || pWnd->hImc)
1445     {
1446         hIMC = (pIMC ? UserHMGetHandle(pIMC) : NULL);
1447         if (pWnd->hImc != hIMC)
1448         {
1449             IntAssociateInputContext(pWnd, pIMC);
1450             if (pWnd == pwndFocus)
1451                 ret = 1;
1452         }
1453     }
1454 
1455     return ret;
1456 }
1457 
1458 DWORD
1459 NTAPI
1460 NtUserAssociateInputContext(HWND hWnd, HIMC hIMC, DWORD dwFlags)
1461 {
1462     DWORD ret = 2;
1463     PWND pWnd;
1464     PIMC pIMC;
1465 
1466     UserEnterExclusive();
1467 
1468     if (!IS_IMM_MODE())
1469     {
1470         ERR("!IS_IMM_MODE()\n");
1471         goto Quit;
1472     }
1473 
1474     pWnd = ValidateHwndNoErr(hWnd);
1475     if (!pWnd)
1476         goto Quit;
1477 
1478     pIMC = (hIMC ? UserGetObjectNoErr(gHandleTable, hIMC, TYPE_INPUTCONTEXT) : NULL);
1479     ret = IntAssociateInputContextEx(pWnd, pIMC, dwFlags);
1480 
1481 Quit:
1482     UserLeave();
1483     return ret;
1484 }
1485 
1486 BOOL FASTCALL UserUpdateInputContext(PIMC pIMC, DWORD dwType, DWORD_PTR dwValue)
1487 {
1488     PTHREADINFO pti = GetW32ThreadInfo();
1489     PTHREADINFO ptiIMC = pIMC->head.pti;
1490 
1491     if (pti->ppi != ptiIMC->ppi) // Different process?
1492         return FALSE;
1493 
1494     switch (dwType)
1495     {
1496         case UIC_CLIENTIMCDATA:
1497             if (pIMC->dwClientImcData)
1498                 return FALSE; // Already set
1499 
1500             pIMC->dwClientImcData = dwValue;
1501             break;
1502 
1503         case UIC_IMEWINDOW:
1504             if (!ValidateHwndNoErr((HWND)dwValue))
1505                 return FALSE; // Invalid HWND
1506 
1507             pIMC->hImeWnd = (HWND)dwValue;
1508             break;
1509 
1510         default:
1511             return FALSE;
1512     }
1513 
1514     return TRUE;
1515 }
1516 
1517 BOOL
1518 NTAPI
1519 NtUserUpdateInputContext(
1520     HIMC hIMC,
1521     DWORD dwType,
1522     DWORD_PTR dwValue)
1523 {
1524     PIMC pIMC;
1525     BOOL ret = FALSE;
1526 
1527     UserEnterExclusive();
1528 
1529     if (!IS_IMM_MODE())
1530     {
1531         ERR("!IS_IMM_MODE()\n");
1532         goto Quit;
1533     }
1534 
1535     pIMC = UserGetObject(gHandleTable, hIMC, TYPE_INPUTCONTEXT);
1536     if (!pIMC)
1537         goto Quit;
1538 
1539     ret = UserUpdateInputContext(pIMC, dwType, dwValue);
1540 
1541 Quit:
1542     UserLeave();
1543     return ret;
1544 }
1545 
1546 DWORD_PTR
1547 NTAPI
1548 NtUserQueryInputContext(HIMC hIMC, DWORD dwType)
1549 {
1550     PIMC pIMC;
1551     PTHREADINFO ptiIMC;
1552     DWORD_PTR ret = 0;
1553 
1554     UserEnterExclusive();
1555 
1556     if (!IS_IMM_MODE())
1557     {
1558         ERR("!IS_IMM_MODE()\n");
1559         goto Quit;
1560     }
1561 
1562     pIMC = UserGetObject(gHandleTable, hIMC, TYPE_INPUTCONTEXT);
1563     if (!pIMC)
1564         goto Quit;
1565 
1566     ptiIMC = pIMC->head.pti;
1567 
1568     switch (dwType)
1569     {
1570         case QIC_INPUTPROCESSID:
1571             ret = (DWORD_PTR)PsGetThreadProcessId(ptiIMC->pEThread);
1572             break;
1573 
1574         case QIC_INPUTTHREADID:
1575             ret = (DWORD_PTR)PsGetThreadId(ptiIMC->pEThread);
1576             break;
1577 
1578         case QIC_DEFAULTWINDOWIME:
1579             if (ptiIMC->spwndDefaultIme)
1580                 ret = (DWORD_PTR)UserHMGetHandle(ptiIMC->spwndDefaultIme);
1581             break;
1582 
1583         case QIC_DEFAULTIMC:
1584             if (ptiIMC->spDefaultImc)
1585                 ret = (DWORD_PTR)UserHMGetHandle(ptiIMC->spDefaultImc);
1586             break;
1587     }
1588 
1589 Quit:
1590     UserLeave();
1591     return ret;
1592 }
1593 
1594 /* EOF */
1595