xref: /reactos/dll/win32/imm32/keymsg.c (revision 6ef7b676)
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 /***********************************************************************
353  *		ImmIsUIMessageA (IMM32.@)
354  */
355 BOOL WINAPI ImmIsUIMessageA(HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam)
356 {
357     TRACE("(%p, 0x%X, %p, %p)\n", hWndIME, msg, wParam, lParam);
358     return ImmIsUIMessageAW(hWndIME, msg, wParam, lParam, TRUE);
359 }
360 
361 /***********************************************************************
362  *		ImmIsUIMessageW (IMM32.@)
363  */
364 BOOL WINAPI ImmIsUIMessageW(HWND hWndIME, UINT msg, WPARAM wParam, LPARAM lParam)
365 {
366     TRACE("(%p, 0x%X, %p, %p)\n", hWndIME, msg, wParam, lParam);
367     return ImmIsUIMessageAW(hWndIME, msg, wParam, lParam, FALSE);
368 }
369 
370 /***********************************************************************
371  *              ImmGetHotKey(IMM32.@)
372  */
373 BOOL WINAPI
374 ImmGetHotKey(IN DWORD dwHotKey, OUT LPUINT lpuModifiers, OUT LPUINT lpuVKey,
375              OUT LPHKL lphKL)
376 {
377     TRACE("(0x%lX, %p, %p, %p)\n", dwHotKey, lpuModifiers, lpuVKey, lphKL);
378     if (lpuModifiers && lpuVKey)
379         return NtUserGetImeHotKey(dwHotKey, lpuModifiers, lpuVKey, lphKL);
380     return FALSE;
381 }
382 
383 /***********************************************************************
384  *              ImmWINNLSGetIMEHotkey (IMM32.@)
385  */
386 UINT WINAPI ImmWINNLSGetIMEHotkey(HWND hwndIme)
387 {
388     TRACE("(%p)\n", hwndIme);
389     UNREFERENCED_PARAMETER(hwndIme);
390     return 0; /* This is correct. This function of Windows just returns zero. */
391 }
392 
393 /***********************************************************************
394  *		ImmSimulateHotKey (IMM32.@)
395  */
396 BOOL WINAPI ImmSimulateHotKey(HWND hWnd, DWORD dwHotKeyID)
397 {
398     HIMC hIMC;
399     DWORD dwThreadId;
400     HKL hKL;
401     BOOL ret;
402 
403     TRACE("(%p, 0x%lX)\n", hWnd, dwHotKeyID);
404 
405     hIMC = ImmGetContext(hWnd);
406     dwThreadId = GetWindowThreadProcessId(hWnd, NULL);
407     hKL = GetKeyboardLayout(dwThreadId);
408     ret = Imm32ProcessHotKey(hWnd, hIMC, hKL, dwHotKeyID);
409     ImmReleaseContext(hWnd, hIMC);
410     return ret;
411 }
412 
413 /***********************************************************************
414  *		ImmGetVirtualKey (IMM32.@)
415  */
416 UINT WINAPI ImmGetVirtualKey(HWND hWnd)
417 {
418     HIMC hIMC;
419     LPINPUTCONTEXTDX pIC;
420     UINT ret = VK_PROCESSKEY;
421 
422     TRACE("(%p)\n", hWnd);
423 
424     hIMC = ImmGetContext(hWnd);
425     pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
426     if (!pIC)
427         return ret;
428 
429     if (pIC->bNeedsTrans)
430         ret = pIC->nVKey;
431 
432     ImmUnlockIMC(hIMC);
433     return ret;
434 }
435 
436 /***********************************************************************
437  *		ImmProcessKey(IMM32.@)
438  *       ( Undocumented, called from user32.dll )
439  */
440 DWORD WINAPI
441 ImmProcessKey(HWND hWnd, HKL hKL, UINT vKey, LPARAM lParam, DWORD dwHotKeyID)
442 {
443     DWORD ret = 0;
444     HIMC hIMC;
445     PIMEDPI pImeDpi;
446     LPINPUTCONTEXTDX pIC;
447     BYTE KeyState[256];
448     UINT vk;
449     BOOL bUseIme = TRUE, bSkipThisKey = FALSE, bLowWordOnly = FALSE;
450 
451     TRACE("(%p, %p, 0x%X, %p, 0x%lX)\n", hWnd, hKL, vKey, lParam, dwHotKeyID);
452 
453     hIMC = ImmGetContext(hWnd);
454     pImeDpi = ImmLockImeDpi(hKL);
455     if (pImeDpi)
456     {
457         pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
458         if (pIC)
459         {
460             if (LOBYTE(vKey) == VK_PACKET &&
461                 !(pImeDpi->ImeInfo.fdwProperty & IME_PROP_ACCEPT_WIDE_VKEY))
462             {
463                 if (ImeDpi_IsUnicode(pImeDpi))
464                 {
465                     bLowWordOnly = TRUE;
466                 }
467                 else
468                 {
469                     bUseIme = FALSE;
470                     if (pIC->fOpen)
471                         bSkipThisKey = TRUE;
472                 }
473             }
474 
475             if (bUseIme)
476             {
477                 if (GetKeyboardState(KeyState))
478                 {
479                     vk = (bLowWordOnly ? LOWORD(vKey) : vKey);
480                     if (pImeDpi->ImeProcessKey(hIMC, vk, lParam, KeyState))
481                     {
482                         pIC->bNeedsTrans = TRUE;
483                         pIC->nVKey = vKey;
484                         ret |= IPHK_PROCESSBYIME;
485                     }
486                 }
487             }
488             else if (bSkipThisKey)
489             {
490                 ret |= IPHK_SKIPTHISKEY;
491             }
492 
493             ImmUnlockIMC(hIMC);
494         }
495 
496         ImmUnlockImeDpi(pImeDpi);
497     }
498 
499     if (dwHotKeyID != INVALID_HOTKEY_ID)
500     {
501         if (Imm32ProcessHotKey(hWnd, hIMC, hKL, dwHotKeyID))
502         {
503             if (vKey != VK_KANJI || dwHotKeyID != IME_JHOTKEY_CLOSE_OPEN)
504                 ret |= IPHK_HOTKEY;
505         }
506     }
507 
508     if (ret & IPHK_PROCESSBYIME)
509     {
510         FIXME("TODO: We have to do something here.\n");
511     }
512 
513     ImmReleaseContext(hWnd, hIMC);
514     return ret;
515 }
516 
517 /***********************************************************************
518  *		ImmSystemHandler(IMM32.@)
519  */
520 LRESULT WINAPI ImmSystemHandler(HIMC hIMC, WPARAM wParam, LPARAM lParam)
521 {
522     TRACE("(%p, %p, %p)\n", hIMC, wParam, lParam);
523 
524     switch (wParam)
525     {
526         case 0x1f:
527             Imm32SendChange((BOOL)lParam);
528             return 0;
529 
530         case 0x20:
531             ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
532             return 0;
533 
534         case 0x23: case 0x24:
535             return Imm32UnknownProcess1((HWND)lParam, (wParam == 0x23));
536 
537         default:
538             return 0;
539     }
540 }
541 
542 /***********************************************************************
543  *		ImmGenerateMessage(IMM32.@)
544  */
545 BOOL WINAPI ImmGenerateMessage(HIMC hIMC)
546 {
547     PCLIENTIMC pClientImc;
548     LPINPUTCONTEXT pIC;
549     LPTRANSMSG pMsgs, pTrans = NULL, pItem;
550     HWND hWnd;
551     DWORD dwIndex, dwCount, cbTrans;
552     HIMCC hMsgBuf = NULL;
553     BOOL bAnsi;
554 
555     TRACE("(%p)\n", hIMC);
556 
557     if (Imm32IsCrossThreadAccess(hIMC))
558         return FALSE;
559 
560     pClientImc = ImmLockClientImc(hIMC);
561     if (pClientImc == NULL)
562         return FALSE;
563 
564     bAnsi = !(pClientImc->dwFlags & CLIENTIMC_WIDE);
565     ImmUnlockClientImc(pClientImc);
566 
567     pIC = ImmLockIMC(hIMC);
568     if (pIC == NULL)
569         return FALSE;
570 
571     dwCount = pIC->dwNumMsgBuf;
572     if (dwCount == 0)
573         goto Quit;
574 
575     hMsgBuf = pIC->hMsgBuf;
576     pMsgs = ImmLockIMCC(hMsgBuf);
577     if (pMsgs == NULL)
578         goto Quit;
579 
580     cbTrans = dwCount * sizeof(TRANSMSG);
581     pTrans = Imm32HeapAlloc(0, cbTrans);
582     if (pTrans == NULL)
583         goto Quit;
584 
585     RtlCopyMemory(pTrans, pMsgs, cbTrans);
586 
587 #ifdef IMM_WIN3_SUPPORT
588     if (GetWin32ClientInfo()->dwExpWinVer < _WIN32_WINNT_NT4) /* old version (3.x)? */
589     {
590         LANGID LangID = LANGIDFROMLCID(GetSystemDefaultLCID());
591         WORD wLang = PRIMARYLANGID(LangID);
592 
593         /* translate the messages if Japanese or Korean */
594         if (wLang == LANG_JAPANESE ||
595             (wLang == LANG_KOREAN && NtUserGetAppImeLevel(pIC->hWnd) == 3))
596         {
597             dwCount = ImmNt3Trans(dwCount, pTrans, hIMC, bAnsi, wLang);
598         }
599     }
600 #endif
601 
602     /* send them */
603     hWnd = pIC->hWnd;
604     pItem = pTrans;
605     for (dwIndex = 0; dwIndex < dwCount; ++dwIndex, ++pItem)
606     {
607         if (bAnsi)
608             SendMessageA(hWnd, pItem->message, pItem->wParam, pItem->lParam);
609         else
610             SendMessageW(hWnd, pItem->message, pItem->wParam, pItem->lParam);
611     }
612 
613 Quit:
614     Imm32HeapFree(pTrans);
615     if (hMsgBuf)
616         ImmUnlockIMCC(hMsgBuf);
617     pIC->dwNumMsgBuf = 0; /* done */
618     ImmUnlockIMC(hIMC);
619     return TRUE;
620 }
621 
622 VOID APIENTRY
623 Imm32PostMessages(HWND hwnd, HIMC hIMC, DWORD dwCount, LPTRANSMSG lpTransMsg)
624 {
625     DWORD dwIndex;
626     PCLIENTIMC pClientImc;
627     LPTRANSMSG pNewTransMsg = lpTransMsg, pItem;
628     BOOL bAnsi;
629 
630     pClientImc = ImmLockClientImc(hIMC);
631     if (pClientImc == NULL)
632         return;
633 
634     bAnsi = !(pClientImc->dwFlags & CLIENTIMC_WIDE);
635     ImmUnlockClientImc(pClientImc);
636 
637 #ifdef IMM_WIN3_SUPPORT
638     if (GetWin32ClientInfo()->dwExpWinVer < _WIN32_WINNT_NT4) /* old version (3.x)? */
639     {
640         LANGID LangID = LANGIDFROMLCID(GetSystemDefaultLCID());
641         WORD Lang = PRIMARYLANGID(LangID);
642 
643         /* translate the messages if Japanese or Korean */
644         if (Lang == LANG_JAPANESE ||
645             (Lang == LANG_KOREAN && NtUserGetAppImeLevel(hwnd) == 3))
646         {
647             DWORD cbTransMsg = dwCount * sizeof(TRANSMSG);
648             pNewTransMsg = Imm32HeapAlloc(0, cbTransMsg);
649             if (pNewTransMsg)
650             {
651                 RtlCopyMemory(pNewTransMsg, lpTransMsg, cbTransMsg);
652                 dwCount = ImmNt3Trans(dwCount, pNewTransMsg, hIMC, bAnsi, Lang);
653             }
654             else
655             {
656                 pNewTransMsg = lpTransMsg;
657             }
658         }
659     }
660 #endif
661 
662     /* post them */
663     pItem = pNewTransMsg;
664     for (dwIndex = 0; dwIndex < dwCount; ++dwIndex, ++pItem)
665     {
666         if (bAnsi)
667             PostMessageA(hwnd, pItem->message, pItem->wParam, pItem->lParam);
668         else
669             PostMessageW(hwnd, pItem->message, pItem->wParam, pItem->lParam);
670     }
671 
672 #ifdef IMM_WIN3_SUPPORT
673     if (pNewTransMsg != lpTransMsg)
674         Imm32HeapFree(pNewTransMsg);
675 #endif
676 }
677 
678 /***********************************************************************
679  *       ImmTranslateMessage(IMM32.@)
680  *       ( Undocumented, call internally and from user32.dll )
681  */
682 BOOL WINAPI ImmTranslateMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lKeyData)
683 {
684 #define MSG_COUNT 0x100
685     BOOL ret = FALSE;
686     INT kret;
687     LPINPUTCONTEXTDX pIC;
688     PIMEDPI pImeDpi = NULL;
689     LPTRANSMSGLIST pList = NULL;
690     LPTRANSMSG pTransMsg;
691     BYTE abKeyState[256];
692     HIMC hIMC;
693     HKL hKL;
694     UINT vk;
695     DWORD dwThreadId, dwCount, cbList;
696     WCHAR wch;
697     WORD wChar;
698 
699     TRACE("(%p, 0x%X, %p, %p)\n", hwnd, msg, wParam, lKeyData);
700 
701     /* filter the message */
702     switch (msg)
703     {
704         case WM_KEYDOWN: case WM_KEYUP: case WM_SYSKEYDOWN: case WM_SYSKEYUP:
705             break;
706         default:
707             return FALSE;
708     }
709 
710     hIMC = ImmGetContext(hwnd);
711     pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
712     if (pIC == NULL)
713     {
714         ImmReleaseContext(hwnd, hIMC);
715         return FALSE;
716     }
717 
718     if (!pIC->bNeedsTrans) /* is translation needed? */
719     {
720         /* directly post them */
721         dwCount = pIC->dwNumMsgBuf;
722         if (dwCount == 0)
723             goto Quit;
724 
725         pTransMsg = ImmLockIMCC(pIC->hMsgBuf);
726         if (pTransMsg)
727         {
728             Imm32PostMessages(hwnd, hIMC, dwCount, pTransMsg);
729             ImmUnlockIMCC(pIC->hMsgBuf);
730             ret = TRUE;
731         }
732         pIC->dwNumMsgBuf = 0; /* done */
733         goto Quit;
734     }
735     pIC->bNeedsTrans = FALSE; /* clear the flag */
736 
737     dwThreadId = GetWindowThreadProcessId(hwnd, NULL);
738     hKL = GetKeyboardLayout(dwThreadId);
739     pImeDpi = ImmLockImeDpi(hKL);
740     if (pImeDpi == NULL)
741         goto Quit;
742 
743     if (!GetKeyboardState(abKeyState)) /* get keyboard ON/OFF status */
744         goto Quit;
745 
746     /* convert a virtual key if IME_PROP_KBD_CHAR_FIRST */
747     vk = pIC->nVKey;
748     if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_KBD_CHAR_FIRST)
749     {
750         if (ImeDpi_IsUnicode(pImeDpi))
751         {
752             wch = 0;
753             kret = ToUnicode(vk, HIWORD(lKeyData), abKeyState, &wch, 1, 0);
754             if (kret == 1)
755                 vk = MAKELONG(LOBYTE(vk), wch);
756         }
757         else
758         {
759             wChar = 0;
760             kret = ToAsciiEx(vk, HIWORD(lKeyData), abKeyState, &wChar, 0, hKL);
761             if (kret > 0)
762                 vk = MAKEWORD(vk, wChar);
763         }
764     }
765 
766     /* allocate a list */
767     cbList = offsetof(TRANSMSGLIST, TransMsg) + MSG_COUNT * sizeof(TRANSMSG);
768     pList = Imm32HeapAlloc(0, cbList);
769     if (!pList)
770         goto Quit;
771 
772     /* use IME conversion engine and convert the list */
773     pList->uMsgCount = MSG_COUNT;
774     kret = pImeDpi->ImeToAsciiEx(vk, HIWORD(lKeyData), abKeyState, pList, 0, hIMC);
775     if (kret <= 0)
776         goto Quit;
777 
778     /* post them */
779     if (kret <= MSG_COUNT)
780     {
781         Imm32PostMessages(hwnd, hIMC, kret, pList->TransMsg);
782         ret = TRUE;
783     }
784     else
785     {
786         pTransMsg = ImmLockIMCC(pIC->hMsgBuf);
787         if (pTransMsg == NULL)
788             goto Quit;
789         Imm32PostMessages(hwnd, hIMC, kret, pTransMsg);
790         ImmUnlockIMCC(pIC->hMsgBuf);
791     }
792 
793 Quit:
794     Imm32HeapFree(pList);
795     ImmUnlockImeDpi(pImeDpi);
796     ImmUnlockIMC(hIMC);
797     ImmReleaseContext(hwnd, hIMC);
798     return ret;
799 #undef MSG_COUNT
800 }
801