1 /* 2 * PROJECT: ReactOS msctfime.ime 3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) 4 * PURPOSE: Bridge 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 /// @implemented 13 CicBridge::CicBridge() 14 { 15 m_bImmxInited = FALSE; 16 m_bUnknown1 = FALSE; 17 m_bDeactivating = FALSE; 18 m_bUnknown2 = FALSE; 19 m_pKeystrokeMgr = NULL; 20 m_pDocMgr = NULL; 21 m_pThreadMgrEventSink = NULL; 22 m_cliendId = 0; 23 m_cRefs = 1; 24 } 25 26 /// @implemented 27 STDMETHODIMP CicBridge::QueryInterface(REFIID riid, LPVOID* ppvObj) 28 { 29 *ppvObj = NULL; 30 31 if (!IsEqualIID(riid, IID_ITfSysHookSink)) 32 return E_NOINTERFACE; 33 34 *ppvObj = this; 35 AddRef(); 36 37 return S_OK; 38 } 39 40 /// @implemented 41 STDMETHODIMP_(ULONG) CicBridge::AddRef() 42 { 43 return ::InterlockedIncrement(&m_cRefs); 44 } 45 46 /// @implemented 47 STDMETHODIMP_(ULONG) CicBridge::Release() 48 { 49 if (::InterlockedDecrement(&m_cRefs) == 0) 50 { 51 delete this; 52 return 0; 53 } 54 return m_cRefs; 55 } 56 57 /// @implemented 58 CicBridge::~CicBridge() 59 { 60 TLS *pTLS = TLS::PeekTLS(); 61 if (!pTLS || !pTLS->m_pThreadMgr) 62 return; 63 64 if (SUCCEEDED(DeactivateIMMX(pTLS, pTLS->m_pThreadMgr))) 65 UnInitIMMX(pTLS); 66 } 67 68 void CicBridge::GetDocumentManager(_Inout_ CicIMCCLock<CTFIMECONTEXT>& imeContext) 69 { 70 CicInputContext *pCicIC = imeContext.get().m_pCicIC; 71 if (pCicIC) 72 { 73 m_pDocMgr = pCicIC->m_pDocumentMgr; 74 m_pDocMgr->AddRef(); 75 } 76 else 77 { 78 m_pDocMgr->Release(); 79 m_pDocMgr = NULL; 80 } 81 } 82 83 /// @unimplemented 84 HRESULT 85 CicBridge::CreateInputContext( 86 _Inout_ TLS *pTLS, 87 _In_ HIMC hIMC) 88 { 89 CicIMCLock imcLock(hIMC); 90 if (FAILED(imcLock.m_hr)) 91 return imcLock.m_hr; 92 93 if (!imcLock.get().hCtfImeContext) 94 { 95 HIMCC hCtfImeContext = ImmCreateIMCC(sizeof(CTFIMECONTEXT)); 96 if (!hCtfImeContext) 97 return E_OUTOFMEMORY; 98 imcLock.get().hCtfImeContext = hCtfImeContext; 99 } 100 101 CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext); 102 CicInputContext *pCicIC = imeContext.get().m_pCicIC; 103 if (!pCicIC) 104 { 105 pCicIC = new(cicNoThrow) CicInputContext(m_cliendId, &m_LibThread, hIMC); 106 if (!pCicIC) 107 { 108 imeContext.unlock(); 109 imcLock.unlock(); 110 DestroyInputContext(pTLS, hIMC); 111 return E_OUTOFMEMORY; 112 } 113 114 if (!pTLS->m_pThreadMgr) 115 { 116 pCicIC->Release(); 117 imeContext.unlock(); 118 imcLock.unlock(); 119 DestroyInputContext(pTLS, hIMC); 120 return E_NOINTERFACE; 121 } 122 123 imeContext.get().m_pCicIC = pCicIC; 124 } 125 126 HRESULT hr = pCicIC->CreateInputContext(pTLS->m_pThreadMgr, imcLock); 127 if (FAILED(hr)) 128 { 129 pCicIC->Release(); 130 imeContext.get().m_pCicIC = NULL; 131 } 132 else 133 { 134 if (imcLock.get().hWnd && imcLock.get().hWnd == ::GetFocus()) 135 { 136 GetDocumentManager(imeContext); 137 //FIXME 138 } 139 } 140 141 return E_NOTIMPL; 142 } 143 144 /// @implemented 145 HRESULT CicBridge::DestroyInputContext(TLS *pTLS, HIMC hIMC) 146 { 147 CicIMCLock imcLock(hIMC); 148 HRESULT hr = imcLock.m_hr; 149 if (FAILED(hr)) 150 return hr; 151 152 hr = E_FAIL; 153 CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext); 154 if (imeContext) 155 hr = imeContext.m_hr; 156 157 if (SUCCEEDED(hr) && !(imeContext.get().m_dwCicFlags & 1)) 158 { 159 imeContext.get().m_dwCicFlags |= 1; 160 161 CicInputContext *pCicIC = imeContext.get().m_pCicIC; 162 if (pCicIC) 163 { 164 imeContext.get().m_pCicIC = NULL; 165 hr = pCicIC->DestroyInputContext(); 166 pCicIC->Release(); 167 imeContext.get().m_pCicIC = NULL; 168 } 169 } 170 171 if (imcLock.get().hCtfImeContext) 172 { 173 ImmDestroyIMCC(imcLock.get().hCtfImeContext); 174 imcLock.get().hCtfImeContext = NULL; 175 hr = S_OK; 176 } 177 178 return hr; 179 } 180 181 /// @implemented 182 ITfContext * 183 CicBridge::GetInputContext(CicIMCCLock<CTFIMECONTEXT>& imeContext) 184 { 185 CicInputContext *pCicIC = imeContext.get().m_pCicIC; 186 if (!pCicIC) 187 return NULL; 188 return pCicIC->m_pContext; 189 } 190 191 /// @implemented 192 HRESULT CicBridge::OnSetOpenStatus( 193 TLS *pTLS, 194 ITfThreadMgr_P *pThreadMgr, 195 CicIMCLock& imcLock, 196 CicInputContext *pCicIC) 197 { 198 if (!imcLock.get().fOpen && imcLock.ValidCompositionString()) 199 pCicIC->EscbCompComplete(imcLock); 200 201 pTLS->m_bNowOpening = TRUE; 202 HRESULT hr = SetCompartmentDWORD(m_cliendId, pThreadMgr, 203 GUID_COMPARTMENT_KEYBOARD_OPENCLOSE, 204 imcLock.get().fOpen, FALSE); 205 pTLS->m_bNowOpening = FALSE; 206 return hr; 207 } 208 209 /// Selects the IME context. 210 /// @implemented 211 HRESULT 212 CicBridge::SelectEx( 213 _Inout_ TLS *pTLS, 214 _Inout_ ITfThreadMgr_P *pThreadMgr, 215 _In_ HIMC hIMC, 216 _In_ BOOL fSelect, 217 _In_ HKL hKL) 218 { 219 CicIMCLock imcLock(hIMC); 220 if (FAILED(imcLock.m_hr)) 221 return imcLock.m_hr; 222 223 CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext); 224 if (FAILED(imeContext.m_hr)) 225 return imeContext.m_hr; 226 227 CicInputContext *pCicIC = imeContext.get().m_pCicIC; 228 if (pCicIC) 229 pCicIC->m_bSelecting = TRUE; 230 231 if (fSelect) 232 { 233 if (pCicIC) 234 pCicIC->m_dwUnknown6[1] &= ~1; 235 if (imcLock.get().fOpen) 236 OnSetOpenStatus(pTLS, pThreadMgr, imcLock, pCicIC); 237 } 238 else 239 { 240 ITfContext *pContext = GetInputContext(imeContext); 241 pThreadMgr->RequestPostponedLock(pContext); 242 if (pCicIC) 243 pCicIC->m_bSelecting = FALSE; 244 if (pContext) 245 pContext->Release(); 246 } 247 248 return imeContext.m_hr; 249 } 250 251 /// Used in CicBridge::EnumCreateInputContextCallback and 252 /// CicBridge::EnumDestroyInputContextCallback. 253 typedef struct ENUM_CREATE_DESTROY_IC 254 { 255 TLS *m_pTLS; 256 CicBridge *m_pBridge; 257 } ENUM_CREATE_DESTROY_IC, *PENUM_CREATE_DESTROY_IC; 258 259 /// Creates input context for the current thread. 260 /// @implemented 261 BOOL CALLBACK CicBridge::EnumCreateInputContextCallback(HIMC hIMC, LPARAM lParam) 262 { 263 PENUM_CREATE_DESTROY_IC pData = (PENUM_CREATE_DESTROY_IC)lParam; 264 pData->m_pBridge->CreateInputContext(pData->m_pTLS, hIMC); 265 return TRUE; 266 } 267 268 /// Destroys input context for the current thread. 269 /// @implemented 270 BOOL CALLBACK CicBridge::EnumDestroyInputContextCallback(HIMC hIMC, LPARAM lParam) 271 { 272 PENUM_CREATE_DESTROY_IC pData = (PENUM_CREATE_DESTROY_IC)lParam; 273 pData->m_pBridge->DestroyInputContext(pData->m_pTLS, hIMC); 274 return TRUE; 275 } 276 277 /// @implemented 278 HRESULT 279 CicBridge::ActivateIMMX( 280 _Inout_ TLS *pTLS, 281 _Inout_ ITfThreadMgr_P *pThreadMgr) 282 { 283 HRESULT hr = pThreadMgr->ActivateEx(&m_cliendId, 1); 284 if (hr != S_OK) 285 { 286 m_cliendId = 0; 287 return E_FAIL; 288 } 289 290 if (m_cActivateLocks++ != 0) 291 return S_OK; 292 293 ITfSourceSingle *pSource = NULL; 294 hr = pThreadMgr->QueryInterface(IID_ITfSourceSingle, (void**)&pSource); 295 if (FAILED(hr)) 296 { 297 DeactivateIMMX(pTLS, pThreadMgr); 298 return hr; 299 } 300 301 CFunctionProvider *pProvider = new(cicNoThrow) CFunctionProvider(m_cliendId); 302 if (!pProvider) 303 { 304 hr = E_FAIL; 305 goto Finish; 306 } 307 308 pSource->AdviseSingleSink(m_cliendId, IID_ITfFunctionProvider, pProvider); 309 pProvider->Release(); 310 311 if (!m_pDocMgr) 312 { 313 hr = pThreadMgr->CreateDocumentMgr(&m_pDocMgr); 314 if (FAILED(hr)) 315 { 316 hr = E_FAIL; 317 goto Finish; 318 } 319 320 SetCompartmentDWORD(m_cliendId, m_pDocMgr, GUID_COMPARTMENT_CTFIME_DIMFLAGS, TRUE, FALSE); 321 } 322 323 pThreadMgr->SetSysHookSink(this); 324 325 hr = S_OK; 326 if (pTLS->m_bDestroyed) 327 { 328 ENUM_CREATE_DESTROY_IC Data = { pTLS, this }; 329 ImmEnumInputContext(0, CicBridge::EnumCreateInputContextCallback, (LPARAM)&Data); 330 } 331 332 Finish: 333 if (FAILED(hr)) 334 DeactivateIMMX(pTLS, pThreadMgr); 335 if (pSource) 336 pSource->Release(); 337 return hr; 338 } 339 340 /// @implemented 341 HRESULT 342 CicBridge::DeactivateIMMX( 343 _Inout_ TLS *pTLS, 344 _Inout_ ITfThreadMgr_P *pThreadMgr) 345 { 346 if (m_bDeactivating) 347 return TRUE; 348 349 m_bDeactivating = TRUE; 350 351 if (m_cliendId) 352 { 353 ENUM_CREATE_DESTROY_IC Data = { pTLS, this }; 354 ImmEnumInputContext(0, CicBridge::EnumDestroyInputContextCallback, (LPARAM)&Data); 355 pTLS->m_bDestroyed = TRUE; 356 357 ITfSourceSingle *pSource = NULL; 358 if (pThreadMgr->QueryInterface(IID_ITfSourceSingle, (void **)&pSource) == S_OK) 359 pSource->UnadviseSingleSink(m_cliendId, IID_ITfFunctionProvider); 360 361 m_cliendId = 0; 362 363 while (m_cActivateLocks > 0) 364 { 365 --m_cActivateLocks; 366 pThreadMgr->Deactivate(); 367 } 368 369 if (pSource) 370 pSource->Release(); 371 } 372 373 if (m_pDocMgr) 374 { 375 m_pDocMgr->Release(); 376 m_pDocMgr = NULL; 377 } 378 379 pThreadMgr->SetSysHookSink(NULL); 380 381 m_bDeactivating = FALSE; 382 383 return S_OK; 384 } 385 386 /// @implemented 387 HRESULT 388 CicBridge::InitIMMX(_Inout_ TLS *pTLS) 389 { 390 if (m_bImmxInited) 391 return S_OK; 392 393 HRESULT hr = S_OK; 394 if (!pTLS->m_pThreadMgr) 395 { 396 ITfThreadMgr *pThreadMgr = NULL; 397 hr = TF_CreateThreadMgr(&pThreadMgr); 398 if (FAILED(hr)) 399 return E_FAIL; 400 401 hr = pThreadMgr->QueryInterface(IID_ITfThreadMgr_P, (void **)&pTLS->m_pThreadMgr); 402 if (pThreadMgr) 403 pThreadMgr->Release(); 404 if (FAILED(hr)) 405 return E_FAIL; 406 } 407 408 if (!m_pThreadMgrEventSink) 409 { 410 m_pThreadMgrEventSink = 411 new(cicNoThrow) CThreadMgrEventSink(CThreadMgrEventSink::DIMCallback, NULL, NULL); 412 if (!m_pThreadMgrEventSink) 413 { 414 UnInitIMMX(pTLS); 415 return E_FAIL; 416 } 417 } 418 419 m_pThreadMgrEventSink->SetCallbackPV(m_pThreadMgrEventSink); 420 m_pThreadMgrEventSink->_Advise(pTLS->m_pThreadMgr); 421 422 if (!pTLS->m_pProfile) 423 { 424 pTLS->m_pProfile = new(cicNoThrow) CicProfile(); 425 if (!pTLS->m_pProfile) 426 return E_OUTOFMEMORY; 427 428 hr = pTLS->m_pProfile->InitProfileInstance(pTLS); 429 if (FAILED(hr)) 430 { 431 UnInitIMMX(pTLS); 432 return E_FAIL; 433 } 434 } 435 436 hr = pTLS->m_pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr_P, (void **)&m_pKeystrokeMgr); 437 if (FAILED(hr)) 438 { 439 UnInitIMMX(pTLS); 440 return E_FAIL; 441 } 442 443 hr = InitDisplayAttrbuteLib(&m_LibThread); 444 if (FAILED(hr)) 445 { 446 UnInitIMMX(pTLS); 447 return E_FAIL; 448 } 449 450 m_bImmxInited = TRUE; 451 return S_OK; 452 } 453 454 /// @implemented 455 BOOL CicBridge::UnInitIMMX(_Inout_ TLS *pTLS) 456 { 457 UninitDisplayAttrbuteLib(&m_LibThread); 458 TFUninitLib_Thread(&m_LibThread); 459 460 if (m_pKeystrokeMgr) 461 { 462 m_pKeystrokeMgr->Release(); 463 m_pKeystrokeMgr = NULL; 464 } 465 466 if (pTLS->m_pProfile) 467 { 468 pTLS->m_pProfile->Release(); 469 pTLS->m_pProfile = NULL; 470 } 471 472 if (m_pThreadMgrEventSink) 473 { 474 m_pThreadMgrEventSink->_Unadvise(); 475 m_pThreadMgrEventSink->Release(); 476 m_pThreadMgrEventSink = NULL; 477 } 478 479 if (pTLS->m_pThreadMgr) 480 { 481 pTLS->m_pThreadMgr->Release(); 482 pTLS->m_pThreadMgr = NULL; 483 } 484 485 m_bImmxInited = FALSE; 486 return TRUE; 487 } 488 489 /// @implemented 490 STDMETHODIMP CicBridge::OnPreFocusDIM(HWND hwnd) 491 { 492 return S_OK; 493 } 494 495 /// @unimplemented 496 STDMETHODIMP CicBridge::OnSysKeyboardProc(UINT, LONG) 497 { 498 return E_NOTIMPL; 499 } 500 501 /// @implemented 502 STDMETHODIMP CicBridge::OnSysShellProc(INT, UINT, LONG) 503 { 504 return S_OK; 505 } 506 507 /// @implemented 508 void 509 CicBridge::PostTransMsg( 510 _In_ HWND hWnd, 511 _In_ INT cTransMsgs, 512 _In_ const TRANSMSG *pTransMsgs) 513 { 514 for (INT i = 0; i < cTransMsgs; ++i, ++pTransMsgs) 515 { 516 ::PostMessageW(hWnd, pTransMsgs->message, pTransMsgs->wParam, pTransMsgs->lParam); 517 } 518 } 519 520 /// @implemented 521 HRESULT 522 CicBridge::ConfigureGeneral( 523 _Inout_ TLS* pTLS, 524 _In_ ITfThreadMgr *pThreadMgr, 525 _In_ HKL hKL, 526 _In_ HWND hWnd) 527 { 528 CicProfile *pProfile = pTLS->m_pProfile; 529 if (!pProfile) 530 return E_OUTOFMEMORY; 531 532 TF_LANGUAGEPROFILE profile; 533 HRESULT hr = pProfile->GetActiveLanguageProfile(hKL, GUID_TFCAT_TIP_KEYBOARD, &profile); 534 if (FAILED(hr)) 535 return hr; 536 537 ITfFunctionProvider *pProvider = NULL; 538 hr = pThreadMgr->GetFunctionProvider(profile.clsid, &pProvider); 539 if (FAILED(hr)) 540 return hr; 541 542 ITfFnConfigure *pFnConfigure = NULL; 543 hr = pProvider->GetFunction(GUID_NULL, IID_ITfFnConfigure, (IUnknown**)&pFnConfigure); 544 if (FAILED(hr)) 545 { 546 pProvider->Release(); 547 return hr; 548 } 549 550 hr = pFnConfigure->Show(hWnd, profile.langid, profile.guidProfile); 551 552 pFnConfigure->Release(); 553 pProvider->Release(); 554 return hr; 555 } 556 557 /// @implemented 558 HRESULT 559 CicBridge::ConfigureRegisterWord( 560 _Inout_ TLS* pTLS, 561 _In_ ITfThreadMgr *pThreadMgr, 562 _In_ HKL hKL, 563 _In_ HWND hWnd, 564 _Inout_opt_ LPVOID lpData) 565 { 566 CicProfile *pProfile = pTLS->m_pProfile; 567 if (!pProfile) 568 return E_OUTOFMEMORY; 569 570 TF_LANGUAGEPROFILE profile; 571 HRESULT hr = pProfile->GetActiveLanguageProfile(hKL, GUID_TFCAT_TIP_KEYBOARD, &profile); 572 if (FAILED(hr)) 573 return hr; 574 575 ITfFunctionProvider *pProvider = NULL; 576 hr = pThreadMgr->GetFunctionProvider(profile.clsid, &pProvider); 577 if (FAILED(hr)) 578 return hr; 579 580 ITfFnConfigureRegisterWord *pFunction = NULL; 581 hr = pProvider->GetFunction(GUID_NULL, IID_ITfFnConfigureRegisterWord, (IUnknown**)&pFunction); 582 if (FAILED(hr)) 583 { 584 pProvider->Release(); 585 return hr; 586 } 587 588 REGISTERWORDW* pRegWord = (REGISTERWORDW*)lpData; 589 if (pRegWord) 590 { 591 if (pRegWord->lpWord) 592 { 593 hr = E_OUTOFMEMORY; 594 BSTR bstrWord = SysAllocString(pRegWord->lpWord); 595 if (bstrWord) 596 { 597 hr = pFunction->Show(hWnd, profile.langid, profile.guidProfile, bstrWord); 598 SysFreeString(bstrWord); 599 } 600 } 601 else 602 { 603 hr = pFunction->Show(hWnd, profile.langid, profile.guidProfile, NULL); 604 } 605 } 606 607 pProvider->Release(); 608 pFunction->Release(); 609 return hr; 610 } 611