xref: /reactos/dll/win32/imm32/keymsg.c (revision 14d3b53c)
1 /*
2  * PROJECT:     ReactOS IMM32
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     Implementing IMM32 keys and messages
5  * COPYRIGHT:   Copyright 1998 Patrik Stridvall
6  *              Copyright 2002, 2003, 2007 CodeWeavers, Aric Stewart
7  *              Copyright 2017 James Tabor <james.tabor@reactos.org>
8  *              Copyright 2018 Amine Khaldi <amine.khaldi@reactos.org>
9  *              Copyright 2020-2021 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
10  */
11 
12 #include "precomp.h"
13 #include <jpnvkeys.h>
14 
15 WINE_DEFAULT_DEBUG_CHANNEL(imm);
16 
17 /* Win: IMENonIMEToggle */
18 BOOL APIENTRY Imm32ImeNonImeToggle(HIMC hIMC, HKL hKL, HWND hWnd, BOOL bNowIME, LANGID LangID)
19 {
20     HKL hOldKL, LayoutList[32], hFoundKL = NULL;
21     UINT iLayout, nLayoutCount;
22 
23     /* Get the previous layout */
24     hOldKL = (HKL)NtUserGetThreadState(THREADSTATE_OLDKEYBOARDLAYOUT);
25 
26     /* Get the layout list */
27     nLayoutCount = GetKeyboardLayoutList(_countof(LayoutList), LayoutList);
28 
29     /* Is there hOldKL in the list in the specified language ID? */
30     if (hOldKL && (LangID == 0 || LOWORD(hOldKL) == LangID))
31     {
32         for (iLayout = 0; iLayout < nLayoutCount; ++iLayout)
33         {
34             if (LayoutList[iLayout] == hOldKL)
35             {
36                 hFoundKL = hOldKL;
37                 break;
38             }
39         }
40     }
41 
42     if (hFoundKL == NULL) /* Not found? */
43     {
44         /* Is there the keyboard layout of another kind in LangID? */
45         for (iLayout = 0; iLayout < nLayoutCount; ++iLayout)
46         {
47             if (bNowIME == ImmIsIME(LayoutList[iLayout])) /* Same kind? */
48                 continue;
49 
50             if (LangID == 0 || LangID == LOWORD(LayoutList[iLayout]))
51             {
52                 hFoundKL = LayoutList[iLayout];
53                 break;
54             }
55         }
56     }
57 
58     if (hFoundKL && hKL != hFoundKL) /* Found and different layout */
59     {
60         PostMessageW(hWnd, WM_INPUTLANGCHANGEREQUEST, 1, (LPARAM)hFoundKL);
61     }
62 
63     return ImmIsIME(hFoundKL);
64 }
65 
66 /* Open or close the IME on Chinese or Taiwanese */
67 /* Win: CIMENonIMEToggle */
68 BOOL APIENTRY Imm32CImeNonImeToggle(HIMC hIMC, HKL hKL, HWND hWnd, LANGID LangID)
69 {
70     LPINPUTCONTEXT pIC;
71     BOOL fOpen;
72 
73     if (IS_NULL_UNEXPECTEDLY(hWnd))
74         return FALSE;
75 
76     if (LOWORD(hKL) != LangID || !ImmIsIME(hKL))
77     {
78         Imm32ImeNonImeToggle(hIMC, hKL, hWnd, FALSE, LangID);
79         return TRUE;
80     }
81 
82     pIC = ImmLockIMC(hIMC);
83     if (IS_NULL_UNEXPECTEDLY(pIC))
84         return TRUE;
85 
86     fOpen = pIC->fOpen;
87     ImmUnlockIMC(hIMC);
88 
89     if (fOpen)
90         Imm32ImeNonImeToggle(hIMC, hKL, hWnd, TRUE, 0);
91     else
92         ImmSetOpenStatus(hIMC, TRUE);
93 
94     return TRUE;
95 }
96 
97 /* Toggle shape mode on Chinese or Taiwanese */
98 /* Win: TShapeToggle */
99 BOOL APIENTRY Imm32CShapeToggle(HIMC hIMC, HKL hKL, HWND hWnd)
100 {
101     LPINPUTCONTEXT pIC;
102     BOOL fOpen;
103     DWORD dwConversion, dwSentence;
104 
105     if (hWnd == NULL || !ImmIsIME(hKL))
106         return FALSE;
107 
108     pIC = ImmLockIMC(hIMC);
109     if (IS_NULL_UNEXPECTEDLY(pIC))
110         return TRUE;
111 
112     fOpen = pIC->fOpen;
113     if (fOpen)
114     {
115         dwConversion = (pIC->fdwConversion ^ IME_CMODE_FULLSHAPE);
116         dwSentence = pIC->fdwSentence;
117     }
118 
119     ImmUnlockIMC(hIMC);
120 
121     if (fOpen)
122         ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
123     else
124         ImmSetOpenStatus(hIMC, TRUE);
125 
126     return TRUE;
127 }
128 
129 /* Win: CSymbolToggle */
130 BOOL APIENTRY Imm32CSymbolToggle(HIMC hIMC, HKL hKL, HWND hWnd)
131 {
132     LPINPUTCONTEXT pIC;
133     BOOL fOpen;
134     DWORD dwConversion, dwSentence;
135 
136     if (hWnd == NULL || !ImmIsIME(hKL))
137         return FALSE;
138 
139     pIC = ImmLockIMC(hIMC);
140     if (IS_NULL_UNEXPECTEDLY(pIC))
141         return TRUE;
142 
143     fOpen = pIC->fOpen;
144     if (fOpen)
145     {
146         dwConversion = (pIC->fdwConversion ^ IME_CMODE_SYMBOL);
147         dwSentence = pIC->fdwSentence;
148     }
149 
150     ImmUnlockIMC(hIMC);
151 
152     if (fOpen)
153         ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
154     else
155         ImmSetOpenStatus(hIMC, TRUE);
156 
157     return TRUE;
158 }
159 
160 /* Open or close Japanese IME */
161 BOOL APIENTRY Imm32JCloseOpen(HIMC hIMC, HKL hKL, HWND hWnd)
162 {
163     BOOL fOpen;
164     LPINPUTCONTEXTDX pIC;
165 
166     if (LOWORD(hKL) == LANGID_JAPANESE && ImmIsIME(hKL)) /* Japanese IME is selected */
167     {
168         fOpen = ImmGetOpenStatus(hIMC);
169         ImmSetOpenStatus(hIMC, !fOpen);
170         return TRUE;
171     }
172 
173     /* Japanese IME is not selected. Select now */
174     if (Imm32ImeNonImeToggle(hIMC, hKL, hWnd, FALSE, LANGID_JAPANESE))
175     {
176         pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
177         if (pIC)
178         {
179             pIC->dwChange |= INPUTCONTEXTDX_CHANGE_FORCE_OPEN;
180             ImmUnlockIMC(hIMC);
181         }
182     }
183 
184     return TRUE;
185 }
186 
187 /* Win: KShapeToggle */
188 BOOL APIENTRY Imm32KShapeToggle(HIMC hIMC)
189 {
190     LPINPUTCONTEXT pIC;
191     DWORD dwConversion, dwSentence;
192 
193     pIC = ImmLockIMC(hIMC);
194     if (IS_NULL_UNEXPECTEDLY(pIC))
195         return FALSE;
196 
197     dwConversion = (pIC->fdwConversion ^ IME_CMODE_FULLSHAPE);
198     dwSentence = pIC->fdwSentence;
199     ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
200 
201     if (pIC->fdwConversion & (IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE))
202         ImmSetOpenStatus(hIMC, TRUE);
203     else
204         ImmSetOpenStatus(hIMC, FALSE);
205 
206     ImmUnlockIMC(hIMC);
207     return TRUE;
208 }
209 
210 /* Win: KHanjaConvert */
211 BOOL APIENTRY Imm32KHanjaConvert(HIMC hIMC)
212 {
213     LPINPUTCONTEXT pIC;
214     DWORD dwConversion, dwSentence;
215 
216     pIC = ImmLockIMC(hIMC);
217     if (IS_NULL_UNEXPECTEDLY(pIC))
218         return FALSE;
219 
220     dwConversion = (pIC->fdwConversion ^ IME_CMODE_HANJACONVERT);
221     dwSentence = pIC->fdwSentence;
222     ImmUnlockIMC(hIMC);
223 
224     ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
225     return TRUE;
226 }
227 
228 /* Win: KEnglishHangul */
229 BOOL APIENTRY Imm32KEnglish(HIMC hIMC)
230 {
231     LPINPUTCONTEXT pIC;
232     DWORD dwConversion, dwSentence;
233     BOOL fOpen;
234 
235     pIC = ImmLockIMC(hIMC);
236     if (IS_NULL_UNEXPECTEDLY(pIC))
237         return FALSE;
238 
239     dwConversion = (pIC->fdwConversion ^ IME_CMODE_NATIVE);
240     dwSentence = pIC->fdwSentence;
241     ImmSetConversionStatus(hIMC, dwConversion, dwSentence);
242 
243     fOpen = ((pIC->fdwConversion & (IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE)) != 0);
244     ImmSetOpenStatus(hIMC, fOpen);
245 
246     ImmUnlockIMC(hIMC);
247     return TRUE;
248 }
249 
250 /* Win: HotKeyIDDispatcher */
251 BOOL APIENTRY Imm32ProcessHotKey(HWND hWnd, HIMC hIMC, HKL hKL, DWORD dwHotKeyID)
252 {
253     PIMEDPI pImeDpi;
254     BOOL ret;
255 
256     if (hIMC && IS_CROSS_THREAD_HIMC(hIMC))
257         return FALSE;
258 
259     switch (dwHotKeyID)
260     {
261         case IME_CHOTKEY_IME_NONIME_TOGGLE:
262             return Imm32CImeNonImeToggle(hIMC, hKL, hWnd, LANGID_CHINESE_SIMPLIFIED);
263 
264         case IME_CHOTKEY_SHAPE_TOGGLE:
265             return Imm32CShapeToggle(hIMC, hKL, hWnd);
266 
267         case IME_CHOTKEY_SYMBOL_TOGGLE:
268             return Imm32CSymbolToggle(hIMC, hKL, hWnd);
269 
270         case IME_JHOTKEY_CLOSE_OPEN:
271             return Imm32JCloseOpen(hIMC, hKL, hWnd);
272 
273         case IME_KHOTKEY_SHAPE_TOGGLE:
274             return Imm32KShapeToggle(hIMC);
275 
276         case IME_KHOTKEY_HANJACONVERT:
277             return Imm32KHanjaConvert(hIMC);
278 
279         case IME_KHOTKEY_ENGLISH:
280             return Imm32KEnglish(hIMC);
281 
282         case IME_THOTKEY_IME_NONIME_TOGGLE:
283             return Imm32CImeNonImeToggle(hIMC, hKL, hWnd, LANGID_CHINESE_TRADITIONAL);
284 
285         case IME_THOTKEY_SHAPE_TOGGLE:
286             return Imm32CShapeToggle(hIMC, hKL, hWnd);
287 
288         case IME_THOTKEY_SYMBOL_TOGGLE:
289             return Imm32CSymbolToggle(hIMC, hKL, hWnd);
290 
291         default:
292             WARN("0x%X\n", dwHotKeyID);
293             break;
294     }
295 
296     if (dwHotKeyID < IME_HOTKEY_PRIVATE_FIRST || IME_HOTKEY_PRIVATE_LAST < dwHotKeyID)
297         return FALSE;
298 
299     pImeDpi = ImmLockImeDpi(hKL);
300     if (IS_NULL_UNEXPECTEDLY(pImeDpi))
301         return FALSE;
302 
303     ret = (BOOL)pImeDpi->ImeEscape(hIMC, IME_ESC_PRIVATE_HOTKEY, &dwHotKeyID);
304     ImmUnlockImeDpi(pImeDpi);
305     return ret;
306 }
307 
308 /* Win: ImmIsUIMessageWorker */
309 static BOOL APIENTRY
310 ImmIsUIMessageAW(HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam, BOOL bAnsi)
311 {
312     switch (msg)
313     {
314         case WM_IME_STARTCOMPOSITION: case WM_IME_ENDCOMPOSITION:
315         case WM_IME_COMPOSITION: case WM_IME_SETCONTEXT: case WM_IME_NOTIFY:
316         case WM_IME_COMPOSITIONFULL: case WM_IME_SELECT: case WM_IME_SYSTEM:
317             break;
318         default:
319             return FALSE;
320     }
321 
322     if (IS_NULL_UNEXPECTEDLY(hWndIME))
323         return TRUE;
324 
325     if (bAnsi)
326         SendMessageA(hWndIME, msg, wParam, lParam);
327     else
328         SendMessageW(hWndIME, msg, wParam, lParam);
329 
330     return TRUE;
331 }
332 
333 static BOOL CALLBACK
334 Imm32SendNotificationProc(
335     _In_ HIMC hIMC,
336     _In_ LPARAM lParam)
337 {
338     HWND hWnd;
339     LPINPUTCONTEXTDX pIC;
340 
341     UNREFERENCED_PARAMETER(lParam);
342 
343     pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
344     if (IS_NULL_UNEXPECTEDLY(pIC))
345         return TRUE;
346 
347     hWnd = pIC->hWnd;
348     if (!IsWindow(hWnd))
349         goto Quit;
350 
351     TRACE("dwChange: 0x%08X\n", pIC->dwChange);
352 
353     if (pIC->dwChange & INPUTCONTEXTDX_CHANGE_OPEN)
354         SendMessageW(hWnd, WM_IME_NOTIFY, IMN_SETOPENSTATUS, 0);
355     if (pIC->dwChange & INPUTCONTEXTDX_CHANGE_CONVERSION)
356         SendMessageW(hWnd, WM_IME_NOTIFY, IMN_SETCONVERSIONMODE, 0);
357     if (pIC->dwChange & (INPUTCONTEXTDX_CHANGE_OPEN | INPUTCONTEXTDX_CHANGE_CONVERSION))
358         NtUserNotifyIMEStatus(hWnd, pIC->fOpen, pIC->fdwConversion);
359     if (pIC->dwChange & INPUTCONTEXTDX_CHANGE_SENTENCE)
360         SendMessageW(hWnd, WM_IME_NOTIFY, IMN_SETSENTENCEMODE, 0);
361 Quit:
362     pIC->dwChange = 0;
363     ImmUnlockIMC(hIMC); // ??? Windows doesn't unlock here
364     return TRUE;
365 }
366 
367 BOOL APIENTRY Imm32SendNotification(BOOL bProcess)
368 {
369     return ImmEnumInputContext((bProcess ? -1 : 0), Imm32SendNotificationProc, 0);
370 }
371 
372 LRESULT APIENTRY
373 Imm32ProcessRequest(HIMC hIMC, PWND pWnd, DWORD dwCommand, LPVOID pData, BOOL bAnsiAPI)
374 {
375     HWND hWnd;
376     DWORD ret = 0, dwCharPos, cchCompStr, dwSize;
377     LPVOID pCS, pTempData = pData;
378     LPRECONVERTSTRING pRS;
379     LPIMECHARPOSITION pICP;
380     PCLIENTIMC pClientImc;
381     UINT uCodePage = CP_ACP;
382     BOOL bAnsiWnd = !!(pWnd->state & WNDS_ANSIWINDOWPROC);
383     static const size_t acbData[7 * 2] =
384     {
385         /* UNICODE */
386         sizeof(COMPOSITIONFORM), sizeof(CANDIDATEFORM), sizeof(LOGFONTW),
387         sizeof(RECONVERTSTRING), sizeof(RECONVERTSTRING),
388         sizeof(IMECHARPOSITION), sizeof(RECONVERTSTRING),
389         /* ANSI */
390         sizeof(COMPOSITIONFORM), sizeof(CANDIDATEFORM), sizeof(LOGFONTA),
391         sizeof(RECONVERTSTRING), sizeof(RECONVERTSTRING),
392         sizeof(IMECHARPOSITION), sizeof(RECONVERTSTRING),
393     };
394 
395     if (dwCommand == 0 || dwCommand > IMR_DOCUMENTFEED)
396     {
397         ERR("Out of boundary\n");
398         return 0; /* Out of range */
399     }
400 
401     dwSize = acbData[bAnsiAPI * 7 + dwCommand - 1];
402     if (pData && IsBadWritePtr(pData, dwSize))
403     {
404         ERR("\n");
405         return 0; /* Invalid pointer */
406     }
407 
408     /* Sanity check */
409     switch (dwCommand)
410     {
411         case IMR_RECONVERTSTRING: case IMR_DOCUMENTFEED:
412             pRS = pData;
413             if (pRS && (pRS->dwVersion != 0 || pRS->dwSize < sizeof(RECONVERTSTRING)))
414             {
415                 ERR("Invalid pRS\n");
416                 return 0;
417             }
418             break;
419 
420         case IMR_CONFIRMRECONVERTSTRING:
421             pRS = pData;
422             if (!pRS || pRS->dwVersion != 0)
423             {
424                 ERR("Invalid pRS\n");
425                 return 0;
426             }
427             break;
428 
429         default:
430             if (IS_NULL_UNEXPECTEDLY(pData))
431                 return 0;
432             break;
433     }
434 
435     pClientImc = ImmLockClientImc(hIMC);
436     if (pClientImc)
437     {
438         uCodePage = pClientImc->uCodePage;
439         ImmUnlockClientImc(pClientImc);
440     }
441 
442     /* Prepare */
443     switch (dwCommand)
444     {
445         case IMR_COMPOSITIONFONT:
446             if (bAnsiAPI == bAnsiWnd)
447                 goto DoIt; /* No conversion needed */
448 
449             if (bAnsiWnd)
450                 pTempData = ImmLocalAlloc(0, sizeof(LOGFONTA));
451             else
452                 pTempData = ImmLocalAlloc(0, sizeof(LOGFONTW));
453 
454             if (IS_NULL_UNEXPECTEDLY(pTempData))
455                 return 0;
456             break;
457 
458         case IMR_RECONVERTSTRING: case IMR_CONFIRMRECONVERTSTRING: case IMR_DOCUMENTFEED:
459             if (bAnsiAPI == bAnsiWnd || !pData)
460                 goto DoIt; /* No conversion needed */
461 
462             if (bAnsiWnd)
463                 ret = Imm32ReconvertAnsiFromWide(NULL, pData, uCodePage);
464             else
465                 ret = Imm32ReconvertWideFromAnsi(NULL, pData, uCodePage);
466 
467             pTempData = ImmLocalAlloc(0, ret + sizeof(WCHAR));
468             if (IS_NULL_UNEXPECTEDLY(pTempData))
469                 return 0;
470 
471             pRS = pTempData;
472             pRS->dwSize = ret;
473             pRS->dwVersion = 0;
474 
475             if (dwCommand == IMR_CONFIRMRECONVERTSTRING)
476             {
477                 if (bAnsiWnd)
478                     ret = Imm32ReconvertAnsiFromWide(pTempData, pData, uCodePage);
479                 else
480                     ret = Imm32ReconvertWideFromAnsi(pTempData, pData, uCodePage);
481             }
482             break;
483 
484         case IMR_QUERYCHARPOSITION:
485             if (bAnsiAPI == bAnsiWnd)
486                 goto DoIt; /* No conversion needed */
487 
488             pICP = pData;
489             dwCharPos = pICP->dwCharPos;
490 
491             if (bAnsiAPI)
492             {
493                 cchCompStr = ImmGetCompositionStringA(hIMC, GCS_COMPSTR, NULL, 0);
494                 if (IS_ZERO_UNEXPECTEDLY(cchCompStr))
495                     return 0;
496 
497                 pCS = ImmLocalAlloc(0, (cchCompStr + 1) * sizeof(CHAR));
498                 if (IS_NULL_UNEXPECTEDLY(pCS))
499                     return 0;
500 
501                 ImmGetCompositionStringA(hIMC, GCS_COMPSTR, pCS, cchCompStr);
502                 pICP->dwCharPos = IchWideFromAnsi(pICP->dwCharPos, pCS, uCodePage);
503             }
504             else
505             {
506                 cchCompStr = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, NULL, 0);
507                 if (IS_ZERO_UNEXPECTEDLY(cchCompStr))
508                     return 0;
509 
510                 pCS = ImmLocalAlloc(0, (cchCompStr + 1) * sizeof(WCHAR));
511                 if (IS_NULL_UNEXPECTEDLY(pCS))
512                     return 0;
513 
514                 ImmGetCompositionStringW(hIMC, GCS_COMPSTR, pCS, cchCompStr);
515                 pICP->dwCharPos = IchAnsiFromWide(pICP->dwCharPos, pCS, uCodePage);
516             }
517 
518             ImmLocalFree(pCS);
519             break;
520 
521         default:
522             WARN("0x%X\n", dwCommand);
523             break;
524     }
525 
526 DoIt:
527     /* The main task */
528     hWnd = pWnd->head.h;
529     if (bAnsiWnd)
530         ret = SendMessageA(hWnd, WM_IME_REQUEST, dwCommand, (LPARAM)pTempData);
531     else
532         ret = SendMessageW(hWnd, WM_IME_REQUEST, dwCommand, (LPARAM)pTempData);
533 
534     if (bAnsiAPI == bAnsiWnd)
535         goto Quit; /* No conversion needed */
536 
537     /* Get back to caller */
538     switch (dwCommand)
539     {
540         case IMR_COMPOSITIONFONT:
541             if (bAnsiAPI)
542                 LogFontWideToAnsi(pTempData, pData);
543             else
544                 LogFontAnsiToWide(pTempData, pData);
545             break;
546 
547         case IMR_RECONVERTSTRING: case IMR_DOCUMENTFEED:
548             if (!ret)
549                 break;
550 
551             if (ret < sizeof(RECONVERTSTRING))
552             {
553                 ret = 0;
554                 break;
555             }
556 
557             if (pTempData)
558             {
559                 if (bAnsiWnd)
560                     ret = Imm32ReconvertWideFromAnsi(pData, pTempData, uCodePage);
561                 else
562                     ret = Imm32ReconvertAnsiFromWide(pData, pTempData, uCodePage);
563             }
564             break;
565 
566         case IMR_QUERYCHARPOSITION:
567             pICP->dwCharPos = dwCharPos;
568             break;
569 
570         default:
571             WARN("0x%X\n", dwCommand);
572             break;
573     }
574 
575 Quit:
576     if (pTempData != pData)
577         ImmLocalFree(pTempData);
578     return ret;
579 }
580 
581 /* Win: ImmRequestMessageWorker */
582 LRESULT APIENTRY ImmRequestMessageAW(HIMC hIMC, WPARAM wParam, LPARAM lParam, BOOL bAnsi)
583 {
584     LRESULT ret = 0;
585     LPINPUTCONTEXT pIC;
586     HWND hWnd;
587     PWND pWnd = NULL;
588 
589     if (IS_NULL_UNEXPECTEDLY(hIMC) || IS_CROSS_THREAD_HIMC(hIMC))
590         return FALSE;
591 
592     pIC = ImmLockIMC(hIMC);
593     if (IS_NULL_UNEXPECTEDLY(pIC))
594         return FALSE;
595 
596     hWnd = pIC->hWnd;
597     if (hWnd)
598         pWnd = ValidateHwnd(hWnd);
599 
600     if (pWnd && pWnd->head.pti == Imm32CurrentPti())
601         ret = Imm32ProcessRequest(hIMC, pWnd, (DWORD)wParam, (LPVOID)lParam, bAnsi);
602 
603     ImmUnlockIMC(hIMC);
604     return ret;
605 }
606 
607 /***********************************************************************
608  *		ImmIsUIMessageA (IMM32.@)
609  */
610 BOOL WINAPI ImmIsUIMessageA(HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam)
611 {
612     TRACE("(%p, 0x%X, %p, %p)\n", hWndIME, msg, wParam, lParam);
613     return ImmIsUIMessageAW(hWndIME, msg, wParam, lParam, TRUE);
614 }
615 
616 /***********************************************************************
617  *		ImmIsUIMessageW (IMM32.@)
618  */
619 BOOL WINAPI ImmIsUIMessageW(HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam)
620 {
621     TRACE("(%p, 0x%X, %p, %p)\n", hWndIME, msg, wParam, lParam);
622     return ImmIsUIMessageAW(hWndIME, msg, wParam, lParam, FALSE);
623 }
624 
625 /***********************************************************************
626  *              ImmGetHotKey(IMM32.@)
627  */
628 BOOL WINAPI
629 ImmGetHotKey(IN DWORD dwHotKey, OUT LPUINT lpuModifiers, OUT LPUINT lpuVKey,
630              OUT LPHKL lphKL)
631 {
632     TRACE("(0x%lX, %p, %p, %p)\n", dwHotKey, lpuModifiers, lpuVKey, lphKL);
633     if (lpuModifiers && lpuVKey)
634         return NtUserGetImeHotKey(dwHotKey, lpuModifiers, lpuVKey, lphKL);
635     return FALSE;
636 }
637 
638 /***********************************************************************
639  *              ImmWINNLSGetIMEHotkey (IMM32.@)
640  */
641 UINT WINAPI ImmWINNLSGetIMEHotkey(HWND hwndIme)
642 {
643     TRACE("(%p)\n", hwndIme);
644     UNREFERENCED_PARAMETER(hwndIme);
645     return 0; /* This is correct. This function of Windows just returns zero. */
646 }
647 
648 /***********************************************************************
649  *		ImmSimulateHotKey (IMM32.@)
650  */
651 BOOL WINAPI ImmSimulateHotKey(HWND hWnd, DWORD dwHotKeyID)
652 {
653     HIMC hIMC;
654     DWORD dwThreadId;
655     HKL hKL;
656     BOOL ret;
657 
658     TRACE("(%p, 0x%lX)\n", hWnd, dwHotKeyID);
659 
660     hIMC = ImmGetContext(hWnd);
661     dwThreadId = GetWindowThreadProcessId(hWnd, NULL);
662     hKL = GetKeyboardLayout(dwThreadId);
663     ret = Imm32ProcessHotKey(hWnd, hIMC, hKL, dwHotKeyID);
664     ImmReleaseContext(hWnd, hIMC);
665     return ret;
666 }
667 
668 /***********************************************************************
669  *		ImmGetVirtualKey (IMM32.@)
670  */
671 UINT WINAPI ImmGetVirtualKey(HWND hWnd)
672 {
673     HIMC hIMC;
674     LPINPUTCONTEXTDX pIC;
675     UINT ret = VK_PROCESSKEY;
676 
677     TRACE("(%p)\n", hWnd);
678 
679     hIMC = ImmGetContext(hWnd);
680     pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
681     if (IS_NULL_UNEXPECTEDLY(pIC))
682         return ret;
683 
684     if (pIC->bNeedsTrans)
685         ret = pIC->nVKey;
686 
687     ImmUnlockIMC(hIMC);
688     return ret;
689 }
690 
691 /***********************************************************************
692  *		ImmGetAppCompatFlags (IMM32.@)
693  */
694 DWORD WINAPI ImmGetAppCompatFlags(HIMC hIMC)
695 {
696     PCLIENTIMC pClientIMC;
697     DWORD dwFlags;
698 
699     TRACE("(%p)\n", hIMC);
700 
701     pClientIMC = ImmLockClientImc(hIMC);
702     if (IS_NULL_UNEXPECTEDLY(pClientIMC))
703         return 0;
704 
705     dwFlags = pClientIMC->dwCompatFlags;
706     ImmUnlockClientImc(pClientIMC);
707     return (dwFlags | g_aimm_compat_flags);
708 }
709 
710 /***********************************************************************
711  *		ImmProcessKey(IMM32.@)
712  *       ( Undocumented, called from user32.dll )
713  */
714 DWORD WINAPI
715 ImmProcessKey(HWND hWnd, HKL hKL, UINT vKey, LPARAM lParam, DWORD dwHotKeyID)
716 {
717     DWORD ret = 0;
718     HIMC hIMC;
719     PIMEDPI pImeDpi;
720     LPINPUTCONTEXTDX pIC;
721     BYTE KeyState[256];
722     BOOL bLowWordOnly = FALSE, bSkipThisKey = FALSE, bHotKeyDone = TRUE;
723 
724     TRACE("(%p, %p, 0x%X, %p, 0x%lX)\n", hWnd, hKL, vKey, lParam, dwHotKeyID);
725 
726     /* Process the key by the IME */
727     hIMC = ImmGetContext(hWnd);
728     pImeDpi = ImmLockImeDpi(hKL);
729     if (pImeDpi)
730     {
731         pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
732         if (pIC)
733         {
734             if ((LOBYTE(vKey) == VK_PACKET) &&
735                 !(pImeDpi->ImeInfo.fdwProperty & IME_PROP_ACCEPT_WIDE_VKEY))
736             {
737                 if (ImeDpi_IsUnicode(pImeDpi))
738                 {
739                     bLowWordOnly = TRUE;
740                 }
741                 else
742                 {
743                     if (pIC->fOpen)
744                         ret |= IPHK_SKIPTHISKEY;
745 
746                     bSkipThisKey = TRUE;
747                 }
748             }
749 
750             if (!bSkipThisKey && GetKeyboardState(KeyState))
751             {
752                 UINT vk = (bLowWordOnly ? LOWORD(vKey) : vKey);
753                 if (pImeDpi->ImeProcessKey(hIMC, vk, lParam, KeyState))
754                 {
755                     pIC->bNeedsTrans = TRUE;
756                     pIC->nVKey = vKey;
757                     ret |= IPHK_PROCESSBYIME;
758                 }
759             }
760 
761             ImmUnlockIMC(hIMC);
762         }
763 
764         ImmUnlockImeDpi(pImeDpi);
765     }
766 
767     /* Process the hot-key if necessary */
768     if (!CtfImmIsCiceroStartedInThread()) /* Not Cicero? */
769     {
770         /* Process IMM IME hotkey */
771         if ((dwHotKeyID == INVALID_HOTKEY_ID) || !Imm32ProcessHotKey(hWnd, hIMC, hKL, dwHotKeyID))
772             bHotKeyDone = FALSE;
773     }
774     else if (!CtfImeProcessCicHotkey(hIMC, vKey, lParam)) /* CTF IME not processed the hotkey? */
775     {
776         /* Process IMM IME hotkey */
777         if (!IS_IME_HKL(hKL) ||
778             ((dwHotKeyID == INVALID_HOTKEY_ID) || !Imm32ProcessHotKey(hWnd, hIMC, hKL, dwHotKeyID)))
779         {
780             bHotKeyDone = FALSE;
781         }
782     }
783 
784     if (bHotKeyDone && ((vKey != VK_KANJI) || (dwHotKeyID != IME_JHOTKEY_CLOSE_OPEN)))
785         ret |= IPHK_HOTKEY;
786 
787     if ((ret & IPHK_PROCESSBYIME) && (ImmGetAppCompatFlags(hIMC) & 0x10000))
788     {
789         /* The key has been processed by IME's ImeProcessKey */
790         LANGID wLangID = LANGIDFROMLCID(GetSystemDefaultLCID());
791         if ((PRIMARYLANGID(wLangID) == LANG_KOREAN) &&
792             ((vKey == VK_PROCESSKEY) || (ret & IPHK_HOTKEY)))
793         {
794             /* Korean don't want VK_PROCESSKEY and IME hot-keys */
795         }
796         else
797         {
798             /* Add WM_KEYDOWN:VK_PROCESSKEY message */
799             ImmTranslateMessage(hWnd, WM_KEYDOWN, VK_PROCESSKEY, lParam);
800 
801             ret &= ~IPHK_PROCESSBYIME;
802             ret |= IPHK_SKIPTHISKEY;
803         }
804     }
805 
806     ImmReleaseContext(hWnd, hIMC);
807     return ret; /* Returns IPHK_... flags */
808 }
809 
810 /***********************************************************************
811  *		ImmSystemHandler(IMM32.@)
812  */
813 LRESULT WINAPI ImmSystemHandler(HIMC hIMC, WPARAM wParam, LPARAM lParam)
814 {
815     TRACE("(%p, %p, %p)\n", hIMC, wParam, lParam);
816 
817     switch (wParam)
818     {
819         case IMS_SENDNOTIFICATION:
820             Imm32SendNotification((BOOL)lParam);
821             return 0;
822 
823         case IMS_COMPLETECOMPSTR:
824             ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
825             return 0;
826 
827         case IMS_SETLANGBAND:
828         case IMS_UNSETLANGBAND:
829             return CtfImmSetLangBand((HWND)lParam, (wParam == IMS_SETLANGBAND));
830 
831         default:
832             WARN("%p\n", wParam);
833             return 0;
834     }
835 }
836 
837 /***********************************************************************
838  *		ImmGenerateMessage(IMM32.@)
839  */
840 BOOL WINAPI ImmGenerateMessage(HIMC hIMC)
841 {
842     PCLIENTIMC pClientImc;
843     LPINPUTCONTEXT pIC;
844     LPTRANSMSG pMsgs, pTrans = NULL, pItem;
845     HWND hWnd;
846     DWORD dwIndex, dwCount, cbTrans;
847     HIMCC hMsgBuf = NULL;
848     BOOL bAnsi;
849 
850     TRACE("(%p)\n", hIMC);
851 
852     if (IS_CROSS_THREAD_HIMC(hIMC))
853         return FALSE;
854 
855     pClientImc = ImmLockClientImc(hIMC);
856     if (IS_NULL_UNEXPECTEDLY(pClientImc))
857         return FALSE;
858 
859     bAnsi = !(pClientImc->dwFlags & CLIENTIMC_WIDE);
860     ImmUnlockClientImc(pClientImc);
861 
862     pIC = ImmLockIMC(hIMC);
863     if (IS_NULL_UNEXPECTEDLY(pIC))
864         return FALSE;
865 
866     dwCount = pIC->dwNumMsgBuf;
867     if (dwCount == 0)
868         goto Quit;
869 
870     hMsgBuf = pIC->hMsgBuf;
871     pMsgs = ImmLockIMCC(hMsgBuf);
872     if (IS_NULL_UNEXPECTEDLY(pMsgs))
873         goto Quit;
874 
875     cbTrans = dwCount * sizeof(TRANSMSG);
876     pTrans = ImmLocalAlloc(0, cbTrans);
877     if (IS_NULL_UNEXPECTEDLY(pTrans))
878         goto Quit;
879 
880     RtlCopyMemory(pTrans, pMsgs, cbTrans);
881 
882 #ifdef IMM_WIN3_SUPPORT
883     if (GetWin32ClientInfo()->dwExpWinVer < _WIN32_WINNT_NT4) /* old version (3.x)? */
884     {
885         LANGID LangID = LANGIDFROMLCID(GetSystemDefaultLCID());
886         WORD wLang = PRIMARYLANGID(LangID);
887 
888         /* translate the messages if Japanese or Korean */
889         if (wLang == LANG_JAPANESE ||
890             (wLang == LANG_KOREAN && NtUserGetAppImeLevel(pIC->hWnd) == 3))
891         {
892             dwCount = WINNLSTranslateMessage(dwCount, pTrans, hIMC, bAnsi, wLang);
893         }
894     }
895 #endif
896 
897     /* send them */
898     hWnd = pIC->hWnd;
899     pItem = pTrans;
900     for (dwIndex = 0; dwIndex < dwCount; ++dwIndex, ++pItem)
901     {
902         if (bAnsi)
903             SendMessageA(hWnd, pItem->message, pItem->wParam, pItem->lParam);
904         else
905             SendMessageW(hWnd, pItem->message, pItem->wParam, pItem->lParam);
906     }
907 
908 Quit:
909     ImmLocalFree(pTrans);
910     if (hMsgBuf)
911         ImmUnlockIMCC(hMsgBuf);
912     pIC->dwNumMsgBuf = 0; /* done */
913     ImmUnlockIMC(hIMC);
914     return TRUE;
915 }
916 
917 VOID APIENTRY
918 ImmPostMessages(HWND hwnd, HIMC hIMC, DWORD dwCount, LPTRANSMSG lpTransMsg)
919 {
920     DWORD dwIndex;
921     PCLIENTIMC pClientImc;
922     LPTRANSMSG pNewTransMsg = lpTransMsg, pItem;
923     BOOL bAnsi;
924 
925     pClientImc = ImmLockClientImc(hIMC);
926     if (IS_NULL_UNEXPECTEDLY(pClientImc))
927         return;
928 
929     bAnsi = !(pClientImc->dwFlags & CLIENTIMC_WIDE);
930     ImmUnlockClientImc(pClientImc);
931 
932 #ifdef IMM_WIN3_SUPPORT
933     if (GetWin32ClientInfo()->dwExpWinVer < _WIN32_WINNT_NT4) /* old version (3.x)? */
934     {
935         LANGID LangID = LANGIDFROMLCID(GetSystemDefaultLCID());
936         WORD Lang = PRIMARYLANGID(LangID);
937 
938         /* translate the messages if Japanese or Korean */
939         if (Lang == LANG_JAPANESE ||
940             (Lang == LANG_KOREAN && NtUserGetAppImeLevel(hwnd) == 3))
941         {
942             DWORD cbTransMsg = dwCount * sizeof(TRANSMSG);
943             pNewTransMsg = ImmLocalAlloc(0, cbTransMsg);
944             if (pNewTransMsg)
945             {
946                 RtlCopyMemory(pNewTransMsg, lpTransMsg, cbTransMsg);
947                 dwCount = WINNLSTranslateMessage(dwCount, pNewTransMsg, hIMC, bAnsi, Lang);
948             }
949             else
950             {
951                 pNewTransMsg = lpTransMsg;
952             }
953         }
954     }
955 #endif
956 
957     /* post them */
958     pItem = pNewTransMsg;
959     for (dwIndex = 0; dwIndex < dwCount; ++dwIndex, ++pItem)
960     {
961         if (bAnsi)
962             PostMessageA(hwnd, pItem->message, pItem->wParam, pItem->lParam);
963         else
964             PostMessageW(hwnd, pItem->message, pItem->wParam, pItem->lParam);
965     }
966 
967 #ifdef IMM_WIN3_SUPPORT
968     if (pNewTransMsg != lpTransMsg)
969         ImmLocalFree(pNewTransMsg);
970 #endif
971 }
972 
973 /***********************************************************************
974  *       ImmTranslateMessage(IMM32.@)
975  *       ( Undocumented, call internally and from user32.dll )
976  */
977 BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyData)
978 {
979 #define MSG_COUNT 0x100
980     BOOL ret = FALSE;
981     INT kret;
982     LPINPUTCONTEXTDX pIC;
983     PIMEDPI pImeDpi = NULL;
984     LPTRANSMSGLIST pList = NULL;
985     LPTRANSMSG pTransMsg;
986     BYTE abKeyState[256];
987     HIMC hIMC;
988     HKL hKL;
989     UINT vk;
990     DWORD dwThreadId, dwCount, cbList;
991     WCHAR wch;
992     WORD wChar;
993 
994     TRACE("(%p, 0x%X, %p, %p)\n", hwnd, msg, wParam, lKeyData);
995 
996     /* filter the message */
997     switch (msg)
998     {
999         case WM_KEYDOWN: case WM_KEYUP: case WM_SYSKEYDOWN: case WM_SYSKEYUP:
1000             break;
1001         default:
1002             return FALSE;
1003     }
1004 
1005     hIMC = ImmGetContext(hwnd);
1006     pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
1007     if (IS_NULL_UNEXPECTEDLY(pIC))
1008     {
1009         ImmReleaseContext(hwnd, hIMC);
1010         return FALSE;
1011     }
1012 
1013     if (!pIC->bNeedsTrans) /* is translation needed? */
1014     {
1015         /* directly post them */
1016         dwCount = pIC->dwNumMsgBuf;
1017         if (dwCount == 0)
1018             goto Quit;
1019 
1020         pTransMsg = ImmLockIMCC(pIC->hMsgBuf);
1021         if (pTransMsg)
1022         {
1023             ImmPostMessages(hwnd, hIMC, dwCount, pTransMsg);
1024             ImmUnlockIMCC(pIC->hMsgBuf);
1025             ret = TRUE;
1026         }
1027         pIC->dwNumMsgBuf = 0; /* done */
1028         goto Quit;
1029     }
1030     pIC->bNeedsTrans = FALSE; /* clear the flag */
1031 
1032     dwThreadId = GetWindowThreadProcessId(hwnd, NULL);
1033     hKL = GetKeyboardLayout(dwThreadId);
1034     pImeDpi = ImmLockImeDpi(hKL);
1035     if (IS_NULL_UNEXPECTEDLY(pImeDpi))
1036         goto Quit;
1037 
1038     if (!GetKeyboardState(abKeyState)) /* get keyboard ON/OFF status */
1039     {
1040         WARN("\n");
1041         goto Quit;
1042     }
1043 
1044     /* convert a virtual key if IME_PROP_KBD_CHAR_FIRST */
1045     vk = pIC->nVKey;
1046     if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_KBD_CHAR_FIRST)
1047     {
1048         if (ImeDpi_IsUnicode(pImeDpi))
1049         {
1050             wch = 0;
1051             kret = ToUnicode(vk, HIWORD(lKeyData), abKeyState, &wch, 1, 0);
1052             if (kret == 1)
1053                 vk = MAKELONG(LOBYTE(vk), wch);
1054         }
1055         else
1056         {
1057             wChar = 0;
1058             kret = ToAsciiEx(vk, HIWORD(lKeyData), abKeyState, &wChar, 0, hKL);
1059             if (kret > 0)
1060             {
1061                 if ((BYTE)vk == VK_PACKET)
1062                 {
1063                     vk &= 0xFF;
1064                     vk |= (wChar << 8);
1065                 }
1066                 else
1067                 {
1068                     vk = MAKEWORD(vk, wChar);
1069                 }
1070             }
1071         }
1072     }
1073 
1074     /* allocate a list */
1075     cbList = offsetof(TRANSMSGLIST, TransMsg) + MSG_COUNT * sizeof(TRANSMSG);
1076     pList = ImmLocalAlloc(0, cbList);
1077     if (IS_NULL_UNEXPECTEDLY(pList))
1078         goto Quit;
1079 
1080     /* use IME conversion engine and convert the list */
1081     pList->uMsgCount = MSG_COUNT;
1082     kret = pImeDpi->ImeToAsciiEx(vk, HIWORD(lKeyData), abKeyState, pList, 0, hIMC);
1083     if (kret <= 0)
1084         goto Quit;
1085 
1086     /* post them */
1087     if (kret <= MSG_COUNT)
1088     {
1089         ImmPostMessages(hwnd, hIMC, kret, pList->TransMsg);
1090         ret = TRUE;
1091     }
1092     else
1093     {
1094         pTransMsg = ImmLockIMCC(pIC->hMsgBuf);
1095         if (IS_NULL_UNEXPECTEDLY(pTransMsg))
1096             goto Quit;
1097         ImmPostMessages(hwnd, hIMC, kret, pTransMsg);
1098         ImmUnlockIMCC(pIC->hMsgBuf);
1099     }
1100 
1101 Quit:
1102     ImmLocalFree(pList);
1103     ImmUnlockImeDpi(pImeDpi);
1104     ImmUnlockIMC(hIMC);
1105     ImmReleaseContext(hwnd, hIMC);
1106     TRACE("ret: %d\n", ret);
1107     return ret;
1108 #undef MSG_COUNT
1109 }
1110 
1111 /***********************************************************************
1112  *              ImmRequestMessageA(IMM32.@)
1113  */
1114 LRESULT WINAPI ImmRequestMessageA(HIMC hIMC, WPARAM wParam, LPARAM lParam)
1115 {
1116     TRACE("(%p, %p, %p)\n", hIMC, wParam, lParam);
1117     return ImmRequestMessageAW(hIMC, wParam, lParam, TRUE);
1118 }
1119 
1120 /***********************************************************************
1121  *              ImmRequestMessageW(IMM32.@)
1122  */
1123 LRESULT WINAPI ImmRequestMessageW(HIMC hIMC, WPARAM wParam, LPARAM lParam)
1124 {
1125     TRACE("(%p, %p, %p)\n", hIMC, wParam, lParam);
1126     return ImmRequestMessageAW(hIMC, wParam, lParam, FALSE);
1127 }
1128 
1129 /***********************************************************************
1130  *              ImmCallImeConsoleIME (IMM32.@)
1131  */
1132 DWORD WINAPI
1133 ImmCallImeConsoleIME(
1134     _In_ HWND hWnd,
1135     _In_ UINT uMsg,
1136     _In_ WPARAM wParam,
1137     _In_ LPARAM lParam,
1138     _Out_ LPUINT puVK)
1139 {
1140     DWORD dwThreadId, ret = 0;
1141     HKL hKL;
1142     PWND pWnd = NULL;
1143     HIMC hIMC;
1144     PIMEDPI pImeDpi;
1145     UINT uVK;
1146     PIMC pIMC;
1147 
1148     switch (uMsg)
1149     {
1150         case WM_KEYDOWN:
1151         case WM_KEYUP:
1152         case WM_SYSKEYDOWN:
1153         case WM_SYSKEYUP:
1154             break;
1155 
1156         default:
1157             return 0;
1158     }
1159 
1160     dwThreadId = GetWindowThreadProcessId(hWnd, NULL);
1161     hKL = GetKeyboardLayout(dwThreadId);
1162 
1163     if (hWnd && gpsi)
1164         pWnd = ValidateHwndNoErr(hWnd);
1165     if (IS_NULL_UNEXPECTEDLY(pWnd))
1166         return 0;
1167 
1168     hIMC = ImmGetContext(hWnd);
1169     if (IS_NULL_UNEXPECTEDLY(hIMC))
1170         return 0;
1171 
1172     uVK = *puVK = (wParam & 0xFF);
1173 
1174     pIMC = ValidateHandleNoErr(hIMC, TYPE_INPUTCONTEXT);
1175     if (IS_NULL_UNEXPECTEDLY(pIMC))
1176         return 0;
1177 
1178     pImeDpi = ImmLockImeDpi(hKL);
1179     if (IS_NULL_UNEXPECTEDLY(pImeDpi))
1180         return 0;
1181 
1182     if ((lParam & MAKELPARAM(0, KF_UP)) && (pImeDpi->ImeInfo.fdwProperty & IME_PROP_IGNORE_UPKEYS))
1183         goto Quit;
1184 
1185     switch (uVK)
1186     {
1187         case VK_DBE_ROMAN:
1188         case VK_DBE_NOROMAN:
1189         case VK_DBE_HIRAGANA:
1190         case VK_DBE_KATAKANA:
1191         case VK_DBE_CODEINPUT:
1192         case VK_DBE_NOCODEINPUT:
1193         case VK_DBE_ENTERWORDREGISTERMODE:
1194         case VK_DBE_ENTERCONFIGMODE:
1195             break;
1196 
1197         default:
1198         {
1199             if (uMsg == WM_SYSKEYDOWN || uMsg == WM_SYSKEYUP)
1200             {
1201                 if (uVK != VK_MENU && uVK != VK_F10)
1202                     goto Quit;
1203             }
1204 
1205             if (!(pImeDpi->ImeInfo.fdwProperty & IME_PROP_NEED_ALTKEY))
1206             {
1207                 if (uVK == VK_MENU || (lParam & MAKELPARAM(0, KF_ALTDOWN)))
1208                     goto Quit;
1209             }
1210         }
1211     }
1212 
1213     ret = ImmProcessKey(hWnd, hKL, uVK, lParam, INVALID_HOTKEY_ID);
1214 
1215 Quit:
1216     ImmUnlockImeDpi(pImeDpi);
1217     return ret;
1218 }
1219