xref: /reactos/win32ss/user/user32/misc/imm.c (revision 7e22dc05)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS user32.dll
4  * FILE:            win32ss/user/user32/misc/imm.c
5  * PURPOSE:         User32.dll Imm functions
6  * PROGRAMMERS:     Dmitry Chapyshev (dmitry@reactos.org)
7  *                  Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
8  */
9 
10 #include <user32.h>
11 #include <strsafe.h>
12 #include <ddk/immdev.h>
13 
14 WINE_DEFAULT_DEBUG_CHANNEL(user32);
15 
16 #define IMM_INIT_MAGIC 0x19650412
17 #define MAX_CANDIDATEFORM 4
18 
19 /* Is != NULL when we have loaded the IMM ourselves */
20 HINSTANCE ghImm32 = NULL; // Win: ghImm32
21 
22 BOOL gbImmInitializing = FALSE; // Win: bImmInitializing
23 
24 INT gfConIme = -1; // Win: gfConIme
25 
26 // Win: GetTopLevelWindow
27 PWND FASTCALL User32GetTopLevelWindow(PWND pwnd)
28 {
29     if (!pwnd)
30         return NULL;
31 
32     while (pwnd->style & WS_CHILD)
33         pwnd = pwnd->spwndParent;
34 
35     return pwnd;
36 }
37 
38 /* define stub functions */
39 #undef DEFINE_IMM_ENTRY
40 #define DEFINE_IMM_ENTRY(type, name, params, retval, retkind) \
41     static type WINAPI IMMSTUB_##name params { IMM_RETURN_##retkind((type)retval); }
42 #include "immtable.h"
43 
44 // Win: gImmApiEntries
45 Imm32ApiTable gImmApiEntries = {
46 /* initialize by stubs */
47 #undef DEFINE_IMM_ENTRY
48 #define DEFINE_IMM_ENTRY(type, name, params, retval, retkind) \
49     IMMSTUB_##name,
50 #include "immtable.h"
51 };
52 
53 // Win: GetImmFileName
54 HRESULT
55 User32GetImmFileName(_Out_ LPWSTR lpBuffer, _In_ size_t cchBuffer)
56 {
57     UINT length = GetSystemDirectoryW(lpBuffer, cchBuffer);
58     if (length && length < cchBuffer)
59     {
60         StringCchCatW(lpBuffer, cchBuffer, L"\\");
61         return StringCchCatW(lpBuffer, cchBuffer, L"imm32.dll");
62     }
63     return StringCchCopyW(lpBuffer, cchBuffer, L"imm32.dll");
64 }
65 
66 // @unimplemented
67 // Win: _InitializeImmEntryTable
68 static BOOL IntInitializeImmEntryTable(VOID)
69 {
70     WCHAR ImmFile[MAX_PATH];
71     HMODULE imm32 = ghImm32;
72 
73     /* Check whether the IMM table has already been initialized */
74     if (IMM_FN(ImmWINNLSEnableIME) != IMMSTUB_ImmWINNLSEnableIME)
75         return TRUE;
76 
77     User32GetImmFileName(ImmFile, _countof(ImmFile));
78     TRACE("File %S\n", ImmFile);
79 
80     /* If IMM32 is already loaded, use it without increasing reference count. */
81     if (imm32 == NULL)
82         imm32 = GetModuleHandleW(ImmFile);
83 
84     /*
85      * Loading imm32.dll will call imm32!DllMain function.
86      * imm32!DllMain calls User32InitializeImmEntryTable.
87      * Thus, if imm32.dll was loaded, the table has been loaded.
88      */
89     if (imm32 == NULL)
90     {
91         imm32 = ghImm32 = LoadLibraryW(ImmFile);
92         if (imm32 == NULL)
93         {
94             ERR("Did not load imm32.dll!\n");
95             return FALSE;
96         }
97         return TRUE;
98     }
99 
100 /* load imm procedures */
101 #undef DEFINE_IMM_ENTRY
102 #define DEFINE_IMM_ENTRY(type, name, params, retval, retkind) \
103     do { \
104         FN_##name proc = (FN_##name)GetProcAddress(imm32, #name); \
105         if (!proc) { \
106             ERR("Could not load %s\n", #name); \
107             return FALSE; \
108         } \
109         IMM_FN(name) = proc; \
110     } while (0);
111 #include "immtable.h"
112 
113     return TRUE;
114 }
115 
116 // Win: InitializeImmEntryTable
117 BOOL WINAPI InitializeImmEntryTable(VOID)
118 {
119     gbImmInitializing = TRUE;
120     return IntInitializeImmEntryTable();
121 }
122 
123 // Win: User32InitializeImmEntryTable
124 BOOL WINAPI User32InitializeImmEntryTable(DWORD magic)
125 {
126     TRACE("Imm (%x)\n", magic);
127 
128     if (magic != IMM_INIT_MAGIC)
129         return FALSE;
130 
131     /* Check whether the IMM table has already been initialized */
132     if (IMM_FN(ImmWINNLSEnableIME) != IMMSTUB_ImmWINNLSEnableIME)
133         return TRUE;
134 
135     IntInitializeImmEntryTable();
136 
137     if (ghImm32 == NULL && !gbImmInitializing)
138     {
139         WCHAR ImmFile[MAX_PATH];
140         User32GetImmFileName(ImmFile, _countof(ImmFile));
141         ghImm32 = LoadLibraryW(ImmFile);
142         if (ghImm32 == NULL)
143         {
144             ERR("Did not load imm32.dll!\n");
145             return FALSE;
146         }
147     }
148 
149     return IMM_FN(ImmRegisterClient)(&gSharedInfo, ghImm32);
150 }
151 
152 // Win: ImeIsUsableContext
153 static BOOL User32CanSetImeWindowToImc(HIMC hIMC, HWND hImeWnd)
154 {
155     PIMC pIMC = ValidateHandle(hIMC, TYPE_INPUTCONTEXT);
156     return pIMC && (!pIMC->hImeWnd || pIMC->hImeWnd == hImeWnd || !ValidateHwnd(pIMC->hImeWnd));
157 }
158 
159 // Win: GetIMEShowStatus
160 static BOOL User32GetImeShowStatus(VOID)
161 {
162     return (BOOL)NtUserCallNoParam(NOPARAM_ROUTINE_GETIMESHOWSTATUS);
163 }
164 
165 // Win: SendMessageToUI(pimeui, uMsg, wParam, lParam, !unicode)
166 static LRESULT
167 User32SendImeUIMessage(PIMEUI pimeui, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL unicode)
168 {
169     LRESULT ret = 0;
170     HWND hwndUI = pimeui->hwndUI;
171     PWND pwnd, pwndUI;
172 
173     ASSERT(pimeui->spwnd != NULL);
174 
175     pwnd = pimeui->spwnd;
176     pwndUI = ValidateHwnd(hwndUI);
177     if (!pwnd || (pwnd->state & WNDS_DESTROYED) || (pwnd->state2 & WNDS2_INDESTROY) ||
178         !pwndUI || (pwndUI->state & WNDS_DESTROYED) || (pwndUI->state2 & WNDS2_INDESTROY))
179     {
180         return 0;
181     }
182 
183     InterlockedIncrement(&pimeui->nCntInIMEProc);
184 
185     if (unicode)
186         ret = SendMessageW(hwndUI, uMsg, wParam, lParam);
187     else
188         ret = SendMessageA(hwndUI, uMsg, wParam, lParam);
189 
190     InterlockedDecrement(&pimeui->nCntInIMEProc);
191 
192     return ret;
193 }
194 
195 // Win: SendOpenStatusNotify
196 static VOID User32NotifyOpenStatus(PIMEUI pimeui, HWND hwndIMC, BOOL bOpen)
197 {
198     WPARAM wParam = (bOpen ? IMN_OPENSTATUSWINDOW : IMN_CLOSESTATUSWINDOW);
199 
200     ASSERT(pimeui->spwnd != NULL);
201 
202     pimeui->fShowStatus = bOpen;
203 
204     if (LOWORD(GetWin32ClientInfo()->dwExpWinVer) >= 0x400)
205         SendMessageW(hwndIMC, WM_IME_NOTIFY, wParam, 0);
206     else
207         User32SendImeUIMessage(pimeui, WM_IME_NOTIFY, wParam, 0, TRUE);
208 }
209 
210 // Win: ImeMarkUsedContext
211 static VOID User32SetImeWindowOfImc(HIMC hIMC, HWND hImeWnd)
212 {
213     PIMC pIMC = ValidateHandle(hIMC, TYPE_INPUTCONTEXT);
214     if (!pIMC || pIMC->hImeWnd == hImeWnd)
215         return;
216 
217     NtUserUpdateInputContext(hIMC, UIC_IMEWINDOW, (ULONG_PTR)hImeWnd);
218 }
219 
220 // Win: ImeSetImc
221 static VOID User32UpdateImcOfImeUI(PIMEUI pimeui, HIMC hNewIMC)
222 {
223     HWND hImeWnd;
224     HIMC hOldIMC = pimeui->hIMC;
225 
226     ASSERT(pimeui->spwnd != NULL);
227     hImeWnd = UserHMGetHandle(pimeui->spwnd);
228 
229     if (hNewIMC == hOldIMC)
230         return;
231 
232     if (hOldIMC)
233         User32SetImeWindowOfImc(hOldIMC, NULL);
234 
235     pimeui->hIMC = hNewIMC;
236 
237     if (hNewIMC)
238         User32SetImeWindowOfImc(hNewIMC, hImeWnd);
239 }
240 
241 // Win: ImeNotifyHandler
242 static LRESULT ImeWnd_OnImeNotify(PIMEUI pimeui, WPARAM wParam, LPARAM lParam)
243 {
244     LRESULT ret = 0;
245     HIMC hIMC;
246     LPINPUTCONTEXT pIC;
247     HWND hwndUI, hwndIMC, hImeWnd, hwndOwner;
248 
249     ASSERT(pimeui->spwnd != NULL);
250 
251     switch (wParam)
252     {
253         case IMN_SETCONVERSIONMODE:
254         case IMN_SETOPENSTATUS:
255             hIMC = pimeui->hIMC;
256             pIC = IMM_FN(ImmLockIMC)(hIMC);
257             if (pIC)
258             {
259                 hwndIMC = pimeui->hwndIMC;
260                 if (IsWindow(hwndIMC))
261                 {
262                     NtUserNotifyIMEStatus(hwndIMC, pIC->fOpen, pIC->fdwConversion);
263                 }
264                 else if (gfConIme == TRUE && pimeui->spwnd)
265                 {
266                     hImeWnd = UserHMGetHandle(pimeui->spwnd);
267                     hwndOwner = GetWindow(hImeWnd, GW_OWNER);
268                     if (hwndOwner)
269                     {
270                         NtUserNotifyIMEStatus(hwndOwner, pIC->fOpen, pIC->fdwConversion);
271                     }
272                 }
273 
274                 IMM_FN(ImmUnlockIMC)(hIMC);
275             }
276             /* FALL THROUGH */
277         default:
278             ret = User32SendImeUIMessage(pimeui, WM_IME_NOTIFY, wParam, lParam, TRUE);
279             break;
280 
281         case IMN_PRIVATE:
282             hwndUI = pimeui->hwndUI;
283             if (IsWindow(hwndUI))
284                 ret = SendMessageW(hwndUI, WM_IME_NOTIFY, wParam, lParam);
285             break;
286     }
287 
288     return ret;
289 }
290 
291 // Win: CreateIMEUI
292 static HWND User32CreateImeUIWindow(PIMEUI pimeui, HKL hKL)
293 {
294     IMEINFOEX ImeInfoEx;
295     PIMEDPI pImeDpi;
296     WNDCLASSW wc;
297     HWND hwndUI = NULL;
298     CHAR szUIClass[32];
299     PWND pwnd = pimeui->spwnd;
300 
301     ASSERT(pimeui->spwnd != NULL);
302 
303     if (!pwnd || !IMM_FN(ImmGetImeInfoEx)(&ImeInfoEx, ImeInfoExKeyboardLayout, &hKL))
304         return NULL;
305 
306     pImeDpi = IMM_FN(ImmLockImeDpi)(hKL);
307     if (!pImeDpi)
308         return NULL;
309 
310     if (!GetClassInfoW(pImeDpi->hInst, ImeInfoEx.wszUIClass, &wc))
311         goto Quit;
312 
313     if (ImeInfoEx.ImeInfo.fdwProperty & IME_PROP_UNICODE)
314     {
315         hwndUI = CreateWindowW(ImeInfoEx.wszUIClass, ImeInfoEx.wszUIClass, WS_POPUP | WS_DISABLED,
316                                0, 0, 0, 0, UserHMGetHandle(pwnd), 0, wc.hInstance, NULL);
317     }
318     else
319     {
320         WideCharToMultiByte(CP_ACP, 0, ImeInfoEx.wszUIClass, -1,
321                             szUIClass, _countof(szUIClass), NULL, NULL);
322         szUIClass[_countof(szUIClass) - 1] = 0;
323 
324         hwndUI = CreateWindowA(szUIClass, szUIClass, WS_POPUP | WS_DISABLED,
325                                0, 0, 0, 0, UserHMGetHandle(pwnd), 0, wc.hInstance, NULL);
326     }
327 
328     if (hwndUI)
329         NtUserSetWindowLong(hwndUI, IMMGWL_IMC, (LONG_PTR)pimeui->hIMC, FALSE);
330 
331 Quit:
332     IMM_FN(ImmUnlockImeDpi)(pImeDpi);
333     return hwndUI;
334 }
335 
336 // Win: ImeWndCreateHandler
337 static BOOL ImeWnd_OnCreate(PIMEUI pimeui, LPCREATESTRUCT lpCS)
338 {
339     PWND pParentWnd, pWnd = pimeui->spwnd;
340     HIMC hIMC = NULL;
341 
342     if (!pWnd || (pWnd->style & (WS_DISABLED | WS_POPUP)) != (WS_DISABLED | WS_POPUP))
343         return FALSE;
344 
345     pParentWnd = ValidateHwnd(lpCS->hwndParent);
346     if (pParentWnd)
347     {
348         hIMC = pParentWnd->hImc;
349         if (hIMC && !User32CanSetImeWindowToImc(hIMC, UserHMGetHandle(pWnd)))
350             hIMC = NULL;
351     }
352 
353     User32UpdateImcOfImeUI(pimeui, hIMC);
354 
355     pimeui->fShowStatus = FALSE;
356     pimeui->nCntInIMEProc = 0;
357     pimeui->fActivate = FALSE;
358     pimeui->fDestroy = FALSE;
359     pimeui->hwndIMC = NULL;
360     pimeui->hKL = GetWin32ClientInfo()->hKL;
361     pimeui->fCtrlShowStatus = TRUE;
362 
363     IMM_FN(ImmLoadIME)(pimeui->hKL);
364 
365     pimeui->hwndUI = NULL;
366 
367     return TRUE;
368 }
369 
370 // Win: DestroyIMEUI
371 static VOID User32DestroyImeUIWindow(PIMEUI pimeui)
372 {
373     HWND hwndUI = pimeui->hwndUI;
374 
375     if (IsWindow(hwndUI))
376     {
377         pimeui->fDestroy = TRUE;
378         NtUserDestroyWindow(hwndUI);
379     }
380 
381     pimeui->fShowStatus = pimeui->fDestroy = FALSE;
382     pimeui->hwndUI = NULL;
383 }
384 
385 // Win: ImeSelectHandler
386 static VOID ImeWnd_OnImeSelect(PIMEUI pimeui, WPARAM wParam, LPARAM lParam)
387 {
388     HKL hKL;
389     HWND hwndUI, hwndIMC = pimeui->hwndIMC;
390 
391     if (wParam)
392     {
393         pimeui->hKL = hKL = (HKL)lParam;
394         pimeui->hwndUI = hwndUI = User32CreateImeUIWindow(pimeui, hKL);
395         if (hwndUI)
396             User32SendImeUIMessage(pimeui, WM_IME_SELECT, wParam, lParam, TRUE);
397 
398         if (User32GetImeShowStatus() && pimeui->fCtrlShowStatus)
399         {
400             if (!pimeui->fShowStatus && pimeui->fActivate && IsWindow(hwndIMC))
401                 User32NotifyOpenStatus(pimeui, hwndIMC, TRUE);
402         }
403     }
404     else
405     {
406         if (pimeui->fShowStatus && pimeui->fActivate && IsWindow(hwndIMC))
407             User32NotifyOpenStatus(pimeui, hwndIMC, FALSE);
408 
409         User32SendImeUIMessage(pimeui, WM_IME_SELECT, wParam, lParam, TRUE);
410         User32DestroyImeUIWindow(pimeui);
411         pimeui->hKL = NULL;
412     }
413 }
414 
415 // Win: ImeControlHandler(pimeui, wParam, lParam, !unicode)
416 static LRESULT
417 ImeWnd_OnImeControl(PIMEUI pimeui, WPARAM wParam, LPARAM lParam, BOOL unicode)
418 {
419     HIMC hIMC = pimeui->hIMC;
420     DWORD dwConversion, dwSentence;
421     POINT pt;
422 
423     if (!hIMC)
424         return 0;
425 
426     switch (wParam)
427     {
428         case IMC_GETCONVERSIONMODE:
429             if (!IMM_FN(ImmGetConversionStatus)(hIMC, &dwConversion, &dwSentence))
430                 return 1;
431             return dwConversion;
432 
433         case IMC_GETSENTENCEMODE:
434             if (!IMM_FN(ImmGetConversionStatus)(hIMC, &dwConversion, &dwSentence))
435                 return 1;
436             return dwSentence;
437 
438         case IMC_GETOPENSTATUS:
439             return IMM_FN(ImmGetOpenStatus)(hIMC);
440 
441         case IMC_SETCONVERSIONMODE:
442             if (!IMM_FN(ImmGetConversionStatus)(hIMC, &dwConversion, &dwSentence) ||
443                 !IMM_FN(ImmSetConversionStatus)(hIMC, (DWORD)lParam, dwSentence))
444             {
445                 return 1;
446             }
447             break;
448 
449         case IMC_SETSENTENCEMODE:
450             if (!IMM_FN(ImmGetConversionStatus)(hIMC, &dwConversion, &dwSentence) ||
451                 !IMM_FN(ImmSetConversionStatus)(hIMC, dwConversion, (DWORD)lParam))
452             {
453                 return 1;
454             }
455             break;
456 
457         case IMC_SETOPENSTATUS:
458             if (!IMM_FN(ImmSetOpenStatus)(hIMC, (BOOL)lParam))
459                 return 1;
460             break;
461 
462         case IMC_GETCANDIDATEPOS:
463         case IMC_GETCOMPOSITIONWINDOW:
464         case IMC_GETSOFTKBDPOS:
465         case IMC_SETSOFTKBDPOS:
466         case IMC_GETSTATUSWINDOWPOS:
467             return User32SendImeUIMessage(pimeui, WM_IME_CONTROL, wParam, lParam, unicode);
468 
469         case IMC_SETCANDIDATEPOS:
470             if (!IMM_FN(ImmSetCandidateWindow)(hIMC, (LPCANDIDATEFORM)lParam))
471                 return 1;
472             break;
473 
474         case IMC_GETCOMPOSITIONFONT:
475             if (unicode)
476             {
477                 if (!IMM_FN(ImmGetCompositionFontW)(hIMC, (LPLOGFONTW)lParam))
478                     return 1;
479             }
480             else
481             {
482                 if (!IMM_FN(ImmGetCompositionFontA)(hIMC, (LPLOGFONTA)lParam))
483                     return 1;
484             }
485             break;
486 
487         case IMC_SETCOMPOSITIONFONT:
488             if (unicode)
489             {
490                 if (!IMM_FN(ImmSetCompositionFontW)(hIMC, (LPLOGFONTW)lParam))
491                     return 1;
492             }
493             else
494             {
495                 if (!IMM_FN(ImmSetCompositionFontA)(hIMC, (LPLOGFONTA)lParam))
496                     return 1;
497             }
498             break;
499 
500         case IMC_SETCOMPOSITIONWINDOW:
501             if (!IMM_FN(ImmSetCompositionWindow)(hIMC, (LPCOMPOSITIONFORM)lParam))
502                 return 1;
503             break;
504 
505         case IMC_SETSTATUSWINDOWPOS:
506             pt.x = GET_X_LPARAM(lParam);
507             pt.y = GET_Y_LPARAM(lParam);
508             if (!IMM_FN(ImmSetStatusWindowPos)(hIMC, &pt))
509                 return 1;
510             break;
511 
512         case IMC_CLOSESTATUSWINDOW:
513             if (pimeui->fShowStatus && User32GetImeShowStatus())
514             {
515                 pimeui->fShowStatus = FALSE;
516                 User32SendImeUIMessage(pimeui, WM_IME_NOTIFY, IMN_CLOSESTATUSWINDOW, 0, TRUE);
517             }
518             pimeui->fCtrlShowStatus = FALSE;
519             break;
520 
521         case IMC_OPENSTATUSWINDOW:
522             if (!pimeui->fShowStatus && User32GetImeShowStatus())
523             {
524                 pimeui->fShowStatus = TRUE;
525                 User32SendImeUIMessage(pimeui, WM_IME_NOTIFY, IMN_OPENSTATUSWINDOW, 0, TRUE);
526             }
527             pimeui->fCtrlShowStatus = TRUE;
528             break;
529 
530         default:
531             break;
532     }
533 
534     return 0;
535 }
536 
537 // Win: FocusSetIMCContext
538 static VOID FASTCALL User32SetImeActivenessOfWindow(HWND hWnd, BOOL bActive)
539 {
540     HIMC hIMC;
541 
542     if (!hWnd || !IsWindow(hWnd))
543     {
544         IMM_FN(ImmSetActiveContext)(NULL, NULL, bActive);
545         return;
546     }
547 
548     hIMC = IMM_FN(ImmGetContext)(hWnd);
549     IMM_FN(ImmSetActiveContext)(hWnd, hIMC, bActive);
550     IMM_FN(ImmReleaseContext)(hWnd, hIMC);
551 }
552 
553 // Win: ImeSystemHandler
554 static LRESULT ImeWnd_OnImeSystem(PIMEUI pimeui, WPARAM wParam, LPARAM lParam)
555 {
556     LRESULT ret = 0;
557     LPINPUTCONTEXTDX pIC;
558     HIMC hIMC = pimeui->hIMC;
559     LPCANDIDATEFORM pCandForm;
560     LPCOMPOSITIONFORM pCompForm;
561     DWORD dwConversion, dwSentence;
562     HWND hImeWnd;
563     BOOL bCompForm;
564     CANDIDATEFORM CandForm;
565     COMPOSITIONFORM CompForm;
566     UINT iCandForm;
567 
568     ASSERT(pimeui->spwnd != NULL);
569 
570     switch (wParam)
571     {
572         case 0x05:
573             if (User32GetImeShowStatus() == !lParam)
574             {
575                 hImeWnd = UserHMGetHandle(pimeui->spwnd);
576                 NtUserCallHwndParamLock(hImeWnd, lParam, X_ROUTINE_IMESHOWSTATUSCHANGE);
577             }
578             break;
579 
580         case 0x06:
581             if (!hIMC)
582                 break;
583 
584             bCompForm = TRUE;
585             pIC = IMM_FN(ImmLockIMC)(hIMC);
586             if (pIC)
587             {
588                 bCompForm = !(pIC->dwUIFlags & 0x2);
589                 IMM_FN(ImmUnlockIMC)(hIMC);
590             }
591 
592             if (!IsWindow(pimeui->hwndIMC))
593                 break;
594 
595             if (bCompForm && IMM_FN(ImmGetCompositionWindow)(hIMC, &CompForm))
596             {
597                 if (CompForm.dwStyle)
598                     IMM_FN(ImmSetCompositionWindow)(hIMC, &CompForm);
599             }
600 
601             for (iCandForm = 0; iCandForm < MAX_CANDIDATEFORM; ++iCandForm)
602             {
603                 if (IMM_FN(ImmGetCandidateWindow)(hIMC, iCandForm, &CandForm))
604                 {
605                     if (CandForm.dwStyle)
606                         IMM_FN(ImmSetCandidateWindow)(hIMC, &CandForm);
607                 }
608             }
609             break;
610 
611         case 0x09:
612             pIC = IMM_FN(ImmLockIMC)(hIMC);
613             if (!pIC)
614                 break;
615 
616             pCandForm = &pIC->cfCandForm[lParam];
617             IMM_FN(ImmSetCandidateWindow)(hIMC, pCandForm);
618             IMM_FN(ImmUnlockIMC)(hIMC);
619             break;
620 
621         case 0x0A:
622             pIC = IMM_FN(ImmLockIMC)(hIMC);
623             if (!pIC)
624                 break;
625 
626             IMM_FN(ImmSetCompositionFontW)(hIMC, &pIC->lfFont.W);
627             IMM_FN(ImmUnlockIMC)(hIMC);
628             break;
629 
630         case 0x0B:
631             pIC = IMM_FN(ImmLockIMC)(hIMC);
632             if (!pIC)
633                 break;
634 
635             pCompForm = &pIC->cfCompForm;
636             pIC->dwUIFlags |= 0x8;
637             IMM_FN(ImmSetCompositionWindow)(hIMC, pCompForm);
638             IMM_FN(ImmUnlockIMC)(hIMC);
639             break;
640 
641         case 0x0D:
642             IMM_FN(ImmConfigureIMEW)((HKL)lParam, pimeui->hwndIMC, IME_CONFIG_GENERAL, NULL);
643             break;
644 
645         case 0x0F:
646             if (hIMC)
647                 IMM_FN(ImmSetOpenStatus)(hIMC, (BOOL)lParam);
648             break;
649 
650         case 0x11:
651             ret = IMM_FN(ImmFreeLayout)((DWORD)lParam);
652             break;
653 
654         case 0x13:
655             // TODO:
656             break;
657 
658         case 0x14:
659             IMM_FN(ImmGetConversionStatus)(hIMC, &dwConversion, &dwSentence);
660             ret = dwConversion;
661             break;
662 
663         case 0x15:
664             // TODO:
665             break;
666 
667         case IMS_IMEACTIVATE:
668             User32SetImeActivenessOfWindow((HWND)lParam, TRUE);
669             break;
670 
671         case IMS_IMEDEACTIVATE:
672             User32SetImeActivenessOfWindow((HWND)lParam, FALSE);
673             break;
674 
675         case IMS_ACTIVATELAYOUT:
676             ret = IMM_FN(ImmActivateLayout)((HKL)lParam);
677             break;
678 
679         case 0x1C:
680             ret = IMM_FN(ImmPutImeMenuItemsIntoMappedFile)((HIMC)lParam);
681             break;
682 
683         case 0x1D:
684             // TODO:
685             break;
686 
687         case 0x1E:
688             ret = (ULONG_PTR)IMM_FN(ImmGetContext)((HWND)lParam);
689             break;
690 
691         case 0x1F:
692         case 0x20:
693             ret = IMM_FN(ImmSystemHandler)(hIMC, wParam, lParam);
694             break;
695 
696         default:
697             break;
698     }
699 
700     return ret;
701 }
702 
703 // Win: ImeSetContextHandler
704 LRESULT ImeWnd_OnImeSetContext(PIMEUI pimeui, WPARAM wParam, LPARAM lParam)
705 {
706     LRESULT ret;
707     HIMC hIMC;
708     LPINPUTCONTEXTDX pIC;
709     HWND hwndFocus, hwndOldImc, hwndNewImc, hImeWnd, hwndActive, hwndOwner;
710     PWND pwndFocus, pwndOldImc, pwndNewImc, pImeWnd, pwndOwner;
711     COMPOSITIONFORM CompForm;
712 
713     pimeui->fActivate = !!wParam;
714     hwndOldImc = pimeui->hwndIMC;
715     ASSERT(pimeui->spwnd != NULL);
716 
717     if (wParam)
718     {
719         if (!pimeui->hwndUI)
720             pimeui->hwndUI = User32CreateImeUIWindow(pimeui, pimeui->hKL);
721 
722         if (gfConIme == -1)
723         {
724             gfConIme = (INT)NtUserGetThreadState(THREADSTATE_CHECKCONIME);
725             if (gfConIme)
726                 pimeui->fCtrlShowStatus = FALSE;
727         }
728 
729         hImeWnd = UserHMGetHandle(pimeui->spwnd);
730 
731         if (gfConIme)
732         {
733             hwndOwner = GetWindow(hImeWnd, GW_OWNER);
734             pwndOwner = ValidateHwnd(hwndOwner);
735             if (pwndOwner)
736             {
737                 User32UpdateImcOfImeUI(pimeui, pwndOwner->hImc);
738 
739                 if (pimeui->hwndUI)
740                     SetWindowLongPtrW(pimeui->hwndUI, IMMGWLP_IMC, (LONG_PTR)pwndOwner->hImc);
741             }
742 
743             return User32SendImeUIMessage(pimeui, WM_IME_SETCONTEXT, wParam, lParam, TRUE);
744         }
745 
746         hwndFocus = (HWND)NtUserQueryWindow(hImeWnd, QUERY_WINDOW_FOCUS);
747 
748         hIMC = IMM_FN(ImmGetContext)(hwndFocus);
749 
750         if (hIMC && !User32CanSetImeWindowToImc(hIMC, hImeWnd))
751         {
752             User32UpdateImcOfImeUI(pimeui, NULL);
753             return 0;
754         }
755 
756         User32UpdateImcOfImeUI(pimeui, hIMC);
757 
758         if (pimeui->hwndUI)
759             SetWindowLongPtrW(pimeui->hwndUI, IMMGWLP_IMC, (LONG_PTR)hIMC);
760 
761         if (hIMC)
762         {
763             pIC = IMM_FN(ImmLockIMC)(hIMC);
764             if (!pIC)
765                 return 0;
766 
767             if (hwndFocus != pIC->hWnd)
768             {
769                 IMM_FN(ImmUnlockIMC)(hIMC);
770                 return 0;
771             }
772 
773             if ((pIC->dwUIFlags & 0x40000) && hwndOldImc != hwndFocus)
774             {
775                 RtlZeroMemory(&CompForm, sizeof(CompForm));
776                 IMM_FN(ImmSetCompositionWindow)(hIMC, &CompForm);
777 
778                 pIC->dwUIFlags &= ~0x40000;
779             }
780 
781             IMM_FN(ImmUnlockIMC)(hIMC);
782 
783             hImeWnd = UserHMGetHandle(pimeui->spwnd);
784             if (NtUserSetImeOwnerWindow(hImeWnd, hwndFocus))
785                 pimeui->hwndIMC = hwndFocus;
786         }
787         else
788         {
789             pimeui->hwndIMC = hwndFocus;
790 
791             hImeWnd = UserHMGetHandle(pimeui->spwnd);
792             NtUserSetImeOwnerWindow(hImeWnd, NULL);
793         }
794     }
795 
796     ret = User32SendImeUIMessage(pimeui, WM_IME_SETCONTEXT, wParam, lParam, TRUE);
797 
798     if (!pimeui->spwnd)
799         return 0;
800 
801     if (!pimeui->fCtrlShowStatus || !User32GetImeShowStatus())
802         return ret;
803 
804     hImeWnd = UserHMGetHandle(pimeui->spwnd);
805     hwndFocus = (HWND)NtUserQueryWindow(hImeWnd, QUERY_WINDOW_FOCUS);
806     pwndFocus = ValidateHwnd(hwndFocus);
807 
808     if (wParam)
809     {
810         pImeWnd = ValidateHwnd(hImeWnd);
811         if (pwndFocus && pImeWnd && pImeWnd->head.pti == pwndFocus->head.pti)
812         {
813             hwndNewImc = pimeui->hwndIMC;
814             if (pimeui->fShowStatus)
815             {
816                 pwndNewImc = ValidateHwnd(hwndNewImc);
817                 pwndOldImc = ValidateHwnd(hwndOldImc);
818                 if (pwndNewImc && pwndOldImc && pwndNewImc != pwndOldImc &&
819                     User32GetTopLevelWindow(pwndNewImc) != User32GetTopLevelWindow(pwndOldImc))
820                 {
821                     User32NotifyOpenStatus(pimeui, hwndOldImc, FALSE);
822                     User32NotifyOpenStatus(pimeui, hwndNewImc, TRUE);
823                 }
824             }
825             else
826             {
827                 if (ValidateHwnd(hwndNewImc))
828                     User32NotifyOpenStatus(pimeui, hwndNewImc, TRUE);
829             }
830         }
831 
832         pImeWnd = pimeui->spwnd;
833         hImeWnd = (pImeWnd ? UserHMGetHandle(pImeWnd) : NULL);
834         if (hImeWnd)
835             NtUserCallHwndLock(hImeWnd, HWNDLOCK_ROUTINE_CHECKIMESHOWSTATUSINTHRD);
836     }
837     else
838     {
839         pImeWnd = pimeui->spwnd;
840         hImeWnd = UserHMGetHandle(pImeWnd);
841         hwndActive = (HWND)NtUserQueryWindow(hImeWnd, QUERY_WINDOW_ACTIVE);
842         if (!pwndFocus || !hwndActive || pImeWnd->head.pti != pwndFocus->head.pti)
843         {
844             if (IsWindow(hwndOldImc))
845             {
846                 User32NotifyOpenStatus(pimeui, hwndOldImc, FALSE);
847             }
848             else
849             {
850                 pimeui->fShowStatus = FALSE;
851                 User32SendImeUIMessage(pimeui, WM_IME_NOTIFY, IMN_CLOSESTATUSWINDOW, 0, TRUE);
852             }
853         }
854     }
855 
856     return ret;
857 }
858 
859 // Win: ImeWndProcWorker(hwnd, msg, wParam, lParam, !unicode)
860 LRESULT WINAPI
861 ImeWndProc_common(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, BOOL unicode) // ReactOS
862 {
863     PWND pWnd;
864     PIMEUI pimeui;
865 
866     pWnd = ValidateHwnd(hwnd);
867     if (pWnd)
868     {
869        if (!pWnd->fnid)
870        {
871           if (msg != WM_NCCREATE)
872           {
873              if (unicode)
874                 return DefWindowProcW(hwnd, msg, wParam, lParam);
875              return DefWindowProcA(hwnd, msg, wParam, lParam);
876           }
877           NtUserSetWindowFNID(hwnd, FNID_IME);
878           pimeui = HeapAlloc( GetProcessHeap(), 0, sizeof(IMEUI) );
879           pimeui->spwnd = pWnd;
880           SetWindowLongPtrW(hwnd, IMMGWLP_IMC, (LONG_PTR)pimeui);
881        }
882        else
883        {
884           if (pWnd->fnid != FNID_IME)
885           {
886              ERR("Wrong window class for Ime! fnId 0x%x\n",pWnd->fnid);
887              return 0;
888           }
889           pimeui = ((PIMEWND)pWnd)->pimeui;
890           if (pimeui == NULL)
891           {
892              ERR("Window is not set to IME!\n");
893              return 0;
894           }
895        }
896     }
897 
898     if (pimeui->nCntInIMEProc > 0)
899     {
900         switch (msg)
901         {
902             case WM_IME_CHAR:
903             case WM_IME_COMPOSITIONFULL:
904             case WM_IME_CONTROL:
905             case WM_IME_NOTIFY:
906             case WM_IME_REQUEST:
907             case WM_IME_SELECT:
908             case WM_IME_SETCONTEXT:
909             case WM_IME_STARTCOMPOSITION:
910             case WM_IME_COMPOSITION:
911             case WM_IME_ENDCOMPOSITION:
912                 return 0;
913 
914             case WM_IME_SYSTEM:
915                 switch (wParam)
916                 {
917                     case 0x03:
918                     case 0x10:
919                     case 0x13:
920                         break;
921 
922                     default:
923                         return 0;
924                 }
925                 break;
926 
927             default:
928             {
929                 if (unicode)
930                     return DefWindowProcW(hwnd, msg, wParam, lParam);
931                 return DefWindowProcA(hwnd, msg, wParam, lParam);
932             }
933         }
934     }
935 
936     switch (msg)
937     {
938         case WM_CREATE:
939             return (ImeWnd_OnCreate(pimeui, (LPCREATESTRUCT)lParam) ? 0 : -1);
940 
941         case WM_DESTROY:
942             User32DestroyImeUIWindow(pimeui);
943             break;
944 
945         case WM_NCDESTROY:
946             HeapFree(GetProcessHeap(), 0, pimeui);
947             SetWindowLongPtrW(hwnd, IMMGWLP_IMC, 0);
948             NtUserSetWindowFNID(hwnd, FNID_DESTROY);
949             break;
950 
951         case WM_ERASEBKGND:
952             return TRUE;
953 
954         case WM_PAINT:
955             break;
956 
957         case WM_COPYDATA:
958             // TODO:
959             break;
960 
961         case WM_IME_STARTCOMPOSITION:
962         case WM_IME_COMPOSITION:
963         case WM_IME_ENDCOMPOSITION:
964             return User32SendImeUIMessage(pimeui, msg, wParam, lParam, unicode);
965 
966         case WM_IME_CONTROL:
967             return ImeWnd_OnImeControl(pimeui, wParam, lParam, unicode);
968 
969         case WM_IME_NOTIFY:
970             return ImeWnd_OnImeNotify(pimeui, wParam, lParam);
971 
972         case WM_IME_REQUEST:
973             break;
974 
975         case WM_IME_SELECT:
976             ImeWnd_OnImeSelect(pimeui, wParam, lParam);
977             break;
978 
979         case WM_IME_SETCONTEXT:
980             return ImeWnd_OnImeSetContext(pimeui, wParam, lParam);
981 
982         case WM_IME_SYSTEM:
983             return ImeWnd_OnImeSystem(pimeui, wParam, lParam);
984 
985         default:
986         {
987             if (unicode)
988                 return DefWindowProcW(hwnd, msg, wParam, lParam);
989             return DefWindowProcA(hwnd, msg, wParam, lParam);
990         }
991     }
992 
993     return 0;
994 }
995 
996 // Win: ImeWndProcA
997 LRESULT WINAPI ImeWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
998 {
999     return ImeWndProc_common(hwnd, msg, wParam, lParam, FALSE);
1000 }
1001 
1002 // Win: ImeWndProcW
1003 LRESULT WINAPI ImeWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
1004 {
1005     return ImeWndProc_common(hwnd, msg, wParam, lParam, TRUE);
1006 }
1007 
1008 // Win: UpdatePerUserImmEnabling
1009 BOOL WINAPI UpdatePerUserImmEnabling(VOID)
1010 {
1011     HMODULE imm32;
1012     BOOL ret;
1013 
1014     ret = NtUserCallNoParam(NOPARAM_ROUTINE_UPDATEPERUSERIMMENABLING);
1015     if (!ret || !(gpsi->dwSRVIFlags & SRVINFO_IMM32))
1016         return FALSE;
1017 
1018     imm32 = GetModuleHandleW(L"imm32.dll");
1019     if (imm32)
1020         return TRUE;
1021 
1022     imm32 = LoadLibraryW(L"imm32.dll");
1023     if (imm32)
1024     {
1025         ERR("UPUIE: Imm32 not installed!\n");
1026         ret = FALSE;
1027     }
1028 
1029     return ret;
1030 }
1031 
1032 BOOL
1033 WINAPI
1034 RegisterIMEClass(VOID)
1035 {
1036     ATOM atom;
1037     WNDCLASSEXW WndClass = { sizeof(WndClass) };
1038 
1039     WndClass.lpszClassName  = L"IME";
1040     WndClass.style          = CS_GLOBALCLASS;
1041     WndClass.lpfnWndProc    = ImeWndProcW;
1042     WndClass.cbWndExtra     = sizeof(LONG_PTR);
1043     WndClass.hCursor        = LoadCursorW(NULL, IDC_ARROW);
1044 
1045     atom = RegisterClassExWOWW(&WndClass, 0, FNID_IME, 0, FALSE);
1046     if (!atom)
1047     {
1048         ERR("Failed to register IME Class!\n");
1049         return FALSE;
1050     }
1051 
1052     RegisterDefaultClasses |= ICLASS_TO_MASK(ICLS_IME);
1053     TRACE("RegisterIMEClass atom = %u\n", atom);
1054     return TRUE;
1055 }
1056 
1057 /*
1058  * @unimplemented
1059  */
1060 BOOL WINAPI CliImmSetHotKey(DWORD dwID, UINT uModifiers, UINT uVirtualKey, HKL hKl)
1061 {
1062   UNIMPLEMENTED;
1063   return FALSE;
1064 }
1065 
1066 /*
1067  * @implemented
1068  */
1069 BOOL
1070 WINAPI
1071 IMPSetIMEW(HWND hwnd, LPIMEPROW ime)
1072 {
1073     return IMM_FN(ImmIMPSetIMEW)(hwnd, ime);
1074 }
1075 
1076 /*
1077  * @implemented
1078  */
1079 BOOL
1080 WINAPI
1081 IMPQueryIMEW(LPIMEPROW ime)
1082 {
1083     return IMM_FN(ImmIMPQueryIMEW)(ime);
1084 }
1085 
1086 /*
1087  * @implemented
1088  */
1089 BOOL
1090 WINAPI
1091 IMPGetIMEW(HWND hwnd, LPIMEPROW ime)
1092 {
1093     return IMM_FN(ImmIMPGetIMEW)(hwnd, ime);
1094 }
1095 
1096 /*
1097  * @implemented
1098  */
1099 BOOL
1100 WINAPI
1101 IMPSetIMEA(HWND hwnd, LPIMEPROA ime)
1102 {
1103     return IMM_FN(ImmIMPSetIMEA)(hwnd, ime);
1104 }
1105 
1106 /*
1107  * @implemented
1108  */
1109 BOOL
1110 WINAPI
1111 IMPQueryIMEA(LPIMEPROA ime)
1112 {
1113     return IMM_FN(ImmIMPQueryIMEA)(ime);
1114 }
1115 
1116 /*
1117  * @implemented
1118  */
1119 BOOL
1120 WINAPI
1121 IMPGetIMEA(HWND hwnd, LPIMEPROA ime)
1122 {
1123     return IMM_FN(ImmIMPGetIMEA)(hwnd, ime);
1124 }
1125 
1126 /*
1127  * @implemented
1128  */
1129 LRESULT
1130 WINAPI
1131 SendIMEMessageExW(HWND hwnd, LPARAM lParam)
1132 {
1133     return IMM_FN(ImmSendIMEMessageExW)(hwnd, lParam);
1134 }
1135 
1136 /*
1137  * @implemented
1138  */
1139 LRESULT
1140 WINAPI
1141 SendIMEMessageExA(HWND hwnd, LPARAM lParam)
1142 {
1143     return IMM_FN(ImmSendIMEMessageExA)(hwnd, lParam);
1144 }
1145 
1146 /*
1147  * @implemented
1148  */
1149 BOOL
1150 WINAPI
1151 WINNLSEnableIME(HWND hwnd, BOOL enable)
1152 {
1153     return IMM_FN(ImmWINNLSEnableIME)(hwnd, enable);
1154 }
1155 
1156 /*
1157  * @implemented
1158  */
1159 BOOL
1160 WINAPI
1161 WINNLSGetEnableStatus(HWND hwnd)
1162 {
1163     return IMM_FN(ImmWINNLSGetEnableStatus)(hwnd);
1164 }
1165 
1166 /*
1167  * @implemented
1168  */
1169 UINT
1170 WINAPI
1171 WINNLSGetIMEHotkey(HWND hwnd)
1172 {
1173     return FALSE;
1174 }
1175