xref: /reactos/dll/win32/imm32/imm.c (revision a03ef85b)
1 /*
2  * PROJECT:     ReactOS IMM32
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     Implementing Far-Eastern languages input
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-2022 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
10  */
11 
12 #include "precomp.h"
13 
14 WINE_DEFAULT_DEBUG_CHANNEL(imm);
15 
16 HMODULE ghImm32Inst = NULL; // Win: ghInst
17 PSERVERINFO gpsi = NULL; // Win: gpsi
18 SHAREDINFO gSharedInfo = { NULL }; // Win: gSharedInfo
19 BYTE gfImmInitialized = FALSE; // Win: gfInitialized
20 
21 // Win: ImmInitializeGlobals
22 static BOOL APIENTRY ImmInitializeGlobals(HMODULE hMod)
23 {
24     NTSTATUS status;
25 
26     if (hMod)
27         ghImm32Inst = hMod;
28 
29     if (gfImmInitialized)
30         return TRUE;
31 
32     status = RtlInitializeCriticalSection(&gcsImeDpi);
33     if (NT_ERROR(status))
34         return FALSE;
35 
36     gfImmInitialized = TRUE;
37     return TRUE;
38 }
39 
40 /***********************************************************************
41  *		ImmRegisterClient(IMM32.@)
42  *       ( Undocumented, called from user32.dll )
43  */
44 BOOL WINAPI ImmRegisterClient(PSHAREDINFO ptr, HINSTANCE hMod)
45 {
46     gSharedInfo = *ptr;
47     gpsi = gSharedInfo.psi;
48     return ImmInitializeGlobals(hMod);
49 }
50 
51 /***********************************************************************
52  *		ImmLoadLayout (IMM32.@)
53  */
54 BOOL WINAPI ImmLoadLayout(HKL hKL, PIMEINFOEX pImeInfoEx)
55 {
56     DWORD cbData, dwType;
57     HKEY hLayoutKey;
58     LONG error;
59     WCHAR szLayout[MAX_PATH];
60 
61     TRACE("(%p, %p)\n", hKL, pImeInfoEx);
62 
63     ZeroMemory(pImeInfoEx, sizeof(IMEINFOEX));
64 
65     if (IS_IME_HKL(hKL) || !IS_CICERO_MODE() || IS_16BIT_MODE())
66     {
67         StringCchPrintfW(szLayout, _countof(szLayout), L"%s\\%08lX",
68                          REGKEY_KEYBOARD_LAYOUTS, HandleToUlong(hKL));
69 
70         error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, szLayout, 0, KEY_READ, &hLayoutKey);
71         if (error)
72         {
73             ERR("RegOpenKeyExW: 0x%08lX\n", error);
74             return FALSE;
75         }
76     }
77     else
78     {
79         error = RegOpenKeyExW(HKEY_LOCAL_MACHINE, REGKEY_IMM, 0, KEY_READ, &hLayoutKey);
80         if (error)
81         {
82             ERR("RegOpenKeyExW: 0x%08lX\n", error);
83             return FALSE;
84         }
85     }
86 
87     cbData = sizeof(pImeInfoEx->wszImeFile);
88     error = RegQueryValueExW(hLayoutKey, L"Ime File", NULL, &dwType,
89                              (LPBYTE)pImeInfoEx->wszImeFile, &cbData);
90     pImeInfoEx->wszImeFile[_countof(pImeInfoEx->wszImeFile) - 1] = UNICODE_NULL;
91 
92     RegCloseKey(hLayoutKey);
93 
94     pImeInfoEx->fLoadFlag = 0;
95 
96     if (error != ERROR_SUCCESS || dwType != REG_SZ)
97     {
98         ERR("RegQueryValueExW: 0x%lX, 0x%lX\n", error, dwType);
99         return FALSE;
100     }
101 
102     pImeInfoEx->hkl = hKL;
103     return Imm32LoadImeVerInfo(pImeInfoEx);
104 }
105 
106 /***********************************************************************
107  *		ImmFreeLayout (IMM32.@)
108  */
109 BOOL WINAPI ImmFreeLayout(DWORD dwUnknown)
110 {
111     WCHAR szKBD[KL_NAMELENGTH];
112     UINT iKL, cKLs;
113     HKL hOldKL, hNewKL, *pList;
114     PIMEDPI pImeDpi;
115     LANGID LangID;
116 
117     TRACE("(0x%lX)\n", dwUnknown);
118 
119     hOldKL = GetKeyboardLayout(0);
120 
121     if (dwUnknown == 1)
122     {
123         if (!IS_IME_HKL(hOldKL))
124             return TRUE;
125 
126         LangID = LANGIDFROMLCID(GetSystemDefaultLCID());
127 
128         cKLs = GetKeyboardLayoutList(0, NULL);
129         if (cKLs)
130         {
131             pList = ImmLocalAlloc(0, cKLs * sizeof(HKL));
132             if (pList == NULL)
133                 return FALSE;
134 
135             cKLs = GetKeyboardLayoutList(cKLs, pList);
136             for (iKL = 0; iKL < cKLs; ++iKL)
137             {
138                 if (!IS_IME_HKL(pList[iKL]))
139                 {
140                     LangID = LOWORD(pList[iKL]);
141                     break;
142                 }
143             }
144 
145             ImmLocalFree(pList);
146         }
147 
148         StringCchPrintfW(szKBD, _countof(szKBD), L"%08X", LangID);
149         if (!LoadKeyboardLayoutW(szKBD, KLF_ACTIVATE))
150             LoadKeyboardLayoutW(L"00000409", KLF_ACTIVATE | 0x200);
151     }
152     else if (dwUnknown == 2)
153     {
154         RtlEnterCriticalSection(&gcsImeDpi);
155 Retry:
156         for (pImeDpi = gpImeDpiList; pImeDpi; pImeDpi = pImeDpi->pNext)
157         {
158             if (Imm32ReleaseIME(pImeDpi->hKL))
159                 goto Retry;
160         }
161         RtlLeaveCriticalSection(&gcsImeDpi);
162     }
163     else
164     {
165         hNewKL = (HKL)(DWORD_PTR)dwUnknown;
166         if (IS_IME_HKL(hNewKL) && hNewKL != hOldKL)
167             Imm32ReleaseIME(hNewKL);
168     }
169 
170     return TRUE;
171 }
172 
173 // Win: SelectInputContext
174 VOID APIENTRY Imm32SelectInputContext(HKL hNewKL, HKL hOldKL, HIMC hIMC)
175 {
176     PCLIENTIMC pClientImc;
177     LPINPUTCONTEXTDX pIC;
178     LPGUIDELINE pGL;
179     LPCANDIDATEINFO pCI;
180     LPCOMPOSITIONSTRING pCS;
181     LOGFONTA LogFontA;
182     LOGFONTW LogFontW;
183     BOOL fOpen, bIsNewHKLIme = TRUE, bIsOldHKLIme = TRUE, bClientWide, bNewDpiWide;
184     DWORD cbNewPrivate = 0, cbOldPrivate = 0, dwConversion, dwSentence, dwSize, dwNewSize;
185     PIMEDPI pNewImeDpi = NULL, pOldImeDpi = NULL;
186     HANDLE hPrivate;
187     PIME_STATE pNewState = NULL, pOldState = NULL;
188 
189     pClientImc = ImmLockClientImc(hIMC);
190     if (!pClientImc)
191         return;
192 
193     pNewImeDpi = ImmLockImeDpi(hNewKL);
194 
195     if (hNewKL != hOldKL)
196         pOldImeDpi = ImmLockImeDpi(hOldKL);
197 
198     if (pNewImeDpi)
199     {
200         cbNewPrivate = pNewImeDpi->ImeInfo.dwPrivateDataSize;
201         pClientImc->uCodePage = pNewImeDpi->uCodePage;
202     }
203     else
204     {
205         pClientImc->uCodePage = CP_ACP;
206     }
207 
208     if (cbNewPrivate < sizeof(DWORD))
209         cbNewPrivate = sizeof(DWORD);
210 
211     if (pOldImeDpi)
212         cbOldPrivate = pOldImeDpi->ImeInfo.dwPrivateDataSize;
213 
214     if (cbOldPrivate < sizeof(DWORD))
215         cbOldPrivate = sizeof(DWORD);
216 
217     if (pClientImc->hKL == hOldKL)
218     {
219         if (pOldImeDpi)
220         {
221             if (IS_IME_HKL(hOldKL))
222                 pOldImeDpi->ImeSelect(hIMC, FALSE);
223             else if (IS_CICERO_MODE() && !IS_16BIT_MODE())
224                 pOldImeDpi->CtfImeSelectEx(hIMC, FALSE, hOldKL);
225         }
226         pClientImc->hKL = NULL;
227     }
228 
229     if (CtfImmIsTextFrameServiceDisabled())
230     {
231         if (IS_IMM_MODE() && !IS_CICERO_MODE())
232         {
233             bIsNewHKLIme = IS_IME_HKL(hNewKL);
234             bIsOldHKLIme = IS_IME_HKL(hOldKL);
235         }
236     }
237 
238     pIC = (LPINPUTCONTEXTDX)Imm32InternalLockIMC(hIMC, FALSE);
239     if (!pIC)
240     {
241         if (pNewImeDpi)
242         {
243             if (IS_IME_HKL(hNewKL))
244                 pNewImeDpi->ImeSelect(hIMC, TRUE);
245             else if (IS_CICERO_MODE() && !IS_16BIT_MODE())
246                 pNewImeDpi->CtfImeSelectEx(hIMC, TRUE, hNewKL);
247 
248             pClientImc->hKL = hNewKL;
249         }
250     }
251     else
252     {
253         dwConversion = pIC->fdwConversion;
254         dwSentence = pIC->fdwSentence;
255         fOpen = pIC->fOpen;
256 
257         if (pNewImeDpi)
258         {
259             bClientWide = (pClientImc->dwFlags & CLIENTIMC_WIDE);
260             bNewDpiWide = ImeDpi_IsUnicode(pNewImeDpi);
261             if (bClientWide && !bNewDpiWide)
262             {
263                 if (pIC->fdwInit & INIT_LOGFONT)
264                 {
265                     LogFontWideToAnsi(&pIC->lfFont.W, &LogFontA);
266                     pIC->lfFont.A = LogFontA;
267                 }
268                 pClientImc->dwFlags &= ~CLIENTIMC_WIDE;
269             }
270             else if (!bClientWide && bNewDpiWide)
271             {
272                 if (pIC->fdwInit & INIT_LOGFONT)
273                 {
274                     LogFontAnsiToWide(&pIC->lfFont.A, &LogFontW);
275                     pIC->lfFont.W = LogFontW;
276                 }
277                 pClientImc->dwFlags |= CLIENTIMC_WIDE;
278             }
279         }
280 
281         if (cbOldPrivate != cbNewPrivate)
282         {
283             hPrivate = ImmReSizeIMCC(pIC->hPrivate, cbNewPrivate);
284             if (!hPrivate)
285             {
286                 ImmDestroyIMCC(pIC->hPrivate);
287                 hPrivate = ImmCreateIMCC(cbNewPrivate);
288             }
289             pIC->hPrivate = hPrivate;
290         }
291 
292 #define MAX_IMCC_SIZE 0x1000
293         dwSize = ImmGetIMCCSize(pIC->hMsgBuf);
294         if (ImmGetIMCCLockCount(pIC->hMsgBuf) || dwSize > MAX_IMCC_SIZE)
295         {
296             ImmDestroyIMCC(pIC->hMsgBuf);
297             pIC->hMsgBuf = ImmCreateIMCC(sizeof(UINT));
298             pIC->dwNumMsgBuf = 0;
299         }
300 
301         dwSize = ImmGetIMCCSize(pIC->hGuideLine);
302         dwNewSize = sizeof(GUIDELINE);
303         if (ImmGetIMCCLockCount(pIC->hGuideLine) ||
304             dwSize < dwNewSize || dwSize > MAX_IMCC_SIZE)
305         {
306             ImmDestroyIMCC(pIC->hGuideLine);
307             pIC->hGuideLine = ImmCreateIMCC(dwNewSize);
308             pGL = ImmLockIMCC(pIC->hGuideLine);
309             if (pGL)
310             {
311                 pGL->dwSize = dwNewSize;
312                 ImmUnlockIMCC(pIC->hGuideLine);
313             }
314         }
315 
316         dwSize = ImmGetIMCCSize(pIC->hCandInfo);
317         dwNewSize = sizeof(CANDIDATEINFO);
318         if (ImmGetIMCCLockCount(pIC->hCandInfo) ||
319             dwSize < dwNewSize || dwSize > MAX_IMCC_SIZE)
320         {
321             ImmDestroyIMCC(pIC->hCandInfo);
322             pIC->hCandInfo = ImmCreateIMCC(dwNewSize);
323             pCI = ImmLockIMCC(pIC->hCandInfo);
324             if (pCI)
325             {
326                 pCI->dwSize = dwNewSize;
327                 ImmUnlockIMCC(pIC->hCandInfo);
328             }
329         }
330 
331         dwSize = ImmGetIMCCSize(pIC->hCompStr);
332         dwNewSize = sizeof(COMPOSITIONSTRING);
333         if (ImmGetIMCCLockCount(pIC->hCompStr) ||
334             dwSize < dwNewSize || dwSize > MAX_IMCC_SIZE)
335         {
336             ImmDestroyIMCC(pIC->hCompStr);
337             pIC->hCompStr = ImmCreateIMCC(dwNewSize);
338             pCS = ImmLockIMCC(pIC->hCompStr);
339             if (pCS)
340             {
341                 pCS->dwSize = dwNewSize;
342                 ImmUnlockIMCC(pIC->hCompStr);
343             }
344         }
345 #undef MAX_IMCC_SIZE
346 
347         if (pOldImeDpi && bIsOldHKLIme)
348         {
349             pOldState = Imm32FetchImeState(pIC, hOldKL);
350             if (pOldState)
351                 Imm32SaveImeStateSentence(pIC, pOldState, hOldKL);
352         }
353 
354         if (pNewImeDpi && bIsNewHKLIme)
355             pNewState = Imm32FetchImeState(pIC, hNewKL);
356 
357         if (pOldState != pNewState)
358         {
359             if (pOldState)
360             {
361                 pOldState->fOpen = !!pIC->fOpen;
362                 pOldState->dwConversion = (pIC->fdwConversion & ~IME_CMODE_EUDC);
363                 pOldState->dwSentence = pIC->fdwSentence;
364                 pOldState->dwInit = pIC->fdwInit;
365             }
366 
367             if (pNewState)
368             {
369                 if (pIC->dwChange & INPUTCONTEXTDX_CHANGE_FORCE_OPEN)
370                 {
371                     pIC->dwChange &= ~INPUTCONTEXTDX_CHANGE_FORCE_OPEN;
372                     pIC->fOpen = TRUE;
373                 }
374                 else
375                 {
376                     pIC->fOpen = pNewState->fOpen;
377                 }
378 
379                 pIC->fdwConversion = (pNewState->dwConversion & ~IME_CMODE_EUDC);
380                 pIC->fdwSentence = pNewState->dwSentence;
381                 pIC->fdwInit = pNewState->dwInit;
382             }
383         }
384 
385         if (pNewState)
386             Imm32LoadImeStateSentence(pIC, pNewState, hNewKL);
387 
388         if (pNewImeDpi)
389         {
390             if (IS_IME_HKL(hNewKL))
391                 pNewImeDpi->ImeSelect(hIMC, TRUE);
392             else if (IS_CICERO_MODE() && !IS_16BIT_MODE())
393                 pNewImeDpi->CtfImeSelectEx(hIMC, TRUE, hNewKL);
394 
395             pClientImc->hKL = hNewKL;
396         }
397 
398         pIC->dwChange = 0;
399         if (pIC->fOpen != fOpen)
400             pIC->dwChange |= INPUTCONTEXTDX_CHANGE_OPEN;
401         if (pIC->fdwConversion != dwConversion)
402             pIC->dwChange |= INPUTCONTEXTDX_CHANGE_CONVERSION;
403         if (pIC->fdwSentence != dwSentence)
404             pIC->dwChange |= INPUTCONTEXTDX_CHANGE_SENTENCE;
405 
406         ImmUnlockIMC(hIMC);
407     }
408 
409     ImmUnlockImeDpi(pOldImeDpi);
410     ImmUnlockImeDpi(pNewImeDpi);
411     ImmUnlockClientImc(pClientImc);
412 }
413 
414 typedef struct SELECT_LAYOUT
415 {
416     HKL hNewKL;
417     HKL hOldKL;
418 } SELECT_LAYOUT, *LPSELECT_LAYOUT;
419 
420 // Win: SelectContextProc
421 static BOOL CALLBACK Imm32SelectContextProc(HIMC hIMC, LPARAM lParam)
422 {
423     LPSELECT_LAYOUT pSelect = (LPSELECT_LAYOUT)lParam;
424     Imm32SelectInputContext(pSelect->hNewKL, pSelect->hOldKL, hIMC);
425     return TRUE;
426 }
427 
428 // Win: NotifyIMEProc
429 static BOOL CALLBACK Imm32NotifyIMEProc(HIMC hIMC, LPARAM lParam)
430 {
431     ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, (DWORD)lParam, 0);
432     return TRUE;
433 }
434 
435 /***********************************************************************
436  *		ImmActivateLayout (IMM32.@)
437  */
438 BOOL WINAPI ImmActivateLayout(HKL hKL)
439 {
440     PIMEDPI pImeDpi;
441     HKL hOldKL;
442     LPARAM lParam;
443     HWND hwndDefIME = NULL;
444     SELECT_LAYOUT SelectLayout;
445 
446     hOldKL = GetKeyboardLayout(0);
447 
448     if (hOldKL == hKL && !(GetWin32ClientInfo()->CI_flags & CI_IMMACTIVATE))
449         return TRUE;
450 
451     ImmLoadIME(hKL);
452 
453     if (hOldKL != hKL)
454     {
455         pImeDpi = ImmLockImeDpi(hOldKL);
456         if (pImeDpi)
457         {
458             if (pImeDpi->ImeInfo.fdwProperty & IME_PROP_COMPLETE_ON_UNSELECT)
459                 lParam = CPS_COMPLETE;
460             else
461                 lParam = CPS_CANCEL;
462             ImmUnlockImeDpi(pImeDpi);
463 
464             ImmEnumInputContext(0, Imm32NotifyIMEProc, lParam);
465         }
466 
467         hwndDefIME = ImmGetDefaultIMEWnd(NULL);
468         if (IsWindow(hwndDefIME))
469             SendMessageW(hwndDefIME, WM_IME_SELECT, FALSE, (LPARAM)hOldKL);
470 
471         NtUserSetThreadLayoutHandles(hKL, hOldKL);
472     }
473 
474     SelectLayout.hNewKL = hKL;
475     SelectLayout.hOldKL = hOldKL;
476     ImmEnumInputContext(0, Imm32SelectContextProc, (LPARAM)&SelectLayout);
477 
478     if (IsWindow(hwndDefIME))
479         SendMessageW(hwndDefIME, WM_IME_SELECT, TRUE, (LPARAM)hKL);
480 
481     return TRUE;
482 }
483 
484 static VOID APIENTRY Imm32CiceroSetActiveContext(HIMC hIMC, BOOL fActive, HWND hWnd, HKL hKL)
485 {
486     TRACE("We have to do something\n");
487 }
488 
489 /***********************************************************************
490  *		ImmAssociateContext (IMM32.@)
491  */
492 HIMC WINAPI ImmAssociateContext(HWND hWnd, HIMC hIMC)
493 {
494     PWND pWnd;
495     HWND hwndFocus;
496     DWORD dwValue;
497     HIMC hOldIMC;
498 
499     TRACE("(%p, %p)\n", hWnd, hIMC);
500 
501     if (!IS_IMM_MODE())
502         return NULL;
503 
504     pWnd = ValidateHwnd(hWnd);
505     if (!pWnd)
506         return NULL;
507 
508     if (hIMC && Imm32IsCrossThreadAccess(hIMC))
509         return NULL;
510 
511     hOldIMC = pWnd->hImc;
512     if (hOldIMC == hIMC)
513         return hIMC;
514 
515     dwValue = NtUserAssociateInputContext(hWnd, hIMC, 0);
516     if (dwValue == 0)
517         return hOldIMC;
518     if (dwValue != 1)
519         return NULL;
520 
521     hwndFocus = (HWND)NtUserQueryWindow(hWnd, QUERY_WINDOW_FOCUS);
522     if (hwndFocus == hWnd)
523     {
524         ImmSetActiveContext(hWnd, hOldIMC, FALSE);
525         ImmSetActiveContext(hWnd, hIMC, TRUE);
526     }
527 
528     return hOldIMC;
529 }
530 
531 /***********************************************************************
532  *              ImmAssociateContextEx (IMM32.@)
533  */
534 BOOL WINAPI ImmAssociateContextEx(HWND hWnd, HIMC hIMC, DWORD dwFlags)
535 {
536     HWND hwndFocus;
537     PWND pFocusWnd;
538     HIMC hOldIMC = NULL;
539     DWORD dwValue;
540 
541     TRACE("(%p, %p, 0x%lX)\n", hWnd, hIMC, dwFlags);
542 
543     if (!IS_IMM_MODE())
544         return FALSE;
545 
546     if (hIMC && !(dwFlags & IACE_DEFAULT) && Imm32IsCrossThreadAccess(hIMC))
547         return FALSE;
548 
549     hwndFocus = (HWND)NtUserQueryWindow(hWnd, QUERY_WINDOW_FOCUS);
550     pFocusWnd = ValidateHwnd(hwndFocus);
551     if (pFocusWnd)
552         hOldIMC = pFocusWnd->hImc;
553 
554     dwValue = NtUserAssociateInputContext(hWnd, hIMC, dwFlags);
555     switch (dwValue)
556     {
557         case 0:
558             return TRUE;
559 
560         case 1:
561             pFocusWnd = ValidateHwnd(hwndFocus);
562             if (pFocusWnd)
563             {
564                 hIMC = pFocusWnd->hImc;
565                 if (hIMC != hOldIMC)
566                 {
567                     ImmSetActiveContext(hwndFocus, hOldIMC, FALSE);
568                     ImmSetActiveContext(hwndFocus, hIMC, TRUE);
569                 }
570             }
571             return TRUE;
572 
573         default:
574             return FALSE;
575     }
576 }
577 
578 /***********************************************************************
579  *		ImmCreateContext (IMM32.@)
580  */
581 HIMC WINAPI ImmCreateContext(void)
582 {
583     PCLIENTIMC pClientImc;
584     HIMC hIMC;
585 
586     TRACE("()\n");
587 
588     if (!IS_IMM_MODE())
589         return NULL;
590 
591     pClientImc = ImmLocalAlloc(HEAP_ZERO_MEMORY, sizeof(CLIENTIMC));
592     if (pClientImc == NULL)
593         return NULL;
594 
595     hIMC = NtUserCreateInputContext((ULONG_PTR)pClientImc);
596     if (hIMC == NULL)
597     {
598         ImmLocalFree(pClientImc);
599         return NULL;
600     }
601 
602     RtlInitializeCriticalSection(&pClientImc->cs);
603 
604     pClientImc->dwCompatFlags = (DWORD)NtUserGetThreadState(THREADSTATE_IMECOMPATFLAGS);
605 
606     return hIMC;
607 }
608 
609 // Win: DestroyImeModeSaver
610 static VOID APIENTRY Imm32DestroyImeModeSaver(LPINPUTCONTEXTDX pIC)
611 {
612     PIME_STATE pState, pNext;
613     PIME_SUBSTATE pSubState, pSubNext;
614 
615     for (pState = pIC->pState; pState; pState = pNext)
616     {
617         pNext = pState->pNext;
618 
619         for (pSubState = pState->pSubState; pSubState; pSubState = pSubNext)
620         {
621             pSubNext = pSubState->pNext;
622             ImmLocalFree(pSubState);
623         }
624 
625         ImmLocalFree(pState);
626     }
627 
628     pIC->pState = NULL;
629 }
630 
631 // Win: DestroyInputContext
632 BOOL APIENTRY Imm32DestroyInputContext(HIMC hIMC, HKL hKL, BOOL bKeep)
633 {
634     PIMEDPI pImeDpi;
635     LPINPUTCONTEXTDX pIC;
636     PCLIENTIMC pClientImc;
637     PIMC pIMC;
638 
639     if (!hIMC || !IS_IMM_MODE())
640         return FALSE;
641 
642     pIMC = ValidateHandle(hIMC, TYPE_INPUTCONTEXT);
643     if (!pIMC || pIMC->head.pti != Imm32CurrentPti())
644     {
645         ERR("invalid pIMC: %p\n", pIMC);
646         return FALSE;
647     }
648 
649     pClientImc = (PCLIENTIMC)pIMC->dwClientImcData;
650     if (!pClientImc)
651         goto Finish;
652 
653     if ((pClientImc->dwFlags & CLIENTIMC_UNKNOWN2) && !bKeep)
654     {
655         ERR("CLIENTIMC_UNKNOWN2\n");
656         return FALSE;
657     }
658 
659     if (pClientImc->dwFlags & CLIENTIMC_DESTROY)
660         return TRUE;
661 
662     InterlockedIncrement(&pClientImc->cLockObj);
663 
664     if (!pClientImc->hInputContext)
665         goto Quit;
666 
667     pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
668     if (!pIC)
669     {
670         ImmUnlockClientImc(pClientImc);
671         ERR("!pIC\n");
672         return FALSE;
673     }
674 
675     CtfImmTIMDestroyInputContext(hIMC);
676 
677     if (pClientImc->hKL == hKL)
678     {
679         pImeDpi = ImmLockImeDpi(hKL);
680         if (pImeDpi)
681         {
682             if (IS_IME_HKL(hKL))
683                 pImeDpi->ImeSelect(hIMC, FALSE);
684             else if (IS_CICERO_MODE() && !IS_16BIT_MODE())
685                 pImeDpi->CtfImeSelectEx(hIMC, FALSE, hKL);
686 
687             ImmUnlockImeDpi(pImeDpi);
688         }
689 
690         pClientImc->hKL = NULL;
691     }
692 
693     ImmDestroyIMCC(pIC->hPrivate);
694     ImmDestroyIMCC(pIC->hMsgBuf);
695     ImmDestroyIMCC(pIC->hGuideLine);
696     ImmDestroyIMCC(pIC->hCandInfo);
697     ImmDestroyIMCC(pIC->hCompStr);
698     Imm32DestroyImeModeSaver(pIC);
699     ImmUnlockIMC(hIMC);
700 
701 Quit:
702     pClientImc->dwFlags |= CLIENTIMC_DESTROY;
703     ImmUnlockClientImc(pClientImc);
704 
705 Finish:
706     if (bKeep)
707         return TRUE;
708     return NtUserDestroyInputContext(hIMC);
709 }
710 
711 // NOTE: Windows does recursive call ImmLockIMC here but we don't do so.
712 // Win: BOOL CreateInputContext(HIMC hIMC, HKL hKL, BOOL fSelect)
713 BOOL APIENTRY
714 Imm32CreateInputContext(HIMC hIMC, LPINPUTCONTEXT pIC, PCLIENTIMC pClientImc, HKL hKL, BOOL fSelect)
715 {
716     DWORD dwIndex, cbPrivate;
717     PIMEDPI pImeDpi = NULL;
718     LPCOMPOSITIONSTRING pCS;
719     LPCANDIDATEINFO pCI;
720     LPGUIDELINE pGL;
721 
722     /* Create IC components */
723     pIC->hCompStr = ImmCreateIMCC(sizeof(COMPOSITIONSTRING));
724     pIC->hCandInfo = ImmCreateIMCC(sizeof(CANDIDATEINFO));
725     pIC->hGuideLine = ImmCreateIMCC(sizeof(GUIDELINE));
726     pIC->hMsgBuf = ImmCreateIMCC(sizeof(UINT));
727     if (!pIC->hCompStr || !pIC->hCandInfo || !pIC->hGuideLine || !pIC->hMsgBuf)
728         goto Fail;
729 
730     /* Initialize IC components */
731     pCS = ImmLockIMCC(pIC->hCompStr);
732     if (!pCS)
733         goto Fail;
734     pCS->dwSize = sizeof(COMPOSITIONSTRING);
735     ImmUnlockIMCC(pIC->hCompStr);
736 
737     pCI = ImmLockIMCC(pIC->hCandInfo);
738     if (!pCI)
739         goto Fail;
740     pCI->dwSize = sizeof(CANDIDATEINFO);
741     ImmUnlockIMCC(pIC->hCandInfo);
742 
743     pGL = ImmLockIMCC(pIC->hGuideLine);
744     if (!pGL)
745         goto Fail;
746     pGL->dwSize = sizeof(GUIDELINE);
747     ImmUnlockIMCC(pIC->hGuideLine);
748 
749     pIC->dwNumMsgBuf = 0;
750     pIC->fOpen = FALSE;
751     pIC->fdwConversion = pIC->fdwSentence = 0;
752 
753     for (dwIndex = 0; dwIndex < MAX_CANDIDATEFORM; ++dwIndex)
754         pIC->cfCandForm[dwIndex].dwIndex = IMM_INVALID_CANDFORM;
755 
756     /* Get private data size */
757     pImeDpi = ImmLockImeDpi(hKL);
758     if (!pImeDpi)
759     {
760         cbPrivate = sizeof(DWORD);
761     }
762     else
763     {
764         /* Update CLIENTIMC */
765         pClientImc->uCodePage = pImeDpi->uCodePage;
766         if (ImeDpi_IsUnicode(pImeDpi))
767             pClientImc->dwFlags |= CLIENTIMC_WIDE;
768 
769         cbPrivate = pImeDpi->ImeInfo.dwPrivateDataSize;
770     }
771 
772     /* Create private data */
773     pIC->hPrivate = ImmCreateIMCC(cbPrivate);
774     if (!pIC->hPrivate)
775         goto Fail;
776 
777     if (pImeDpi)
778     {
779         /* Select the IME */
780         if (fSelect)
781         {
782             if (IS_IME_HKL(hKL))
783                 pImeDpi->ImeSelect(hIMC, TRUE);
784             else if (IS_CICERO_MODE() && !IS_16BIT_MODE())
785                 pImeDpi->CtfImeSelectEx(hIMC, TRUE, hKL);
786         }
787 
788         /* Set HKL */
789         pClientImc->hKL = hKL;
790 
791         ImmUnlockImeDpi(pImeDpi);
792     }
793 
794     return TRUE;
795 
796 Fail:
797     if (pImeDpi)
798         ImmUnlockImeDpi(pImeDpi);
799 
800     pIC->hMsgBuf = ImmDestroyIMCC(pIC->hMsgBuf);
801     pIC->hGuideLine = ImmDestroyIMCC(pIC->hGuideLine);
802     pIC->hCandInfo = ImmDestroyIMCC(pIC->hCandInfo);
803     pIC->hCompStr = ImmDestroyIMCC(pIC->hCompStr);
804     return FALSE;
805 }
806 
807 // Win: InternalImmLockIMC
808 LPINPUTCONTEXT APIENTRY Imm32InternalLockIMC(HIMC hIMC, BOOL fSelect)
809 {
810     HANDLE hIC;
811     LPINPUTCONTEXT pIC = NULL;
812     PCLIENTIMC pClientImc;
813     WORD LangID;
814     DWORD dwThreadId;
815     HKL hOldKL, hNewKL;
816     PIMEDPI pImeDpi = NULL;
817 
818     pClientImc = ImmLockClientImc(hIMC);
819     if (!pClientImc)
820         return NULL;
821 
822     RtlEnterCriticalSection(&pClientImc->cs);
823 
824     if (pClientImc->hInputContext)
825     {
826         pIC = LocalLock(pClientImc->hInputContext);
827         if (pIC)
828             goto Success;
829         else
830             goto Failure;
831     }
832 
833     dwThreadId = (DWORD)NtUserQueryInputContext(hIMC, QIC_INPUTTHREADID);
834     if (dwThreadId == GetCurrentThreadId() && IS_CICERO_MODE() && !IS_16BIT_MODE())
835     {
836         hOldKL = GetKeyboardLayout(0);
837         LangID = LOWORD(hOldKL);
838         hNewKL = (HKL)(DWORD_PTR)MAKELONG(LangID, LangID);
839 
840         pImeDpi = Imm32FindOrLoadImeDpi(hNewKL);
841         if (pImeDpi)
842         {
843             CtfImmTIMActivate(hNewKL);
844         }
845     }
846 
847     if (!NtUserQueryInputContext(hIMC, QIC_DEFAULTWINDOWIME))
848         goto Failure;
849 
850     hIC = LocalAlloc(LHND, sizeof(INPUTCONTEXTDX));
851     pIC = LocalLock(hIC);
852     if (!pIC)
853     {
854         LocalFree(hIC);
855         goto Failure;
856     }
857     pClientImc->hInputContext = hIC;
858 
859     hNewKL = GetKeyboardLayout(dwThreadId);
860     if (!Imm32CreateInputContext(hIMC, pIC, pClientImc, hNewKL, fSelect))
861     {
862         pClientImc->hInputContext = LocalFree(pClientImc->hInputContext);
863         goto Failure;
864     }
865 
866 Success:
867     CtfImmTIMCreateInputContext(hIMC);
868     RtlLeaveCriticalSection(&pClientImc->cs);
869     InterlockedIncrement(&pClientImc->cLockObj);
870     ImmUnlockClientImc(pClientImc);
871     return pIC;
872 
873 Failure:
874     RtlLeaveCriticalSection(&pClientImc->cs);
875     ImmUnlockClientImc(pClientImc);
876     return NULL;
877 }
878 
879 /***********************************************************************
880  *		ImmDestroyContext (IMM32.@)
881  */
882 BOOL WINAPI ImmDestroyContext(HIMC hIMC)
883 {
884     HKL hKL;
885 
886     TRACE("(%p)\n", hIMC);
887 
888     if (!IS_IMM_MODE())
889         return FALSE;
890 
891     if (Imm32IsCrossThreadAccess(hIMC))
892         return FALSE;
893 
894     hKL = GetKeyboardLayout(0);
895     return Imm32DestroyInputContext(hIMC, hKL, FALSE);
896 }
897 
898 /***********************************************************************
899  *		ImmLockClientImc (IMM32.@)
900  */
901 PCLIENTIMC WINAPI ImmLockClientImc(HIMC hImc)
902 {
903     PIMC pIMC;
904     PCLIENTIMC pClientImc;
905 
906     TRACE("(%p)\n", hImc);
907 
908     if (!hImc)
909         return NULL;
910 
911     pIMC = ValidateHandle(hImc, TYPE_INPUTCONTEXT);
912     if (!pIMC || !Imm32CheckImcProcess(pIMC))
913         return NULL;
914 
915     pClientImc = (PCLIENTIMC)pIMC->dwClientImcData;
916     if (pClientImc)
917     {
918         if (pClientImc->dwFlags & CLIENTIMC_DESTROY)
919             return NULL;
920         goto Finish;
921     }
922 
923     pClientImc = ImmLocalAlloc(HEAP_ZERO_MEMORY, sizeof(CLIENTIMC));
924     if (!pClientImc)
925         return NULL;
926 
927     RtlInitializeCriticalSection(&pClientImc->cs);
928     pClientImc->dwCompatFlags = (DWORD)NtUserGetThreadState(THREADSTATE_IMECOMPATFLAGS);
929 
930     if (!NtUserUpdateInputContext(hImc, UIC_CLIENTIMCDATA, (DWORD_PTR)pClientImc))
931     {
932         ImmLocalFree(pClientImc);
933         return NULL;
934     }
935 
936     pClientImc->dwFlags |= CLIENTIMC_UNKNOWN2;
937 
938 Finish:
939     InterlockedIncrement(&pClientImc->cLockObj);
940     return pClientImc;
941 }
942 
943 /***********************************************************************
944  *		ImmUnlockClientImc (IMM32.@)
945  */
946 VOID WINAPI ImmUnlockClientImc(PCLIENTIMC pClientImc)
947 {
948     LONG cLocks;
949     HANDLE hInputContext;
950 
951     TRACE("(%p)\n", pClientImc);
952 
953     cLocks = InterlockedDecrement(&pClientImc->cLockObj);
954     if (cLocks != 0 || !(pClientImc->dwFlags & CLIENTIMC_DESTROY))
955         return;
956 
957     hInputContext = pClientImc->hInputContext;
958     if (hInputContext)
959         LocalFree(hInputContext);
960 
961     RtlDeleteCriticalSection(&pClientImc->cs);
962     ImmLocalFree(pClientImc);
963 }
964 
965 // Win: ImmGetSaveContext
966 static HIMC APIENTRY ImmGetSaveContext(HWND hWnd, DWORD dwContextFlags)
967 {
968     HIMC hIMC;
969     PCLIENTIMC pClientImc;
970     PWND pWnd;
971 
972     if (!IS_IMM_MODE())
973         return NULL;
974 
975     if (!hWnd)
976     {
977         hIMC = (HIMC)NtUserGetThreadState(THREADSTATE_DEFAULTINPUTCONTEXT);
978         goto Quit;
979     }
980 
981     pWnd = ValidateHwnd(hWnd);
982     if (!pWnd || Imm32IsCrossProcessAccess(hWnd))
983         return NULL;
984 
985     hIMC = pWnd->hImc;
986     if (!hIMC && (dwContextFlags & 1))
987         hIMC = (HIMC)NtUserQueryWindow(hWnd, QUERY_WINDOW_DEFAULT_ICONTEXT);
988 
989 Quit:
990     pClientImc = ImmLockClientImc(hIMC);
991     if (pClientImc == NULL)
992         return NULL;
993     if ((dwContextFlags & 2) && (pClientImc->dwFlags & CLIENTIMC_DISABLEIME))
994         hIMC = NULL;
995     ImmUnlockClientImc(pClientImc);
996     return hIMC;
997 }
998 
999 /***********************************************************************
1000  *		ImmGetContext (IMM32.@)
1001  */
1002 HIMC WINAPI ImmGetContext(HWND hWnd)
1003 {
1004     TRACE("(%p)\n", hWnd);
1005     if (hWnd == NULL)
1006         return NULL;
1007     return ImmGetSaveContext(hWnd, 2);
1008 }
1009 
1010 /***********************************************************************
1011  *		ImmLockIMC(IMM32.@)
1012  *
1013  * NOTE: This is not ImmLockIMCC. Don't confuse.
1014  */
1015 LPINPUTCONTEXT WINAPI ImmLockIMC(HIMC hIMC)
1016 {
1017     TRACE("(%p)\n", hIMC);
1018     return Imm32InternalLockIMC(hIMC, TRUE);
1019 }
1020 
1021 /***********************************************************************
1022 *		ImmUnlockIMC(IMM32.@)
1023 */
1024 BOOL WINAPI ImmUnlockIMC(HIMC hIMC)
1025 {
1026     PCLIENTIMC pClientImc;
1027 
1028     pClientImc = ImmLockClientImc(hIMC);
1029     if (pClientImc == NULL)
1030         return FALSE;
1031 
1032     if (pClientImc->hInputContext)
1033         LocalUnlock(pClientImc->hInputContext);
1034 
1035     InterlockedDecrement(&pClientImc->cLockObj);
1036     ImmUnlockClientImc(pClientImc);
1037     return TRUE;
1038 }
1039 
1040 /***********************************************************************
1041  *		ImmReleaseContext (IMM32.@)
1042  */
1043 BOOL WINAPI ImmReleaseContext(HWND hWnd, HIMC hIMC)
1044 {
1045     TRACE("(%p, %p)\n", hWnd, hIMC);
1046     UNREFERENCED_PARAMETER(hWnd);
1047     UNREFERENCED_PARAMETER(hIMC);
1048     return TRUE; // Do nothing. This is correct.
1049 }
1050 
1051 /***********************************************************************
1052  *              ImmCreateSoftKeyboard(IMM32.@)
1053  */
1054 HWND WINAPI ImmCreateSoftKeyboard(UINT uType, UINT hOwner, int x, int y)
1055 {
1056     FIXME("(%d, %d, %d, %d): stub\n", uType, hOwner, x, y);
1057     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1058     return 0;
1059 }
1060 
1061 /***********************************************************************
1062  *              ImmDestroySoftKeyboard(IMM32.@)
1063  */
1064 BOOL WINAPI ImmDestroySoftKeyboard(HWND hSoftWnd)
1065 {
1066     TRACE("(%p)\n", hSoftWnd);
1067     return DestroyWindow(hSoftWnd);
1068 }
1069 
1070 /***********************************************************************
1071  *              ImmShowSoftKeyboard(IMM32.@)
1072  */
1073 BOOL WINAPI ImmShowSoftKeyboard(HWND hSoftWnd, int nCmdShow)
1074 {
1075     TRACE("(%p, %d)\n", hSoftWnd, nCmdShow);
1076     if (hSoftWnd)
1077         return ShowWindow(hSoftWnd, nCmdShow);
1078     return FALSE;
1079 }
1080 
1081 /***********************************************************************
1082 *		ImmDisableTextFrameService(IMM32.@)
1083 */
1084 BOOL WINAPI ImmDisableTextFrameService(DWORD dwThreadId)
1085 {
1086     FIXME("Stub\n");
1087     return FALSE;
1088 }
1089 
1090 /***********************************************************************
1091  *              ImmEnumInputContext(IMM32.@)
1092  */
1093 BOOL WINAPI ImmEnumInputContext(DWORD dwThreadId, IMCENUMPROC lpfn, LPARAM lParam)
1094 {
1095     HIMC *phList;
1096     DWORD dwIndex, dwCount;
1097     BOOL ret = TRUE;
1098     HIMC hIMC;
1099 
1100     TRACE("(%lu, %p, %p)\n", dwThreadId, lpfn, lParam);
1101 
1102     dwCount = Imm32BuildHimcList(dwThreadId, &phList);
1103     if (!dwCount)
1104         return FALSE;
1105 
1106     for (dwIndex = 0; dwIndex < dwCount; ++dwIndex)
1107     {
1108         hIMC = phList[dwIndex];
1109         ret = (*lpfn)(hIMC, lParam);
1110         if (!ret)
1111             break;
1112     }
1113 
1114     ImmLocalFree(phList);
1115     return ret;
1116 }
1117 
1118 /***********************************************************************
1119  *              ImmSetActiveContext(IMM32.@)
1120  */
1121 BOOL WINAPI ImmSetActiveContext(HWND hWnd, HIMC hIMC, BOOL fActive)
1122 {
1123     PCLIENTIMC pClientImc;
1124     LPINPUTCONTEXTDX pIC;
1125     PIMEDPI pImeDpi;
1126     HKL hKL;
1127     BOOL fOpen = FALSE;
1128     DWORD dwConversion = 0, iShow = ISC_SHOWUIALL;
1129     HWND hwndDefIME;
1130 
1131     TRACE("(%p, %p, %d)\n", hWnd, hIMC, fActive);
1132 
1133     if (!IS_IMM_MODE())
1134         return FALSE;
1135 
1136     pClientImc = ImmLockClientImc(hIMC);
1137 
1138     if (!fActive)
1139     {
1140         if (pClientImc)
1141             pClientImc->dwFlags &= ~CLIENTIMC_UNKNOWN4;
1142     }
1143     else if (hIMC)
1144     {
1145         if (!pClientImc)
1146             return FALSE;
1147 
1148         pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
1149         if (!pIC)
1150         {
1151             ImmUnlockClientImc(pClientImc);
1152             return FALSE;
1153         }
1154 
1155         pIC->hWnd = hWnd;
1156         pClientImc->dwFlags |= CLIENTIMC_UNKNOWN5;
1157 
1158         if (pIC->dwUIFlags & 2)
1159             iShow = (ISC_SHOWUIGUIDELINE | ISC_SHOWUIALLCANDIDATEWINDOW);
1160 
1161         fOpen = pIC->fOpen;
1162         dwConversion = pIC->fdwConversion;
1163 
1164         ImmUnlockIMC(hIMC);
1165     }
1166     else
1167     {
1168         hIMC = ImmGetSaveContext(hWnd, 1);
1169         pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
1170         if (pIC)
1171         {
1172             pIC->hWnd = hWnd;
1173             ImmUnlockIMC(hIMC);
1174         }
1175         hIMC = NULL;
1176     }
1177 
1178     hKL = GetKeyboardLayout(0);
1179 
1180     if (IS_CICERO_MODE() && !IS_16BIT_MODE())
1181     {
1182         Imm32CiceroSetActiveContext(hIMC, fActive, hWnd, hKL);
1183         hKL = GetKeyboardLayout(0);
1184     }
1185 
1186     pImeDpi = ImmLockImeDpi(hKL);
1187     if (pImeDpi)
1188     {
1189         if (IS_IME_HKL(hKL))
1190             pImeDpi->ImeSetActiveContext(hIMC, fActive);
1191         ImmUnlockImeDpi(pImeDpi);
1192     }
1193 
1194     if (IsWindow(hWnd))
1195     {
1196         SendMessageW(hWnd, WM_IME_SETCONTEXT, fActive, iShow);
1197         if (fActive)
1198             NtUserNotifyIMEStatus(hWnd, fOpen, dwConversion);
1199     }
1200     else if (!fActive)
1201     {
1202         hwndDefIME = ImmGetDefaultIMEWnd(NULL);
1203         if (hwndDefIME)
1204             SendMessageW(hwndDefIME, WM_IME_SETCONTEXT, 0, iShow);
1205     }
1206 
1207     if (pClientImc)
1208         ImmUnlockClientImc(pClientImc);
1209 
1210     return TRUE;
1211 }
1212 
1213 /***********************************************************************
1214  *              ImmWINNLSGetEnableStatus (IMM32.@)
1215  */
1216 
1217 BOOL WINAPI ImmWINNLSGetEnableStatus(HWND hWnd)
1218 {
1219     if (!Imm32IsSystemJapaneseOrKorean())
1220     {
1221         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1222         return FALSE;
1223     }
1224 
1225     return !!ImmGetSaveContext(hWnd, 2);
1226 }
1227 
1228 /***********************************************************************
1229  *              ImmSetActiveContextConsoleIME(IMM32.@)
1230  */
1231 BOOL WINAPI ImmSetActiveContextConsoleIME(HWND hwnd, BOOL fFlag)
1232 {
1233     HIMC hIMC;
1234     TRACE("(%p, %d)\n", hwnd, fFlag);
1235 
1236     hIMC = ImmGetContext(hwnd);
1237     if (hIMC)
1238         return ImmSetActiveContext(hwnd, hIMC, fFlag);
1239     return FALSE;
1240 }
1241 
1242 BOOL WINAPI User32InitializeImmEntryTable(DWORD);
1243 
1244 // Win: ImmDllInitialize
1245 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
1246 {
1247     HKL hKL;
1248     HIMC hIMC;
1249 
1250     TRACE("(%p, 0x%X, %p)\n", hinstDLL, fdwReason, lpReserved);
1251 
1252     switch (fdwReason)
1253     {
1254         case DLL_PROCESS_ATTACH:
1255             if (!ImmInitializeGlobals(hinstDLL))
1256             {
1257                 ERR("ImmInitializeGlobals failed\n");
1258                 return FALSE;
1259             }
1260             if (!User32InitializeImmEntryTable(IMM_INIT_MAGIC))
1261             {
1262                 ERR("User32InitializeImmEntryTable failed\n");
1263                 return FALSE;
1264             }
1265             break;
1266 
1267         case DLL_THREAD_ATTACH:
1268             break;
1269 
1270         case DLL_THREAD_DETACH:
1271             if (!IS_IMM_MODE() || NtCurrentTeb()->Win32ThreadInfo == NULL)
1272                 return TRUE;
1273 
1274             hKL = GetKeyboardLayout(0);
1275             hIMC = (HIMC)NtUserGetThreadState(THREADSTATE_DEFAULTINPUTCONTEXT);
1276             Imm32DestroyInputContext(hIMC, hKL, TRUE);
1277             break;
1278 
1279         case DLL_PROCESS_DETACH:
1280             RtlDeleteCriticalSection(&gcsImeDpi);
1281             TRACE("imm32.dll is unloaded\n");
1282             break;
1283     }
1284 
1285     return TRUE;
1286 }
1287