xref: /reactos/dll/win32/imm32/ctf.c (revision a575921e)
1 /*
2  * PROJECT:     ReactOS IMM32
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     Implementing the IMM32 Cicero-aware Text Framework (CTF)
5  * COPYRIGHT:   Copyright 2022-2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6  */
7 
8 #include "precomp.h"
9 #include <msctf.h>
10 #include <ctfutb.h>
11 
12 WINE_DEFAULT_DEBUG_CHANNEL(imm);
13 
14 BOOL
15 Imm32GetFn(
16     _Inout_opt_ FARPROC *ppfn,
17     _Inout_ HINSTANCE *phinstDLL,
18     _In_ LPCWSTR pszDllName,
19     _In_ LPCSTR pszFuncName)
20 {
21     WCHAR szPath[MAX_PATH];
22 
23     if (*ppfn)
24         return TRUE;
25 
26     if (*phinstDLL == NULL)
27     {
28         Imm32GetSystemLibraryPath(szPath, _countof(szPath), pszDllName);
29         *phinstDLL = LoadLibraryExW(szPath, NULL, 0);
30         if (*phinstDLL == NULL)
31             return FALSE;
32     }
33 
34     *ppfn = (FARPROC)GetProcAddress(*phinstDLL, pszFuncName);
35     return *ppfn != NULL;
36 }
37 
38 #define IMM32_GET_FN(ppfn, phinstDLL, dll_name, func_name) \
39     Imm32GetFn((FARPROC*)(ppfn), (phinstDLL), (dll_name), #func_name)
40 
41 /***********************************************************************
42  * OLE32.DLL
43  */
44 
45 HINSTANCE g_hOle32 = NULL;
46 
47 #define OLE32_FN(name) g_pfnOLE32_##name
48 
49 typedef HRESULT (WINAPI *FN_CoInitializeEx)(LPVOID, DWORD);
50 typedef VOID    (WINAPI *FN_CoUninitialize)(VOID);
51 typedef HRESULT (WINAPI *FN_CoRegisterInitializeSpy)(IInitializeSpy*, ULARGE_INTEGER*);
52 typedef HRESULT (WINAPI *FN_CoRevokeInitializeSpy)(ULARGE_INTEGER);
53 
54 FN_CoInitializeEx           OLE32_FN(CoInitializeEx)            = NULL;
55 FN_CoUninitialize           OLE32_FN(CoUninitialize)            = NULL;
56 FN_CoRegisterInitializeSpy  OLE32_FN(CoRegisterInitializeSpy)   = NULL;
57 FN_CoRevokeInitializeSpy    OLE32_FN(CoRevokeInitializeSpy)     = NULL;
58 
59 #define Imm32GetOle32Fn(func_name) \
60     IMM32_GET_FN(&OLE32_FN(func_name), &g_hOle32, L"ole32.dll", #func_name)
61 
62 HRESULT Imm32CoInitializeEx(VOID)
63 {
64     if (!Imm32GetOle32Fn(CoInitializeEx))
65         return E_FAIL;
66 
67     return OLE32_FN(CoInitializeEx)(NULL, COINIT_APARTMENTTHREADED);
68 }
69 
70 VOID Imm32CoUninitialize(VOID)
71 {
72     if (!Imm32GetOle32Fn(CoUninitialize))
73         return;
74 
75     OLE32_FN(CoUninitialize)();
76 }
77 
78 HRESULT Imm32CoRegisterInitializeSpy(IInitializeSpy* spy, ULARGE_INTEGER* cookie)
79 {
80     if (!Imm32GetOle32Fn(CoRegisterInitializeSpy))
81         return E_FAIL;
82 
83     return OLE32_FN(CoRegisterInitializeSpy)(spy, cookie);
84 }
85 
86 HRESULT Imm32CoRevokeInitializeSpy(ULARGE_INTEGER cookie)
87 {
88     if (!Imm32GetOle32Fn(CoRevokeInitializeSpy))
89         return E_FAIL;
90 
91     return OLE32_FN(CoRevokeInitializeSpy)(cookie);
92 }
93 
94 /***********************************************************************
95  * MSCTF.DLL
96  */
97 
98 HINSTANCE g_hMsctf = NULL;
99 
100 #define MSCTF_FN(name) g_pfnMSCTF_##name
101 
102 typedef HRESULT (WINAPI *FN_TF_CreateLangBarMgr)(ITfLangBarMgr**);
103 typedef VOID    (WINAPI *FN_TF_InvalidAssemblyListCacheIfExist)(VOID);
104 
105 FN_TF_CreateLangBarMgr                MSCTF_FN(TF_CreateLangBarMgr)                = NULL;
106 FN_TF_InvalidAssemblyListCacheIfExist MSCTF_FN(TF_InvalidAssemblyListCacheIfExist) = NULL;
107 
108 #define Imm32GetMsctfFn(func_name) \
109     IMM32_GET_FN(&MSCTF_FN(func_name), &g_hMsctf, L"msctf.dll", #func_name)
110 
111 HRESULT Imm32TF_CreateLangBarMgr(_Inout_ ITfLangBarMgr **ppBarMgr)
112 {
113     if (!Imm32GetMsctfFn(TF_CreateLangBarMgr))
114         return E_FAIL;
115 
116     return MSCTF_FN(TF_CreateLangBarMgr)(ppBarMgr);
117 }
118 
119 VOID Imm32TF_InvalidAssemblyListCacheIfExist(VOID)
120 {
121     if (!Imm32GetMsctfFn(TF_InvalidAssemblyListCacheIfExist))
122         return;
123 
124     MSCTF_FN(TF_InvalidAssemblyListCacheIfExist)();
125 }
126 
127 /***********************************************************************
128  * CTF IME support
129  *
130  * TSF stands for "Text Services Framework". "Cicero" is the code name of TSF.
131  * CTF stands for "Cicero-aware Text Framework".
132  *
133  * Comparing with old-style IMM IME, the combination of CTF IME and TSF provides
134  * new-style and high-level input method.
135  *
136  * The CTF IME file is a DLL file that the software developer distributes.
137  * The export functions of the CTF IME file are defined in "CtfImeTable.h" of
138  * this folder.
139  */
140 
141 /* "Active IMM" compatibility flags */
142 DWORD g_aimm_compat_flags = 0;
143 
144 /* The instance of the CTF IME file */
145 HINSTANCE g_hCtfIme = NULL;
146 
147 /* Define the function types (FN_...) for CTF IME functions */
148 #undef DEFINE_CTF_IME_FN
149 #define DEFINE_CTF_IME_FN(func_name, ret_type, params) \
150     typedef ret_type (WINAPI *FN_##func_name)params;
151 #include "CtfImeTable.h"
152 
153 /* Define the global variables (g_pfn...) for CTF IME functions */
154 #undef DEFINE_CTF_IME_FN
155 #define DEFINE_CTF_IME_FN(func_name, ret_type, params) \
156     FN_##func_name g_pfn##func_name = NULL;
157 #include "CtfImeTable.h"
158 
159 /* The macro that gets the variable name from the CTF IME function name */
160 #define CTF_IME_FN(func_name) g_pfn##func_name
161 
162 /* The type of ApphelpCheckIME function in apphelp.dll */
163 typedef BOOL (WINAPI *FN_ApphelpCheckIME)(_In_z_ LPCWSTR AppName);
164 
165 /* FIXME: This is kernel32 function. We have to declare this in some header. */
166 BOOL WINAPI
167 BaseCheckAppcompatCache(_In_z_ LPCWSTR ApplicationName,
168                         _In_ HANDLE FileHandle,
169                         _In_opt_z_ LPCWSTR Environment,
170                         _Out_ PULONG pdwReason);
171 
172 /***********************************************************************
173  * This function checks whether the app's IME is disabled by application
174  * compatibility patcher.
175  */
176 BOOL
177 Imm32CheckAndApplyAppCompat(
178     _In_ ULONG dwReason,
179     _In_z_ LPCWSTR pszAppName)
180 {
181     HINSTANCE hinstApphelp;
182     FN_ApphelpCheckIME pApphelpCheckIME;
183 
184     /* Query the application compatibility patcher */
185     if (BaseCheckAppcompatCache(pszAppName, INVALID_HANDLE_VALUE, NULL, &dwReason))
186         return TRUE; /* The app's IME is not disabled */
187 
188     /* Load apphelp.dll if necessary */
189     hinstApphelp = GetModuleHandleW(L"apphelp.dll");
190     if (!hinstApphelp)
191     {
192         hinstApphelp = LoadLibraryW(L"apphelp.dll");
193         if (!hinstApphelp)
194             return TRUE; /* There is no apphelp.dll. The app's IME is not disabled */
195     }
196 
197     /* Is ApphelpCheckIME implemented? */
198     pApphelpCheckIME = (FN_ApphelpCheckIME)GetProcAddress(hinstApphelp, "ApphelpCheckIME");
199     if (!pApphelpCheckIME)
200         return TRUE; /* Not implemented. The app's IME is not disabled */
201 
202     /* Is the app's IME disabled or not? */
203     return pApphelpCheckIME(pszAppName);
204 }
205 
206 /***********************************************************************
207  * This function loads the CTF IME file if necessary and establishes
208  * communication with the CTF IME.
209  */
210 HINSTANCE
211 Imm32LoadCtfIme(VOID)
212 {
213     BOOL bSuccess = FALSE;
214     IMEINFOEX ImeInfoEx;
215     WCHAR szImeFile[MAX_PATH];
216 
217     /* Lock the IME interface */
218     RtlEnterCriticalSection(&gcsImeDpi);
219 
220     do
221     {
222         if (g_hCtfIme) /* Already loaded? */
223         {
224             bSuccess = TRUE;
225             break;
226         }
227 
228         /*
229          * NOTE: (HKL)0x04090409 is English US keyboard (default).
230          * The Cicero keyboard logically uses English US keyboard.
231          */
232         if (!ImmLoadLayout((HKL)ULongToHandle(0x04090409), &ImeInfoEx))
233             break;
234 
235         /* Build a path string in system32. The installed IME file must be in system32. */
236         Imm32GetSystemLibraryPath(szImeFile, _countof(szImeFile), ImeInfoEx.wszImeFile);
237 
238         /* Is the CTF IME disabled by app compatibility patcher? */
239         if (!Imm32CheckAndApplyAppCompat(0, szImeFile))
240             break; /* This IME is disabled */
241 
242         /* Load a CTF IME file */
243         g_hCtfIme = LoadLibraryW(szImeFile);
244         if (!g_hCtfIme)
245             break;
246 
247         /* Assume success */
248         bSuccess = TRUE;
249 
250         /* Retrieve the CTF IME functions */
251 #undef DEFINE_CTF_IME_FN
252 #define DEFINE_CTF_IME_FN(func_name, ret_type, params) \
253         CTF_IME_FN(func_name) = (FN_##func_name)GetProcAddress(g_hCtfIme, #func_name); \
254         if (!CTF_IME_FN(func_name)) \
255         { \
256             bSuccess = FALSE; /* Failed */ \
257             break; \
258         }
259 #include "CtfImeTable.h"
260     } while (0);
261 
262     /* Unload the CTF IME if failed */
263     if (!bSuccess)
264     {
265         /* Set NULL to the function pointers */
266 #undef DEFINE_CTF_IME_FN
267 #define DEFINE_CTF_IME_FN(func_name, ret_type, params) CTF_IME_FN(func_name) = NULL;
268 #include "CtfImeTable.h"
269 
270         if (g_hCtfIme)
271         {
272             FreeLibrary(g_hCtfIme);
273             g_hCtfIme = NULL;
274         }
275     }
276 
277     /* Unlock the IME interface */
278     RtlLeaveCriticalSection(&gcsImeDpi);
279 
280     return g_hCtfIme;
281 }
282 
283 /***********************************************************************
284  * This function calls the same name function of the CTF IME side.
285  */
286 HRESULT
287 CtfImeCreateThreadMgr(VOID)
288 {
289     if (!Imm32LoadCtfIme())
290         return E_FAIL;
291 
292     return CTF_IME_FN(CtfImeCreateThreadMgr)();
293 }
294 
295 /***********************************************************************
296  * This function calls the same name function of the CTF IME side.
297  */
298 HRESULT
299 CtfImeDestroyThreadMgr(VOID)
300 {
301     if (!Imm32LoadCtfIme())
302         return E_FAIL;
303 
304     return CTF_IME_FN(CtfImeDestroyThreadMgr)();
305 }
306 
307 /***********************************************************************
308  *		CtfAImmIsIME (IMM32.@)
309  *
310  * @return TRUE if CTF IME or IMM IME is enabled.
311  */
312 BOOL WINAPI
313 CtfAImmIsIME(_In_ HKL hKL)
314 {
315     TRACE("(%p)\n", hKL);
316     if (!Imm32LoadCtfIme())
317         return ImmIsIME(hKL);
318     return CTF_IME_FN(CtfImeIsIME)(hKL);
319 }
320 
321 /***********************************************************************
322  *		CtfImmIsCiceroStartedInThread (IMM32.@)
323  *
324  * @return TRUE if Cicero is started in the current thread.
325  */
326 BOOL WINAPI
327 CtfImmIsCiceroStartedInThread(VOID)
328 {
329     TRACE("()\n");
330     return !!(GetWin32ClientInfo()->CI_flags & 0x200);
331 }
332 
333 /***********************************************************************
334  *		CtfImmSetAppCompatFlags (IMM32.@)
335  *
336  * Sets the application compatibility flags.
337  */
338 VOID WINAPI
339 CtfImmSetAppCompatFlags(_In_ DWORD dwFlags)
340 {
341     TRACE("(0x%08X)\n", dwFlags);
342     if (!(dwFlags & 0xF0FFFFFF))
343         g_aimm_compat_flags = dwFlags;
344 }
345 
346 /***********************************************************************
347  * This function calls the same name function of the CTF IME side.
348  */
349 HRESULT
350 CtfImeCreateInputContext(
351     _In_ HIMC hIMC)
352 {
353     if (!Imm32LoadCtfIme())
354         return E_FAIL;
355 
356     return CTF_IME_FN(CtfImeCreateInputContext)(hIMC);
357 }
358 
359 /***********************************************************************
360  * This function calls the same name function of the CTF IME side.
361  */
362 HRESULT
363 CtfImeDestroyInputContext(_In_ HIMC hIMC)
364 {
365     if (!Imm32LoadCtfIme())
366         return E_FAIL;
367 
368     return CTF_IME_FN(CtfImeDestroyInputContext)(hIMC);
369 }
370 
371 /***********************************************************************
372  * The callback function to activate CTF IMEs. Used in CtfAImmActivate.
373  */
374 static BOOL CALLBACK
375 Imm32EnumCreateCtfICProc(
376     _In_ HIMC hIMC,
377     _In_ LPARAM lParam)
378 {
379     UNREFERENCED_PARAMETER(lParam);
380     CtfImeCreateInputContext(hIMC);
381     return TRUE; /* Continue */
382 }
383 
384 /***********************************************************************
385  * This function calls CtfImeDestroyInputContext if possible.
386  */
387 HRESULT
388 CtfImmTIMDestroyInputContext(
389     _In_ HIMC hIMC)
390 {
391     if (!IS_CICERO_MODE() || (GetWin32ClientInfo()->dwCompatFlags2 & 2))
392         return E_NOINTERFACE;
393 
394     return CtfImeDestroyInputContext(hIMC);
395 }
396 
397 HRESULT
398 CtfImmTIMCreateInputContext(
399     _In_ HIMC hIMC)
400 {
401     TRACE("(%p)\n", hIMC);
402     return E_NOTIMPL;
403 }
404 
405 /***********************************************************************
406  *      CtfAImmActivate (IMM32.@)
407  *
408  * This function activates "Active IMM" (AIMM) and TSF.
409  */
410 HRESULT WINAPI
411 CtfAImmActivate(
412     _Out_opt_ HINSTANCE *phinstCtfIme)
413 {
414     HRESULT hr;
415     HINSTANCE hinstCtfIme;
416 
417     TRACE("(%p)\n", phinstCtfIme);
418 
419     /* Load a CTF IME file if necessary */
420     hinstCtfIme = Imm32LoadCtfIme();
421 
422     /* Create a thread manager of the CTF IME */
423     hr = CtfImeCreateThreadMgr();
424     if (hr == S_OK)
425     {
426         /* Update CI_... flags of the thread client info */
427         GetWin32ClientInfo()->CI_flags |= CI_AIMMACTIVATED; /* Activate AIMM */
428         GetWin32ClientInfo()->CI_flags &= ~CI_TSFDISABLED;  /* Enable TSF */
429 
430         /* Create the CTF input contexts */
431         ImmEnumInputContext(0, Imm32EnumCreateCtfICProc, 0);
432     }
433 
434     if (phinstCtfIme)
435         *phinstCtfIme = hinstCtfIme;
436 
437     return hr;
438 }
439 
440 /***********************************************************************
441  *      CtfAImmDeactivate (IMM32.@)
442  *
443  * This function de-activates "Active IMM" (AIMM) and TSF.
444  */
445 HRESULT WINAPI
446 CtfAImmDeactivate(
447     _In_ BOOL bDestroy)
448 {
449     HRESULT hr;
450 
451     if (!bDestroy)
452         return E_FAIL;
453 
454     hr = CtfImeDestroyThreadMgr();
455     if (hr == S_OK)
456     {
457         GetWin32ClientInfo()->CI_flags &= ~CI_AIMMACTIVATED; /* Deactivate AIMM */
458         GetWin32ClientInfo()->CI_flags |= CI_TSFDISABLED;    /* Disable TSF */
459     }
460 
461     return hr;
462 }
463 
464 /***********************************************************************
465  *		CtfImmIsCiceroEnabled (IMM32.@)
466  *
467  * @return TRUE if Cicero is enabled.
468  */
469 BOOL WINAPI
470 CtfImmIsCiceroEnabled(VOID)
471 {
472     return IS_CICERO_MODE();
473 }
474 
475 /***********************************************************************
476  *		CtfImmIsTextFrameServiceDisabled(IMM32.@)
477  *
478  * @return TRUE if TSF is disabled.
479  */
480 BOOL WINAPI
481 CtfImmIsTextFrameServiceDisabled(VOID)
482 {
483     return !!(GetWin32ClientInfo()->CI_flags & CI_TSFDISABLED);
484 }
485 
486 /***********************************************************************
487  *		CtfImmTIMActivate(IMM32.@)
488  */
489 HRESULT WINAPI
490 CtfImmTIMActivate(_In_ HKL hKL)
491 {
492     FIXME("(%p)\n", hKL);
493     return E_NOTIMPL;
494 }
495 
496 /***********************************************************************
497  *		CtfImmHideToolbarWnd(IMM32.@)
498  *
499  * Used with CtfImmRestoreToolbarWnd.
500  */
501 DWORD WINAPI
502 CtfImmHideToolbarWnd(VOID)
503 {
504     ITfLangBarMgr *pBarMgr;
505     DWORD dwShowFlags = 0;
506     BOOL bShown;
507 
508     TRACE("()\n");
509 
510     if (FAILED(Imm32TF_CreateLangBarMgr(&pBarMgr)))
511         return dwShowFlags;
512 
513     if (SUCCEEDED(pBarMgr->lpVtbl->GetShowFloatingStatus(pBarMgr, &dwShowFlags)))
514     {
515         bShown = !(dwShowFlags & 0x800);
516         dwShowFlags &= 0xF;
517         if (bShown)
518             pBarMgr->lpVtbl->ShowFloating(pBarMgr, 8);
519     }
520 
521     pBarMgr->lpVtbl->Release(pBarMgr);
522     return dwShowFlags;
523 }
524 
525 /***********************************************************************
526  *		CtfImmRestoreToolbarWnd(IMM32.@)
527  *
528  * Used with CtfImmHideToolbarWnd.
529  */
530 VOID WINAPI
531 CtfImmRestoreToolbarWnd(
532     _In_ LPVOID pUnused,
533     _In_ DWORD dwShowFlags)
534 {
535     HRESULT hr;
536     ITfLangBarMgr *pBarMgr;
537 
538     UNREFERENCED_PARAMETER(pUnused);
539 
540     TRACE("(%p, 0x%X)\n", pUnused, dwShowFlags);
541 
542     hr = Imm32TF_CreateLangBarMgr(&pBarMgr);
543     if (FAILED(hr))
544         return;
545 
546     if (dwShowFlags)
547         pBarMgr->lpVtbl->ShowFloating(pBarMgr, dwShowFlags);
548 
549     pBarMgr->lpVtbl->Release(pBarMgr);
550 }
551 
552 BOOL Imm32InsideLoaderLock(VOID)
553 {
554     return (NtCurrentTeb()->ProcessEnvironmentBlock->LoaderLock->OwningThread ==
555             NtCurrentTeb()->ClientId.UniqueThread);
556 }
557 
558 /* FIXME: Use RTL */
559 BOOL WINAPI RtlDllShutdownInProgress(VOID)
560 {
561     return FALSE;
562 }
563 
564 /***********************************************************************
565  *		CtfImmDispatchDefImeMessage(IMM32.@)
566  */
567 LRESULT WINAPI
568 CtfImmDispatchDefImeMessage(
569     _In_ HWND hWnd,
570     _In_ UINT uMsg,
571     _In_ WPARAM wParam,
572     _In_ LPARAM lParam)
573 {
574     TRACE("(%p, %u, %p, %p)\n", hWnd, uMsg, wParam, lParam);
575 
576     if (RtlDllShutdownInProgress() || Imm32InsideLoaderLock() || !Imm32LoadCtfIme())
577         return 0;
578 
579     return CTF_IME_FN(CtfImeDispatchDefImeMessage)(hWnd, uMsg, wParam, lParam);
580 }
581 
582 /***********************************************************************
583  *		CtfImmIsGuidMapEnable(IMM32.@)
584  */
585 BOOL WINAPI
586 CtfImmIsGuidMapEnable(
587     _In_ HIMC hIMC)
588 {
589     DWORD dwThreadId;
590     HKL hKL;
591     PIMEDPI pImeDpi;
592     BOOL ret = FALSE;
593 
594     TRACE("(%p)\n", hIMC);
595 
596     if (!IS_CICERO_MODE() || IS_16BIT_MODE())
597         return ret;
598 
599     dwThreadId = (DWORD)NtUserQueryInputContext(hIMC, QIC_INPUTTHREADID);
600     hKL = GetKeyboardLayout(dwThreadId);
601 
602     if (IS_IME_HKL(hKL))
603         return ret;
604 
605     pImeDpi = Imm32FindOrLoadImeDpi(hKL);
606     if (IS_NULL_UNEXPECTEDLY(pImeDpi))
607         return ret;
608 
609     ret = pImeDpi->CtfImeIsGuidMapEnable(hIMC);
610 
611     ImmUnlockImeDpi(pImeDpi);
612     return ret;
613 }
614 
615 /***********************************************************************
616  *		CtfImmGetGuidAtom(IMM32.@)
617  */
618 HRESULT WINAPI
619 CtfImmGetGuidAtom(
620     _In_ HIMC hIMC,
621     _In_ DWORD dwUnknown,
622     _Out_ LPDWORD pdwGuidAtom)
623 {
624     HRESULT hr = E_FAIL;
625     PIMEDPI pImeDpi;
626     DWORD dwThreadId;
627     HKL hKL;
628 
629     TRACE("(%p, 0xlX, %p)\n", hIMC, dwUnknown, pdwGuidAtom);
630 
631     *pdwGuidAtom = 0;
632 
633     if (!IS_CICERO_MODE() || IS_16BIT_MODE())
634         return hr;
635 
636     dwThreadId = (DWORD)NtUserQueryInputContext(hIMC, QIC_INPUTTHREADID);
637     hKL = GetKeyboardLayout(dwThreadId);
638     if (IS_IME_HKL(hKL))
639         return S_OK;
640 
641     pImeDpi = Imm32FindOrLoadImeDpi(hKL);
642     if (IS_NULL_UNEXPECTEDLY(pImeDpi))
643         return hr;
644 
645     hr = pImeDpi->CtfImeGetGuidAtom(hIMC, dwUnknown, pdwGuidAtom);
646 
647     ImmUnlockImeDpi(pImeDpi);
648     return hr;
649 }
650