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