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