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