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