xref: /reactos/dll/ime/msctfime/bridge.cpp (revision 14d3b53c)
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