xref: /reactos/dll/ime/msctfime/bridge.cpp (revision c5e64563)
1 /*
2  * PROJECT:     ReactOS msctfime.ime
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     The bridge 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 /// @implemented
CicBridge()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
QueryInterface(REFIID riid,LPVOID * ppvObj)27 STDMETHODIMP CicBridge::QueryInterface(REFIID riid, LPVOID* ppvObj)
28 {
29     static const QITAB c_tab[] =
30     {
31         QITABENT(CicBridge, ITfSysHookSink),
32         { NULL }
33     };
34     return ::QISearch(this, c_tab, riid, ppvObj);
35 }
36 
37 /// @implemented
STDMETHODIMP_(ULONG)38 STDMETHODIMP_(ULONG) CicBridge::AddRef()
39 {
40     return ::InterlockedIncrement(&m_cRefs);
41 }
42 
43 /// @implemented
STDMETHODIMP_(ULONG)44 STDMETHODIMP_(ULONG) CicBridge::Release()
45 {
46     if (::InterlockedDecrement(&m_cRefs) == 0)
47     {
48         delete this;
49         return 0;
50     }
51     return m_cRefs;
52 }
53 
54 /// @implemented
~CicBridge()55 CicBridge::~CicBridge()
56 {
57     TLS *pTLS = TLS::PeekTLS();
58     if (!pTLS || !pTLS->m_pThreadMgr)
59         return;
60 
61     if (SUCCEEDED(DeactivateIMMX(pTLS, pTLS->m_pThreadMgr)))
62         UnInitIMMX(pTLS);
63 }
64 
65 /// @implemented
66 ITfDocumentMgr*
GetDocumentManager(_Inout_ CicIMCCLock<CTFIMECONTEXT> & imeContext)67 CicBridge::GetDocumentManager(_Inout_ CicIMCCLock<CTFIMECONTEXT>& imeContext)
68 {
69     CicInputContext *pCicIC = imeContext.get().m_pCicIC;
70     if (!pCicIC)
71         return NULL;
72 
73     pCicIC->m_pDocumentMgr->AddRef();
74     return pCicIC->m_pDocumentMgr;
75 }
76 
77 /// @implemented
78 HRESULT
CreateInputContext(_Inout_ TLS * pTLS,_In_ HIMC hIMC)79 CicBridge::CreateInputContext(
80     _Inout_ TLS *pTLS,
81     _In_ HIMC hIMC)
82 {
83     CicIMCLock imcLock(hIMC);
84     if (FAILED(imcLock.m_hr))
85         return imcLock.m_hr;
86 
87     if (!imcLock.get().hCtfImeContext)
88     {
89         HIMCC hCtfImeContext = ImmCreateIMCC(sizeof(CTFIMECONTEXT));
90         if (!hCtfImeContext)
91             return E_OUTOFMEMORY;
92         imcLock.get().hCtfImeContext = hCtfImeContext;
93     }
94 
95     CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext);
96     CicInputContext *pCicIC = imeContext.get().m_pCicIC;
97     if (pCicIC)
98         return S_OK;
99 
100     pCicIC = new(cicNoThrow) CicInputContext(m_cliendId, &m_LibThread, hIMC);
101     if (!pCicIC)
102     {
103         imeContext.unlock();
104         imcLock.unlock();
105         DestroyInputContext(pTLS, hIMC);
106         return E_OUTOFMEMORY;
107     }
108 
109     if (!pTLS->m_pThreadMgr)
110     {
111         pCicIC->Release();
112         imeContext.unlock();
113         imcLock.unlock();
114         DestroyInputContext(pTLS, hIMC);
115         return E_NOINTERFACE;
116     }
117 
118     imeContext.get().m_pCicIC = pCicIC;
119 
120     HRESULT hr = pCicIC->CreateInputContext(pTLS->m_pThreadMgr, imcLock);
121     if (FAILED(hr))
122     {
123         pCicIC->Release();
124         imeContext.get().m_pCicIC = NULL;
125         return hr;
126     }
127 
128     HWND hWnd = imcLock.get().hWnd;
129     if (hWnd && hWnd == ::GetFocus())
130     {
131         ITfDocumentMgr *pDocMgr = GetDocumentManager(imeContext);
132         if (pDocMgr)
133         {
134             SetAssociate(pTLS, hWnd, hIMC, pTLS->m_pThreadMgr, pDocMgr);
135             pDocMgr->Release();
136         }
137     }
138 
139     return hr;
140 }
141 
142 /// @implemented
DestroyInputContext(TLS * pTLS,HIMC hIMC)143 HRESULT CicBridge::DestroyInputContext(TLS *pTLS, HIMC hIMC)
144 {
145     CicIMCLock imcLock(hIMC);
146     HRESULT hr = imcLock.m_hr;
147     if (FAILED(hr))
148         return hr;
149 
150     hr = E_FAIL;
151     CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext);
152     if (imeContext)
153         hr = imeContext.m_hr;
154 
155     if (SUCCEEDED(hr) && !(imeContext.get().m_dwCicFlags & 1))
156     {
157         imeContext.get().m_dwCicFlags |= 1;
158 
159         CicInputContext *pCicIC = imeContext.get().m_pCicIC;
160         if (pCicIC)
161         {
162             imeContext.get().m_pCicIC = NULL;
163             hr = pCicIC->DestroyInputContext();
164             pCicIC->Release();
165             imeContext.get().m_pCicIC = NULL;
166         }
167     }
168 
169     if (imcLock.get().hCtfImeContext)
170     {
171         ImmDestroyIMCC(imcLock.get().hCtfImeContext);
172         imcLock.get().hCtfImeContext = NULL;
173         hr = S_OK;
174     }
175 
176     return hr;
177 }
178 
179 /// @implemented
180 ITfContext *
GetInputContext(CicIMCCLock<CTFIMECONTEXT> & imeContext)181 CicBridge::GetInputContext(CicIMCCLock<CTFIMECONTEXT>& imeContext)
182 {
183     CicInputContext *pCicIC = imeContext.get().m_pCicIC;
184     if (!pCicIC)
185         return NULL;
186     return pCicIC->m_pContext;
187 }
188 
189 /// @implemented
OnSetOpenStatus(TLS * pTLS,ITfThreadMgr_P * pThreadMgr,CicIMCLock & imcLock,CicInputContext * pCicIC)190 HRESULT CicBridge::OnSetOpenStatus(
191     TLS *pTLS,
192     ITfThreadMgr_P *pThreadMgr,
193     CicIMCLock& imcLock,
194     CicInputContext *pCicIC)
195 {
196     if (!imcLock.get().fOpen && imcLock.ValidCompositionString())
197         pCicIC->EscbCompComplete(imcLock);
198 
199     pTLS->m_bNowOpening = TRUE;
200     HRESULT hr = SetCompartmentDWORD(m_cliendId, pThreadMgr,
201                                      GUID_COMPARTMENT_KEYBOARD_OPENCLOSE,
202                                      imcLock.get().fOpen, FALSE);
203     pTLS->m_bNowOpening = FALSE;
204     return hr;
205 }
206 
207 /// Selects the IME context.
208 /// @implemented
209 HRESULT
SelectEx(_Inout_ TLS * pTLS,_Inout_ ITfThreadMgr_P * pThreadMgr,_In_ HIMC hIMC,_In_ BOOL fSelect,_In_ HKL hKL)210 CicBridge::SelectEx(
211     _Inout_ TLS *pTLS,
212     _Inout_ ITfThreadMgr_P *pThreadMgr,
213     _In_ HIMC hIMC,
214     _In_ BOOL fSelect,
215     _In_ HKL hKL)
216 {
217     CicIMCLock imcLock(hIMC);
218     if (FAILED(imcLock.m_hr))
219         return imcLock.m_hr;
220 
221     CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext);
222     if (FAILED(imeContext.m_hr))
223         return imeContext.m_hr;
224 
225     CicInputContext *pCicIC = imeContext.get().m_pCicIC;
226     if (pCicIC)
227         pCicIC->m_bSelecting = TRUE;
228 
229     if (fSelect)
230     {
231         if (pCicIC)
232             pCicIC->m_bCandidateOpen = FALSE;
233         if (imcLock.get().fOpen)
234             OnSetOpenStatus(pTLS, pThreadMgr, imcLock, pCicIC);
235     }
236     else
237     {
238         ITfContext *pContext = GetInputContext(imeContext);
239         pThreadMgr->RequestPostponedLock(pContext);
240         if (pCicIC)
241             pCicIC->m_bSelecting = FALSE;
242         if (pContext)
243             pContext->Release();
244     }
245 
246     return imeContext.m_hr;
247 }
248 
249 /// Used in CicBridge::EnumCreateInputContextCallback and
250 /// CicBridge::EnumDestroyInputContextCallback.
251 typedef struct ENUM_CREATE_DESTROY_IC
252 {
253     TLS *m_pTLS;
254     CicBridge *m_pBridge;
255 } ENUM_CREATE_DESTROY_IC, *PENUM_CREATE_DESTROY_IC;
256 
257 /// Creates input context for the current thread.
258 /// @implemented
EnumCreateInputContextCallback(HIMC hIMC,LPARAM lParam)259 BOOL CALLBACK CicBridge::EnumCreateInputContextCallback(HIMC hIMC, LPARAM lParam)
260 {
261     PENUM_CREATE_DESTROY_IC pData = (PENUM_CREATE_DESTROY_IC)lParam;
262     pData->m_pBridge->CreateInputContext(pData->m_pTLS, hIMC);
263     return TRUE;
264 }
265 
266 /// Destroys input context for the current thread.
267 /// @implemented
EnumDestroyInputContextCallback(HIMC hIMC,LPARAM lParam)268 BOOL CALLBACK CicBridge::EnumDestroyInputContextCallback(HIMC hIMC, LPARAM lParam)
269 {
270     PENUM_CREATE_DESTROY_IC pData = (PENUM_CREATE_DESTROY_IC)lParam;
271     pData->m_pBridge->DestroyInputContext(pData->m_pTLS, hIMC);
272     return TRUE;
273 }
274 
275 /// @implemented
276 HRESULT
ActivateIMMX(_Inout_ TLS * pTLS,_Inout_ ITfThreadMgr_P * pThreadMgr)277 CicBridge::ActivateIMMX(
278     _Inout_ TLS *pTLS,
279     _Inout_ ITfThreadMgr_P *pThreadMgr)
280 {
281     HRESULT hr = pThreadMgr->ActivateEx(&m_cliendId, 1);
282     if (hr != S_OK)
283     {
284         m_cliendId = 0;
285         return E_FAIL;
286     }
287 
288     if (m_cActivateLocks++ != 0)
289         return S_OK;
290 
291     ITfSourceSingle *pSource = NULL;
292     hr = pThreadMgr->QueryInterface(IID_ITfSourceSingle, (void**)&pSource);
293     if (FAILED(hr))
294     {
295         DeactivateIMMX(pTLS, pThreadMgr);
296         return hr;
297     }
298 
299     CFunctionProvider *pProvider = new(cicNoThrow) CFunctionProvider(m_cliendId);
300     if (!pProvider)
301     {
302         hr = E_FAIL;
303         goto Finish;
304     }
305 
306     pSource->AdviseSingleSink(m_cliendId, IID_ITfFunctionProvider, pProvider);
307     pProvider->Release();
308 
309     if (!m_pDocMgr)
310     {
311         hr = pThreadMgr->CreateDocumentMgr(&m_pDocMgr);
312         if (FAILED(hr))
313         {
314             hr = E_FAIL;
315             goto Finish;
316         }
317 
318         SetCompartmentDWORD(m_cliendId, m_pDocMgr, GUID_COMPARTMENT_CTFIME_DIMFLAGS, TRUE, FALSE);
319     }
320 
321     pThreadMgr->SetSysHookSink(this);
322 
323     hr = S_OK;
324     if (pTLS->m_bDestroyed)
325     {
326         ENUM_CREATE_DESTROY_IC Data = { pTLS, this };
327         ImmEnumInputContext(0, CicBridge::EnumCreateInputContextCallback, (LPARAM)&Data);
328     }
329 
330 Finish:
331     if (FAILED(hr))
332         DeactivateIMMX(pTLS, pThreadMgr);
333     if (pSource)
334         pSource->Release();
335     return hr;
336 }
337 
338 /// @implemented
339 HRESULT
DeactivateIMMX(_Inout_ TLS * pTLS,_Inout_ ITfThreadMgr_P * pThreadMgr)340 CicBridge::DeactivateIMMX(
341     _Inout_ TLS *pTLS,
342     _Inout_ ITfThreadMgr_P *pThreadMgr)
343 {
344     if (m_bDeactivating)
345         return TRUE;
346 
347     m_bDeactivating = TRUE;
348 
349     if (m_cliendId)
350     {
351         ENUM_CREATE_DESTROY_IC Data = { pTLS, this };
352         ImmEnumInputContext(0, CicBridge::EnumDestroyInputContextCallback, (LPARAM)&Data);
353         pTLS->m_bDestroyed = TRUE;
354 
355         ITfSourceSingle *pSource = NULL;
356         if (pThreadMgr->QueryInterface(IID_ITfSourceSingle, (void **)&pSource) == S_OK)
357             pSource->UnadviseSingleSink(m_cliendId, IID_ITfFunctionProvider);
358 
359         m_cliendId = 0;
360 
361         while (m_cActivateLocks > 0)
362         {
363             --m_cActivateLocks;
364             pThreadMgr->Deactivate();
365         }
366 
367         if (pSource)
368             pSource->Release();
369     }
370 
371     if (m_pDocMgr)
372     {
373         m_pDocMgr->Release();
374         m_pDocMgr = NULL;
375     }
376 
377     pThreadMgr->SetSysHookSink(NULL);
378 
379     m_bDeactivating = FALSE;
380 
381     return S_OK;
382 }
383 
384 /// @implemented
385 HRESULT
InitIMMX(_Inout_ TLS * pTLS)386 CicBridge::InitIMMX(_Inout_ TLS *pTLS)
387 {
388     if (m_bImmxInited)
389         return S_OK;
390 
391     HRESULT hr = S_OK;
392     if (!pTLS->m_pThreadMgr)
393     {
394         ITfThreadMgr *pThreadMgr = NULL;
395         hr = TF_CreateThreadMgr(&pThreadMgr);
396         if (FAILED(hr))
397             return E_FAIL;
398 
399         hr = pThreadMgr->QueryInterface(IID_ITfThreadMgr_P, (void **)&pTLS->m_pThreadMgr);
400         if (pThreadMgr)
401             pThreadMgr->Release();
402         if (FAILED(hr))
403             return E_FAIL;
404     }
405 
406     if (!m_pThreadMgrEventSink)
407     {
408         m_pThreadMgrEventSink =
409             new(cicNoThrow) CThreadMgrEventSink(CThreadMgrEventSink::DIMCallback, NULL, NULL);
410         if (!m_pThreadMgrEventSink)
411         {
412             UnInitIMMX(pTLS);
413             return E_FAIL;
414         }
415     }
416 
417     m_pThreadMgrEventSink->SetCallbackPV(m_pThreadMgrEventSink);
418     m_pThreadMgrEventSink->_Advise(pTLS->m_pThreadMgr);
419 
420     if (!pTLS->m_pProfile)
421     {
422         pTLS->m_pProfile = new(cicNoThrow) CicProfile();
423         if (!pTLS->m_pProfile)
424             return E_OUTOFMEMORY;
425 
426         hr = pTLS->m_pProfile->InitProfileInstance(pTLS);
427         if (FAILED(hr))
428         {
429             UnInitIMMX(pTLS);
430             return E_FAIL;
431         }
432     }
433 
434     hr = pTLS->m_pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr_P, (void **)&m_pKeystrokeMgr);
435     if (FAILED(hr))
436     {
437         UnInitIMMX(pTLS);
438         return E_FAIL;
439     }
440 
441     hr = InitDisplayAttrbuteLib(&m_LibThread);
442     if (FAILED(hr))
443     {
444         UnInitIMMX(pTLS);
445         return E_FAIL;
446     }
447 
448     m_bImmxInited = TRUE;
449     return S_OK;
450 }
451 
452 /// @implemented
UnInitIMMX(_Inout_ TLS * pTLS)453 BOOL CicBridge::UnInitIMMX(_Inout_ TLS *pTLS)
454 {
455     UninitDisplayAttrbuteLib(&m_LibThread);
456     TFUninitLib_Thread(&m_LibThread);
457 
458     if (m_pKeystrokeMgr)
459     {
460         m_pKeystrokeMgr->Release();
461         m_pKeystrokeMgr = NULL;
462     }
463 
464     if (pTLS->m_pProfile)
465     {
466         pTLS->m_pProfile->Release();
467         pTLS->m_pProfile = NULL;
468     }
469 
470     if (m_pThreadMgrEventSink)
471     {
472         m_pThreadMgrEventSink->_Unadvise();
473         m_pThreadMgrEventSink->Release();
474         m_pThreadMgrEventSink = NULL;
475     }
476 
477     if (pTLS->m_pThreadMgr)
478     {
479         pTLS->m_pThreadMgr->Release();
480         pTLS->m_pThreadMgr = NULL;
481     }
482 
483     m_bImmxInited = FALSE;
484     return TRUE;
485 }
486 
487 /// @implemented
OnPreFocusDIM(HWND hwnd)488 STDMETHODIMP CicBridge::OnPreFocusDIM(HWND hwnd)
489 {
490     return S_OK;
491 }
492 
493 /// @unimplemented
OnSysKeyboardProc(UINT,LONG)494 STDMETHODIMP CicBridge::OnSysKeyboardProc(UINT, LONG)
495 {
496     return E_NOTIMPL;
497 }
498 
499 /// @implemented
OnSysShellProc(INT,UINT,LONG)500 STDMETHODIMP CicBridge::OnSysShellProc(INT, UINT, LONG)
501 {
502     return S_OK;
503 }
504 
505 /// @implemented
506 void
PostTransMsg(_In_ HWND hWnd,_In_ INT cTransMsgs,_In_ const TRANSMSG * pTransMsgs)507 CicBridge::PostTransMsg(
508     _In_ HWND hWnd,
509     _In_ INT cTransMsgs,
510     _In_ const TRANSMSG *pTransMsgs)
511 {
512     for (INT i = 0; i < cTransMsgs; ++i, ++pTransMsgs)
513     {
514         ::PostMessageW(hWnd, pTransMsgs->message, pTransMsgs->wParam, pTransMsgs->lParam);
515     }
516 }
517 
518 /// @implemented
519 HRESULT
ConfigureGeneral(_Inout_ TLS * pTLS,_In_ ITfThreadMgr * pThreadMgr,_In_ HKL hKL,_In_ HWND hWnd)520 CicBridge::ConfigureGeneral(
521     _Inout_ TLS* pTLS,
522     _In_ ITfThreadMgr *pThreadMgr,
523     _In_ HKL hKL,
524     _In_ HWND hWnd)
525 {
526     CicProfile *pProfile = pTLS->m_pProfile;
527     if (!pProfile)
528         return E_OUTOFMEMORY;
529 
530     TF_LANGUAGEPROFILE profile;
531     HRESULT hr = pProfile->GetActiveLanguageProfile(hKL, GUID_TFCAT_TIP_KEYBOARD, &profile);
532     if (FAILED(hr))
533         return hr;
534 
535     ITfFunctionProvider *pProvider = NULL;
536     hr = pThreadMgr->GetFunctionProvider(profile.clsid, &pProvider);
537     if (FAILED(hr))
538         return hr;
539 
540     ITfFnConfigure *pFnConfigure = NULL;
541     hr = pProvider->GetFunction(GUID_NULL, IID_ITfFnConfigure, (IUnknown**)&pFnConfigure);
542     if (FAILED(hr))
543     {
544         pProvider->Release();
545         return hr;
546     }
547 
548     hr = pFnConfigure->Show(hWnd, profile.langid, profile.guidProfile);
549 
550     pFnConfigure->Release();
551     pProvider->Release();
552     return hr;
553 }
554 
555 /// @implemented
556 HRESULT
ConfigureRegisterWord(_Inout_ TLS * pTLS,_In_ ITfThreadMgr * pThreadMgr,_In_ HKL hKL,_In_ HWND hWnd,_Inout_opt_ LPVOID lpData)557 CicBridge::ConfigureRegisterWord(
558     _Inout_ TLS* pTLS,
559     _In_ ITfThreadMgr *pThreadMgr,
560     _In_ HKL hKL,
561     _In_ HWND hWnd,
562     _Inout_opt_ LPVOID lpData)
563 {
564     CicProfile *pProfile = pTLS->m_pProfile;
565     if (!pProfile)
566         return E_OUTOFMEMORY;
567 
568     TF_LANGUAGEPROFILE profile;
569     HRESULT hr = pProfile->GetActiveLanguageProfile(hKL, GUID_TFCAT_TIP_KEYBOARD, &profile);
570     if (FAILED(hr))
571         return hr;
572 
573     ITfFunctionProvider *pProvider = NULL;
574     hr = pThreadMgr->GetFunctionProvider(profile.clsid, &pProvider);
575     if (FAILED(hr))
576         return hr;
577 
578     ITfFnConfigureRegisterWord *pFunction = NULL;
579     hr = pProvider->GetFunction(GUID_NULL, IID_ITfFnConfigureRegisterWord, (IUnknown**)&pFunction);
580     if (FAILED(hr))
581     {
582         pProvider->Release();
583         return hr;
584     }
585 
586     REGISTERWORDW* pRegWord = (REGISTERWORDW*)lpData;
587     if (pRegWord)
588     {
589         if (pRegWord->lpWord)
590         {
591             hr = E_OUTOFMEMORY;
592             BSTR bstrWord = SysAllocString(pRegWord->lpWord);
593             if (bstrWord)
594             {
595                 hr = pFunction->Show(hWnd, profile.langid, profile.guidProfile, bstrWord);
596                 SysFreeString(bstrWord);
597             }
598         }
599         else
600         {
601             hr = pFunction->Show(hWnd, profile.langid, profile.guidProfile, NULL);
602         }
603     }
604 
605     pProvider->Release();
606     pFunction->Release();
607     return hr;
608 }
609 
610 /// @unimplemented
SetAssociate(TLS * pTLS,HWND hWnd,HIMC hIMC,ITfThreadMgr_P * pThreadMgr,ITfDocumentMgr * pDocMgr)611 void CicBridge::SetAssociate(
612     TLS *pTLS,
613     HWND hWnd,
614     HIMC hIMC,
615     ITfThreadMgr_P *pThreadMgr,
616     ITfDocumentMgr *pDocMgr)
617 {
618     //FIXME
619 }
620 
621 HRESULT
SetActiveContextAlways(TLS * pTLS,HIMC hIMC,BOOL fActive,HWND hWnd,HKL hKL)622 CicBridge::SetActiveContextAlways(TLS *pTLS, HIMC hIMC, BOOL fActive, HWND hWnd, HKL hKL)
623 {
624     auto pThreadMgr = pTLS->m_pThreadMgr;
625     if (!pThreadMgr)
626         return E_OUTOFMEMORY;
627 
628     if (fActive)
629     {
630         if (!hIMC)
631         {
632             SetAssociate(pTLS, hWnd, hIMC, pThreadMgr, m_pDocMgr);
633             return S_OK;
634         }
635 
636         CicIMCLock imcLock(hIMC);
637         if (FAILED(imcLock.m_hr))
638             return imcLock.m_hr;
639 
640         CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext);
641         if (FAILED(imeContext.m_hr))
642             return imeContext.m_hr;
643 
644         if (hIMC == ::ImmGetContext(hWnd))
645         {
646             ITfDocumentMgr *pDocMgr = GetDocumentManager(imeContext);
647             if (pDocMgr)
648             {
649                 SetAssociate(pTLS, imcLock.get().hWnd, hIMC, pThreadMgr, pDocMgr);
650                 pDocMgr->Release();
651             }
652         }
653 
654         return S_OK;
655     }
656 
657     if (hIMC && !IsEALang(LOWORD(hKL)))
658     {
659         CicIMCLock imcLock(hIMC);
660         if (FAILED(imcLock.m_hr))
661             return imcLock.m_hr;
662 
663         CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext);
664         if (FAILED(imeContext.m_hr))
665             return imeContext.m_hr;
666 
667         CicInputContext *pCicIC = imeContext.get().m_pCicIC;
668         if (!pCicIC->m_dwUnknown6_5[2] && !pCicIC->m_dwUnknown6_5[3])
669             ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
670     }
671 
672     if (!hIMC || (::GetFocus() != hWnd) || (hIMC != ::ImmGetContext(hWnd)))
673         SetAssociate(pTLS, hWnd, hIMC, pThreadMgr, m_pDocMgr);
674 
675     return S_OK;
676 }
677 
678 /// @unimplemented
679 BOOL
DoOpenCandidateHanja(ITfThreadMgr_P * pThreadMgr,CicIMCLock & imcLock,CicInputContext * pCicIC)680 CicBridge::DoOpenCandidateHanja(
681     ITfThreadMgr_P *pThreadMgr,
682     CicIMCLock& imcLock,
683     CicInputContext *pCicIC)
684 {
685     return FALSE;
686 }
687 
688 /// @unimplemented
689 HRESULT
OnSetConversionSentenceMode(ITfThreadMgr_P * pThreadMgr,CicIMCLock & imcLock,CicInputContext * pCicIC,DWORD dwValue,LANGID LangID)690 CicBridge::OnSetConversionSentenceMode(
691     ITfThreadMgr_P *pThreadMgr,
692     CicIMCLock& imcLock,
693     CicInputContext *pCicIC,
694     DWORD dwValue,
695     LANGID LangID)
696 {
697     return E_NOTIMPL;
698 }
699 
700 /// @implemented
Notify(TLS * pTLS,ITfThreadMgr_P * pThreadMgr,HIMC hIMC,DWORD dwAction,DWORD dwIndex,DWORD_PTR dwValue)701 HRESULT CicBridge::Notify(
702     TLS *pTLS,
703     ITfThreadMgr_P *pThreadMgr,
704     HIMC hIMC,
705     DWORD dwAction,
706     DWORD dwIndex,
707     DWORD_PTR dwValue)
708 {
709     CicIMCLock imcLock(hIMC);
710     if (FAILED(imcLock.m_hr))
711         return imcLock.m_hr;
712 
713     CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext);
714     if (FAILED(imeContext.m_hr))
715         return imeContext.m_hr;
716 
717     CicInputContext *pCicIC = imeContext.get().m_pCicIC;
718     if (!pCicIC)
719         return E_OUTOFMEMORY;
720 
721     CicProfile *pProfile = pTLS->m_pProfile;
722     if (!pProfile)
723         return E_OUTOFMEMORY;
724 
725     LANGID LangID;
726     pProfile->GetLangId(&LangID);
727 
728     switch (dwAction)
729     {
730         case NI_OPENCANDIDATE:
731             if (PRIMARYLANGID(LangID) == LANG_KOREAN)
732             {
733                 if (DoOpenCandidateHanja(pThreadMgr, imcLock, pCicIC))
734                     return S_OK;
735                 return E_FAIL;
736             }
737             return E_NOTIMPL;
738 
739         case NI_COMPOSITIONSTR:
740             switch (dwIndex)
741             {
742                 case CPS_COMPLETE:
743                     pCicIC->EscbCompComplete(imcLock);
744                     break;
745 
746                 case CPS_CONVERT:
747                 case CPS_REVERT:
748                     return E_NOTIMPL;
749 
750                 case CPS_CANCEL:
751                     pCicIC->EscbCompCancel(imcLock);
752                     break;
753 
754                 default:
755                     return E_FAIL;
756             }
757             return S_OK;
758 
759         case NI_CONTEXTUPDATED:
760             switch (dwValue)
761             {
762                 case IMC_SETCONVERSIONMODE:
763                 case IMC_SETSENTENCEMODE:
764                     return OnSetConversionSentenceMode(pThreadMgr, imcLock, pCicIC, dwValue, LangID);
765 
766                 case IMC_SETOPENSTATUS:
767                     return OnSetOpenStatus(pTLS, pThreadMgr, imcLock, pCicIC);
768 
769                 case IMC_SETCANDIDATEPOS:
770                     return pCicIC->OnSetCandidatePos(pTLS, imcLock);
771 
772                 case IMC_SETCOMPOSITIONFONT:
773                 case IMC_SETCOMPOSITIONWINDOW:
774                     return E_NOTIMPL;
775 
776                 default:
777                     return E_FAIL;
778             }
779             break;
780 
781         default:
782             return E_NOTIMPL;
783     }
784 }
785 
786 /// @unimplemented
ProcessKey(TLS * pTLS,ITfThreadMgr_P * pThreadMgr,HIMC hIMC,WPARAM wParam,LPARAM lParam,CONST LPBYTE lpbKeyState,INT * pnUnknown60)787 BOOL CicBridge::ProcessKey(
788     TLS *pTLS,
789     ITfThreadMgr_P *pThreadMgr,
790     HIMC hIMC,
791     WPARAM wParam,
792     LPARAM lParam,
793     CONST LPBYTE lpbKeyState,
794     INT *pnUnknown60)
795 {
796     return FALSE; // FIXME
797 }
798 
799 /// @unimplemented
800 HRESULT
ToAsciiEx(TLS * pTLS,ITfThreadMgr_P * pThreadMgr,UINT uVirtKey,UINT uScanCode,CONST LPBYTE lpbKeyState,LPTRANSMSGLIST lpTransBuf,UINT fuState,HIMC hIMC,UINT * pResult)801 CicBridge::ToAsciiEx(
802     TLS *pTLS,
803     ITfThreadMgr_P *pThreadMgr,
804     UINT uVirtKey,
805     UINT uScanCode,
806     CONST LPBYTE lpbKeyState,
807     LPTRANSMSGLIST lpTransBuf,
808     UINT fuState,
809     HIMC hIMC,
810     UINT *pResult)
811 {
812     return E_NOTIMPL; // FIXME
813 }
814 
815 /// @implemented
816 BOOL
SetCompositionString(TLS * pTLS,ITfThreadMgr_P * pThreadMgr,HIMC hIMC,DWORD dwIndex,LPCVOID lpComp,DWORD dwCompLen,LPCVOID lpRead,DWORD dwReadLen)817 CicBridge::SetCompositionString(
818     TLS *pTLS,
819     ITfThreadMgr_P *pThreadMgr,
820     HIMC hIMC,
821     DWORD dwIndex,
822     LPCVOID lpComp,
823     DWORD dwCompLen,
824     LPCVOID lpRead,
825     DWORD dwReadLen)
826 {
827     CicIMCLock imcLock(hIMC);
828     if (FAILED(imcLock.m_hr))
829         return FALSE;
830 
831     CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext);
832     if (FAILED(imeContext.m_hr))
833         return FALSE;
834 
835     CicInputContext *pCicIC = imeContext.get().m_pCicIC;
836     auto pProfile = pTLS->m_pProfile;
837     if (!pCicIC || !pProfile)
838         return FALSE;
839 
840     UINT uCodePage;
841     pProfile->GetCodePageA(&uCodePage);
842 
843     LANGID LangID;
844     if (dwIndex != SCS_SETSTR ||
845         !lpComp || *(WORD*)lpComp ||
846         !dwCompLen ||
847         FAILED(pProfile->GetLangId(&LangID)) ||
848         PRIMARYLANGID(LangID) != LANG_KOREAN)
849     {
850         return pCicIC->SetCompositionString(imcLock, pThreadMgr, dwIndex,
851                                             lpComp, dwCompLen, lpRead, dwReadLen,
852                                             uCodePage);
853     }
854 
855     if (imcLock.get().fdwConversion & IME_CMODE_NATIVE)
856     {
857         ::ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
858         return TRUE;
859     }
860 
861     return FALSE;
862 }
863 
864 /// @unimplemented
865 LRESULT
EscHanjaMode(TLS * pTLS,HIMC hIMC,LPVOID lpData)866 CicBridge::EscHanjaMode(TLS *pTLS, HIMC hIMC, LPVOID lpData)
867 {
868     CicIMCLock imcLock(hIMC);
869     if (FAILED(imcLock.m_hr))
870         return imcLock.m_hr;
871 
872     CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext);
873     if (FAILED(imeContext.m_hr))
874         return imeContext.m_hr;
875 
876     CicInputContext *pCicIC = imeContext.get().m_pCicIC;
877     if (!pCicIC)
878         return TRUE;
879 
880     if (pCicIC->m_bCandidateOpen)
881         return TRUE;
882 
883     pCicIC->m_dwUnknown6_5[4] |= 0x1;
884 
885     //FIXME
886 
887     pCicIC->m_dwUnknown6_5[4] &= ~0x1;
888 
889     return TRUE;
890 }
891 
892 /// @implemented
893 LRESULT
EscapeKorean(TLS * pTLS,HIMC hIMC,UINT uSubFunc,LPVOID lpData)894 CicBridge::EscapeKorean(TLS *pTLS, HIMC hIMC, UINT uSubFunc, LPVOID lpData)
895 {
896     if (uSubFunc == IME_ESC_QUERY_SUPPORT)
897         return *(DWORD*)lpData == IME_ESC_HANJA_MODE;
898     if (uSubFunc == IME_ESC_HANJA_MODE)
899         return EscHanjaMode(pTLS, hIMC, lpData);
900     return 0;
901 }
902 
903 /// @implemented
IsOwnDim(ITfDocumentMgr * pDocMgr)904 BOOL CicBridge::IsOwnDim(ITfDocumentMgr *pDocMgr)
905 {
906     DWORD dwDimFlags = 0;
907     HRESULT hr = ::GetCompartmentDWORD(pDocMgr, GUID_COMPARTMENT_CTFIME_DIMFLAGS,
908                                        &dwDimFlags, FALSE);
909     if (FAILED(hr))
910         return FALSE;
911     return !!(dwDimFlags & 0x1);
912 }
913