xref: /reactos/win32ss/user/ntuser/ime.c (revision ec24b547)
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
IntGetImeCompatFlags(PTHREADINFO pti)44 DWORD FASTCALL IntGetImeCompatFlags(PTHREADINFO pti)
45 {
46     if (!pti)
47         pti = PsGetCurrentThreadWin32Thread();
48 
49     return pti->ppi->dwImeCompatFlags;
50 }
51 
52 // Win: GetLangIdMatchLevel
IntGetImeHotKeyLanguageScore(HKL hKL,LANGID HotKeyLangId)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
IntGetActiveKeyboardLayout(VOID)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
IntGetImeHotKeyLangId(DWORD dwHotKeyId)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
IntAddImeHotKey(PIMEHOTKEY * ppList,PIMEHOTKEY pHotKey)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
IntGetImeHotKeyById(PIMEHOTKEY pList,DWORD dwHotKeyId)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
IntGetImeHotKeyByKeyAndLang(PIMEHOTKEY pList,UINT uModKeys,UINT uLeftRight,UINT uVirtualKey,LANGID TargetLangId)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
IntDeleteImeHotKey(PIMEHOTKEY * ppList,PIMEHOTKEY pHotKey)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
IntGetImeHotKeyByKey(PIMEHOTKEY pList,UINT uModKeys,UINT uLeftRight,UINT uVirtualKey)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
IntCheckImeHotKey(PUSER_MESSAGE_QUEUE MessageQueue,UINT uVirtualKey,LPARAM lParam)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
IntFreeImeHotKeys(VOID)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
IntSetImeHotKey(DWORD dwHotKeyId,UINT uModifiers,UINT uVirtualKey,HKL hKL,DWORD dwAction)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
NtUserGetImeHotKey(DWORD dwHotKeyId,LPUINT lpuModifiers,LPUINT lpuVirtualKey,LPHKL lphKL)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
NtUserSetImeHotKey(DWORD dwHotKeyId,UINT uModifiers,UINT uVirtualKey,HKL hKL,DWORD dwAction)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
NtUserCheckImeHotKey(UINT uVirtualKey,LPARAM lParam)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
IntGetTopLevelWindow(PWND pwnd)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
IntAssociateInputContext(PWND pWnd,PIMC pImc)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
NtUserSetThreadLayoutHandles(HKL hNewKL,HKL hOldKL)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
UserBuildHimcList(PTHREADINFO pti,DWORD dwCount,HIMC * phList)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 UINT FASTCALL
IntImmProcessKey(PUSER_MESSAGE_QUEUE MessageQueue,PWND pWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)569 IntImmProcessKey(PUSER_MESSAGE_QUEUE MessageQueue, PWND pWnd, UINT uMsg,
570                  WPARAM wParam, LPARAM lParam)
571 {
572     UINT uVirtualKey, ret;
573     DWORD dwHotKeyId;
574     PKL pKL;
575     PIMC pIMC;
576     PIMEHOTKEY pImeHotKey;
577     HKL hKL;
578     HWND hWnd;
579 
580     ASSERT_REFS_CO(pWnd);
581 
582     switch (uMsg)
583     {
584         case WM_KEYDOWN:
585         case WM_KEYUP:
586         case WM_SYSKEYDOWN:
587         case WM_SYSKEYUP:
588             break;
589 
590         default:
591             return 0;
592     }
593 
594     pIMC = NULL;
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 & (KF_UP << 16)) &&
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
NtUserBuildHimcList(DWORD dwThreadId,DWORD dwCount,HIMC * phList,LPDWORD pdwCount)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
UserSetImeConversionKeyState(PTHREADINFO pti,DWORD dwConversion)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
NtUserNotifyIMEStatus(HWND hwnd,BOOL fOpen,DWORD dwConversion)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
NtUserDisableThreadIme(DWORD dwThreadID)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
NtUserGetAppImeLevel(HWND hWnd)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 = HandleToUlong(UserGetProp(pWnd, AtomImeLevel, TRUE));
943 
944 Quit:
945     UserLeave();
946     return ret;
947 }
948 
949 // Win: GetImeInfoEx
950 BOOL FASTCALL
UserGetImeInfoEx(_Inout_ PWINSTATION_OBJECT pWinSta,_Inout_ PIMEINFOEX pInfoEx,_In_ IMEINFOEXCLASS SearchType)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
NtUserGetImeInfoEx(PIMEINFOEX pImeInfoEx,IMEINFOEXCLASS SearchType)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
NtUserSetAppImeLevel(HWND hWnd,DWORD dwLevel)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, UlongToHandle(dwLevel), TRUE);
1082 
1083 Quit:
1084     UserLeave();
1085     return ret;
1086 }
1087 
1088 // Win: SetImeInfoEx
1089 BOOL FASTCALL
UserSetImeInfoEx(_Inout_ PWINSTATION_OBJECT pWinSta,_Inout_ PIMEINFOEX pImeInfoEx)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
NtUserSetImeInfoEx(PIMEINFOEX pImeInfoEx)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
IntImeSetFutureOwner(PWND pImeWnd,PWND pwndOwner)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
IntGetLastTopMostWindowNoIME(PWND pImeWnd)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
IntImeSetTopMost(PWND pImeWnd,BOOL bTopMost,PWND pwndInsertBefore)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
IntImeCheckTopmost(PWND pImeWnd)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
NtUserSetImeOwnerWindow(HWND hImeWnd,HWND hwndFocus)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
AllocInputContextObject(PDESKTOP pDesk,PTHREADINFO pti,SIZE_T Size,PVOID * HandleOwner)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 
UserFreeInputContext(PVOID Object)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 
UserDestroyInputContext(PVOID Object)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
IntDestroyInputContext(PIMC pIMC)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 
NtUserDestroyInputContext(HIMC hIMC)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
UserCreateInputContext(ULONG_PTR dwClientImcData)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
NtUserCreateInputContext(ULONG_PTR dwClientImcData)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
IntAssociateInputContextEx(PWND pWnd,PIMC pIMC,DWORD dwFlags)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
NtUserAssociateInputContext(HWND hWnd,HIMC hIMC,DWORD dwFlags)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
UserUpdateInputContext(PIMC pIMC,DWORD dwType,DWORD_PTR dwValue)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
NtUserUpdateInputContext(HIMC hIMC,DWORD dwType,DWORD_PTR dwValue)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
NtUserQueryInputContext(HIMC hIMC,DWORD dwType)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         goto Quit;
1801 
1802     pIMC = UserGetObject(gHandleTable, hIMC, TYPE_INPUTCONTEXT);
1803     if (!pIMC)
1804         goto Quit;
1805 
1806     ptiIMC = pIMC->head.pti;
1807 
1808     switch (dwType)
1809     {
1810         case QIC_INPUTPROCESSID:
1811             ret = (DWORD_PTR)PsGetThreadProcessId(ptiIMC->pEThread);
1812             break;
1813 
1814         case QIC_INPUTTHREADID:
1815             ret = (DWORD_PTR)PsGetThreadId(ptiIMC->pEThread);
1816             break;
1817 
1818         case QIC_DEFAULTWINDOWIME:
1819             if (ptiIMC->spwndDefaultIme)
1820                 ret = (DWORD_PTR)UserHMGetHandle(ptiIMC->spwndDefaultIme);
1821             break;
1822 
1823         case QIC_DEFAULTIMC:
1824             if (ptiIMC->spDefaultImc)
1825                 ret = (DWORD_PTR)UserHMGetHandle(ptiIMC->spDefaultImc);
1826             break;
1827     }
1828 
1829 Quit:
1830     UserLeave();
1831     return ret;
1832 }
1833 
1834 // Searchs a non-IME-related window of the same thread of pwndTarget,
1835 // other than pwndTarget, around pwndParent. Returns TRUE if found.
1836 //
1837 // Win: IsChildSameThread
IntFindNonImeRelatedWndOfSameThread(PWND pwndParent,PWND pwndTarget)1838 BOOL IntFindNonImeRelatedWndOfSameThread(PWND pwndParent, PWND pwndTarget)
1839 {
1840     PWND pwnd, pwndOwner, pwndNode;
1841     PTHREADINFO ptiTarget = pwndTarget->head.pti;
1842 
1843     // For all the children of pwndParent, ...
1844     for (pwnd = pwndParent->spwndChild; pwnd; pwnd = pwnd->spwndNext)
1845     {
1846         if (pwnd == pwndTarget || pwnd->head.pti != ptiTarget || IS_WND_MENU(pwnd))
1847             continue;
1848 
1849         if (!IS_WND_CHILD(pwnd))
1850         {
1851             // Check if any IME-like owner.
1852             BOOL bFound1 = FALSE;
1853             for (pwndOwner = pwnd; pwndOwner; pwndOwner = pwndOwner->spwndOwner)
1854             {
1855                 if (IS_WND_IMELIKE(pwndOwner))
1856                 {
1857                     bFound1 = TRUE;
1858                     break;
1859                 }
1860             }
1861             if (bFound1)
1862                 continue; // Skip if any IME-like owner.
1863         }
1864 
1865         pwndNode = pwnd;
1866 
1867         if (IS_WND_CHILD(pwndNode))
1868         {
1869             // Check if any same-thread IME-like ancestor.
1870             BOOL bFound2 = FALSE;
1871             for (; IS_WND_CHILD(pwndNode); pwndNode = pwndNode->spwndParent)
1872             {
1873                 if (pwndNode->head.pti != ptiTarget)
1874                     break;
1875 
1876                 if (IS_WND_IMELIKE(pwndNode))
1877                 {
1878                     bFound2 = TRUE;
1879                     break;
1880                 }
1881             }
1882             if (bFound2)
1883                 continue;
1884             // Now, pwndNode is non-child or non-same-thread window.
1885         }
1886 
1887         if (!IS_WND_CHILD(pwndNode)) // pwndNode is non-child
1888         {
1889             // Check if any same-thread IME-like owner.
1890             BOOL bFound3 = FALSE;
1891             for (; pwndNode; pwndNode = pwndNode->spwndOwner)
1892             {
1893                 if (pwndNode->head.pti != ptiTarget)
1894                     break;
1895 
1896                 if (IS_WND_IMELIKE(pwndNode))
1897                 {
1898                     bFound3 = TRUE;
1899                     break;
1900                 }
1901             }
1902             if (bFound3)
1903                 continue;
1904         }
1905 
1906         return TRUE;
1907     }
1908 
1909     return FALSE;
1910 }
1911 
1912 // Determines whether the target window needs the IME window.
1913 // Win: WantImeWindow(pwndParent, pwndTarget)
IntWantImeWindow(PWND pwndTarget)1914 BOOL FASTCALL IntWantImeWindow(PWND pwndTarget)
1915 {
1916     PDESKTOP rpdesk;
1917     PWINSTATION_OBJECT rpwinstaParent;
1918     PWND pwndNode, pwndParent = pwndTarget->spwndParent;
1919 
1920     if (gptiCurrent->TIF_flags & TIF_DISABLEIME)
1921         return FALSE;
1922 
1923     if (IS_WND_IMELIKE(pwndTarget))
1924         return FALSE;
1925 
1926     if (pwndTarget->fnid == FNID_DESKTOP || pwndTarget->fnid == FNID_MESSAGEWND)
1927         return FALSE;
1928 
1929     if (pwndTarget->state & WNDS_SERVERSIDEWINDOWPROC)
1930         return FALSE;
1931 
1932     rpdesk = pwndTarget->head.rpdesk;
1933     if (!rpdesk)
1934         return FALSE;
1935 
1936     rpwinstaParent = rpdesk->rpwinstaParent;
1937     if (!rpwinstaParent || (rpwinstaParent->Flags & WSS_NOIO))
1938         return FALSE;
1939 
1940     for (pwndNode = pwndParent; pwndNode; pwndNode = pwndNode->spwndParent)
1941     {
1942         if (rpdesk != pwndNode->head.rpdesk)
1943             break;
1944 
1945         if (pwndNode == rpdesk->spwndMessage)
1946             return FALSE;
1947     }
1948 
1949     return TRUE;
1950 }
1951 
1952 // Create the default IME window for the target window.
1953 // Win: xxxCreateDefaultImeWindow(pwndTarget, ATOM, hInst)
co_IntCreateDefaultImeWindow(PWND pwndTarget,HINSTANCE hInst)1954 PWND FASTCALL co_IntCreateDefaultImeWindow(PWND pwndTarget, HINSTANCE hInst)
1955 {
1956     LARGE_UNICODE_STRING WindowName;
1957     UNICODE_STRING ClassName;
1958     PWND pImeWnd;
1959     PIMEUI pimeui;
1960     CREATESTRUCTW Cs;
1961     USER_REFERENCE_ENTRY Ref;
1962     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1963     HANDLE pid = PsGetThreadProcessId(pti->pEThread);
1964 
1965     if (!(pti->spDefaultImc) && pid == gpidLogon)
1966         UserCreateInputContext(0);
1967 
1968     if (!(pti->spDefaultImc) || IS_WND_IMELIKE(pwndTarget) || !(pti->rpdesk->pheapDesktop))
1969         return NULL;
1970 
1971     if (IS_WND_CHILD(pwndTarget) && !(pwndTarget->style & WS_VISIBLE) &&
1972         pwndTarget->spwndParent->head.pti->ppi != pti->ppi)
1973     {
1974         return NULL;
1975     }
1976 
1977     RtlInitLargeUnicodeString(&WindowName, L"Default IME", 0);
1978 
1979     ClassName.Buffer = UlongToPtr(gpsi->atomSysClass[ICLS_IME]);
1980     ClassName.Length = 0;
1981     ClassName.MaximumLength = 0;
1982 
1983     UserRefObjectCo(pwndTarget, &Ref);
1984 
1985     RtlZeroMemory(&Cs, sizeof(Cs));
1986     Cs.style = WS_POPUP | WS_DISABLED;
1987     Cs.hInstance = hInst;
1988     Cs.hwndParent = UserHMGetHandle(pwndTarget);
1989     Cs.lpszName = WindowName.Buffer;
1990     Cs.lpszClass = ClassName.Buffer;
1991 
1992     // NOTE: LARGE_UNICODE_STRING is compatible to LARGE_STRING.
1993     pImeWnd = co_UserCreateWindowEx(&Cs, &ClassName, (PLARGE_STRING)&WindowName, NULL, WINVER);
1994     if (pImeWnd)
1995     {
1996         pimeui = ((PIMEWND)pImeWnd)->pimeui;
1997         _SEH2_TRY
1998         {
1999             ProbeForWrite(pimeui, sizeof(IMEUI), 1);
2000             pimeui->fDefault = TRUE;
2001             if (IS_WND_CHILD(pwndTarget) && pwndTarget->spwndParent->head.pti != pti)
2002                 pimeui->fChildThreadDef = TRUE;
2003         }
2004         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2005         {
2006             ERR("%p\n", pimeui);
2007         }
2008         _SEH2_END;
2009     }
2010 
2011     UserDerefObjectCo(pwndTarget);
2012     return pImeWnd;
2013 }
2014 
2015 // Determines whether the system can destroy the default IME window for the target child window.
2016 // Win: ImeCanDestroyDefIMEforChild
IntImeCanDestroyDefIMEforChild(PWND pImeWnd,PWND pwndTarget)2017 BOOL FASTCALL IntImeCanDestroyDefIMEforChild(PWND pImeWnd, PWND pwndTarget)
2018 {
2019     PWND pwndNode;
2020     PIMEUI pimeui;
2021     IMEUI SafeImeUI;
2022 
2023     pimeui = ((PIMEWND)pImeWnd)->pimeui;
2024     if (!pimeui || (LONG_PTR)pimeui == (LONG_PTR)-1)
2025         return FALSE;
2026 
2027     // Check IMEUI.fChildThreadDef
2028     _SEH2_TRY
2029     {
2030         ProbeForRead(pimeui, sizeof(IMEUI), 1);
2031         SafeImeUI = *pimeui;
2032         if (!SafeImeUI.fChildThreadDef)
2033             return FALSE;
2034     }
2035     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2036     {
2037         ERR("%p\n", pimeui);
2038     }
2039     _SEH2_END;
2040 
2041     // The parent of pwndTarget is NULL or of the same thread of pwndTarget?
2042     if (pwndTarget->spwndParent == NULL ||
2043         pwndTarget->head.pti == pwndTarget->spwndParent->head.pti)
2044     {
2045         return FALSE;
2046     }
2047 
2048     for (pwndNode = pwndTarget; pwndNode; pwndNode = pwndNode->spwndParent)
2049     {
2050         if (pwndNode == pwndNode->head.rpdesk->pDeskInfo->spwnd)
2051             break;
2052 
2053         if (IntFindNonImeRelatedWndOfSameThread(pwndNode->spwndParent, pwndTarget))
2054             return FALSE;
2055     }
2056 
2057     return TRUE;
2058 }
2059 
2060 // Determines whether the system can destroy the default IME window for the non-child target window.
2061 // Win: ImeCanDestroyDefIME
IntImeCanDestroyDefIME(PWND pImeWnd,PWND pwndTarget)2062 BOOL FASTCALL IntImeCanDestroyDefIME(PWND pImeWnd, PWND pwndTarget)
2063 {
2064     PWND pwndNode;
2065     PIMEUI pimeui;
2066     IMEUI SafeImeUI;
2067 
2068     pimeui = ((PIMEWND)pImeWnd)->pimeui;
2069     if (!pimeui || (LONG_PTR)pimeui == (LONG_PTR)-1)
2070         return FALSE;
2071 
2072     // Check IMEUI.fDestroy
2073     _SEH2_TRY
2074     {
2075         ProbeForRead(pimeui, sizeof(IMEUI), 1);
2076         SafeImeUI = *pimeui;
2077         if (SafeImeUI.fDestroy)
2078             return FALSE;
2079     }
2080     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2081     {
2082         ERR("%p\n", pimeui);
2083     }
2084     _SEH2_END;
2085 
2086     // Any ancestor of pImeWnd is pwndTarget?
2087     if (pImeWnd->spwndOwner)
2088     {
2089         for (pwndNode = pImeWnd->spwndOwner; pwndNode; pwndNode = pwndNode->spwndOwner)
2090         {
2091             if (pwndNode == pwndTarget)
2092                 break;
2093         }
2094 
2095         if (!pwndNode)
2096             return FALSE;
2097     }
2098 
2099     // Any ancestor of pwndTarget is IME-like?
2100     for (pwndNode = pwndTarget; pwndNode; pwndNode = pwndNode->spwndOwner)
2101     {
2102         if (IS_WND_IMELIKE(pwndNode))
2103             return FALSE;
2104     }
2105 
2106     // Adjust the ordering and top-mode status
2107     IntImeSetFutureOwner(pImeWnd, pwndTarget);
2108     for (pwndNode = pImeWnd->spwndOwner; pwndNode; pwndNode = pwndNode->spwndNext)
2109     {
2110         if (pwndNode == pImeWnd)
2111             break;
2112     }
2113     if (pwndNode == pImeWnd)
2114         IntImeCheckTopmost(pImeWnd);
2115 
2116     // Is the owner of pImeWnd NULL or pwndTarget?
2117     if (pImeWnd->spwndOwner && pwndTarget != pImeWnd->spwndOwner)
2118         return FALSE;
2119 
2120     WndSetOwner(pImeWnd, NULL);
2121     return TRUE;
2122 }
2123 
2124 // Update IMEUI.fShowStatus flags and Send the WM_IME_NOTIFY messages.
2125 // Win: xxxCheckImeShowStatus
IntCheckImeShowStatus(PWND pwndIme,PTHREADINFO pti)2126 BOOL FASTCALL IntCheckImeShowStatus(PWND pwndIme, PTHREADINFO pti)
2127 {
2128     BOOL ret = FALSE, bDifferent;
2129     PWINDOWLIST pwl;
2130     HWND *phwnd;
2131     PWND pwndNode, pwndIMC;
2132     PTHREADINFO ptiCurrent = GetW32ThreadInfo();
2133     PIMEUI pimeui;
2134     IMEUI SafeImeUI;
2135 
2136     if (pwndIme->state2 & WNDS2_INDESTROY)
2137         return FALSE;
2138 
2139     // Build a window list
2140     pwl = IntBuildHwndList(pwndIme->spwndParent->spwndChild, IACE_LIST, NULL);
2141     if (!pwl)
2142         return FALSE;
2143 
2144     ret = TRUE;
2145     for (phwnd = pwl->ahwnd; *phwnd != HWND_TERMINATOR; ++phwnd)
2146     {
2147         pwndNode = ValidateHwndNoErr(*phwnd);
2148 
2149         if (!pwndNode || pwndIme == pwndNode)
2150             continue;
2151 
2152         if (pwndNode->pcls->atomClassName != gpsi->atomSysClass[ICLS_IME] ||
2153             (pwndNode->state2 & WNDS2_INDESTROY))
2154         {
2155             continue;
2156         }
2157 
2158         pimeui = ((PIMEWND)pwndNode)->pimeui;
2159         if (!pimeui || pimeui == (PIMEUI)-1)
2160             continue;
2161 
2162         if (pti && pti != pwndNode->head.pti)
2163             continue;
2164 
2165         // Attach to the process if necessary
2166         bDifferent = FALSE;
2167         if (pwndNode->head.pti->ppi != ptiCurrent->ppi)
2168         {
2169             KeAttachProcess(&(pwndNode->head.pti->ppi->peProcess->Pcb));
2170             bDifferent = TRUE;
2171         }
2172 
2173         // Get pwndIMC and update IMEUI.fShowStatus flag
2174         _SEH2_TRY
2175         {
2176             ProbeForWrite(pimeui, sizeof(IMEUI), 1);
2177             SafeImeUI = *pimeui;
2178             if (SafeImeUI.fShowStatus)
2179             {
2180                 pwndIMC = ValidateHwndNoErr(pimeui->hwndIMC);
2181                 if (pwndIMC)
2182                     pimeui->fShowStatus = FALSE;
2183             }
2184             else
2185             {
2186                 pwndIMC = NULL;
2187             }
2188         }
2189         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2190         {
2191             ERR("%p\n", pimeui);
2192             pwndIMC = NULL;
2193         }
2194         _SEH2_END;
2195 
2196         // Detach from the process if necessary
2197         if (bDifferent)
2198             KeDetachProcess();
2199 
2200         // Send the WM_IME_NOTIFY message
2201         if (pwndIMC && pwndIMC->head.pti && !(pwndIMC->head.pti->TIF_flags & TIF_INCLEANUP))
2202         {
2203             HWND hImeWnd;
2204             USER_REFERENCE_ENTRY Ref;
2205 
2206             UserRefObjectCo(pwndIMC, &Ref);
2207 
2208             hImeWnd = UserHMGetHandle(pwndIMC);
2209             co_IntSendMessage(hImeWnd, WM_IME_NOTIFY, IMN_CLOSESTATUSWINDOW, 0);
2210 
2211             UserDerefObjectCo(pwndIMC);
2212         }
2213     }
2214 
2215     // Free the window list
2216     IntFreeHwndList(pwl);
2217     return ret;
2218 }
2219 
2220 // Send a UI message.
2221 LRESULT FASTCALL
IntSendMessageToUI(PTHREADINFO ptiIME,PIMEUI pimeui,UINT uMsg,WPARAM wParam,LPARAM lParam)2222 IntSendMessageToUI(PTHREADINFO ptiIME, PIMEUI pimeui, UINT uMsg, WPARAM wParam, LPARAM lParam)
2223 {
2224     PWND pwndUI;
2225     LRESULT ret = 0;
2226     IMEUI SafeImeUI;
2227     BOOL bDifferent = FALSE;
2228     USER_REFERENCE_ENTRY Ref;
2229 
2230     // Attach to the process if necessary
2231     if (ptiIME != GetW32ThreadInfo())
2232     {
2233         bDifferent = TRUE;
2234         KeAttachProcess(&(ptiIME->ppi->peProcess->Pcb));
2235     }
2236 
2237     // Get the pwndUI
2238     _SEH2_TRY
2239     {
2240         ProbeForRead(pimeui, sizeof(IMEUI), 1);
2241         SafeImeUI = *pimeui;
2242         pwndUI = ValidateHwndNoErr(SafeImeUI.hwndUI);
2243     }
2244     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2245     {
2246         ERR("%p\n", pimeui);
2247         pwndUI = NULL;
2248     }
2249     _SEH2_END;
2250 
2251     if (!pwndUI)
2252         goto Quit;
2253 
2254     // Increment the recursion count of the IME procedure.
2255     // See also ImeWndProc_common of user32.
2256     _SEH2_TRY
2257     {
2258         ProbeForWrite(&pimeui->nCntInIMEProc, sizeof(LONG), 1);
2259         InterlockedIncrement(&pimeui->nCntInIMEProc);
2260     }
2261     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2262     {
2263         ERR("%p\n", pimeui);
2264         _SEH2_YIELD(goto Quit);
2265     }
2266     _SEH2_END;
2267 
2268     // Detach from the process if necessary
2269     if (bDifferent)
2270         KeDetachProcess();
2271 
2272     UserRefObjectCo(pwndUI, &Ref);
2273     ret = co_IntSendMessage(UserHMGetHandle(pwndUI), uMsg, wParam, lParam);
2274     UserDerefObjectCo(pwndUI);
2275 
2276     // Attach to the process if necessary
2277     if (bDifferent)
2278         KeAttachProcess(&(ptiIME->ppi->peProcess->Pcb));
2279 
2280     // Decrement the recursion count of the IME procedure
2281     _SEH2_TRY
2282     {
2283         ProbeForWrite(&pimeui->nCntInIMEProc, sizeof(LONG), 1);
2284         InterlockedDecrement(&pimeui->nCntInIMEProc);
2285     }
2286     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2287     {
2288         ERR("%p\n", pimeui);
2289         _SEH2_YIELD(goto Quit);
2290     }
2291     _SEH2_END;
2292 
2293 Quit:
2294     // Detach from the process if necessary
2295     if (bDifferent)
2296         KeDetachProcess();
2297 
2298     return ret;
2299 }
2300 
2301 // Send the open status notification.
2302 // Win: xxxSendOpenStatusNotify
2303 VOID FASTCALL
IntSendOpenStatusNotify(PTHREADINFO ptiIME,PIMEUI pimeui,PWND pWnd,BOOL bOpen)2304 IntSendOpenStatusNotify(PTHREADINFO ptiIME, PIMEUI pimeui, PWND pWnd, BOOL bOpen)
2305 {
2306     WPARAM wParam = (bOpen ? IMN_OPENSTATUSWINDOW : IMN_CLOSESTATUSWINDOW);
2307     PTHREADINFO ptiWnd = pWnd->head.pti;
2308     USER_REFERENCE_ENTRY Ref;
2309 
2310     if (ptiWnd->dwExpWinVer >= WINVER_WINNT4 && pWnd->hImc)
2311     {
2312         UserRefObjectCo(pWnd, &Ref);
2313         co_IntSendMessage(UserHMGetHandle(pWnd), WM_IME_NOTIFY, wParam, 0);
2314         UserDerefObjectCo(pWnd);
2315     }
2316     else
2317     {
2318         IntSendMessageToUI(ptiIME, pimeui, WM_IME_NOTIFY, wParam, 0);
2319     }
2320 }
2321 
2322 // Update the IME status and send a notification.
IntNotifyImeShowStatus(PWND pImeWnd)2323 VOID FASTCALL IntNotifyImeShowStatus(PWND pImeWnd)
2324 {
2325     PIMEUI pimeui;
2326     PWND pWnd;
2327     PTHREADINFO pti, ptiIME;
2328     BOOL bShow, bSendNotify = FALSE;
2329     IMEUI SafeImeUI;
2330 
2331     if (!IS_IMM_MODE() || (pImeWnd->state2 & WNDS2_INDESTROY))
2332         return;
2333 
2334     pti = PsGetCurrentThreadWin32Thread();
2335     ptiIME = pImeWnd->head.pti;
2336 
2337     // Attach to the process if necessary
2338     if (pti != ptiIME)
2339         KeAttachProcess(&(ptiIME->ppi->peProcess->Pcb));
2340 
2341     // Get an IMEUI and check whether hwndIMC is valid and update fShowStatus
2342     _SEH2_TRY
2343     {
2344         ProbeForWrite(pImeWnd, sizeof(IMEWND), 1);
2345         pimeui = ((PIMEWND)pImeWnd)->pimeui;
2346         SafeImeUI = *pimeui;
2347 
2348         bShow = (gfIMEShowStatus == TRUE) && SafeImeUI.fCtrlShowStatus;
2349 
2350         pWnd = ValidateHwndNoErr(SafeImeUI.hwndIMC);
2351         if (!pWnd)
2352             pWnd = ptiIME->MessageQueue->spwndFocus;
2353 
2354         if (pWnd)
2355         {
2356             bSendNotify = TRUE;
2357             pimeui->fShowStatus = bShow;
2358         }
2359     }
2360     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2361     {
2362         ERR("%p, %p\n", pImeWnd, pimeui);
2363 
2364         if (pti != ptiIME)
2365             KeDetachProcess();
2366 
2367         _SEH2_YIELD(return);
2368     }
2369     _SEH2_END;
2370 
2371     // Detach from the process if necessary
2372     if (pti != ptiIME)
2373         KeDetachProcess();
2374 
2375     if (bSendNotify)
2376         IntSendOpenStatusNotify(ptiIME, &SafeImeUI, pWnd, bShow);
2377 
2378     if (!(pImeWnd->state2 & WNDS2_INDESTROY))
2379         IntCheckImeShowStatus(pImeWnd, NULL);
2380 }
2381 
2382 // Win: xxxBroadcastImeShowStatusChange
IntBroadcastImeShowStatusChange(PWND pImeWnd,BOOL bShow)2383 BOOL FASTCALL IntBroadcastImeShowStatusChange(PWND pImeWnd, BOOL bShow)
2384 {
2385     if (gfIMEShowStatus == bShow || !IS_IMM_MODE())
2386         return TRUE;
2387 
2388     gfIMEShowStatus = bShow;
2389     IntNotifyImeShowStatus(pImeWnd);
2390     return TRUE;
2391 }
2392 
2393 /* Win: xxxCheckImeShowStatusInThread */
IntCheckImeShowStatusInThread(PWND pImeWnd)2394 VOID FASTCALL IntCheckImeShowStatusInThread(PWND pImeWnd)
2395 {
2396     if (IS_IMM_MODE() && !(pImeWnd->state2 & WNDS2_INDESTROY))
2397         IntCheckImeShowStatus(pImeWnd, pImeWnd->head.pti);
2398 }
2399 
2400 /* EOF */
2401