1 /* 2 * PROJECT: ReactOS msctfime.ime 3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) 4 * PURPOSE: Miscellaneous of msctfime.ime 5 * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 6 */ 7 8 #include "msctfime.h" 9 10 WINE_DEFAULT_DEBUG_CHANNEL(msctfime); 11 12 /// East-Asian language? 13 /// @implemented 14 BOOL IsEALang(_In_opt_ LANGID LangID) 15 { 16 if (LangID == 0) 17 { 18 TLS *pTLS = TLS::GetTLS(); 19 if (!pTLS || !pTLS->m_pProfile) 20 return FALSE; 21 22 pTLS->m_pProfile->GetLangId(&LangID); 23 } 24 25 switch (PRIMARYLANGID(LangID)) 26 { 27 case LANG_CHINESE: 28 case LANG_JAPANESE: 29 case LANG_KOREAN: 30 return TRUE; 31 32 default: 33 return FALSE; 34 } 35 } 36 37 typedef BOOLEAN (WINAPI *FN_DllShutdownInProgress)(VOID); 38 39 /// This function calls ntdll!RtlDllShutdownInProgress. 40 /// It can detect the system is shutting down or not. 41 /// @implemented 42 BOOLEAN DllShutdownInProgress(VOID) 43 { 44 HMODULE hNTDLL; 45 static FN_DllShutdownInProgress s_fnDllShutdownInProgress = NULL; 46 47 if (s_fnDllShutdownInProgress) 48 return s_fnDllShutdownInProgress(); 49 50 hNTDLL = cicGetSystemModuleHandle(L"ntdll.dll", FALSE); 51 s_fnDllShutdownInProgress = 52 (FN_DllShutdownInProgress)GetProcAddress(hNTDLL, "RtlDllShutdownInProgress"); 53 if (!s_fnDllShutdownInProgress) 54 return FALSE; 55 56 return s_fnDllShutdownInProgress(); 57 } 58 59 /// This function checks if the current user logon session is interactive. 60 /// @implemented 61 BOOL IsInteractiveUserLogon(VOID) 62 { 63 BOOL bOK, IsMember = FALSE; 64 PSID pSid; 65 SID_IDENTIFIER_AUTHORITY IdentAuth = { SECURITY_NT_AUTHORITY }; 66 67 if (!AllocateAndInitializeSid(&IdentAuth, 1, SECURITY_INTERACTIVE_RID, 68 0, 0, 0, 0, 0, 0, 0, &pSid)) 69 { 70 ERR("Error: %ld\n", GetLastError()); 71 return FALSE; 72 } 73 74 bOK = CheckTokenMembership(NULL, pSid, &IsMember); 75 76 if (pSid) 77 FreeSid(pSid); 78 79 return bOK && IsMember; 80 } 81 82 /// Gets the charset from a language ID. 83 /// @implemented 84 BYTE GetCharsetFromLangId(_In_ DWORD dwValue) 85 { 86 CHARSETINFO info; 87 if (!::TranslateCharsetInfo((DWORD*)(DWORD_PTR)dwValue, &info, TCI_SRCLOCALE)) 88 return 0; 89 return info.ciCharset; 90 } 91 92 /// Get the active input context. 93 /// @implemented 94 HIMC GetActiveContext(VOID) 95 { 96 HWND hwndFocus = ::GetFocus(); 97 if (!hwndFocus) 98 hwndFocus = ::GetActiveWindow(); 99 return ::ImmGetContext(hwndFocus); 100 } 101 102 // MSIMTF.dll!MsimtfIsGuidMapEnable 103 typedef BOOL (WINAPI *FN_MsimtfIsGuidMapEnable)(HIMC hIMC, LPBOOL pbValue); 104 HINSTANCE g_hMSIMTF = NULL; 105 106 /// @implemented 107 BOOL MsimtfIsGuidMapEnable(_In_ HIMC hIMC, _Out_opt_ LPBOOL pbValue) 108 { 109 static FN_MsimtfIsGuidMapEnable s_fn = NULL; 110 if (!cicGetFN(g_hMSIMTF, s_fn, L"msimtf.dll", "MsimtfIsGuidMapEnable")) 111 return FALSE; 112 return s_fn(hIMC, pbValue); 113 } 114 115 /// @implemented 116 BOOL IsVKDBEKey(_In_ UINT uVirtKey) 117 { 118 switch (uVirtKey) 119 { 120 case VK_KANJI: 121 case VK_CONVERT: 122 return TRUE; 123 default: 124 return (VK_OEM_ATTN <= uVirtKey && uVirtKey <= VK_PA1); 125 } 126 } 127 128 /// @implemented 129 ITfCategoryMgr *GetUIMCat(PCIC_LIBTHREAD pLibThread) 130 { 131 if (!pLibThread) 132 return NULL; 133 134 if (pLibThread->m_pCategoryMgr) 135 return pLibThread->m_pCategoryMgr; 136 137 if (FAILED(cicCoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, 138 IID_ITfCategoryMgr, (void **)&pLibThread->m_pCategoryMgr))) 139 { 140 return NULL; 141 } 142 return pLibThread->m_pCategoryMgr; 143 } 144 145 /// @implemented 146 static HRESULT 147 LibEnumItemsInCategory(PCIC_LIBTHREAD pLibThread, REFGUID rguid, IEnumGUID **ppEnum) 148 { 149 ITfCategoryMgr *pCat = GetUIMCat(pLibThread); 150 if (!pCat) 151 return E_FAIL; 152 return pCat->EnumItemsInCategory(rguid, ppEnum); 153 } 154 155 /// @implemented 156 HRESULT InitDisplayAttrbuteLib(PCIC_LIBTHREAD pLibThread) 157 { 158 if (!pLibThread) 159 return E_FAIL; 160 161 if (pLibThread->m_pDisplayAttrMgr) 162 { 163 pLibThread->m_pDisplayAttrMgr->Release(); 164 pLibThread->m_pDisplayAttrMgr = NULL; 165 } 166 167 if (FAILED(cicCoCreateInstance(CLSID_TF_DisplayAttributeMgr, NULL, CLSCTX_INPROC_SERVER, 168 IID_ITfDisplayAttributeMgr, 169 (void **)&pLibThread->m_pDisplayAttrMgr))) 170 { 171 return E_FAIL; 172 } 173 174 IEnumGUID *pEnumGuid; 175 LibEnumItemsInCategory(pLibThread, GUID_TFCAT_DISPLAYATTRIBUTEPROPERTY, &pEnumGuid); 176 177 HRESULT hr = E_OUTOFMEMORY; 178 179 ::EnterCriticalSection(&g_csLock); 180 if (pEnumGuid && !g_pPropCache) 181 { 182 g_pPropCache = new(cicNoThrow) CDispAttrPropCache(); 183 if (g_pPropCache) 184 { 185 g_pPropCache->Add(GUID_PROP_ATTRIBUTE); 186 GUID guid; 187 while (pEnumGuid->Next(1, &guid, NULL) == S_OK) 188 { 189 if (!IsEqualGUID(guid, GUID_PROP_ATTRIBUTE)) 190 g_pPropCache->Add(guid); 191 } 192 hr = S_OK; 193 } 194 } 195 ::LeaveCriticalSection(&g_csLock); 196 197 return hr; 198 } 199 200 /// @implemented 201 HRESULT UninitDisplayAttrbuteLib(PCIC_LIBTHREAD pLibThread) 202 { 203 if (!pLibThread) 204 return E_FAIL; 205 206 if (pLibThread->m_pDisplayAttrMgr) 207 { 208 pLibThread->m_pDisplayAttrMgr->Release(); 209 pLibThread->m_pDisplayAttrMgr = NULL; 210 } 211 212 return S_OK; 213 } 214 215 /***********************************************************************/ 216 217 /// @implemented 218 HRESULT 219 GetCompartment( 220 IUnknown *pUnknown, 221 REFGUID rguid, 222 ITfCompartment **ppComp, 223 BOOL bThread) 224 { 225 *ppComp = NULL; 226 227 ITfThreadMgr *pThreadMgr = NULL; 228 ITfCompartmentMgr *pCompMgr = NULL; 229 230 HRESULT hr; 231 if (bThread) 232 { 233 hr = pUnknown->QueryInterface(IID_ITfThreadMgr, (void **)&pThreadMgr); 234 if (FAILED(hr)) 235 return hr; 236 237 hr = pThreadMgr->GetGlobalCompartment(&pCompMgr); 238 } 239 else 240 { 241 hr = pUnknown->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompMgr); 242 } 243 244 if (SUCCEEDED(hr)) 245 { 246 hr = E_FAIL; 247 if (pCompMgr) 248 { 249 hr = pCompMgr->GetCompartment(rguid, ppComp); 250 pCompMgr->Release(); 251 } 252 } 253 254 if (pThreadMgr) 255 pThreadMgr->Release(); 256 257 return hr; 258 } 259 260 /// @implemented 261 HRESULT 262 SetCompartmentDWORD( 263 TfEditCookie cookie, 264 IUnknown *pUnknown, 265 REFGUID rguid, 266 DWORD dwValue, 267 BOOL bThread) 268 { 269 ITfCompartment *pComp = NULL; 270 HRESULT hr = GetCompartment(pUnknown, rguid, &pComp, bThread); 271 if (FAILED(hr)) 272 return hr; 273 274 VARIANT vari; 275 V_I4(&vari) = dwValue; 276 V_VT(&vari) = VT_I4; 277 hr = pComp->SetValue(cookie, &vari); 278 279 pComp->Release(); 280 return hr; 281 } 282 283 /// @implemented 284 HRESULT 285 GetCompartmentDWORD( 286 IUnknown *pUnknown, 287 REFGUID rguid, 288 LPDWORD pdwValue, 289 BOOL bThread) 290 { 291 *pdwValue = 0; 292 293 ITfCompartment *pComp = NULL; 294 HRESULT hr = GetCompartment(pUnknown, rguid, &pComp, bThread); 295 if (FAILED(hr)) 296 return hr; 297 298 VARIANT vari; 299 hr = pComp->GetValue(&vari); 300 if (hr == S_OK) 301 *pdwValue = V_I4(&vari); 302 303 pComp->Release(); 304 return hr; 305 } 306 307 /// @implemented 308 HRESULT 309 SetCompartmentUnknown( 310 TfEditCookie cookie, 311 IUnknown *pUnknown, 312 REFGUID rguid, 313 IUnknown *punkValue) 314 { 315 ITfCompartment *pComp = NULL; 316 HRESULT hr = GetCompartment(pUnknown, rguid, &pComp, FALSE); 317 if (FAILED(hr)) 318 return hr; 319 320 VARIANT vari; 321 V_UNKNOWN(&vari) = punkValue; 322 V_VT(&vari) = VT_UNKNOWN; 323 hr = pComp->SetValue(cookie, &vari); 324 325 pComp->Release(); 326 return hr; 327 } 328 329 /// @implemented 330 HRESULT 331 ClearCompartment( 332 TfClientId tid, 333 IUnknown *pUnknown, 334 REFGUID rguid, 335 BOOL bThread) 336 { 337 ITfCompartmentMgr *pCompMgr = NULL; 338 ITfThreadMgr *pThreadMgr = NULL; 339 340 HRESULT hr; 341 if (bThread) 342 { 343 hr = pUnknown->QueryInterface(IID_ITfThreadMgr, (void **)&pThreadMgr); 344 if (FAILED(hr)) 345 return hr; 346 347 hr = pThreadMgr->GetGlobalCompartment(&pCompMgr); 348 } 349 else 350 { 351 hr = pUnknown->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompMgr); 352 } 353 354 if (SUCCEEDED(hr)) 355 { 356 hr = E_FAIL; 357 if (pCompMgr) 358 { 359 hr = pCompMgr->ClearCompartment(tid, rguid); 360 pCompMgr->Release(); 361 } 362 } 363 364 if (pThreadMgr) 365 pThreadMgr->Release(); 366 367 return hr; 368 } 369 370 /***********************************************************************/ 371 372 struct MODEBIAS 373 { 374 REFGUID m_guid; 375 LONG m_bias; 376 }; 377 378 static const MODEBIAS g_ModeBiasMap[] = 379 { 380 { GUID_MODEBIAS_FILENAME, 0x00000001 }, 381 { GUID_MODEBIAS_NUMERIC, 0x00000004 }, 382 { GUID_MODEBIAS_URLHISTORY, 0x00010000 }, 383 { GUID_MODEBIAS_DEFAULT, 0x00000000 }, 384 { GUID_MODEBIAS_NONE, 0x00000000 }, 385 }; 386 387 /// @implemented 388 void CModeBias::SetModeBias(REFGUID rguid) 389 { 390 m_guid = rguid; 391 } 392 393 /// @implemented 394 GUID CModeBias::ConvertModeBias(LONG bias) 395 { 396 const GUID *pguid = &GUID_NULL; 397 for (auto& item : g_ModeBiasMap) 398 { 399 if (item.m_bias == bias) 400 { 401 pguid = &item.m_guid; 402 break; 403 } 404 } 405 406 return *pguid; 407 } 408 409 /// @implemented 410 LONG CModeBias::ConvertModeBias(REFGUID guid) 411 { 412 for (auto& item : g_ModeBiasMap) 413 { 414 if (IsEqualGUID(guid, item.m_guid)) 415 return item.m_bias; 416 } 417 return 0; 418 } 419 420 /***********************************************************************/ 421 422 /// @implemented 423 CFunctionProviderBase::CFunctionProviderBase(_In_ TfClientId clientId) 424 { 425 m_clientId = clientId; 426 m_guid = GUID_NULL; 427 m_bstr = NULL; 428 m_cRefs = 1; 429 } 430 431 /// @implemented 432 CFunctionProviderBase::~CFunctionProviderBase() 433 { 434 if (!DllShutdownInProgress()) 435 ::SysFreeString(m_bstr); 436 } 437 438 /// @implemented 439 BOOL 440 CFunctionProviderBase::Init( 441 _In_ REFGUID rguid, 442 _In_ LPCWSTR psz) 443 { 444 m_bstr = ::SysAllocString(psz); 445 m_guid = rguid; 446 return (m_bstr != NULL); 447 } 448 449 /// @implemented 450 STDMETHODIMP 451 CFunctionProviderBase::QueryInterface( 452 _In_ REFIID riid, 453 _Out_ LPVOID* ppvObj) 454 { 455 static const QITAB c_tab[] = 456 { 457 QITABENT(CFunctionProviderBase, ITfFunctionProvider), 458 { NULL } 459 }; 460 return ::QISearch(this, c_tab, riid, ppvObj); 461 } 462 463 /// @implemented 464 STDMETHODIMP_(ULONG) CFunctionProviderBase::AddRef() 465 { 466 return ::InterlockedIncrement(&m_cRefs); 467 } 468 469 /// @implemented 470 STDMETHODIMP_(ULONG) CFunctionProviderBase::Release() 471 { 472 if (::InterlockedDecrement(&m_cRefs) == 0) 473 { 474 delete this; 475 return 0; 476 } 477 return m_cRefs; 478 } 479 480 /// @implemented 481 STDMETHODIMP CFunctionProviderBase::GetType(_Out_ GUID *guid) 482 { 483 *guid = m_guid; 484 return S_OK; 485 } 486 487 /// @implemented 488 STDMETHODIMP CFunctionProviderBase::GetDescription(_Out_ BSTR *desc) 489 { 490 *desc = ::SysAllocString(m_bstr); 491 return (*desc ? S_OK : E_OUTOFMEMORY); 492 } 493 494 /***********************************************************************/ 495 496 /// @implemented 497 CFunctionProvider::CFunctionProvider(_In_ TfClientId clientId) : CFunctionProviderBase(clientId) 498 { 499 Init(CLSID_CAImmLayer, L"MSCTFIME::Function Provider"); 500 } 501 502 /// @implemented 503 STDMETHODIMP 504 CFunctionProvider::GetFunction( 505 _In_ REFGUID guid, 506 _In_ REFIID riid, 507 _Out_ IUnknown **func) 508 { 509 *func = NULL; 510 511 if (IsEqualGUID(guid, GUID_NULL) && 512 IsEqualIID(riid, IID_IAImmFnDocFeed)) 513 { 514 *func = new(cicNoThrow) CFnDocFeed(); 515 if (*func) 516 return S_OK; 517 } 518 519 return E_NOINTERFACE; 520 } 521 522 /***********************************************************************/ 523 524 CFnDocFeed::CFnDocFeed() 525 { 526 m_cRefs = 1; 527 } 528 529 CFnDocFeed::~CFnDocFeed() 530 { 531 } 532 533 /// @implemented 534 STDMETHODIMP CFnDocFeed::QueryInterface(_In_ REFIID riid, _Out_ LPVOID* ppvObj) 535 { 536 static const QITAB c_tab[] = 537 { 538 QITABENT(CFnDocFeed, IAImmFnDocFeed), 539 { NULL } 540 }; 541 return ::QISearch(this, c_tab, riid, ppvObj); 542 } 543 544 /// @implemented 545 STDMETHODIMP_(ULONG) CFnDocFeed::AddRef() 546 { 547 return ::InterlockedIncrement(&m_cRefs); 548 } 549 550 /// @implemented 551 STDMETHODIMP_(ULONG) CFnDocFeed::Release() 552 { 553 if (::InterlockedDecrement(&m_cRefs) == 0) 554 { 555 delete this; 556 return 0; 557 } 558 return m_cRefs; 559 } 560 561 /// @implemented 562 STDMETHODIMP CFnDocFeed::DocFeed() 563 { 564 TLS *pTLS = TLS::GetTLS(); 565 if (!pTLS) 566 return E_OUTOFMEMORY; 567 568 HIMC hIMC = GetActiveContext(); 569 CicIMCLock imcLock(hIMC); 570 if (FAILED(imcLock.m_hr)) 571 return imcLock.m_hr; 572 573 CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext); 574 if (FAILED(imeContext.m_hr)) 575 return imeContext.m_hr; 576 CicInputContext *pCicIC = imeContext.get().m_pCicIC; 577 if (!pCicIC) 578 return E_FAIL; 579 580 UINT uCodePage = CP_ACP; 581 pTLS->m_pProfile->GetCodePageA(&uCodePage); 582 pCicIC->SetupDocFeedString(imcLock, uCodePage); 583 return S_OK; 584 } 585 586 /// @implemented 587 STDMETHODIMP CFnDocFeed::ClearDocFeedBuffer() 588 { 589 if (!TLS::GetTLS()) 590 return E_OUTOFMEMORY; 591 592 HIMC hIMC = GetActiveContext(); 593 CicIMCLock imcLock(hIMC); 594 if (FAILED(imcLock.m_hr)) 595 return imcLock.m_hr; 596 597 CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext); 598 if (FAILED(imeContext.m_hr)) 599 return imeContext.m_hr; 600 601 CicInputContext *pCicIC = imeContext.get().m_pCicIC; 602 if (!pCicIC) 603 return E_FAIL; 604 605 pCicIC->EscbClearDocFeedBuffer(imcLock, TRUE); 606 return S_OK; 607 } 608 609 /// @unimplemented 610 STDMETHODIMP CFnDocFeed::StartReconvert() 611 { 612 TLS *pTLS = TLS::GetTLS(); 613 if (!pTLS) 614 return E_OUTOFMEMORY; 615 auto *pThreadMgr = pTLS->m_pThreadMgr; 616 if (!pThreadMgr) 617 return E_OUTOFMEMORY; 618 619 HIMC hIMC = GetActiveContext(); 620 CicIMCLock imcLock(hIMC); 621 if (FAILED(imcLock.m_hr)) 622 return imcLock.m_hr; 623 624 CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext); 625 if (FAILED(imeContext.m_hr)) 626 return imeContext.m_hr; 627 CicInputContext *pCicIC = imeContext.get().m_pCicIC; 628 if (!pCicIC) 629 return E_FAIL; 630 631 UINT uCodePage = CP_ACP; 632 pTLS->m_pProfile->GetCodePageA(&uCodePage); 633 634 pCicIC->m_bReconverting = TRUE; 635 pCicIC->SetupReconvertString(imcLock, pThreadMgr, uCodePage, 0, 0); 636 pCicIC->EndReconvertString(imcLock); 637 pCicIC->m_bReconverting = FALSE; 638 return S_OK; 639 } 640 641 /// @implemented 642 STDMETHODIMP CFnDocFeed::StartUndoCompositionString() 643 { 644 TLS *pTLS = TLS::GetTLS(); 645 if (!pTLS) 646 return E_OUTOFMEMORY; 647 auto *pThreadMgr = pTLS->m_pThreadMgr; 648 if (!pThreadMgr) 649 return E_OUTOFMEMORY; 650 651 HIMC hIMC = GetActiveContext(); 652 CicIMCLock imcLock(hIMC); 653 if (FAILED(imcLock.m_hr)) 654 return imcLock.m_hr; 655 656 CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext); 657 if (FAILED(imeContext.m_hr)) 658 return imeContext.m_hr; 659 CicInputContext *pCicIC = imeContext.get().m_pCicIC; 660 if (!pCicIC) 661 return E_FAIL; 662 663 UINT uCodePage = CP_ACP; 664 pTLS->m_pProfile->GetCodePageA(&uCodePage); 665 666 pCicIC->SetupReconvertString(imcLock, pThreadMgr, uCodePage, 0, TRUE); 667 pCicIC->EndReconvertString(imcLock); 668 return S_OK; 669 } 670