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