xref: /reactos/dll/ime/msctfime/msctfime.cpp (revision 3a49e26f)
1 /*
2  * PROJECT:     ReactOS msctfime.ime
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     Supporting IME interface of Text Input Processors (TIPs)
5  * COPYRIGHT:   Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6  */
7 
8 #include "msctfime.h"
9 
10 WINE_DEFAULT_DEBUG_CHANNEL(msctfime);
11 
12 HINSTANCE g_hInst = NULL; /* The instance of this module */
13 BOOL g_bWinLogon = FALSE;
14 UINT g_uACP = CP_ACP;
15 DWORD g_dwOSInfo = 0;
16 BOOL gfTFInitLib = FALSE;
17 CRITICAL_SECTION g_csLock;
18 CDispAttrPropCache *g_pPropCache = NULL;
19 
20 EXTERN_C void __cxa_pure_virtual(void)
21 {
22     ERR("__cxa_pure_virtual\n");
23 }
24 
25 /// Selects or unselects the input context.
26 /// @implemented
27 static HRESULT
28 InternalSelectEx(
29     _In_ HIMC hIMC,
30     _In_ BOOL fSelect,
31     _In_ LANGID LangID)
32 {
33     CicIMCLock imcLock(hIMC);
34     if (FAILED(imcLock.m_hr))
35         return imcLock.m_hr;
36 
37     if (PRIMARYLANGID(LangID) == LANG_CHINESE)
38     {
39         imcLock.get().cfCandForm[0].dwStyle = 0;
40         imcLock.get().cfCandForm[0].dwIndex = (DWORD)-1;
41     }
42 
43     if (!fSelect)
44     {
45         imcLock.get().fdwInit &= ~INIT_GUIDMAP;
46         return imcLock.m_hr;
47     }
48 
49     if (!imcLock.ClearCand())
50         return imcLock.m_hr;
51 
52     // Populate conversion mode
53     if (!(imcLock.get().fdwInit & INIT_CONVERSION))
54     {
55         DWORD dwConv = (imcLock.get().fdwConversion & IME_CMODE_SOFTKBD);
56         if (LangID)
57         {
58             if (PRIMARYLANGID(LangID) == LANG_JAPANESE)
59             {
60                 dwConv |= IME_CMODE_ROMAN | IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE;
61             }
62             else if (PRIMARYLANGID(LangID) != LANG_KOREAN)
63             {
64                 dwConv |= IME_CMODE_NATIVE;
65             }
66         }
67         imcLock.get().fdwConversion |= dwConv;
68         imcLock.get().fdwInit |= INIT_CONVERSION;
69     }
70 
71     // Populate sentence mode
72     imcLock.get().fdwSentence |= IME_SMODE_PHRASEPREDICT;
73 
74     // Populate LOGFONT
75     if (!(imcLock.get().fdwInit & INIT_LOGFONT))
76     {
77         // Get logical font
78         LOGFONTW lf;
79         HDC hDC = ::GetDC(imcLock.get().hWnd);
80         HGDIOBJ hFont = ::GetCurrentObject(hDC, OBJ_FONT);
81         ::GetObjectW(hFont, sizeof(LOGFONTW), &lf);
82         ::ReleaseDC(imcLock.get().hWnd, hDC);
83 
84         imcLock.get().lfFont.W = lf;
85         imcLock.get().fdwInit |= INIT_LOGFONT;
86     }
87     imcLock.get().lfFont.W.lfCharSet = GetCharsetFromLangId(LangID);
88 
89     imcLock.InitContext();
90 
91     return imcLock.m_hr;
92 }
93 
94 /// Retrieves the IME information.
95 /// @implemented
96 HRESULT
97 Inquire(
98     _Out_ LPIMEINFO lpIMEInfo,
99     _Out_ LPWSTR lpszWndClass,
100     _In_ DWORD dwSystemInfoFlags,
101     _In_ HKL hKL)
102 {
103     if (!lpIMEInfo)
104         return E_OUTOFMEMORY;
105 
106     StringCchCopyW(lpszWndClass, 64, L"MSCTFIME UI");
107     lpIMEInfo->dwPrivateDataSize = 0;
108 
109     switch (LOWORD(hKL)) // Language ID
110     {
111         case MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT): // Japanese
112         {
113             lpIMEInfo->fdwProperty = IME_PROP_COMPLETE_ON_UNSELECT | IME_PROP_SPECIAL_UI |
114                                      IME_PROP_AT_CARET | IME_PROP_NEED_ALTKEY |
115                                      IME_PROP_KBD_CHAR_FIRST;
116             lpIMEInfo->fdwConversionCaps = IME_CMODE_FULLSHAPE | IME_CMODE_KATAKANA |
117                                            IME_CMODE_NATIVE;
118             lpIMEInfo->fdwSentenceCaps = IME_SMODE_CONVERSATION | IME_SMODE_PLAURALCLAUSE;
119             lpIMEInfo->fdwSelectCaps = SELECT_CAP_SENTENCE | SELECT_CAP_CONVERSION;
120             lpIMEInfo->fdwSCSCaps = SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD |
121                                     SCS_CAP_COMPSTR;
122             lpIMEInfo->fdwUICaps = UI_CAP_ROT90;
123             break;
124         }
125         case MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT): // Korean
126         {
127             lpIMEInfo->fdwProperty = IME_PROP_COMPLETE_ON_UNSELECT | IME_PROP_SPECIAL_UI |
128                                      IME_PROP_AT_CARET | IME_PROP_NEED_ALTKEY |
129                                      IME_PROP_KBD_CHAR_FIRST;
130             lpIMEInfo->fdwConversionCaps = IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE;
131             lpIMEInfo->fdwSentenceCaps = 0;
132             lpIMEInfo->fdwSCSCaps = SCS_CAP_SETRECONVERTSTRING | SCS_CAP_COMPSTR;
133             lpIMEInfo->fdwSelectCaps = SELECT_CAP_CONVERSION;
134             lpIMEInfo->fdwUICaps = UI_CAP_ROT90;
135             break;
136         }
137         case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED): // Simplified Chinese
138         case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL): // Traditional Chinese
139         {
140             lpIMEInfo->fdwProperty = IME_PROP_SPECIAL_UI | IME_PROP_AT_CARET |
141                                      IME_PROP_NEED_ALTKEY | IME_PROP_KBD_CHAR_FIRST;
142             lpIMEInfo->fdwConversionCaps = IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE;
143             lpIMEInfo->fdwSentenceCaps = SELECT_CAP_CONVERSION;
144             lpIMEInfo->fdwSelectCaps = 0;
145             lpIMEInfo->fdwSCSCaps = SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD |
146                                     SCS_CAP_COMPSTR;
147             lpIMEInfo->fdwUICaps = UI_CAP_ROT90;
148             break;
149         }
150         default: // Otherwise
151         {
152             lpIMEInfo->fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET;
153             lpIMEInfo->fdwConversionCaps = 0;
154             lpIMEInfo->fdwSentenceCaps = 0;
155             lpIMEInfo->fdwSCSCaps = 0;
156             lpIMEInfo->fdwUICaps = 0;
157             lpIMEInfo->fdwSelectCaps = 0;
158             break;
159         }
160     }
161 
162     return S_OK;
163 }
164 
165 /***********************************************************************
166  *      ImeInquire (MSCTFIME.@)
167  *
168  * MSCTFIME's ImeInquire does nothing.
169  *
170  * @implemented
171  * @see CtfImeInquireExW
172  * @see https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/ImeInquire.html
173  */
174 EXTERN_C
175 BOOL WINAPI
176 ImeInquire(
177     _Out_ LPIMEINFO lpIMEInfo,
178     _Out_ LPWSTR lpszWndClass,
179     _In_ DWORD dwSystemInfoFlags)
180 {
181     TRACE("(%p, %p, 0x%lX)\n", lpIMEInfo, lpszWndClass, dwSystemInfoFlags);
182     return FALSE;
183 }
184 
185 /***********************************************************************
186  *      ImeConversionList (MSCTFIME.@)
187  *
188  * MSCTFIME's ImeConversionList does nothing.
189  *
190  * @implemented
191  * @see ImmGetConversionListW
192  * @see https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/ImeConversionList.html
193  */
194 EXTERN_C DWORD WINAPI
195 ImeConversionList(
196     _In_ HIMC hIMC,
197     _In_ LPCWSTR lpSrc,
198     _Out_ LPCANDIDATELIST lpDst,
199     _In_ DWORD dwBufLen,
200     _In_ UINT uFlag)
201 {
202     TRACE("(%p, %s, %p, 0x%lX, %u)\n", hIMC, debugstr_w(lpSrc), lpDst, dwBufLen, uFlag);
203     return 0;
204 }
205 
206 /***********************************************************************
207  *      ImeRegisterWord (MSCTFIME.@)
208  *
209  * MSCTFIME's ImeRegisterWord does nothing.
210  *
211  * @implemented
212  * @see ImeUnregisterWord
213  * @see https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/ImeRegisterWord.html
214  */
215 EXTERN_C BOOL WINAPI
216 ImeRegisterWord(
217     _In_ LPCWSTR lpszReading,
218     _In_ DWORD dwStyle,
219     _In_ LPCWSTR lpszString)
220 {
221     TRACE("(%s, 0x%lX, %s)\n", debugstr_w(lpszReading), dwStyle, debugstr_w(lpszString));
222     return FALSE;
223 }
224 
225 /***********************************************************************
226  *      ImeUnregisterWord (MSCTFIME.@)
227  *
228  * MSCTFIME's ImeUnregisterWord does nothing.
229  *
230  * @implemented
231  * @see ImeRegisterWord
232  * @see https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/ImeUnregisterWord.html
233  */
234 EXTERN_C BOOL WINAPI
235 ImeUnregisterWord(
236     _In_ LPCWSTR lpszReading,
237     _In_ DWORD dwStyle,
238     _In_ LPCWSTR lpszString)
239 {
240     TRACE("(%s, 0x%lX, %s)\n", debugstr_w(lpszReading), dwStyle, debugstr_w(lpszString));
241     return FALSE;
242 }
243 
244 /***********************************************************************
245  *      ImeGetRegisterWordStyle (MSCTFIME.@)
246  *
247  * MSCTFIME's ImeGetRegisterWordStyle does nothing.
248  *
249  * @implemented
250  * @see ImeRegisterWord
251  * @see https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/ImeGetRegisterWordStyle.html
252  */
253 EXTERN_C UINT WINAPI
254 ImeGetRegisterWordStyle(
255     _In_ UINT nItem,
256     _Out_ LPSTYLEBUFW lpStyleBuf)
257 {
258     TRACE("(%u, %p)\n", nItem, lpStyleBuf);
259     return 0;
260 }
261 
262 /***********************************************************************
263  *      ImeEnumRegisterWord (MSCTFIME.@)
264  *
265  * MSCTFIME's ImeEnumRegisterWord does nothing.
266  *
267  * @implemented
268  * @see ImeRegisterWord
269  * @see https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/ImeEnumRegisterWord.html
270  */
271 EXTERN_C UINT WINAPI
272 ImeEnumRegisterWord(
273     _In_ REGISTERWORDENUMPROCW lpfnEnumProc,
274     _In_opt_ LPCWSTR lpszReading,
275     _In_ DWORD dwStyle,
276     _In_opt_ LPCWSTR lpszString,
277     _In_opt_ LPVOID lpData)
278 {
279     TRACE("(%p, %s, %lu, %s, %p)\n", lpfnEnumProc, debugstr_w(lpszReading),
280           dwStyle, debugstr_w(lpszString), lpData);
281     return 0;
282 }
283 
284 /***********************************************************************
285  *      ImeConfigure (MSCTFIME.@)
286  *
287  * @implemented
288  * @see https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/ImeConfigure.html
289  */
290 EXTERN_C BOOL WINAPI
291 ImeConfigure(
292     _In_ HKL hKL,
293     _In_ HWND hWnd,
294     _In_ DWORD dwMode,
295     _Inout_opt_ LPVOID lpData)
296 {
297     TRACE("(%p, %p, %lu, %p)\n", hKL, hWnd, dwMode, lpData);
298 
299     TLS *pTLS = TLS::GetTLS();
300     if (!pTLS || !pTLS->m_pBridge || !pTLS->m_pThreadMgr)
301         return FALSE;
302 
303     auto pBridge = pTLS->m_pBridge;
304     auto pThreadMgr = pTLS->m_pThreadMgr;
305 
306     if (dwMode & 0x1)
307         return (pBridge->ConfigureGeneral(pTLS, pThreadMgr, hKL, hWnd) == S_OK);
308 
309     if (dwMode & 0x2)
310         return (pBridge->ConfigureRegisterWord(pTLS, pThreadMgr, hKL, hWnd, lpData) == S_OK);
311 
312     return FALSE;
313 }
314 
315 /***********************************************************************
316  *      ImeDestroy (MSCTFIME.@)
317  *
318  * @implemented
319  * @see https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/ImeDestroy.html
320  */
321 EXTERN_C BOOL WINAPI
322 ImeDestroy(
323     _In_ UINT uReserved)
324 {
325     TRACE("(%u)\n", uReserved);
326 
327     TLS *pTLS = TLS::PeekTLS();
328     if (pTLS)
329         return FALSE;
330 
331     if (!pTLS->m_pBridge || !pTLS->m_pThreadMgr)
332         return FALSE;
333 
334     if (pTLS->m_dwSystemInfoFlags & IME_SYSINFO_WINLOGON)
335         return TRUE;
336 
337     if (pTLS->m_pBridge->DeactivateIMMX(pTLS, pTLS->m_pThreadMgr) != S_OK)
338         return FALSE;
339 
340     return pTLS->m_pBridge->UnInitIMMX(pTLS);
341 }
342 
343 /***********************************************************************
344  *      ImeEscape (MSCTFIME.@)
345  *
346  * MSCTFIME's ImeEscape does nothing.
347  *
348  * @implemented
349  * @see CtfImeEscapeEx
350  * @see https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/ImeEscape.html
351  */
352 EXTERN_C LRESULT WINAPI
353 ImeEscape(
354     _In_ HIMC hIMC,
355     _In_ UINT uEscape,
356     _Inout_opt_ LPVOID lpData)
357 {
358     TRACE("(%p, %u, %p)\n", hIMC, uEscape, lpData);
359     return 0;
360 }
361 
362 /***********************************************************************
363  *      ImeProcessKey (MSCTFIME.@)
364  *
365  * @implemented
366  * @see https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/ImeProcessKey.html
367  */
368 EXTERN_C BOOL WINAPI
369 ImeProcessKey(
370     _In_ HIMC hIMC,
371     _In_ UINT uVirtKey,
372     _In_ LPARAM lParam,
373     _In_ CONST LPBYTE lpbKeyState)
374 {
375     TRACE("(%p, %u, %p, lpbKeyState)\n", hIMC, uVirtKey, lParam, lpbKeyState);
376 
377     TLS *pTLS = TLS::GetTLS();
378     if (!pTLS)
379         return FALSE;
380 
381     auto pBridge = pTLS->m_pBridge;
382     auto pThreadMgr = pTLS->m_pThreadMgr;
383     if (!pBridge || !pThreadMgr)
384         return FALSE;
385 
386     if (pTLS->m_dwFlags1 & 0x1)
387     {
388         ITfDocumentMgr *pDocMgr = NULL;
389         pThreadMgr->GetFocus(&pDocMgr);
390         if (pDocMgr && !CicBridge::IsOwnDim(pDocMgr))
391         {
392             pDocMgr->Release();
393             return FALSE;
394         }
395 
396         if (pDocMgr)
397             pDocMgr->Release();
398     }
399 
400     LANGID LangID = LOWORD(::GetKeyboardLayout(0));
401     if (((pTLS->m_dwFlags2 & 1) && MsimtfIsGuidMapEnable(hIMC, NULL)) ||
402         ((lParam & (KF_ALTDOWN << 16)) &&
403          (LangID == MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT)) &&
404          IsVKDBEKey(uVirtKey)))
405     {
406         return FALSE;
407     }
408 
409     INT nUnknown60 = 0;
410     return pBridge->ProcessKey(pTLS, pThreadMgr, hIMC, uVirtKey, lParam, lpbKeyState, &nUnknown60);
411 }
412 
413 /***********************************************************************
414  *      ImeSelect (MSCTFIME.@)
415  *
416  * MSCTFIME's ImeSelect does nothing.
417  *
418  * @implemented
419  * @see CtfImeSelectEx
420  * @see https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/ImeSelect.html
421  */
422 EXTERN_C BOOL WINAPI
423 ImeSelect(
424     _In_ HIMC hIMC,
425     _In_ BOOL fSelect)
426 {
427     TRACE("(%p, %u)\n", hIMC, fSelect);
428     return FALSE;
429 }
430 
431 /***********************************************************************
432  *      ImeSetActiveContext (MSCTFIME.@)
433  *
434  * MSCTFIME's ImeSetActiveContext does nothing.
435  *
436  * @implemented
437  * @see CtfImeSetActiveContextAlways
438  * @see https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/ImeSetActiveContext.html
439  */
440 EXTERN_C BOOL WINAPI
441 ImeSetActiveContext(
442     _In_ HIMC hIMC,
443     _In_ BOOL fFlag)
444 {
445     TRACE("(%p, %u)\n", hIMC, fFlag);
446     return FALSE;
447 }
448 
449 /***********************************************************************
450  *      ImeToAsciiEx (MSCTFIME.@)
451  *
452  * @implemented
453  * @see https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/ImeToAsciiEx.html
454  */
455 EXTERN_C UINT WINAPI
456 ImeToAsciiEx(
457     _In_ UINT uVirtKey,
458     _In_ UINT uScanCode,
459     _In_ CONST LPBYTE lpbKeyState,
460     _Out_ LPTRANSMSGLIST lpTransMsgList,
461     _In_ UINT fuState,
462     _In_ HIMC hIMC)
463 {
464     TRACE("(%u, %u, %p, %p, %u, %p)\n", uVirtKey, uScanCode, lpbKeyState, lpTransMsgList,
465           fuState, hIMC);
466 
467     TLS *pTLS = TLS::GetTLS();
468     if (!pTLS)
469         return 0;
470 
471     auto pBridge = pTLS->m_pBridge;
472     auto pThreadMgr = pTLS->m_pThreadMgr;
473     if (!pBridge || !pThreadMgr)
474         return 0;
475 
476     UINT ret = 0;
477     HRESULT hr = pBridge->ToAsciiEx(pTLS, pThreadMgr, uVirtKey, uScanCode, lpbKeyState,
478                                     lpTransMsgList, fuState, hIMC, &ret);
479     return ((hr == S_OK) ? ret : 0);
480 }
481 
482 /***********************************************************************
483  *      NotifyIME (MSCTFIME.@)
484  *
485  * @implemented
486  * @see https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/NotifyIME.html
487  */
488 EXTERN_C BOOL WINAPI
489 NotifyIME(
490     _In_ HIMC hIMC,
491     _In_ DWORD dwAction,
492     _In_ DWORD dwIndex,
493     _In_ DWORD_PTR dwValue)
494 {
495     TRACE("(%p, 0x%lX, 0x%lX, %p)\n", hIMC, dwAction, dwIndex, dwValue);
496 
497     TLS *pTLS = TLS::GetTLS();
498     if (!pTLS)
499         return FALSE;
500 
501     auto pBridge = pTLS->m_pBridge;
502     auto pThreadMgr = pTLS->m_pThreadMgr;
503     if (!pBridge || !pThreadMgr)
504         return FALSE;
505 
506     HRESULT hr = pBridge->Notify(pTLS, pThreadMgr, hIMC, dwAction, dwIndex, dwValue);
507     return (hr == S_OK);
508 }
509 
510 /***********************************************************************
511  *      ImeSetCompositionString (MSCTFIME.@)
512  *
513  * @implemented
514  * @see https://katahiromz.web.fc2.com/colony3rd/imehackerz/en/ImeSetCompositionString.html
515  */
516 EXTERN_C BOOL WINAPI
517 ImeSetCompositionString(
518     _In_ HIMC hIMC,
519     _In_ DWORD dwIndex,
520     _In_opt_ LPCVOID lpComp,
521     _In_ DWORD dwCompLen,
522     _In_opt_ LPCVOID lpRead,
523     _In_ DWORD dwReadLen)
524 {
525     TRACE("(%p, 0x%lX, %p, 0x%lX, %p, 0x%lX)\n", hIMC, dwIndex, lpComp, dwCompLen,
526           lpRead, dwReadLen);
527 
528     TLS *pTLS = TLS::GetTLS();
529     if (!pTLS)
530         return FALSE;
531 
532     auto pBridge = pTLS->m_pBridge;
533     auto pThreadMgr = pTLS->m_pThreadMgr;
534     if (!pBridge || !pThreadMgr)
535         return FALSE;
536 
537     return pBridge->SetCompositionString(pTLS, pThreadMgr, hIMC, dwIndex,
538                                          lpComp, dwCompLen, lpRead, dwReadLen);
539 }
540 
541 /***********************************************************************
542  *      CtfImeInquireExW (MSCTFIME.@)
543  *
544  * @implemented
545  */
546 EXTERN_C HRESULT WINAPI
547 CtfImeInquireExW(
548     _Out_ LPIMEINFO lpIMEInfo,
549     _Out_ LPWSTR lpszWndClass,
550     _In_ DWORD dwSystemInfoFlags,
551     _In_ HKL hKL)
552 {
553     TRACE("(%p, %p, 0x%lX, %p)\n", lpIMEInfo, lpszWndClass, dwSystemInfoFlags, hKL);
554 
555     TLS *pTLS = TLS::GetTLS();
556     if (!pTLS)
557         return E_OUTOFMEMORY;
558 
559     if (!IsInteractiveUserLogon())
560     {
561         dwSystemInfoFlags |= IME_SYSINFO_WINLOGON;
562         g_bWinLogon = TRUE;
563     }
564 
565     pTLS->m_dwSystemInfoFlags = dwSystemInfoFlags;
566 
567     return Inquire(lpIMEInfo, lpszWndClass, dwSystemInfoFlags, hKL);
568 }
569 
570 /***********************************************************************
571  *      CtfImeSelectEx (MSCTFIME.@)
572  *
573  * @implemented
574  */
575 EXTERN_C BOOL WINAPI
576 CtfImeSelectEx(
577     _In_ HIMC hIMC,
578     _In_ BOOL fSelect,
579     _In_ HKL hKL)
580 {
581     TRACE("(%p, %d, %p)\n", hIMC, fSelect, hKL);
582 
583     TLS *pTLS = TLS::PeekTLS();
584     if (!pTLS)
585         return E_OUTOFMEMORY;
586 
587     InternalSelectEx(hIMC, fSelect, LOWORD(hKL));
588 
589     if (!pTLS->m_pBridge || !pTLS->m_pThreadMgr)
590         return E_OUTOFMEMORY;
591 
592     return pTLS->m_pBridge->SelectEx(pTLS, pTLS->m_pThreadMgr, hIMC, fSelect, hKL);
593 }
594 
595 /***********************************************************************
596  *      CtfImeEscapeEx (MSCTFIME.@)
597  *
598  * @implemented
599  */
600 EXTERN_C LRESULT WINAPI
601 CtfImeEscapeEx(
602     _In_ HIMC hIMC,
603     _In_ UINT uSubFunc,
604     _Inout_opt_ LPVOID lpData,
605     _In_ HKL hKL)
606 {
607     TRACE("(%p, %u, %p, %p)\n", hIMC, uSubFunc, lpData, hKL);
608 
609     if (LOWORD(hKL) != MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT))
610         return 0;
611 
612     TLS *pTLS = TLS::GetTLS();
613     if (!pTLS || !pTLS->m_pBridge)
614         return 0;
615 
616     return pTLS->m_pBridge->EscapeKorean(pTLS, hIMC, uSubFunc, lpData);
617 }
618 
619 /***********************************************************************
620  *      CtfImeGetGuidAtom (MSCTFIME.@)
621  *
622  * @implemented
623  */
624 EXTERN_C HRESULT WINAPI
625 CtfImeGetGuidAtom(
626     _In_ HIMC hIMC,
627     _In_ DWORD dwUnknown,
628     _Out_opt_ LPDWORD pdwGuidAtom)
629 {
630     TRACE("(%p, 0x%lX, %p)\n", hIMC, dwUnknown, pdwGuidAtom);
631 
632     CicIMCLock imcLock(hIMC);
633     if (FAILED(imcLock.m_hr))
634         return imcLock.m_hr;
635 
636     CicIMCCLock<CTFIMECONTEXT> imccLock(imcLock.get().hCtfImeContext);
637     if (FAILED(imccLock.m_hr))
638         return imccLock.m_hr;
639 
640     if (!imccLock.get().m_pCicIC)
641         return E_OUTOFMEMORY;
642 
643     return imccLock.get().m_pCicIC->GetGuidAtom(imcLock, dwUnknown, pdwGuidAtom);
644 }
645 
646 /***********************************************************************
647  *      CtfImeIsGuidMapEnable (MSCTFIME.@)
648  *
649  * @implemented
650  */
651 EXTERN_C BOOL WINAPI
652 CtfImeIsGuidMapEnable(
653     _In_ HIMC hIMC)
654 {
655     TRACE("(%p)\n", hIMC);
656 
657     BOOL ret = FALSE;
658     CicIMCLock imcLock(hIMC);
659     if (SUCCEEDED(imcLock.m_hr))
660         ret = !!(imcLock.get().fdwInit & INIT_GUIDMAP);
661 
662     return ret;
663 }
664 
665 /***********************************************************************
666  *      CtfImeCreateThreadMgr (MSCTFIME.@)
667  *
668  * @implemented
669  */
670 EXTERN_C HRESULT WINAPI
671 CtfImeCreateThreadMgr(VOID)
672 {
673     TRACE("()\n");
674 
675     TLS *pTLS = TLS::GetTLS();
676     if (!pTLS)
677         return E_OUTOFMEMORY;
678 
679     if (!pTLS->m_pBridge)
680     {
681         pTLS->m_pBridge = new(cicNoThrow) CicBridge();
682         if (!pTLS->m_pBridge)
683             return E_OUTOFMEMORY;
684     }
685 
686     HRESULT hr = S_OK;
687     if (!g_bWinLogon && !(pTLS->m_dwSystemInfoFlags & IME_SYSINFO_WINLOGON))
688     {
689         hr = pTLS->m_pBridge->InitIMMX(pTLS);
690         if (SUCCEEDED(hr))
691         {
692             if (!pTLS->m_pThreadMgr)
693                 return E_OUTOFMEMORY;
694 
695             hr = pTLS->m_pBridge->ActivateIMMX(pTLS, pTLS->m_pThreadMgr);
696             if (FAILED(hr))
697                 pTLS->m_pBridge->UnInitIMMX(pTLS);
698         }
699     }
700 
701     return hr;
702 }
703 
704 /***********************************************************************
705  *      CtfImeDestroyThreadMgr (MSCTFIME.@)
706  *
707  * @implemented
708  */
709 EXTERN_C HRESULT WINAPI
710 CtfImeDestroyThreadMgr(VOID)
711 {
712     TRACE("()\n");
713 
714     TLS *pTLS = TLS::PeekTLS();
715     if (!pTLS)
716         return E_OUTOFMEMORY;
717 
718     if (pTLS->m_pBridge)
719     {
720         pTLS->m_pBridge = new(cicNoThrow) CicBridge();
721         if (!pTLS->m_pBridge)
722             return E_OUTOFMEMORY;
723     }
724 
725     if (!pTLS->m_pThreadMgr)
726         return E_OUTOFMEMORY;
727 
728     if (pTLS->m_dwSystemInfoFlags & IME_SYSINFO_WINLOGON)
729         return S_OK;
730 
731     HRESULT hr = pTLS->m_pBridge->DeactivateIMMX(pTLS, pTLS->m_pThreadMgr);
732     if (hr == S_OK)
733         pTLS->m_pBridge->UnInitIMMX(pTLS);
734 
735     return hr;
736 }
737 
738 /***********************************************************************
739  *      CtfImeCreateInputContext (MSCTFIME.@)
740  *
741  * @implemented
742  */
743 EXTERN_C HRESULT WINAPI
744 CtfImeCreateInputContext(
745     _In_ HIMC hIMC)
746 {
747     TRACE("(%p)\n", hIMC);
748 
749     TLS *pTLS = TLS::GetTLS();
750     if (!pTLS || !pTLS->m_pBridge)
751         return E_OUTOFMEMORY;
752 
753     return pTLS->m_pBridge->CreateInputContext(pTLS, hIMC);
754 }
755 
756 /***********************************************************************
757  *      CtfImeDestroyInputContext (MSCTFIME.@)
758  *
759  * @implemented
760  */
761 EXTERN_C HRESULT WINAPI
762 CtfImeDestroyInputContext(
763     _In_ HIMC hIMC)
764 {
765     TRACE("(%p)\n", hIMC);
766 
767     TLS *pTLS = TLS::PeekTLS();
768     if (!pTLS || !pTLS->m_pBridge)
769         return E_OUTOFMEMORY;
770 
771     return pTLS->m_pBridge->DestroyInputContext(pTLS, hIMC);
772 }
773 
774 /***********************************************************************
775  *      CtfImeSetActiveContextAlways (MSCTFIME.@)
776  *
777  * @implemented
778  */
779 EXTERN_C HRESULT WINAPI
780 CtfImeSetActiveContextAlways(
781     _In_ HIMC hIMC,
782     _In_ BOOL fActive,
783     _In_ HWND hWnd,
784     _In_ HKL hKL)
785 {
786     TRACE("(%p, %d, %p, %p)\n", hIMC, fActive, hWnd, hKL);
787 
788     TLS *pTLS = TLS::GetTLS();
789     if (!pTLS || !pTLS->m_pBridge)
790         return E_OUTOFMEMORY;
791     return pTLS->m_pBridge->SetActiveContextAlways(pTLS, hIMC, fActive, hWnd, hKL);
792 }
793 
794 /***********************************************************************
795  *      CtfImeProcessCicHotkey (MSCTFIME.@)
796  *
797  * @implemented
798  */
799 EXTERN_C HRESULT WINAPI
800 CtfImeProcessCicHotkey(
801     _In_ HIMC hIMC,
802     _In_ UINT vKey,
803     _In_ LPARAM lParam)
804 {
805     TRACE("(%p, %u, %p)\n", hIMC, vKey, lParam);
806 
807     TLS *pTLS = TLS::GetTLS();
808     if (!pTLS)
809         return S_OK;
810 
811     HRESULT hr = S_OK;
812     ITfThreadMgr *pThreadMgr = NULL;
813     ITfThreadMgr_P *pThreadMgr_P = NULL;
814     if ((TF_GetThreadMgr(&pThreadMgr) == S_OK) &&
815         (pThreadMgr->QueryInterface(IID_ITfThreadMgr_P, (void**)&pThreadMgr_P) == S_OK) &&
816         CtfImmIsCiceroStartedInThread())
817     {
818         HRESULT hr2;
819         if (SUCCEEDED(pThreadMgr_P->CallImm32HotkeyHandler(vKey, lParam, &hr2)))
820             hr = hr2;
821     }
822 
823     if (pThreadMgr)
824         pThreadMgr->Release();
825     if (pThreadMgr_P)
826         pThreadMgr_P->Release();
827 
828     return hr;
829 }
830 
831 /***********************************************************************
832  *      CtfImeDispatchDefImeMessage (MSCTFIME.@)
833  *
834  * @implemented
835  */
836 EXTERN_C LRESULT WINAPI
837 CtfImeDispatchDefImeMessage(
838     _In_ HWND hWnd,
839     _In_ UINT uMsg,
840     _In_ WPARAM wParam,
841     _In_ LPARAM lParam)
842 {
843     TRACE("(%p, %u, %p, %p)\n", hWnd, uMsg, wParam, lParam);
844 
845     TLS *pTLS = TLS::GetTLS();
846     if (pTLS)
847     {
848         if (uMsg == WM_CREATE)
849             ++pTLS->m_cWnds;
850         else if (uMsg == WM_DESTROY)
851             --pTLS->m_cWnds;
852     }
853 
854     if (!IsMsImeMessage(uMsg))
855         return 0;
856 
857     HKL hKL = ::GetKeyboardLayout(0);
858     if (IS_IME_HKL(hKL))
859         return 0;
860 
861     HWND hImeWnd = (HWND)::SendMessageW(hWnd, WM_IME_NOTIFY, 0x17, 0);
862     if (!IsWindow(hImeWnd))
863         return 0;
864 
865     return ::SendMessageW(hImeWnd, uMsg, wParam, lParam);
866 }
867 
868 /***********************************************************************
869  *      CtfImeIsIME (MSCTFIME.@)
870  *
871  * @implemented
872  */
873 EXTERN_C BOOL WINAPI
874 CtfImeIsIME(
875     _In_ HKL hKL)
876 {
877     TRACE("(%p)\n", hKL);
878 
879     if (IS_IME_HKL(hKL))
880         return TRUE;
881 
882     TLS *pTLS = TLS::GetTLS();
883     if (!pTLS || !pTLS->m_pProfile)
884         return FALSE;
885 
886     // The return value of CicProfile::IsIME is brain-damaged
887     return !pTLS->m_pProfile->IsIME(hKL);
888 }
889 
890 /***********************************************************************
891  *      CtfImeThreadDetach (MSCTFIME.@)
892  *
893  * @implemented
894  */
895 EXTERN_C HRESULT WINAPI
896 CtfImeThreadDetach(VOID)
897 {
898     ImeDestroy(0);
899     return S_OK;
900 }
901 
902 /// @implemented
903 BOOL AttachIME(VOID)
904 {
905     return RegisterImeClass() && RegisterMSIMEMessage();
906 }
907 
908 /// @implemented
909 VOID DetachIME(VOID)
910 {
911     UnregisterImeClass();
912 }
913 
914 EXTERN_C VOID TFUninitLib(VOID)
915 {
916     if (g_pPropCache)
917     {
918         delete g_pPropCache;
919         g_pPropCache = NULL;
920     }
921 }
922 
923 /// @implemented
924 BOOL ProcessAttach(HINSTANCE hinstDLL)
925 {
926     g_hInst = hinstDLL;
927 
928     ::InitializeCriticalSectionAndSpinCount(&g_csLock, 0);
929 
930     if (!TLS::Initialize())
931         return FALSE;
932 
933     cicGetOSInfo(&g_uACP, &g_dwOSInfo);
934 
935     cicInitUIFLib();
936 
937     if (!TFInitLib())
938         return FALSE;
939 
940     gfTFInitLib = TRUE;
941     return AttachIME();
942 }
943 
944 /// @implemented
945 VOID ProcessDetach(HINSTANCE hinstDLL)
946 {
947     TF_DllDetachInOther();
948 
949     if (gfTFInitLib)
950     {
951         DetachIME();
952         TFUninitLib();
953     }
954 
955     ::DeleteCriticalSection(&g_csLock);
956     TLS::InternalDestroyTLS();
957     TLS::Uninitialize();
958     cicDoneUIFLib();
959 }
960 
961 /// @implemented
962 EXTERN_C BOOL WINAPI
963 DllMain(
964     _In_ HINSTANCE hinstDLL,
965     _In_ DWORD dwReason,
966     _Inout_opt_ LPVOID lpvReserved)
967 {
968     switch (dwReason)
969     {
970         case DLL_PROCESS_ATTACH:
971         {
972             TRACE("(%p, %lu, %p)\n", hinstDLL, dwReason, lpvReserved);
973             if (!ProcessAttach(hinstDLL))
974             {
975                 ProcessDetach(hinstDLL);
976                 return FALSE;
977             }
978             break;
979         }
980         case DLL_PROCESS_DETACH:
981         {
982             ProcessDetach(hinstDLL);
983             break;
984         }
985         case DLL_THREAD_DETACH:
986         {
987             TF_DllDetachInOther();
988             CtfImeThreadDetach();
989             TLS::InternalDestroyTLS();
990             break;
991         }
992     }
993     return TRUE;
994 }
995