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