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