From b2ec78673d2f2fd8f41565fef070f40297e911da Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Tue, 20 Feb 2024 21:11:08 +0900 Subject: [PATCH] [MSCTFIME] Restructuring (#6505) Improve code flexibility. 3700+ lines of msctfime.cpp was too long. JIRA issue: CORE-19360 - Split msctfime.cpp code to some source files and header files. --- dll/ime/msctfime/CMakeLists.txt | 10 +- dll/ime/msctfime/bridge.cpp | 608 ++++++ dll/ime/msctfime/bridge.h | 80 + dll/ime/msctfime/compartment.cpp | 163 ++ dll/ime/msctfime/compartment.h | 44 + dll/ime/msctfime/functions.cpp | 175 ++ dll/ime/msctfime/functions.h | 63 + dll/ime/msctfime/inputcontext.cpp | 323 ++++ dll/ime/msctfime/inputcontext.h | 91 + dll/ime/msctfime/msctfime.cpp | 2853 +---------------------------- dll/ime/msctfime/msctfime.h | 17 + dll/ime/msctfime/profile.cpp | 173 ++ dll/ime/msctfime/profile.h | 51 + dll/ime/msctfime/sinks.cpp | 539 ++++++ dll/ime/msctfime/sinks.h | 166 ++ dll/ime/msctfime/tls.cpp | 56 + dll/ime/msctfime/tls.h | 74 + dll/ime/msctfime/ui.cpp | 393 ++++ dll/ime/msctfime/ui.h | 68 + 19 files changed, 3102 insertions(+), 2845 deletions(-) create mode 100644 dll/ime/msctfime/bridge.cpp create mode 100644 dll/ime/msctfime/bridge.h create mode 100644 dll/ime/msctfime/compartment.cpp create mode 100644 dll/ime/msctfime/compartment.h create mode 100644 dll/ime/msctfime/functions.cpp create mode 100644 dll/ime/msctfime/functions.h create mode 100644 dll/ime/msctfime/inputcontext.cpp create mode 100644 dll/ime/msctfime/inputcontext.h create mode 100644 dll/ime/msctfime/profile.cpp create mode 100644 dll/ime/msctfime/profile.h create mode 100644 dll/ime/msctfime/sinks.cpp create mode 100644 dll/ime/msctfime/sinks.h create mode 100644 dll/ime/msctfime/tls.cpp create mode 100644 dll/ime/msctfime/tls.h create mode 100644 dll/ime/msctfime/ui.cpp create mode 100644 dll/ime/msctfime/ui.h diff --git a/dll/ime/msctfime/CMakeLists.txt b/dll/ime/msctfime/CMakeLists.txt index 5a034398964..288c14afc7f 100644 --- a/dll/ime/msctfime/CMakeLists.txt +++ b/dll/ime/msctfime/CMakeLists.txt @@ -5,7 +5,15 @@ include_directories( spec2def(msctfime.ime msctfime.spec) list(APPEND SOURCE - msctfime.cpp) + bridge.cpp + compartment.cpp + functions.cpp + inputcontext.cpp + msctfime.cpp + profile.cpp + sinks.cpp + tls.cpp + ui.cpp) file(GLOB msctfime_rc_deps res/*.*) add_rc_deps(msctfime.rc ${msctfime_rc_deps}) diff --git a/dll/ime/msctfime/bridge.cpp b/dll/ime/msctfime/bridge.cpp new file mode 100644 index 00000000000..de9e6ba10ef --- /dev/null +++ b/dll/ime/msctfime/bridge.cpp @@ -0,0 +1,608 @@ +/* + * PROJECT: ReactOS msctfime.ime + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Bridge + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +#include "msctfime.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msctfime); + +/// @implemented +CicBridge::CicBridge() +{ + m_bImmxInited = FALSE; + m_bUnknown1 = FALSE; + m_bDeactivating = FALSE; + m_bUnknown2 = FALSE; + m_pKeystrokeMgr = NULL; + m_pDocMgr = NULL; + m_pThreadMgrEventSink = NULL; + m_cliendId = 0; + m_cRefs = 1; +} + +/// @implemented +STDMETHODIMP CicBridge::QueryInterface(REFIID riid, LPVOID* ppvObj) +{ + *ppvObj = NULL; + + if (!IsEqualIID(riid, IID_ITfSysHookSink)) + return E_NOINTERFACE; + + *ppvObj = this; + AddRef(); + + return S_OK; +} + +/// @implemented +STDMETHODIMP_(ULONG) CicBridge::AddRef() +{ + return ::InterlockedIncrement(&m_cRefs); +} + +/// @implemented +STDMETHODIMP_(ULONG) CicBridge::Release() +{ + if (::InterlockedDecrement(&m_cRefs) == 0) + { + delete this; + return 0; + } + return m_cRefs; +} + +/// @implemented +CicBridge::~CicBridge() +{ + TLS *pTLS = TLS::PeekTLS(); + if (!pTLS || !pTLS->m_pThreadMgr) + return; + + if (SUCCEEDED(DeactivateIMMX(pTLS, pTLS->m_pThreadMgr))) + UnInitIMMX(pTLS); +} + +void CicBridge::GetDocumentManager(_Inout_ CicIMCCLock& imeContext) +{ + CicInputContext *pCicIC = imeContext.get().m_pCicIC; + if (pCicIC) + { + m_pDocMgr = pCicIC->m_pDocumentMgr; + m_pDocMgr->AddRef(); + } + else + { + m_pDocMgr->Release(); + m_pDocMgr = NULL; + } +} + +/// @unimplemented +HRESULT +CicBridge::CreateInputContext( + _Inout_ TLS *pTLS, + _In_ HIMC hIMC) +{ + CicIMCLock imcLock(hIMC); + HRESULT hr = imcLock.m_hr; + if (!imcLock) + hr = E_FAIL; + if (FAILED(hr)) + return hr; + + if (!imcLock.get().hCtfImeContext) + { + HIMCC hCtfImeContext = ImmCreateIMCC(sizeof(CTFIMECONTEXT)); + if (!hCtfImeContext) + return E_OUTOFMEMORY; + imcLock.get().hCtfImeContext = hCtfImeContext; + } + + CicIMCCLock imeContext(imcLock.get().hCtfImeContext); + CicInputContext *pCicIC = imeContext.get().m_pCicIC; + if (!pCicIC) + { + pCicIC = new(cicNoThrow) CicInputContext(m_cliendId, &m_LibThread, hIMC); + if (!pCicIC) + { + imeContext.unlock(); + imcLock.unlock(); + DestroyInputContext(pTLS, hIMC); + return E_OUTOFMEMORY; + } + + if (!pTLS->m_pThreadMgr) + { + pCicIC->Release(); + imeContext.unlock(); + imcLock.unlock(); + DestroyInputContext(pTLS, hIMC); + return E_NOINTERFACE; + } + + imeContext.get().m_pCicIC = pCicIC; + } + + hr = pCicIC->CreateInputContext(pTLS->m_pThreadMgr, imcLock); + if (FAILED(hr)) + { + pCicIC->Release(); + imeContext.get().m_pCicIC = NULL; + } + else + { + if (imcLock.get().hWnd && imcLock.get().hWnd == ::GetFocus()) + { + GetDocumentManager(imeContext); + //FIXME + } + } + + return E_NOTIMPL; +} + +/// @implemented +HRESULT CicBridge::DestroyInputContext(TLS *pTLS, HIMC hIMC) +{ + CicIMCLock imcLock(hIMC); + HRESULT hr = imcLock.m_hr; + if (!imcLock) + hr = E_FAIL; + if (FAILED(hr)) + return hr; + + hr = E_FAIL; + CicIMCCLock imeContext(imcLock.get().hCtfImeContext); + if (imeContext) + hr = imeContext.m_hr; + + if (SUCCEEDED(hr) && !(imeContext.get().m_dwCicFlags & 1)) + { + imeContext.get().m_dwCicFlags |= 1; + + CicInputContext *pCicIC = imeContext.get().m_pCicIC; + if (pCicIC) + { + imeContext.get().m_pCicIC = NULL; + hr = pCicIC->DestroyInputContext(); + pCicIC->Release(); + imeContext.get().m_pCicIC = NULL; + } + } + + if (imcLock.get().hCtfImeContext) + { + ImmDestroyIMCC(imcLock.get().hCtfImeContext); + imcLock.get().hCtfImeContext = NULL; + hr = S_OK; + } + + return hr; +} + +ITfContext * +CicBridge::GetInputContext(CicIMCCLock& imeContext) +{ + CicInputContext *pCicIC = imeContext.get().m_pCicIC; + if (!pCicIC) + return NULL; + return pCicIC->m_pContext; +} + +/// @unimplemented +HRESULT CicBridge::OnSetOpenStatus( + TLS *pTLS, + ITfThreadMgr_P *pThreadMgr, + CicIMCLock& imcLock, + CicInputContext *pCicIC) +{ + return E_NOTIMPL; +} + +/// Selects the IME context. +/// @implemented +HRESULT +CicBridge::SelectEx( + _Inout_ TLS *pTLS, + _Inout_ ITfThreadMgr_P *pThreadMgr, + _In_ HIMC hIMC, + _In_ BOOL fSelect, + _In_ HKL hKL) +{ + CicIMCLock imcLock(hIMC); + if (FAILED(imcLock.m_hr)) + return imcLock.m_hr; + + CicIMCCLock imeContext(imcLock.get().hCtfImeContext); + if (!imeContext) + imeContext.m_hr = E_FAIL; + if (FAILED(imeContext.m_hr)) + return imeContext.m_hr; + + CicInputContext *pCicIC = imeContext.get().m_pCicIC; + if (pCicIC) + pCicIC->m_bSelecting = TRUE; + + if (fSelect) + { + if (pCicIC) + pCicIC->m_dwUnknown6[1] &= ~1; + if (imcLock.get().fOpen) + OnSetOpenStatus(pTLS, pThreadMgr, imcLock, pCicIC); + } + else + { + ITfContext *pContext = GetInputContext(imeContext); + pThreadMgr->RequestPostponedLock(pContext); + if (pCicIC) + pCicIC->m_bSelecting = FALSE; + if (pContext) + pContext->Release(); + } + + return imeContext.m_hr; +} + +/// Used in CicBridge::EnumCreateInputContextCallback and +/// CicBridge::EnumDestroyInputContextCallback. +typedef struct ENUM_CREATE_DESTROY_IC +{ + TLS *m_pTLS; + CicBridge *m_pBridge; +} ENUM_CREATE_DESTROY_IC, *PENUM_CREATE_DESTROY_IC; + +/// Creates input context for the current thread. +/// @implemented +BOOL CALLBACK CicBridge::EnumCreateInputContextCallback(HIMC hIMC, LPARAM lParam) +{ + PENUM_CREATE_DESTROY_IC pData = (PENUM_CREATE_DESTROY_IC)lParam; + pData->m_pBridge->CreateInputContext(pData->m_pTLS, hIMC); + return TRUE; +} + +/// Destroys input context for the current thread. +/// @implemented +BOOL CALLBACK CicBridge::EnumDestroyInputContextCallback(HIMC hIMC, LPARAM lParam) +{ + PENUM_CREATE_DESTROY_IC pData = (PENUM_CREATE_DESTROY_IC)lParam; + pData->m_pBridge->DestroyInputContext(pData->m_pTLS, hIMC); + return TRUE; +} + +/// @implemented +HRESULT +CicBridge::ActivateIMMX( + _Inout_ TLS *pTLS, + _Inout_ ITfThreadMgr_P *pThreadMgr) +{ + HRESULT hr = pThreadMgr->ActivateEx(&m_cliendId, 1); + if (hr != S_OK) + { + m_cliendId = 0; + return E_FAIL; + } + + if (m_cActivateLocks++ != 0) + return S_OK; + + ITfSourceSingle *pSource = NULL; + hr = pThreadMgr->QueryInterface(IID_ITfSourceSingle, (void**)&pSource); + if (FAILED(hr)) + { + DeactivateIMMX(pTLS, pThreadMgr); + return hr; + } + + CFunctionProvider *pProvider = new(cicNoThrow) CFunctionProvider(m_cliendId); + if (!pProvider) + { + hr = E_FAIL; + goto Finish; + } + + pSource->AdviseSingleSink(m_cliendId, IID_ITfFunctionProvider, pProvider); + pProvider->Release(); + + if (!m_pDocMgr) + { + hr = pThreadMgr->CreateDocumentMgr(&m_pDocMgr); + if (FAILED(hr)) + { + hr = E_FAIL; + goto Finish; + } + + SetCompartmentDWORD(m_cliendId, m_pDocMgr, GUID_COMPARTMENT_CTFIME_DIMFLAGS, TRUE, FALSE); + } + + pThreadMgr->SetSysHookSink(this); + + hr = S_OK; + if (pTLS->m_bDestroyed) + { + ENUM_CREATE_DESTROY_IC Data = { pTLS, this }; + ImmEnumInputContext(0, CicBridge::EnumCreateInputContextCallback, (LPARAM)&Data); + } + +Finish: + if (FAILED(hr)) + DeactivateIMMX(pTLS, pThreadMgr); + if (pSource) + pSource->Release(); + return hr; +} + +/// @implemented +HRESULT +CicBridge::DeactivateIMMX( + _Inout_ TLS *pTLS, + _Inout_ ITfThreadMgr_P *pThreadMgr) +{ + if (m_bDeactivating) + return TRUE; + + m_bDeactivating = TRUE; + + if (m_cliendId) + { + ENUM_CREATE_DESTROY_IC Data = { pTLS, this }; + ImmEnumInputContext(0, CicBridge::EnumDestroyInputContextCallback, (LPARAM)&Data); + pTLS->m_bDestroyed = TRUE; + + ITfSourceSingle *pSource = NULL; + if (pThreadMgr->QueryInterface(IID_ITfSourceSingle, (void **)&pSource) == S_OK) + pSource->UnadviseSingleSink(m_cliendId, IID_ITfFunctionProvider); + + m_cliendId = 0; + + while (m_cActivateLocks > 0) + { + --m_cActivateLocks; + pThreadMgr->Deactivate(); + } + + if (pSource) + pSource->Release(); + } + + if (m_pDocMgr) + { + m_pDocMgr->Release(); + m_pDocMgr = NULL; + } + + pThreadMgr->SetSysHookSink(NULL); + + m_bDeactivating = FALSE; + + return S_OK; +} + +/// @implemented +HRESULT +CicBridge::InitIMMX(_Inout_ TLS *pTLS) +{ + if (m_bImmxInited) + return S_OK; + + HRESULT hr = S_OK; + if (!pTLS->m_pThreadMgr) + { + ITfThreadMgr *pThreadMgr = NULL; + hr = TF_CreateThreadMgr(&pThreadMgr); + if (FAILED(hr)) + return E_FAIL; + + hr = pThreadMgr->QueryInterface(IID_ITfThreadMgr_P, (void **)&pTLS->m_pThreadMgr); + if (pThreadMgr) + pThreadMgr->Release(); + if (FAILED(hr)) + return E_FAIL; + } + + if (!m_pThreadMgrEventSink) + { + m_pThreadMgrEventSink = + new(cicNoThrow) CThreadMgrEventSink(CThreadMgrEventSink::DIMCallback, NULL, NULL); + if (!m_pThreadMgrEventSink) + { + UnInitIMMX(pTLS); + return E_FAIL; + } + } + + m_pThreadMgrEventSink->SetCallbackPV(m_pThreadMgrEventSink); + m_pThreadMgrEventSink->_Advise(pTLS->m_pThreadMgr); + + if (!pTLS->m_pProfile) + { + pTLS->m_pProfile = new(cicNoThrow) CicProfile(); + if (!pTLS->m_pProfile) + return E_OUTOFMEMORY; + + hr = pTLS->m_pProfile->InitProfileInstance(pTLS); + if (FAILED(hr)) + { + UnInitIMMX(pTLS); + return E_FAIL; + } + } + + hr = pTLS->m_pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr_P, (void **)&m_pKeystrokeMgr); + if (FAILED(hr)) + { + UnInitIMMX(pTLS); + return E_FAIL; + } + + hr = InitDisplayAttrbuteLib(&m_LibThread); + if (FAILED(hr)) + { + UnInitIMMX(pTLS); + return E_FAIL; + } + + m_bImmxInited = TRUE; + return S_OK; +} + +/// @implemented +BOOL CicBridge::UnInitIMMX(_Inout_ TLS *pTLS) +{ + UninitDisplayAttrbuteLib(&m_LibThread); + TFUninitLib_Thread(&m_LibThread); + + if (m_pKeystrokeMgr) + { + m_pKeystrokeMgr->Release(); + m_pKeystrokeMgr = NULL; + } + + if (pTLS->m_pProfile) + { + pTLS->m_pProfile->Release(); + pTLS->m_pProfile = NULL; + } + + if (m_pThreadMgrEventSink) + { + m_pThreadMgrEventSink->_Unadvise(); + m_pThreadMgrEventSink->Release(); + m_pThreadMgrEventSink = NULL; + } + + if (pTLS->m_pThreadMgr) + { + pTLS->m_pThreadMgr->Release(); + pTLS->m_pThreadMgr = NULL; + } + + m_bImmxInited = FALSE; + return TRUE; +} + +/// @implemented +STDMETHODIMP CicBridge::OnPreFocusDIM(HWND hwnd) +{ + return S_OK; +} + +/// @unimplemented +STDMETHODIMP CicBridge::OnSysKeyboardProc(UINT, LONG) +{ + return E_NOTIMPL; +} + +/// @implemented +STDMETHODIMP CicBridge::OnSysShellProc(INT, UINT, LONG) +{ + return S_OK; +} + +/// @implemented +void +CicBridge::PostTransMsg( + _In_ HWND hWnd, + _In_ INT cTransMsgs, + _In_ const TRANSMSG *pTransMsgs) +{ + for (INT i = 0; i < cTransMsgs; ++i, ++pTransMsgs) + { + ::PostMessageW(hWnd, pTransMsgs->message, pTransMsgs->wParam, pTransMsgs->lParam); + } +} + +/// @implemented +HRESULT +CicBridge::ConfigureGeneral( + _Inout_ TLS* pTLS, + _In_ ITfThreadMgr *pThreadMgr, + _In_ HKL hKL, + _In_ HWND hWnd) +{ + CicProfile *pProfile = pTLS->m_pProfile; + if (!pProfile) + return E_OUTOFMEMORY; + + TF_LANGUAGEPROFILE profile; + HRESULT hr = pProfile->GetActiveLanguageProfile(hKL, GUID_TFCAT_TIP_KEYBOARD, &profile); + if (FAILED(hr)) + return hr; + + ITfFunctionProvider *pProvider = NULL; + hr = pThreadMgr->GetFunctionProvider(profile.clsid, &pProvider); + if (FAILED(hr)) + return hr; + + ITfFnConfigure *pFnConfigure = NULL; + hr = pProvider->GetFunction(GUID_NULL, IID_ITfFnConfigure, (IUnknown**)&pFnConfigure); + if (FAILED(hr)) + { + pProvider->Release(); + return hr; + } + + hr = pFnConfigure->Show(hWnd, profile.langid, profile.guidProfile); + + pFnConfigure->Release(); + pProvider->Release(); + return hr; +} + +/// @implemented +HRESULT +CicBridge::ConfigureRegisterWord( + _Inout_ TLS* pTLS, + _In_ ITfThreadMgr *pThreadMgr, + _In_ HKL hKL, + _In_ HWND hWnd, + _Inout_opt_ LPVOID lpData) +{ + CicProfile *pProfile = pTLS->m_pProfile; + if (!pProfile) + return E_OUTOFMEMORY; + + TF_LANGUAGEPROFILE profile; + HRESULT hr = pProfile->GetActiveLanguageProfile(hKL, GUID_TFCAT_TIP_KEYBOARD, &profile); + if (FAILED(hr)) + return hr; + + ITfFunctionProvider *pProvider = NULL; + hr = pThreadMgr->GetFunctionProvider(profile.clsid, &pProvider); + if (FAILED(hr)) + return hr; + + ITfFnConfigureRegisterWord *pFunction = NULL; + hr = pProvider->GetFunction(GUID_NULL, IID_ITfFnConfigureRegisterWord, (IUnknown**)&pFunction); + if (FAILED(hr)) + { + pProvider->Release(); + return hr; + } + + REGISTERWORDW* pRegWord = (REGISTERWORDW*)lpData; + if (pRegWord) + { + if (pRegWord->lpWord) + { + hr = E_OUTOFMEMORY; + BSTR bstrWord = SysAllocString(pRegWord->lpWord); + if (bstrWord) + { + hr = pFunction->Show(hWnd, profile.langid, profile.guidProfile, bstrWord); + SysFreeString(bstrWord); + } + } + else + { + hr = pFunction->Show(hWnd, profile.langid, profile.guidProfile, NULL); + } + } + + pProvider->Release(); + pFunction->Release(); + return hr; +} diff --git a/dll/ime/msctfime/bridge.h b/dll/ime/msctfime/bridge.h new file mode 100644 index 00000000000..c8b8441203d --- /dev/null +++ b/dll/ime/msctfime/bridge.h @@ -0,0 +1,80 @@ +/* + * PROJECT: ReactOS msctfime.ime + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Bridge + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +#pragma once + +#include "sinks.h" +#include "tls.h" + +class CicBridge : public ITfSysHookSink +{ +protected: + LONG m_cRefs; + BOOL m_bImmxInited; + BOOL m_bUnknown1; + BOOL m_bDeactivating; + DWORD m_cActivateLocks; + ITfKeystrokeMgr *m_pKeystrokeMgr; + ITfDocumentMgr *m_pDocMgr; + CThreadMgrEventSink *m_pThreadMgrEventSink; + TfClientId m_cliendId; + CIC_LIBTHREAD m_LibThread; + BOOL m_bUnknown2; + + static BOOL CALLBACK EnumCreateInputContextCallback(HIMC hIMC, LPARAM lParam); + static BOOL CALLBACK EnumDestroyInputContextCallback(HIMC hIMC, LPARAM lParam); + +public: + CicBridge(); + virtual ~CicBridge(); + + // IUnknown interface + STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + // ITfSysHookSink interface + STDMETHODIMP OnPreFocusDIM(HWND hwnd) override; + STDMETHODIMP OnSysKeyboardProc(UINT, LONG) override; + STDMETHODIMP OnSysShellProc(INT, UINT, LONG) override; + + HRESULT InitIMMX(_Inout_ TLS *pTLS); + BOOL UnInitIMMX(_Inout_ TLS *pTLS); + HRESULT ActivateIMMX(_Inout_ TLS *pTLS, _Inout_ ITfThreadMgr_P *pThreadMgr); + HRESULT DeactivateIMMX(_Inout_ TLS *pTLS, _Inout_ ITfThreadMgr_P *pThreadMgr); + + HRESULT CreateInputContext(TLS *pTLS, HIMC hIMC); + HRESULT DestroyInputContext(TLS *pTLS, HIMC hIMC); + ITfContext *GetInputContext(CicIMCCLock& imeContext); + + HRESULT SelectEx( + _Inout_ TLS *pTLS, + _Inout_ ITfThreadMgr_P *pThreadMgr, + _In_ HIMC hIMC, + _In_ BOOL fSelect, + _In_ HKL hKL); + HRESULT OnSetOpenStatus( + TLS *pTLS, + ITfThreadMgr_P *pThreadMgr, + CicIMCLock& imcLock, + CicInputContext *pCicIC); + + void PostTransMsg(_In_ HWND hWnd, _In_ INT cTransMsgs, _In_ const TRANSMSG *pTransMsgs); + void GetDocumentManager(_Inout_ CicIMCCLock& imeContext); + + HRESULT + ConfigureGeneral(_Inout_ TLS* pTLS, + _In_ ITfThreadMgr *pThreadMgr, + _In_ HKL hKL, + _In_ HWND hWnd); + HRESULT ConfigureRegisterWord( + _Inout_ TLS* pTLS, + _In_ ITfThreadMgr *pThreadMgr, + _In_ HKL hKL, + _In_ HWND hWnd, + _Inout_opt_ LPVOID lpData); +}; diff --git a/dll/ime/msctfime/compartment.cpp b/dll/ime/msctfime/compartment.cpp new file mode 100644 index 00000000000..d92b1b526f4 --- /dev/null +++ b/dll/ime/msctfime/compartment.cpp @@ -0,0 +1,163 @@ +/* + * PROJECT: ReactOS msctfime.ime + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Supporting compartments + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +#include "msctfime.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msctfime); + +/// @implemented +HRESULT +GetCompartment( + IUnknown *pUnknown, + REFGUID rguid, + ITfCompartment **ppComp, + BOOL bThread) +{ + *ppComp = NULL; + + ITfThreadMgr *pThreadMgr = NULL; + ITfCompartmentMgr *pCompMgr = NULL; + + HRESULT hr; + if (bThread) + { + hr = pUnknown->QueryInterface(IID_ITfThreadMgr, (void **)&pThreadMgr); + if (FAILED(hr)) + return hr; + + hr = pThreadMgr->GetGlobalCompartment(&pCompMgr); + } + else + { + hr = pUnknown->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompMgr); + } + + if (SUCCEEDED(hr)) + { + hr = E_FAIL; + if (pCompMgr) + { + hr = pCompMgr->GetCompartment(rguid, ppComp); + pCompMgr->Release(); + } + } + + if (pThreadMgr) + pThreadMgr->Release(); + + return hr; +} + +/// @implemented +HRESULT +SetCompartmentDWORD( + TfEditCookie cookie, + IUnknown *pUnknown, + REFGUID rguid, + DWORD dwValue, + BOOL bThread) +{ + ITfCompartment *pComp = NULL; + HRESULT hr = GetCompartment(pUnknown, rguid, &pComp, bThread); + if (FAILED(hr)) + return hr; + + VARIANT vari; + V_I4(&vari) = dwValue; + V_VT(&vari) = VT_I4; + hr = pComp->SetValue(cookie, &vari); + + pComp->Release(); + return hr; +} + +/// @implemented +HRESULT +GetCompartmentDWORD( + IUnknown *pUnknown, + REFGUID rguid, + LPDWORD pdwValue, + BOOL bThread) +{ + *pdwValue = 0; + + ITfCompartment *pComp = NULL; + HRESULT hr = GetCompartment(pUnknown, rguid, &pComp, bThread); + if (FAILED(hr)) + return hr; + + VARIANT vari; + hr = pComp->GetValue(&vari); + if (hr == S_OK) + *pdwValue = V_I4(&vari); + + pComp->Release(); + return hr; +} + +/// @implemented +HRESULT +SetCompartmentUnknown( + TfEditCookie cookie, + IUnknown *pUnknown, + REFGUID rguid, + IUnknown *punkValue) +{ + ITfCompartment *pComp = NULL; + HRESULT hr = GetCompartment(pUnknown, rguid, &pComp, FALSE); + if (FAILED(hr)) + return hr; + + VARIANT vari; + V_UNKNOWN(&vari) = punkValue; + V_VT(&vari) = VT_UNKNOWN; + hr = pComp->SetValue(cookie, &vari); + + pComp->Release(); + return hr; +} + +/// @implemented +HRESULT +ClearCompartment( + TfClientId tid, + IUnknown *pUnknown, + REFGUID rguid, + BOOL bThread) +{ + ITfCompartmentMgr *pCompMgr = NULL; + ITfThreadMgr *pThreadMgr = NULL; + + HRESULT hr; + if (bThread) + { + hr = pUnknown->QueryInterface(IID_ITfThreadMgr, (void **)&pThreadMgr); + if (FAILED(hr)) + return hr; + + hr = pThreadMgr->GetGlobalCompartment(&pCompMgr); + } + else + { + hr = pUnknown->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompMgr); + } + + if (SUCCEEDED(hr)) + { + hr = E_FAIL; + if (pCompMgr) + { + hr = pCompMgr->ClearCompartment(tid, rguid); + pCompMgr->Release(); + } + } + + if (pThreadMgr) + pThreadMgr->Release(); + + return hr; +} diff --git a/dll/ime/msctfime/compartment.h b/dll/ime/msctfime/compartment.h new file mode 100644 index 00000000000..88805566832 --- /dev/null +++ b/dll/ime/msctfime/compartment.h @@ -0,0 +1,44 @@ +/* + * PROJECT: ReactOS msctfime.ime + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Supporting compartments + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +#pragma once + +HRESULT +GetCompartment( + IUnknown *pUnknown, + REFGUID rguid, + ITfCompartment **ppComp, + BOOL bThread); + +HRESULT +SetCompartmentDWORD( + TfEditCookie cookie, + IUnknown *pUnknown, + REFGUID rguid, + DWORD dwValue, + BOOL bThread); + +HRESULT +GetCompartmentDWORD( + IUnknown *pUnknown, + REFGUID rguid, + LPDWORD pdwValue, + BOOL bThread); + +HRESULT +SetCompartmentUnknown( + TfEditCookie cookie, + IUnknown *pUnknown, + REFGUID rguid, + IUnknown *punkValue); + +HRESULT +ClearCompartment( + TfClientId tid, + IUnknown *pUnknown, + REFGUID rguid, + BOOL bThread); diff --git a/dll/ime/msctfime/functions.cpp b/dll/ime/msctfime/functions.cpp new file mode 100644 index 00000000000..c9832f90cff --- /dev/null +++ b/dll/ime/msctfime/functions.cpp @@ -0,0 +1,175 @@ +/* + * PROJECT: ReactOS msctfime.ime + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: The functions of msctfime.ime + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +#include "msctfime.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msctfime); + +/// @implemented +CFunctionProviderBase::CFunctionProviderBase(_In_ TfClientId clientId) +{ + m_clientId = clientId; + m_guid = GUID_NULL; + m_bstr = NULL; + m_cRefs = 1; +} + +/// @implemented +CFunctionProviderBase::~CFunctionProviderBase() +{ + if (!DllShutdownInProgress()) + ::SysFreeString(m_bstr); +} + +/// @implemented +BOOL +CFunctionProviderBase::Init( + _In_ REFGUID rguid, + _In_ LPCWSTR psz) +{ + m_bstr = ::SysAllocString(psz); + m_guid = rguid; + return (m_bstr != NULL); +} + +/// @implemented +STDMETHODIMP +CFunctionProviderBase::QueryInterface( + _In_ REFIID riid, + _Out_ LPVOID* ppvObj) +{ + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfFunctionProvider)) + { + *ppvObj = this; + AddRef(); + return S_OK; + } + return E_NOINTERFACE; +} + +/// @implemented +STDMETHODIMP_(ULONG) CFunctionProviderBase::AddRef() +{ + return ::InterlockedIncrement(&m_cRefs); +} + +/// @implemented +STDMETHODIMP_(ULONG) CFunctionProviderBase::Release() +{ + if (::InterlockedDecrement(&m_cRefs) == 0) + { + delete this; + return 0; + } + return m_cRefs; +} + +/// @implemented +STDMETHODIMP CFunctionProviderBase::GetType(_Out_ GUID *guid) +{ + *guid = m_guid; + return S_OK; +} + +/// @implemented +STDMETHODIMP CFunctionProviderBase::GetDescription(_Out_ BSTR *desc) +{ + *desc = ::SysAllocString(m_bstr); + return (*desc ? S_OK : E_OUTOFMEMORY); +} + +/***********************************************************************/ + +/// @implemented +CFunctionProvider::CFunctionProvider(_In_ TfClientId clientId) : CFunctionProviderBase(clientId) +{ + Init(CLSID_CAImmLayer, L"MSCTFIME::Function Provider"); +} + +/// @implemented +STDMETHODIMP +CFunctionProvider::GetFunction( + _In_ REFGUID guid, + _In_ REFIID riid, + _Out_ IUnknown **func) +{ + *func = NULL; + + if (IsEqualGUID(guid, GUID_NULL) && + IsEqualIID(riid, IID_IAImmFnDocFeed)) + { + *func = new(cicNoThrow) CFnDocFeed(); + if (*func) + return S_OK; + } + + return E_NOINTERFACE; +} + +/***********************************************************************/ + +CFnDocFeed::CFnDocFeed() +{ + m_cRefs = 1; +} + +CFnDocFeed::~CFnDocFeed() +{ +} + +/// @implemented +STDMETHODIMP CFnDocFeed::QueryInterface(_In_ REFIID riid, _Out_ LPVOID* ppvObj) +{ + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IAImmFnDocFeed)) + { + *ppvObj = this; + AddRef(); + return S_OK; + } + return E_NOINTERFACE; +} + +/// @implemented +STDMETHODIMP_(ULONG) CFnDocFeed::AddRef() +{ + return ::InterlockedIncrement(&m_cRefs); +} + +/// @implemented +STDMETHODIMP_(ULONG) CFnDocFeed::Release() +{ + if (::InterlockedDecrement(&m_cRefs) == 0) + { + delete this; + return 0; + } + return m_cRefs; +} + +/// @unimplemented +STDMETHODIMP CFnDocFeed::DocFeed() +{ + return E_NOTIMPL; +} + +/// @unimplemented +STDMETHODIMP CFnDocFeed::ClearDocFeedBuffer() +{ + return E_NOTIMPL; +} + +/// @unimplemented +STDMETHODIMP CFnDocFeed::StartReconvert() +{ + return E_NOTIMPL; +} + +/// @unimplemented +STDMETHODIMP CFnDocFeed::StartUndoCompositionString() +{ + return E_NOTIMPL; +} diff --git a/dll/ime/msctfime/functions.h b/dll/ime/msctfime/functions.h new file mode 100644 index 00000000000..6938162a6e5 --- /dev/null +++ b/dll/ime/msctfime/functions.h @@ -0,0 +1,63 @@ +/* + * PROJECT: ReactOS msctfime.ime + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: The functions of msctfime.ime + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +class CFunctionProviderBase : public ITfFunctionProvider +{ +protected: + TfClientId m_clientId; + GUID m_guid; + BSTR m_bstr; + LONG m_cRefs; + +public: + CFunctionProviderBase(_In_ TfClientId clientId); + virtual ~CFunctionProviderBase(); + + // IUnknown interface + STDMETHODIMP QueryInterface(_In_ REFIID riid, _Out_ LPVOID* ppvObj) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + // ITfFunctionProvider interface + STDMETHODIMP GetType(_Out_ GUID *guid) override; + STDMETHODIMP GetDescription(_Out_ BSTR *desc) override; + //STDMETHODIMP GetFunction(_In_ REFGUID guid, _In_ REFIID riid, _Out_ IUnknown **func) = 0; + + BOOL Init(_In_ REFGUID rguid, _In_ LPCWSTR psz); +}; + +/***********************************************************************/ + +class CFunctionProvider : public CFunctionProviderBase +{ +public: + CFunctionProvider(_In_ TfClientId clientId); + + STDMETHODIMP GetFunction(_In_ REFGUID guid, _In_ REFIID riid, _Out_ IUnknown **func) override; +}; + +/***********************************************************************/ + +class CFnDocFeed : public IAImmFnDocFeed +{ + LONG m_cRefs; + +public: + CFnDocFeed(); + virtual ~CFnDocFeed(); + + // IUnknown interface + STDMETHODIMP QueryInterface(_In_ REFIID riid, _Out_ LPVOID* ppvObj) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + // IAImmFnDocFeed interface + STDMETHODIMP DocFeed() override; + STDMETHODIMP ClearDocFeedBuffer() override; + STDMETHODIMP StartReconvert() override; + STDMETHODIMP StartUndoCompositionString() override; +}; diff --git a/dll/ime/msctfime/inputcontext.cpp b/dll/ime/msctfime/inputcontext.cpp new file mode 100644 index 00000000000..1bdf64cb62e --- /dev/null +++ b/dll/ime/msctfime/inputcontext.cpp @@ -0,0 +1,323 @@ +/* + * PROJECT: ReactOS msctfime.ime + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Input Context of msctfime.ime + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +#include "msctfime.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msctfime); + +/// @unimplemented +CicInputContext::CicInputContext( + _In_ TfClientId cliendId, + _Inout_ PCIC_LIBTHREAD pLibThread, + _In_ HIMC hIMC) +{ + m_hIMC = hIMC; + m_guid = GUID_NULL; + m_dwQueryPos = 0; + m_cRefs = 1; +} + +/// @implemented +STDMETHODIMP CicInputContext::QueryInterface(REFIID riid, LPVOID* ppvObj) +{ + *ppvObj = NULL; + + if (IsEqualIID(riid, IID_ITfContextOwnerCompositionSink)) + { + *ppvObj = static_cast(this); + AddRef(); + return S_OK; + } + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfCleanupContextSink)) + { + *ppvObj = this; + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +/// @implemented +STDMETHODIMP_(ULONG) CicInputContext::AddRef() +{ + return ::InterlockedIncrement(&m_cRefs); +} + +/// @implemented +STDMETHODIMP_(ULONG) CicInputContext::Release() +{ + if (::InterlockedDecrement(&m_cRefs) == 0) + { + delete this; + return 0; + } + return m_cRefs; +} + +/// @implemented +STDMETHODIMP +CicInputContext::OnStartComposition( + ITfCompositionView *pComposition, + BOOL *pfOk) +{ + if ((m_cCompLocks <= 0) || m_dwUnknown6_5) + { + *pfOk = TRUE; + ++m_cCompLocks; + } + else + { + *pfOk = FALSE; + } + return S_OK; +} + +/// @implemented +STDMETHODIMP +CicInputContext::OnUpdateComposition( + ITfCompositionView *pComposition, + ITfRange *pRangeNew) +{ + return S_OK; +} + +/// @implemented +STDMETHODIMP +CicInputContext::OnEndComposition( + ITfCompositionView *pComposition) +{ + --m_cCompLocks; + return S_OK; +} + +/// @implemented +HRESULT +CicInputContext::GetGuidAtom( + _Inout_ CicIMCLock& imcLock, + _In_ BYTE iAtom, + _Out_opt_ LPDWORD pdwGuidAtom) +{ + CicIMCCLock imeContext(imcLock.get().hCompStr); + HRESULT hr = imeContext.m_hr; + if (!imeContext) + hr = E_FAIL; + if (FAILED(hr)) + return hr; + + hr = E_FAIL; + if (iAtom < m_cGuidAtoms) + { + *pdwGuidAtom = m_adwGuidAtoms[iAtom]; + hr = S_OK; + } + + return hr; +} + +/// @unimplemented +HRESULT +CicInputContext::CreateInputContext( + _Inout_ ITfThreadMgr *pThreadMgr, + _Inout_ CicIMCLock& imcLock) +{ + //FIXME + return E_NOTIMPL; +} + +/// @unimplemented +HRESULT +CicInputContext::DestroyInputContext() +{ + ITfSourceSingle *pSource = NULL; + + if (m_pContext && m_pContext->QueryInterface(IID_ITfSourceSingle, (void **)&pSource) == S_OK) + pSource->UnadviseSingleSink(m_clientId, IID_ITfCleanupContextSink); + + //FIXME: m_dwUnknown5 + + if (m_pTextEventSink) + { + m_pTextEventSink->_Unadvise(); + m_pTextEventSink->Release(); + m_pTextEventSink = NULL; + } + + if (m_pCompEventSink2) + { + m_pCompEventSink2->_Unadvise(); + m_pCompEventSink2->Release(); + m_pCompEventSink2 = NULL; + } + + if (m_pCompEventSink1) + { + m_pCompEventSink1->_Unadvise(); + m_pCompEventSink1->Release(); + m_pCompEventSink1 = NULL; + } + + //FIXME: m_pInputContextOwner + + if (m_pDocumentMgr) + m_pDocumentMgr->Pop(1); + + if (m_pContext) + { + ClearCompartment(m_clientId, m_pContext, GUID_COMPARTMENT_CTFIME_CICINPUTCONTEXT, 0); + m_pContext->Release(); + m_pContext = NULL; + } + + if (m_pContextOwnerServices) + { + m_pContextOwnerServices->Release(); + m_pContextOwnerServices = NULL; + } + + // FIXME: m_pICOwnerCallback + + if (m_pDocumentMgr) + { + m_pDocumentMgr->Release(); + m_pDocumentMgr = NULL; + } + + if (pSource) + pSource->Release(); + + return S_OK; +} + +/// @implemented +STDMETHODIMP +CicInputContext::OnCompositionTerminated(TfEditCookie ecWrite, ITfComposition *pComposition) +{ + return S_OK; +} + +/// @implemented +STDMETHODIMP +CicInputContext::OnCleanupContext( + _In_ TfEditCookie ecWrite, + _Inout_ ITfContext *pic) +{ + TLS *pTLS = TLS::PeekTLS(); + if (!pTLS || !pTLS->m_pProfile) + return E_OUTOFMEMORY; + + LANGID LangID; + pTLS->m_pProfile->GetLangId(&LangID); + + IMEINFO IMEInfo; + WCHAR szPath[MAX_PATH]; + if (Inquire(&IMEInfo, szPath, 0, (HKL)UlongToHandle(LangID)) != S_OK) + return E_FAIL; + + ITfProperty *pProp = NULL; + if (!(IMEInfo.fdwProperty & IME_PROP_COMPLETE_ON_UNSELECT)) + return S_OK; + + HRESULT hr = pic->GetProperty(GUID_PROP_COMPOSING, &pProp); + if (FAILED(hr)) + return S_OK; + + IEnumTfRanges *pRanges = NULL; + hr = pProp->EnumRanges(ecWrite, &pRanges, NULL); + if (SUCCEEDED(hr)) + { + ITfRange *pRange = NULL; + while (pRanges->Next(1, &pRange, 0) == S_OK) + { + VARIANT vari; + V_VT(&vari) = VT_EMPTY; + pProp->GetValue(ecWrite, pRange, &vari); + if (V_VT(&vari) == VT_I4) + { + if (V_I4(&vari)) + pProp->Clear(ecWrite, pRange); + } + pRange->Release(); + pRange = NULL; + } + pRanges->Release(); + } + pProp->Release(); + + return S_OK; +} + +/// Retrieves the IME information. +/// @implemented +HRESULT +Inquire( + _Out_ LPIMEINFO lpIMEInfo, + _Out_ LPWSTR lpszWndClass, + _In_ DWORD dwSystemInfoFlags, + _In_ HKL hKL) +{ + if (!lpIMEInfo) + return E_OUTOFMEMORY; + + StringCchCopyW(lpszWndClass, 64, L"MSCTFIME UI"); + lpIMEInfo->dwPrivateDataSize = 0; + + switch (LOWORD(hKL)) // Language ID + { + case MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT): // Japanese + { + lpIMEInfo->fdwProperty = IME_PROP_COMPLETE_ON_UNSELECT | IME_PROP_SPECIAL_UI | + IME_PROP_AT_CARET | IME_PROP_NEED_ALTKEY | + IME_PROP_KBD_CHAR_FIRST; + lpIMEInfo->fdwConversionCaps = IME_CMODE_FULLSHAPE | IME_CMODE_KATAKANA | + IME_CMODE_NATIVE; + lpIMEInfo->fdwSentenceCaps = IME_SMODE_CONVERSATION | IME_SMODE_PLAURALCLAUSE; + lpIMEInfo->fdwSelectCaps = SELECT_CAP_SENTENCE | SELECT_CAP_CONVERSION; + lpIMEInfo->fdwSCSCaps = SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD | + SCS_CAP_COMPSTR; + lpIMEInfo->fdwUICaps = UI_CAP_ROT90; + break; + } + case MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT): // Korean + { + lpIMEInfo->fdwProperty = IME_PROP_COMPLETE_ON_UNSELECT | IME_PROP_SPECIAL_UI | + IME_PROP_AT_CARET | IME_PROP_NEED_ALTKEY | + IME_PROP_KBD_CHAR_FIRST; + lpIMEInfo->fdwConversionCaps = IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE; + lpIMEInfo->fdwSentenceCaps = 0; + lpIMEInfo->fdwSCSCaps = SCS_CAP_SETRECONVERTSTRING | SCS_CAP_COMPSTR; + lpIMEInfo->fdwSelectCaps = SELECT_CAP_CONVERSION; + lpIMEInfo->fdwUICaps = UI_CAP_ROT90; + break; + } + case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED): // Simplified Chinese + case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL): // Traditional Chinese + { + lpIMEInfo->fdwProperty = IME_PROP_SPECIAL_UI | IME_PROP_AT_CARET | + IME_PROP_NEED_ALTKEY | IME_PROP_KBD_CHAR_FIRST; + lpIMEInfo->fdwConversionCaps = IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE; + lpIMEInfo->fdwSentenceCaps = SELECT_CAP_CONVERSION; + lpIMEInfo->fdwSelectCaps = 0; + lpIMEInfo->fdwSCSCaps = SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD | + SCS_CAP_COMPSTR; + lpIMEInfo->fdwUICaps = UI_CAP_ROT90; + break; + } + default: // Otherwise + { + lpIMEInfo->fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET; + lpIMEInfo->fdwConversionCaps = 0; + lpIMEInfo->fdwSentenceCaps = 0; + lpIMEInfo->fdwSCSCaps = 0; + lpIMEInfo->fdwUICaps = 0; + lpIMEInfo->fdwSelectCaps = 0; + break; + } + } + + return S_OK; +} diff --git a/dll/ime/msctfime/inputcontext.h b/dll/ime/msctfime/inputcontext.h new file mode 100644 index 00000000000..2273161926e --- /dev/null +++ b/dll/ime/msctfime/inputcontext.h @@ -0,0 +1,91 @@ +/* + * PROJECT: ReactOS msctfime.ime + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Input Context of msctfime.ime + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +#pragma once + +#include "sinks.h" + +class CInputContextOwnerCallBack; +class CInputContextOwner; + +HRESULT +Inquire( + _Out_ LPIMEINFO lpIMEInfo, + _Out_ LPWSTR lpszWndClass, + _In_ DWORD dwSystemInfoFlags, + _In_ HKL hKL); + +/*********************************************************************** + * CicInputContext + * + * The msctfime.ime's input context. + */ +class CicInputContext + : public ITfCleanupContextSink + , public ITfContextOwnerCompositionSink + , public ITfCompositionSink +{ +public: + LONG m_cRefs; + HIMC m_hIMC; + ITfDocumentMgr *m_pDocumentMgr; + ITfContext *m_pContext; + ITfContextOwnerServices *m_pContextOwnerServices; + CInputContextOwnerCallBack *m_pICOwnerCallback; + CTextEventSink *m_pTextEventSink; + CCompartmentEventSink *m_pCompEventSink1; + CCompartmentEventSink *m_pCompEventSink2; + CInputContextOwner *m_pInputContextOwner; + DWORD m_dwUnknown3[3]; + DWORD m_dwUnknown4[2]; + DWORD m_dwQueryPos; + DWORD m_dwUnknown5; + GUID m_guid; + DWORD m_dwUnknown6[11]; + BOOL m_bSelecting; + DWORD m_dwUnknown6_5; + LONG m_cCompLocks; + DWORD m_dwUnknown7[5]; + WORD m_cGuidAtoms; + WORD m_padding; + DWORD m_adwGuidAtoms[256]; + DWORD m_dwUnknown8[17]; + TfClientId m_clientId; + DWORD m_dwUnknown9; + +public: + CicInputContext( + _In_ TfClientId cliendId, + _Inout_ PCIC_LIBTHREAD pLibThread, + _In_ HIMC hIMC); + virtual ~CicInputContext() { } + + // IUnknown interface + STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + // ITfCleanupContextSink interface + STDMETHODIMP OnCleanupContext(_In_ TfEditCookie ecWrite, _Inout_ ITfContext *pic) override; + + // ITfContextOwnerCompositionSink interface + STDMETHODIMP OnStartComposition(ITfCompositionView *pComposition, BOOL *pfOk) override; + STDMETHODIMP OnUpdateComposition(ITfCompositionView *pComposition, ITfRange *pRangeNew) override; + STDMETHODIMP OnEndComposition(ITfCompositionView *pComposition) override; + + // ITfCompositionSink interface + STDMETHODIMP OnCompositionTerminated(TfEditCookie ecWrite, ITfComposition *pComposition) override; + + HRESULT + GetGuidAtom( + _Inout_ CicIMCLock& imcLock, + _In_ BYTE iAtom, + _Out_opt_ LPDWORD pdwGuidAtom); + + HRESULT CreateInputContext(_Inout_ ITfThreadMgr *pThreadMgr, _Inout_ CicIMCLock& imcLock); + HRESULT DestroyInputContext(); +}; diff --git a/dll/ime/msctfime/msctfime.cpp b/dll/ime/msctfime/msctfime.cpp index ccd442083ab..445998d6a9c 100644 --- a/dll/ime/msctfime/msctfime.cpp +++ b/dll/ime/msctfime/msctfime.cpp @@ -20,87 +20,31 @@ BOOL gfTFInitLib = FALSE; CRITICAL_SECTION g_csLock; CDispAttrPropCache *g_pPropCache = NULL; -DEFINE_GUID(GUID_COMPARTMENT_CTFIME_DIMFLAGS, 0xA94C5FD2, 0xC471, 0x4031, 0x95, 0x46, 0x70, 0x9C, 0x17, 0x30, 0x0C, 0xB9); -DEFINE_GUID(GUID_COMPARTMENT_CTFIME_CICINPUTCONTEXT, 0x85A688F7, 0x6DC8, 0x4F17, 0xA8, 0x3A, 0xB1, 0x1C, 0x09, 0xCD, 0xD7, 0xBF); - EXTERN_C void __cxa_pure_virtual(void) { ERR("__cxa_pure_virtual\n"); } -UINT WM_MSIME_SERVICE = 0; -UINT WM_MSIME_UIREADY = 0; -UINT WM_MSIME_RECONVERTREQUEST = 0; -UINT WM_MSIME_RECONVERT = 0; -UINT WM_MSIME_DOCUMENTFEED = 0; -UINT WM_MSIME_QUERYPOSITION = 0; -UINT WM_MSIME_MODEBIAS = 0; -UINT WM_MSIME_SHOWIMEPAD = 0; -UINT WM_MSIME_MOUSE = 0; -UINT WM_MSIME_KEYMAP = 0; - -/// @implemented -BOOL IsMsImeMessage(_In_ UINT uMsg) -{ - return (uMsg == WM_MSIME_SERVICE || - uMsg == WM_MSIME_UIREADY || - uMsg == WM_MSIME_RECONVERTREQUEST || - uMsg == WM_MSIME_RECONVERT || - uMsg == WM_MSIME_DOCUMENTFEED || - uMsg == WM_MSIME_QUERYPOSITION || - uMsg == WM_MSIME_MODEBIAS || - uMsg == WM_MSIME_SHOWIMEPAD || - uMsg == WM_MSIME_MOUSE || - uMsg == WM_MSIME_KEYMAP); -} - -/// @implemented -BOOL RegisterMSIMEMessage(VOID) -{ - // Using ANSI (A) version here can reduce binary size. - WM_MSIME_SERVICE = RegisterWindowMessageA("MSIMEService"); - WM_MSIME_UIREADY = RegisterWindowMessageA("MSIMEUIReady"); - WM_MSIME_RECONVERTREQUEST = RegisterWindowMessageA("MSIMEReconvertRequest"); - WM_MSIME_RECONVERT = RegisterWindowMessageA("MSIMEReconvert"); - WM_MSIME_DOCUMENTFEED = RegisterWindowMessageA("MSIMEDocumentFeed"); - WM_MSIME_QUERYPOSITION = RegisterWindowMessageA("MSIMEQueryPosition"); - WM_MSIME_MODEBIAS = RegisterWindowMessageA("MSIMEModeBias"); - WM_MSIME_SHOWIMEPAD = RegisterWindowMessageA("MSIMEShowImePad"); - WM_MSIME_MOUSE = RegisterWindowMessageA("MSIMEMouseOperation"); - WM_MSIME_KEYMAP = RegisterWindowMessageA("MSIMEKeyMap"); - return (WM_MSIME_SERVICE && - WM_MSIME_UIREADY && - WM_MSIME_RECONVERTREQUEST && - WM_MSIME_RECONVERT && - WM_MSIME_DOCUMENTFEED && - WM_MSIME_QUERYPOSITION && - WM_MSIME_MODEBIAS && - WM_MSIME_SHOWIMEPAD && - WM_MSIME_MOUSE && - WM_MSIME_KEYMAP); -} - -typedef BOOLEAN (WINAPI *FN_DllShutDownInProgress)(VOID); +typedef BOOLEAN (WINAPI *FN_DllShutdownInProgress)(VOID); /// This function calls ntdll!RtlDllShutdownInProgress. /// It can detect the system is shutting down or not. /// @implemented -EXTERN_C BOOLEAN WINAPI -DllShutDownInProgress(VOID) +EXTERN_C BOOLEAN WINAPI DllShutdownInProgress(VOID) { HMODULE hNTDLL; - static FN_DllShutDownInProgress s_fnDllShutDownInProgress = NULL; + static FN_DllShutdownInProgress s_fnDllShutdownInProgress = NULL; - if (s_fnDllShutDownInProgress) - return s_fnDllShutDownInProgress(); + if (s_fnDllShutdownInProgress) + return s_fnDllShutdownInProgress(); hNTDLL = cicGetSystemModuleHandle(L"ntdll.dll", FALSE); - s_fnDllShutDownInProgress = - (FN_DllShutDownInProgress)GetProcAddress(hNTDLL, "RtlDllShutdownInProgress"); - if (!s_fnDllShutDownInProgress) + s_fnDllShutdownInProgress = + (FN_DllShutdownInProgress)GetProcAddress(hNTDLL, "RtlDllShutdownInProgress"); + if (!s_fnDllShutdownInProgress) return FALSE; - return s_fnDllShutDownInProgress(); + return s_fnDllShutdownInProgress(); } /// This function checks if the current user logon session is interactive. @@ -302,2426 +246,8 @@ InternalSelectEx( return imcLock.m_hr; } -/*********************************************************************** - * Compartment - */ - -/// @implemented -HRESULT -GetCompartment( - IUnknown *pUnknown, - REFGUID rguid, - ITfCompartment **ppComp, - BOOL bThread) -{ - *ppComp = NULL; - - ITfThreadMgr *pThreadMgr = NULL; - ITfCompartmentMgr *pCompMgr = NULL; - - HRESULT hr; - if (bThread) - { - hr = pUnknown->QueryInterface(IID_ITfThreadMgr, (void **)&pThreadMgr); - if (FAILED(hr)) - return hr; - - hr = pThreadMgr->GetGlobalCompartment(&pCompMgr); - } - else - { - hr = pUnknown->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompMgr); - } - - if (SUCCEEDED(hr)) - { - hr = E_FAIL; - if (pCompMgr) - { - hr = pCompMgr->GetCompartment(rguid, ppComp); - pCompMgr->Release(); - } - } - - if (pThreadMgr) - pThreadMgr->Release(); - - return hr; -} - -/// @implemented -HRESULT -SetCompartmentDWORD( - TfEditCookie cookie, - IUnknown *pUnknown, - REFGUID rguid, - DWORD dwValue, - BOOL bThread) -{ - ITfCompartment *pComp = NULL; - HRESULT hr = GetCompartment(pUnknown, rguid, &pComp, bThread); - if (FAILED(hr)) - return hr; - - VARIANT vari; - V_I4(&vari) = dwValue; - V_VT(&vari) = VT_I4; - hr = pComp->SetValue(cookie, &vari); - - pComp->Release(); - return hr; -} - -/// @implemented -HRESULT -GetCompartmentDWORD( - IUnknown *pUnknown, - REFGUID rguid, - LPDWORD pdwValue, - BOOL bThread) -{ - *pdwValue = 0; - - ITfCompartment *pComp = NULL; - HRESULT hr = GetCompartment(pUnknown, rguid, &pComp, bThread); - if (FAILED(hr)) - return hr; - - VARIANT vari; - hr = pComp->GetValue(&vari); - if (hr == S_OK) - *pdwValue = V_I4(&vari); - - pComp->Release(); - return hr; -} - -/// @implemented -HRESULT -SetCompartmentUnknown( - TfEditCookie cookie, - IUnknown *pUnknown, - REFGUID rguid, - IUnknown *punkValue) -{ - ITfCompartment *pComp = NULL; - HRESULT hr = GetCompartment(pUnknown, rguid, &pComp, FALSE); - if (FAILED(hr)) - return hr; - - VARIANT vari; - V_UNKNOWN(&vari) = punkValue; - V_VT(&vari) = VT_UNKNOWN; - hr = pComp->SetValue(cookie, &vari); - - pComp->Release(); - return hr; -} - -/// @implemented -HRESULT -ClearCompartment( - TfClientId tid, - IUnknown *pUnknown, - REFGUID rguid, - BOOL bThread) -{ - ITfCompartmentMgr *pCompMgr = NULL; - ITfThreadMgr *pThreadMgr = NULL; - - HRESULT hr; - if (bThread) - { - hr = pUnknown->QueryInterface(IID_ITfThreadMgr, (void **)&pThreadMgr); - if (FAILED(hr)) - return hr; - - hr = pThreadMgr->GetGlobalCompartment(&pCompMgr); - } - else - { - hr = pUnknown->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompMgr); - } - - if (SUCCEEDED(hr)) - { - hr = E_FAIL; - if (pCompMgr) - { - hr = pCompMgr->ClearCompartment(tid, rguid); - pCompMgr->Release(); - } - } - - if (pThreadMgr) - pThreadMgr->Release(); - - return hr; -} - -typedef struct CESMAP -{ - ITfCompartment *m_pComp; - DWORD m_dwCookie; -} CESMAP, *PCESMAP; - -typedef INT (CALLBACK *FN_EVENTSINK)(LPVOID, REFGUID); - -class CCompartmentEventSink : public ITfCompartmentEventSink -{ - CicArray m_array; - LONG m_cRefs; - FN_EVENTSINK m_fnEventSink; - LPVOID m_pUserData; - -public: - CCompartmentEventSink(FN_EVENTSINK fnEventSink, LPVOID pUserData); - virtual ~CCompartmentEventSink(); - - HRESULT _Advise(IUnknown *pUnknown, REFGUID rguid, BOOL bThread); - HRESULT _Unadvise(); - - // IUnknown interface - STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override; - STDMETHODIMP_(ULONG) AddRef() override; - STDMETHODIMP_(ULONG) Release() override; - - // ITfCompartmentEventSink interface - STDMETHODIMP OnChange(REFGUID rguid) override; -}; - -/// @implemented -CCompartmentEventSink::CCompartmentEventSink(FN_EVENTSINK fnEventSink, LPVOID pUserData) - : m_array() - , m_cRefs(1) - , m_fnEventSink(fnEventSink) - , m_pUserData(pUserData) -{ -} - -/// @implemented -CCompartmentEventSink::~CCompartmentEventSink() -{ -} - -/// @implemented -STDMETHODIMP CCompartmentEventSink::QueryInterface(REFIID riid, LPVOID* ppvObj) -{ - if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfCompartmentEventSink)) - { - *ppvObj = this; - AddRef(); - return S_OK; - } - - *ppvObj = NULL; - return E_NOINTERFACE; -} - -/// @implemented -STDMETHODIMP_(ULONG) CCompartmentEventSink::AddRef() -{ - return ::InterlockedIncrement(&m_cRefs); -} - -/// @implemented -STDMETHODIMP_(ULONG) CCompartmentEventSink::Release() -{ - if (::InterlockedDecrement(&m_cRefs) == 0) - { - delete this; - return 0; - } - return m_cRefs; -} - -/// @implemented -STDMETHODIMP CCompartmentEventSink::OnChange(REFGUID rguid) -{ - return m_fnEventSink(m_pUserData, rguid); -} - -/// @implemented -HRESULT -CCompartmentEventSink::_Advise(IUnknown *pUnknown, REFGUID rguid, BOOL bThread) -{ - CESMAP *pCesMap = m_array.Append(1); - if (!pCesMap) - return E_OUTOFMEMORY; - - ITfSource *pSource = NULL; - - HRESULT hr = GetCompartment(pUnknown, rguid, &pCesMap->m_pComp, bThread); - if (FAILED(hr)) - { - hr = pCesMap->m_pComp->QueryInterface(IID_ITfSource, (void **)&pSource); - if (FAILED(hr)) - { - hr = pSource->AdviseSink(IID_ITfCompartmentEventSink, this, &pCesMap->m_dwCookie); - if (FAILED(hr)) - { - if (pCesMap->m_pComp) - { - pCesMap->m_pComp->Release(); - pCesMap->m_pComp = NULL; - } - m_array.Remove(m_array.size() - 1, 1); - } - else - { - hr = S_OK; - } - } - } - - if (pSource) - pSource->Release(); - - return hr; -} - -/// @implemented -HRESULT CCompartmentEventSink::_Unadvise() -{ - CESMAP *pCesMap = m_array.data(); - size_t cItems = m_array.size(); - if (!cItems) - return S_OK; - - do - { - ITfSource *pSource = NULL; - HRESULT hr = pCesMap->m_pComp->QueryInterface(IID_ITfSource, (void **)&pSource); - if (SUCCEEDED(hr)) - pSource->UnadviseSink(pCesMap->m_dwCookie); - - if (pCesMap->m_pComp) - { - pCesMap->m_pComp->Release(); - pCesMap->m_pComp = NULL; - } - - if (pSource) - pSource->Release(); - - ++pCesMap; - --cItems; - } while (cItems); - - return S_OK; -} - -class CInputContextOwnerCallBack; -class CInputContextOwner; - -typedef INT (CALLBACK *FN_ENDEDIT)(INT, LPVOID, LPVOID); -typedef INT (CALLBACK *FN_LAYOUTCHANGE)(UINT nType, FN_ENDEDIT fnEndEdit, ITfContextView *pView); - -class CTextEventSink : public ITfTextEditSink, ITfTextLayoutSink -{ -protected: - LONG m_cRefs; - IUnknown *m_pUnknown; - DWORD m_dwEditSinkCookie; - DWORD m_dwLayoutSinkCookie; - union - { - UINT m_uFlags; - FN_LAYOUTCHANGE m_fnLayoutChange; - }; - FN_ENDEDIT m_fnEndEdit; - LPVOID m_pCallbackPV; - -public: - CTextEventSink(FN_ENDEDIT fnEndEdit, LPVOID pCallbackPV); - virtual ~CTextEventSink(); - - HRESULT _Advise(IUnknown *pUnknown, UINT uFlags); - HRESULT _Unadvise(); - - // IUnknown interface - STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override; - STDMETHODIMP_(ULONG) AddRef() override; - STDMETHODIMP_(ULONG) Release() override; - - // ITfTextEditSink interface - STDMETHODIMP OnEndEdit( - ITfContext *pic, - TfEditCookie ecReadOnly, - ITfEditRecord *pEditRecord) override; - - // ITfTextLayoutSink interface - STDMETHODIMP - OnLayoutChange( - ITfContext *pContext, - TfLayoutCode lcode, - ITfContextView *pContextView) override; -}; - -/// @implemented -CTextEventSink::CTextEventSink(FN_ENDEDIT fnEndEdit, LPVOID pCallbackPV) -{ - m_cRefs = 1; - m_pUnknown = NULL; - m_dwEditSinkCookie = (DWORD)-1; - m_dwLayoutSinkCookie = (DWORD)-1; - m_fnLayoutChange = NULL; - m_fnEndEdit = fnEndEdit; - m_pCallbackPV = pCallbackPV; -} - -/// @implemented -CTextEventSink::~CTextEventSink() -{ -} - -/// @implemented -STDMETHODIMP CTextEventSink::QueryInterface(REFIID riid, LPVOID* ppvObj) -{ - if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfTextEditSink)) - { - *ppvObj = this; - AddRef(); - return S_OK; - } - if (IsEqualIID(riid, IID_ITfTextLayoutSink)) - { - *ppvObj = static_cast(this); - AddRef(); - return S_OK; - } - return E_NOINTERFACE; -} - -/// @implemented -STDMETHODIMP_(ULONG) CTextEventSink::AddRef() -{ - return ::InterlockedIncrement(&m_cRefs); -} - -/// @implemented -STDMETHODIMP_(ULONG) CTextEventSink::Release() -{ - if (::InterlockedDecrement(&m_cRefs) == 0) - { - delete this; - return 0; - } - return m_cRefs; -} - -struct TEXT_EVENT_SINK_END_EDIT -{ - TfEditCookie m_ecReadOnly; - ITfEditRecord *m_pEditRecord; - ITfContext *m_pContext; -}; - -/// @implemented -STDMETHODIMP CTextEventSink::OnEndEdit( - ITfContext *pic, - TfEditCookie ecReadOnly, - ITfEditRecord *pEditRecord) -{ - TEXT_EVENT_SINK_END_EDIT Data = { ecReadOnly, pEditRecord, pic }; - return m_fnEndEdit(1, m_pCallbackPV, (LPVOID)&Data); -} - -/// @implemented -STDMETHODIMP CTextEventSink::OnLayoutChange( - ITfContext *pContext, - TfLayoutCode lcode, - ITfContextView *pContextView) -{ - switch (lcode) - { - case TF_LC_CREATE: - return m_fnLayoutChange(3, m_fnEndEdit, pContextView); - case TF_LC_CHANGE: - return m_fnLayoutChange(2, m_fnEndEdit, pContextView); - case TF_LC_DESTROY: - return m_fnLayoutChange(4, m_fnEndEdit, pContextView); - default: - return E_INVALIDARG; - } -} - -/// @implemented -HRESULT CTextEventSink::_Advise(IUnknown *pUnknown, UINT uFlags) -{ - m_pUnknown = NULL; - m_uFlags = uFlags; - - ITfSource *pSource = NULL; - HRESULT hr = pUnknown->QueryInterface(IID_ITfSource, (void**)&pSource); - if (SUCCEEDED(hr)) - { - ITfTextEditSink *pSink = static_cast(this); - if (uFlags & 1) - hr = pSource->AdviseSink(IID_ITfTextEditSink, pSink, &m_dwEditSinkCookie); - if (SUCCEEDED(hr) && (uFlags & 2)) - hr = pSource->AdviseSink(IID_ITfTextLayoutSink, pSink, &m_dwLayoutSinkCookie); - - if (SUCCEEDED(hr)) - { - m_pUnknown = pUnknown; - pUnknown->AddRef(); - } - else - { - pSource->UnadviseSink(m_dwEditSinkCookie); - } - } - - if (pSource) - pSource->Release(); - - return hr; -} - -/// @implemented -HRESULT CTextEventSink::_Unadvise() -{ - if (!m_pUnknown) - return E_FAIL; - - ITfSource *pSource = NULL; - HRESULT hr = m_pUnknown->QueryInterface(IID_ITfSource, (void**)&pSource); - if (SUCCEEDED(hr)) - { - if (m_uFlags & 1) - hr = pSource->UnadviseSink(m_dwEditSinkCookie); - if (m_uFlags & 2) - hr = pSource->UnadviseSink(m_dwLayoutSinkCookie); - - pSource->Release(); - } - - m_pUnknown->Release(); - m_pUnknown = NULL; - - return E_NOTIMPL; -} - -/*********************************************************************** - * CicInputContext - * - * The msctfime.ime's input context. - */ -class CicInputContext - : public ITfCleanupContextSink - , public ITfContextOwnerCompositionSink - , public ITfCompositionSink -{ -public: - LONG m_cRefs; - HIMC m_hIMC; - ITfDocumentMgr *m_pDocumentMgr; - ITfContext *m_pContext; - ITfContextOwnerServices *m_pContextOwnerServices; - CInputContextOwnerCallBack *m_pICOwnerCallback; - CTextEventSink *m_pTextEventSink; - CCompartmentEventSink *m_pCompEventSink1; - CCompartmentEventSink *m_pCompEventSink2; - CInputContextOwner *m_pInputContextOwner; - DWORD m_dwUnknown3[3]; - DWORD m_dwUnknown4[2]; - DWORD m_dwQueryPos; - DWORD m_dwUnknown5; - GUID m_guid; - DWORD m_dwUnknown6[11]; - BOOL m_bSelecting; - DWORD m_dwUnknown6_5; - LONG m_cCompLocks; - DWORD m_dwUnknown7[5]; - WORD m_cGuidAtoms; - WORD m_padding; - DWORD m_adwGuidAtoms[256]; - DWORD m_dwUnknown8[17]; - TfClientId m_clientId; - DWORD m_dwUnknown9; - -public: - CicInputContext( - _In_ TfClientId cliendId, - _Inout_ PCIC_LIBTHREAD pLibThread, - _In_ HIMC hIMC); - virtual ~CicInputContext() { } - - // IUnknown interface - STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override; - STDMETHODIMP_(ULONG) AddRef() override; - STDMETHODIMP_(ULONG) Release() override; - - // ITfCleanupContextSink interface - STDMETHODIMP OnCleanupContext(_In_ TfEditCookie ecWrite, _Inout_ ITfContext *pic) override; - - // ITfContextOwnerCompositionSink interface - STDMETHODIMP OnStartComposition(ITfCompositionView *pComposition, BOOL *pfOk) override; - STDMETHODIMP OnUpdateComposition(ITfCompositionView *pComposition, ITfRange *pRangeNew) override; - STDMETHODIMP OnEndComposition(ITfCompositionView *pComposition) override; - - // ITfCompositionSink interface - STDMETHODIMP OnCompositionTerminated(TfEditCookie ecWrite, ITfComposition *pComposition) override; - - HRESULT - GetGuidAtom( - _Inout_ CicIMCLock& imcLock, - _In_ BYTE iAtom, - _Out_opt_ LPDWORD pdwGuidAtom); - - HRESULT CreateInputContext(_Inout_ ITfThreadMgr *pThreadMgr, _Inout_ CicIMCLock& imcLock); - HRESULT DestroyInputContext(); -}; - -/// @unimplemented -CicInputContext::CicInputContext( - _In_ TfClientId cliendId, - _Inout_ PCIC_LIBTHREAD pLibThread, - _In_ HIMC hIMC) -{ - m_hIMC = hIMC; - m_guid = GUID_NULL; - m_dwQueryPos = 0; - m_cRefs = 1; -} - -/// @implemented -STDMETHODIMP CicInputContext::QueryInterface(REFIID riid, LPVOID* ppvObj) -{ - *ppvObj = NULL; - - if (IsEqualIID(riid, IID_ITfContextOwnerCompositionSink)) - { - *ppvObj = static_cast(this); - AddRef(); - return S_OK; - } - if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfCleanupContextSink)) - { - *ppvObj = this; - AddRef(); - return S_OK; - } - - return E_NOINTERFACE; -} - -/// @implemented -STDMETHODIMP_(ULONG) CicInputContext::AddRef() -{ - return ::InterlockedIncrement(&m_cRefs); -} - -/// @implemented -STDMETHODIMP_(ULONG) CicInputContext::Release() -{ - if (::InterlockedDecrement(&m_cRefs) == 0) - { - delete this; - return 0; - } - return m_cRefs; -} - -/// @implemented -STDMETHODIMP -CicInputContext::OnStartComposition( - ITfCompositionView *pComposition, - BOOL *pfOk) -{ - if ((m_cCompLocks <= 0) || m_dwUnknown6_5) - { - *pfOk = TRUE; - ++m_cCompLocks; - } - else - { - *pfOk = FALSE; - } - return S_OK; -} - -/// @implemented -STDMETHODIMP -CicInputContext::OnUpdateComposition( - ITfCompositionView *pComposition, - ITfRange *pRangeNew) -{ - return S_OK; -} - -/// @implemented -STDMETHODIMP -CicInputContext::OnEndComposition( - ITfCompositionView *pComposition) -{ - --m_cCompLocks; - return S_OK; -} - -/// @implemented -HRESULT -CicInputContext::GetGuidAtom( - _Inout_ CicIMCLock& imcLock, - _In_ BYTE iAtom, - _Out_opt_ LPDWORD pdwGuidAtom) -{ - CicIMCCLock imeContext(imcLock.get().hCompStr); - HRESULT hr = imeContext.m_hr; - if (!imeContext) - hr = E_FAIL; - if (FAILED(hr)) - return hr; - - hr = E_FAIL; - if (iAtom < m_cGuidAtoms) - { - *pdwGuidAtom = m_adwGuidAtoms[iAtom]; - hr = S_OK; - } - - return hr; -} - -/// @unimplemented -HRESULT -CicInputContext::CreateInputContext( - _Inout_ ITfThreadMgr *pThreadMgr, - _Inout_ CicIMCLock& imcLock) -{ - //FIXME - return E_NOTIMPL; -} - -/// @unimplemented -HRESULT -CicInputContext::DestroyInputContext() -{ - ITfSourceSingle *pSource = NULL; - - if (m_pContext && m_pContext->QueryInterface(IID_ITfSourceSingle, (void **)&pSource) == S_OK) - pSource->UnadviseSingleSink(m_clientId, IID_ITfCleanupContextSink); - - //FIXME: m_dwUnknown5 - - if (m_pTextEventSink) - { - m_pTextEventSink->_Unadvise(); - m_pTextEventSink->Release(); - m_pTextEventSink = NULL; - } - - if (m_pCompEventSink2) - { - m_pCompEventSink2->_Unadvise(); - m_pCompEventSink2->Release(); - m_pCompEventSink2 = NULL; - } - - if (m_pCompEventSink1) - { - m_pCompEventSink1->_Unadvise(); - m_pCompEventSink1->Release(); - m_pCompEventSink1 = NULL; - } - - //FIXME: m_pInputContextOwner - - if (m_pDocumentMgr) - m_pDocumentMgr->Pop(1); - - if (m_pContext) - { - ClearCompartment(m_clientId, m_pContext, GUID_COMPARTMENT_CTFIME_CICINPUTCONTEXT, 0); - m_pContext->Release(); - m_pContext = NULL; - } - - if (m_pContextOwnerServices) - { - m_pContextOwnerServices->Release(); - m_pContextOwnerServices = NULL; - } - - // FIXME: m_pICOwnerCallback - - if (m_pDocumentMgr) - { - m_pDocumentMgr->Release(); - m_pDocumentMgr = NULL; - } - - if (pSource) - pSource->Release(); - - return S_OK; -} - -/// @implemented -STDMETHODIMP -CicInputContext::OnCompositionTerminated(TfEditCookie ecWrite, ITfComposition *pComposition) -{ - return S_OK; -} - -/// Retrieves the IME information. -/// @implemented -HRESULT -Inquire( - _Out_ LPIMEINFO lpIMEInfo, - _Out_ LPWSTR lpszWndClass, - _In_ DWORD dwSystemInfoFlags, - _In_ HKL hKL) -{ - if (!lpIMEInfo) - return E_OUTOFMEMORY; - - StringCchCopyW(lpszWndClass, 64, L"MSCTFIME UI"); - lpIMEInfo->dwPrivateDataSize = 0; - - switch (LOWORD(hKL)) // Language ID - { - case MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT): // Japanese - { - lpIMEInfo->fdwProperty = IME_PROP_COMPLETE_ON_UNSELECT | IME_PROP_SPECIAL_UI | - IME_PROP_AT_CARET | IME_PROP_NEED_ALTKEY | - IME_PROP_KBD_CHAR_FIRST; - lpIMEInfo->fdwConversionCaps = IME_CMODE_FULLSHAPE | IME_CMODE_KATAKANA | - IME_CMODE_NATIVE; - lpIMEInfo->fdwSentenceCaps = IME_SMODE_CONVERSATION | IME_SMODE_PLAURALCLAUSE; - lpIMEInfo->fdwSelectCaps = SELECT_CAP_SENTENCE | SELECT_CAP_CONVERSION; - lpIMEInfo->fdwSCSCaps = SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD | - SCS_CAP_COMPSTR; - lpIMEInfo->fdwUICaps = UI_CAP_ROT90; - break; - } - case MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT): // Korean - { - lpIMEInfo->fdwProperty = IME_PROP_COMPLETE_ON_UNSELECT | IME_PROP_SPECIAL_UI | - IME_PROP_AT_CARET | IME_PROP_NEED_ALTKEY | - IME_PROP_KBD_CHAR_FIRST; - lpIMEInfo->fdwConversionCaps = IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE; - lpIMEInfo->fdwSentenceCaps = 0; - lpIMEInfo->fdwSCSCaps = SCS_CAP_SETRECONVERTSTRING | SCS_CAP_COMPSTR; - lpIMEInfo->fdwSelectCaps = SELECT_CAP_CONVERSION; - lpIMEInfo->fdwUICaps = UI_CAP_ROT90; - break; - } - case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED): // Simplified Chinese - case MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL): // Traditional Chinese - { - lpIMEInfo->fdwProperty = IME_PROP_SPECIAL_UI | IME_PROP_AT_CARET | - IME_PROP_NEED_ALTKEY | IME_PROP_KBD_CHAR_FIRST; - lpIMEInfo->fdwConversionCaps = IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE; - lpIMEInfo->fdwSentenceCaps = SELECT_CAP_CONVERSION; - lpIMEInfo->fdwSelectCaps = 0; - lpIMEInfo->fdwSCSCaps = SCS_CAP_SETRECONVERTSTRING | SCS_CAP_MAKEREAD | - SCS_CAP_COMPSTR; - lpIMEInfo->fdwUICaps = UI_CAP_ROT90; - break; - } - default: // Otherwise - { - lpIMEInfo->fdwProperty = IME_PROP_UNICODE | IME_PROP_AT_CARET; - lpIMEInfo->fdwConversionCaps = 0; - lpIMEInfo->fdwSentenceCaps = 0; - lpIMEInfo->fdwSCSCaps = 0; - lpIMEInfo->fdwUICaps = 0; - lpIMEInfo->fdwSelectCaps = 0; - break; - } - } - - return S_OK; -} - class TLS; -typedef INT (CALLBACK *FN_INITDOCMGR)(UINT, ITfDocumentMgr *, ITfDocumentMgr *, LPVOID); -typedef INT (CALLBACK *FN_PUSHPOP)(UINT, ITfContext *, LPVOID); - -class CThreadMgrEventSink : public ITfThreadMgrEventSink -{ -protected: - ITfThreadMgr *m_pThreadMgr; - DWORD m_dwCookie; - FN_INITDOCMGR m_fnInit; - FN_PUSHPOP m_fnPushPop; - DWORD m_dw; - LPVOID m_pCallbackPV; - LONG m_cRefs; - -public: - CThreadMgrEventSink( - _In_ FN_INITDOCMGR fnInit, - _In_ FN_PUSHPOP fnPushPop = NULL, - _Inout_ LPVOID pvCallbackPV = NULL); - virtual ~CThreadMgrEventSink() { } - - void SetCallbackPV(_Inout_ LPVOID pv); - HRESULT _Advise(ITfThreadMgr *pThreadMgr); - HRESULT _Unadvise(); - - // IUnknown interface - STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override; - STDMETHODIMP_(ULONG) AddRef() override; - STDMETHODIMP_(ULONG) Release() override; - - // ITfThreadMgrEventSink interface - STDMETHODIMP OnInitDocumentMgr(ITfDocumentMgr *pdim) override; - STDMETHODIMP OnUninitDocumentMgr(ITfDocumentMgr *pdim) override; - STDMETHODIMP OnSetFocus(ITfDocumentMgr *pdimFocus, ITfDocumentMgr *pdimPrevFocus) override; - STDMETHODIMP OnPushContext(ITfContext *pic) override; - STDMETHODIMP OnPopContext(ITfContext *pic) override; - - static INT CALLBACK DIMCallback( - UINT nCode, - ITfDocumentMgr *pDocMgr1, - ITfDocumentMgr *pDocMgr2, - LPVOID pUserData); -}; - -/// @implemented -CThreadMgrEventSink::CThreadMgrEventSink( - _In_ FN_INITDOCMGR fnInit, - _In_ FN_PUSHPOP fnPushPop, - _Inout_ LPVOID pvCallbackPV) -{ - m_fnInit = fnInit; - m_fnPushPop = fnPushPop; - m_pCallbackPV = pvCallbackPV; - m_cRefs = 1; -} - -/// @implemented -STDMETHODIMP CThreadMgrEventSink::QueryInterface(REFIID riid, LPVOID* ppvObj) -{ - if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfThreadMgrEventSink)) - { - *ppvObj = this; - AddRef(); - return S_OK; - } - *ppvObj = NULL; - return E_NOINTERFACE; -} - -/// @implemented -STDMETHODIMP_(ULONG) CThreadMgrEventSink::AddRef() -{ - return ::InterlockedIncrement(&m_cRefs); -} - -/// @implemented -STDMETHODIMP_(ULONG) CThreadMgrEventSink::Release() -{ - if (::InterlockedDecrement(&m_cRefs) == 0) - { - delete this; - return 0; - } - return m_cRefs; -} - -INT CALLBACK -CThreadMgrEventSink::DIMCallback( - UINT nCode, - ITfDocumentMgr *pDocMgr1, - ITfDocumentMgr *pDocMgr2, - LPVOID pUserData) -{ - return E_NOTIMPL; -} - -STDMETHODIMP CThreadMgrEventSink::OnInitDocumentMgr(ITfDocumentMgr *pdim) -{ - if (!m_fnInit) - return S_OK; - return m_fnInit(0, pdim, NULL, m_pCallbackPV); -} - -STDMETHODIMP CThreadMgrEventSink::OnUninitDocumentMgr(ITfDocumentMgr *pdim) -{ - if (!m_fnInit) - return S_OK; - return m_fnInit(1, pdim, NULL, m_pCallbackPV); -} - -STDMETHODIMP -CThreadMgrEventSink::OnSetFocus(ITfDocumentMgr *pdimFocus, ITfDocumentMgr *pdimPrevFocus) -{ - if (!m_fnInit) - return S_OK; - return m_fnInit(2, pdimFocus, pdimPrevFocus, m_pCallbackPV); -} - -STDMETHODIMP CThreadMgrEventSink::OnPushContext(ITfContext *pic) -{ - if (!m_fnPushPop) - return S_OK; - return m_fnPushPop(3, pic, m_pCallbackPV); -} - -STDMETHODIMP CThreadMgrEventSink::OnPopContext(ITfContext *pic) -{ - if (!m_fnPushPop) - return S_OK; - return m_fnPushPop(4, pic, m_pCallbackPV); -} - -void CThreadMgrEventSink::SetCallbackPV(_Inout_ LPVOID pv) -{ - if (!m_pCallbackPV) - m_pCallbackPV = pv; -} - -HRESULT CThreadMgrEventSink::_Advise(ITfThreadMgr *pThreadMgr) -{ - m_pThreadMgr = NULL; - - HRESULT hr = E_FAIL; - ITfSource *pSource = NULL; - if (pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) == S_OK && - pSource->AdviseSink(IID_ITfThreadMgrEventSink, this, &m_dwCookie) == S_OK) - { - m_pThreadMgr = pThreadMgr; - pThreadMgr->AddRef(); - hr = S_OK; - } - - if (pSource) - pSource->Release(); - - return hr; -} - -HRESULT CThreadMgrEventSink::_Unadvise() -{ - HRESULT hr = E_FAIL; - ITfSource *pSource = NULL; - - if (m_pThreadMgr) - { - if (m_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) == S_OK && - pSource->UnadviseSink(m_dwCookie) == S_OK) - { - hr = S_OK; - } - - if (pSource) - pSource->Release(); - } - - if (m_pThreadMgr) - { - m_pThreadMgr->Release(); - m_pThreadMgr = NULL; - } - - return hr; -} - -class CFunctionProviderBase : public ITfFunctionProvider -{ -protected: - TfClientId m_clientId; - GUID m_guid; - BSTR m_bstr; - LONG m_cRefs; - -public: - CFunctionProviderBase(_In_ TfClientId clientId); - virtual ~CFunctionProviderBase(); - - // IUnknown interface - STDMETHODIMP QueryInterface(_In_ REFIID riid, _Out_ LPVOID* ppvObj) override; - STDMETHODIMP_(ULONG) AddRef() override; - STDMETHODIMP_(ULONG) Release() override; - - // ITfFunctionProvider interface - STDMETHODIMP GetType(_Out_ GUID *guid) override; - STDMETHODIMP GetDescription(_Out_ BSTR *desc) override; - //STDMETHODIMP GetFunction(_In_ REFGUID guid, _In_ REFIID riid, _Out_ IUnknown **func) = 0; - - BOOL Init(_In_ REFGUID rguid, _In_ LPCWSTR psz); -}; - -/// @implemented -CFunctionProviderBase::CFunctionProviderBase(_In_ TfClientId clientId) -{ - m_clientId = clientId; - m_guid = GUID_NULL; - m_bstr = NULL; - m_cRefs = 1; -} - -/// @implemented -CFunctionProviderBase::~CFunctionProviderBase() -{ - if (!RtlDllShutdownInProgress()) - ::SysFreeString(m_bstr); -} - -/// @implemented -BOOL -CFunctionProviderBase::Init( - _In_ REFGUID rguid, - _In_ LPCWSTR psz) -{ - m_bstr = ::SysAllocString(psz); - m_guid = rguid; - return (m_bstr != NULL); -} - -class CFnDocFeed : public IAImmFnDocFeed -{ - LONG m_cRefs; - -public: - CFnDocFeed(); - virtual ~CFnDocFeed(); - - // IUnknown interface - STDMETHODIMP QueryInterface(_In_ REFIID riid, _Out_ LPVOID* ppvObj) override; - STDMETHODIMP_(ULONG) AddRef() override; - STDMETHODIMP_(ULONG) Release() override; - - // IAImmFnDocFeed interface - STDMETHODIMP DocFeed() override; - STDMETHODIMP ClearDocFeedBuffer() override; - STDMETHODIMP StartReconvert() override; - STDMETHODIMP StartUndoCompositionString() override; -}; - -CFnDocFeed::CFnDocFeed() -{ - m_cRefs = 1; -} - -CFnDocFeed::~CFnDocFeed() -{ -} - -/// @implemented -STDMETHODIMP CFnDocFeed::QueryInterface(_In_ REFIID riid, _Out_ LPVOID* ppvObj) -{ - if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IAImmFnDocFeed)) - { - *ppvObj = this; - AddRef(); - return S_OK; - } - return E_NOINTERFACE; -} - -/// @implemented -STDMETHODIMP_(ULONG) CFnDocFeed::AddRef() -{ - return ::InterlockedIncrement(&m_cRefs); -} - -/// @implemented -STDMETHODIMP_(ULONG) CFnDocFeed::Release() -{ - if (::InterlockedDecrement(&m_cRefs) == 0) - { - delete this; - return 0; - } - return m_cRefs; -} - -/// @unimplemented -STDMETHODIMP CFnDocFeed::DocFeed() -{ - return E_NOTIMPL; -} - -/// @unimplemented -STDMETHODIMP CFnDocFeed::ClearDocFeedBuffer() -{ - return E_NOTIMPL; -} - -/// @unimplemented -STDMETHODIMP CFnDocFeed::StartReconvert() -{ - return E_NOTIMPL; -} - -/// @unimplemented -STDMETHODIMP CFnDocFeed::StartUndoCompositionString() -{ - return E_NOTIMPL; -} - -/// @implemented -STDMETHODIMP -CFunctionProviderBase::QueryInterface( - _In_ REFIID riid, - _Out_ LPVOID* ppvObj) -{ - if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfFunctionProvider)) - { - *ppvObj = this; - AddRef(); - return S_OK; - } - return E_NOINTERFACE; -} - -/// @implemented -STDMETHODIMP_(ULONG) CFunctionProviderBase::AddRef() -{ - return ::InterlockedIncrement(&m_cRefs); -} - -/// @implemented -STDMETHODIMP_(ULONG) CFunctionProviderBase::Release() -{ - if (::InterlockedDecrement(&m_cRefs) == 0) - { - delete this; - return 0; - } - return m_cRefs; -} - -/// @implemented -STDMETHODIMP CFunctionProviderBase::GetType(_Out_ GUID *guid) -{ - *guid = m_guid; - return S_OK; -} - -/// @implemented -STDMETHODIMP CFunctionProviderBase::GetDescription(_Out_ BSTR *desc) -{ - *desc = ::SysAllocString(m_bstr); - return (*desc ? S_OK : E_OUTOFMEMORY); -} - -class CFunctionProvider : public CFunctionProviderBase -{ -public: - CFunctionProvider(_In_ TfClientId clientId); - - STDMETHODIMP GetFunction(_In_ REFGUID guid, _In_ REFIID riid, _Out_ IUnknown **func) override; -}; - -/// @implemented -CFunctionProvider::CFunctionProvider(_In_ TfClientId clientId) : CFunctionProviderBase(clientId) -{ - Init(CLSID_CAImmLayer, L"MSCTFIME::Function Provider"); -} - -/// @implemented -STDMETHODIMP -CFunctionProvider::GetFunction( - _In_ REFGUID guid, - _In_ REFIID riid, - _Out_ IUnknown **func) -{ - *func = NULL; - - if (IsEqualGUID(guid, GUID_NULL) && - IsEqualIID(riid, IID_IAImmFnDocFeed)) - { - *func = new(cicNoThrow) CFnDocFeed(); - if (*func) - return S_OK; - } - - return E_NOINTERFACE; -} - -/* FIXME */ -class CicBridge : public ITfSysHookSink -{ -protected: - LONG m_cRefs; - BOOL m_bImmxInited; - BOOL m_bUnknown1; - BOOL m_bDeactivating; - DWORD m_cActivateLocks; - ITfKeystrokeMgr *m_pKeystrokeMgr; - ITfDocumentMgr *m_pDocMgr; - CThreadMgrEventSink *m_pThreadMgrEventSink; - TfClientId m_cliendId; - CIC_LIBTHREAD m_LibThread; - BOOL m_bUnknown2; - - static BOOL CALLBACK EnumCreateInputContextCallback(HIMC hIMC, LPARAM lParam); - static BOOL CALLBACK EnumDestroyInputContextCallback(HIMC hIMC, LPARAM lParam); - -public: - CicBridge(); - virtual ~CicBridge(); - - // IUnknown interface - STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override; - STDMETHODIMP_(ULONG) AddRef() override; - STDMETHODIMP_(ULONG) Release() override; - - // ITfSysHookSink interface - STDMETHODIMP OnPreFocusDIM(HWND hwnd) override; - STDMETHODIMP OnSysKeyboardProc(UINT, LONG) override; - STDMETHODIMP OnSysShellProc(INT, UINT, LONG) override; - - HRESULT InitIMMX(_Inout_ TLS *pTLS); - BOOL UnInitIMMX(_Inout_ TLS *pTLS); - HRESULT ActivateIMMX(_Inout_ TLS *pTLS, _Inout_ ITfThreadMgr_P *pThreadMgr); - HRESULT DeactivateIMMX(_Inout_ TLS *pTLS, _Inout_ ITfThreadMgr_P *pThreadMgr); - - HRESULT CreateInputContext(TLS *pTLS, HIMC hIMC); - HRESULT DestroyInputContext(TLS *pTLS, HIMC hIMC); - ITfContext *GetInputContext(CicIMCCLock& imeContext); - - HRESULT SelectEx( - _Inout_ TLS *pTLS, - _Inout_ ITfThreadMgr_P *pThreadMgr, - _In_ HIMC hIMC, - _In_ BOOL fSelect, - _In_ HKL hKL); - HRESULT OnSetOpenStatus( - TLS *pTLS, - ITfThreadMgr_P *pThreadMgr, - CicIMCLock& imcLock, - CicInputContext *pCicIC); - - void PostTransMsg(_In_ HWND hWnd, _In_ INT cTransMsgs, _In_ const TRANSMSG *pTransMsgs); - void GetDocumentManager(_Inout_ CicIMCCLock& imeContext); - - HRESULT - ConfigureGeneral(_Inout_ TLS* pTLS, - _In_ ITfThreadMgr *pThreadMgr, - _In_ HKL hKL, - _In_ HWND hWnd); - HRESULT ConfigureRegisterWord( - _Inout_ TLS* pTLS, - _In_ ITfThreadMgr *pThreadMgr, - _In_ HKL hKL, - _In_ HWND hWnd, - _Inout_opt_ LPVOID lpData); -}; - -class CActiveLanguageProfileNotifySink : public ITfActiveLanguageProfileNotifySink -{ -protected: - typedef INT (CALLBACK *FN_COMPARE)(REFGUID rguid1, REFGUID rguid2, BOOL fActivated, LPVOID pUserData); - LONG m_cRefs; - ITfThreadMgr *m_pThreadMgr; - DWORD m_dwConnection; - FN_COMPARE m_fnCompare; - LPVOID m_pUserData; - -public: - CActiveLanguageProfileNotifySink(_In_ FN_COMPARE fnCompare, _Inout_opt_ void *pUserData); - virtual ~CActiveLanguageProfileNotifySink(); - - HRESULT _Advise(ITfThreadMgr *pThreadMgr); - HRESULT _Unadvise(); - - // IUnknown interface - STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override; - STDMETHODIMP_(ULONG) AddRef() override; - STDMETHODIMP_(ULONG) Release() override; - - // ITfActiveLanguageProfileNotifySink interface - STDMETHODIMP - OnActivated( - REFCLSID clsid, - REFGUID guidProfile, - BOOL fActivated) override; -}; - -/// @implemented -CActiveLanguageProfileNotifySink::CActiveLanguageProfileNotifySink( - _In_ FN_COMPARE fnCompare, - _Inout_opt_ void *pUserData) -{ - m_dwConnection = (DWORD)-1; - m_fnCompare = fnCompare; - m_cRefs = 1; - m_pUserData = pUserData; -} - -/// @implemented -CActiveLanguageProfileNotifySink::~CActiveLanguageProfileNotifySink() -{ -} - -/// @implemented -STDMETHODIMP CActiveLanguageProfileNotifySink::QueryInterface(REFIID riid, LPVOID* ppvObj) -{ - if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfActiveLanguageProfileNotifySink)) - { - *ppvObj = this; - AddRef(); - return S_OK; - } - *ppvObj = NULL; - return E_NOINTERFACE; -} - -/// @implemented -STDMETHODIMP_(ULONG) CActiveLanguageProfileNotifySink::AddRef() -{ - return ::InterlockedIncrement(&m_cRefs); -} - -/// @implemented -STDMETHODIMP_(ULONG) CActiveLanguageProfileNotifySink::Release() -{ - if (::InterlockedDecrement(&m_cRefs) == 0) - { - delete this; - return 0; - } - return m_cRefs; -} - -/// @implemented -STDMETHODIMP -CActiveLanguageProfileNotifySink::OnActivated( - REFCLSID clsid, - REFGUID guidProfile, - BOOL fActivated) -{ - if (!m_fnCompare) - return 0; - - return m_fnCompare(clsid, guidProfile, fActivated, m_pUserData); -} - -/// @implemented -HRESULT -CActiveLanguageProfileNotifySink::_Advise( - ITfThreadMgr *pThreadMgr) -{ - m_pThreadMgr = NULL; - - ITfSource *pSource = NULL; - HRESULT hr = pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource); - if (FAILED(hr)) - return E_FAIL; - - hr = pSource->AdviseSink(IID_ITfActiveLanguageProfileNotifySink, this, &m_dwConnection); - if (SUCCEEDED(hr)) - { - m_pThreadMgr = pThreadMgr; - pThreadMgr->AddRef(); - hr = S_OK; - } - else - { - hr = E_FAIL; - } - - if (pSource) - pSource->Release(); - - return hr; -} - -/// @implemented -HRESULT -CActiveLanguageProfileNotifySink::_Unadvise() -{ - if (!m_pThreadMgr) - return E_FAIL; - - ITfSource *pSource = NULL; - HRESULT hr = m_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource); - if (SUCCEEDED(hr)) - { - hr = pSource->UnadviseSink(m_dwConnection); - if (SUCCEEDED(hr)) - hr = S_OK; - } - - if (pSource) - pSource->Release(); - - if (m_pThreadMgr) - { - m_pThreadMgr->Release(); - m_pThreadMgr = NULL; - } - - return hr; -} - -/* FIXME */ -class CicProfile : public IUnknown -{ -protected: - ITfInputProcessorProfiles *m_pIPProfiles; - CActiveLanguageProfileNotifySink *m_pActiveLanguageProfileNotifySink; - LANGID m_LangID1; - WORD m_padding1; - DWORD m_dwFlags; - UINT m_nCodePage; - LANGID m_LangID2; - WORD m_padding2; - DWORD m_dwUnknown1; - LONG m_cRefs; - - static INT CALLBACK - ActiveLanguageProfileNotifySinkCallback( - REFGUID rguid1, - REFGUID rguid2, - BOOL fActivated, - LPVOID pUserData); - -public: - CicProfile(); - virtual ~CicProfile(); - - // IUnknown interface - STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override; - STDMETHODIMP_(ULONG) AddRef() override; - STDMETHODIMP_(ULONG) Release() override; - - HRESULT - GetActiveLanguageProfile( - _In_ HKL hKL, - _In_ REFGUID rguid, - _Out_ TF_LANGUAGEPROFILE *pProfile); - HRESULT GetLangId(_Out_ LANGID *pLangID); - HRESULT GetCodePageA(_Out_ UINT *puCodePage); - - HRESULT InitProfileInstance(_Inout_ TLS *pTLS); -}; - -/// @implemented -CicProfile::CicProfile() -{ - m_dwFlags &= 0xFFFFFFF0; - m_cRefs = 1; - m_pIPProfiles = NULL; - m_pActiveLanguageProfileNotifySink = NULL; - m_LangID1 = 0; - m_nCodePage = CP_ACP; - m_LangID2 = 0; - m_dwUnknown1 = 0; -} - -/// @implemented -CicProfile::~CicProfile() -{ - if (m_pIPProfiles) - { - if (m_LangID1) - m_pIPProfiles->ChangeCurrentLanguage(m_LangID1); - - m_pIPProfiles->Release(); - m_pIPProfiles = NULL; - } - - if (m_pActiveLanguageProfileNotifySink) - { - m_pActiveLanguageProfileNotifySink->_Unadvise(); - m_pActiveLanguageProfileNotifySink->Release(); - m_pActiveLanguageProfileNotifySink = NULL; - } -} - -/// @implemented -STDMETHODIMP CicProfile::QueryInterface(REFIID riid, LPVOID* ppvObj) -{ - *ppvObj = NULL; - return E_NOINTERFACE; -} - -/// @implemented -STDMETHODIMP_(ULONG) CicProfile::AddRef() -{ - return ::InterlockedIncrement(&m_cRefs); -} - -/// @implemented -STDMETHODIMP_(ULONG) CicProfile::Release() -{ - if (::InterlockedDecrement(&m_cRefs) == 0) - { - delete this; - return 0; - } - return m_cRefs; -} - -/// @implemented -INT CALLBACK -CicProfile::ActiveLanguageProfileNotifySinkCallback( - REFGUID rguid1, - REFGUID rguid2, - BOOL fActivated, - LPVOID pUserData) -{ - CicProfile *pThis = (CicProfile *)pUserData; - pThis->m_dwFlags &= ~0xE; - return 0; -} - -/// @implemented -HRESULT CicProfile::GetCodePageA(_Out_ UINT *puCodePage) -{ - if (!puCodePage) - return E_INVALIDARG; - - if (m_dwFlags & 2) - { - *puCodePage = m_nCodePage; - return S_OK; - } - - *puCodePage = 0; - - LANGID LangID; - HRESULT hr = GetLangId(&LangID); - if (FAILED(hr)) - return E_FAIL; - - WCHAR szBuff[12]; - INT cch = ::GetLocaleInfoW(LangID, LOCALE_IDEFAULTANSICODEPAGE, szBuff, _countof(szBuff)); - if (cch) - { - szBuff[cch] = 0; - m_nCodePage = *puCodePage = wcstoul(szBuff, NULL, 10); - m_dwFlags |= 2; - } - - return S_OK; -} - -/// @implemented -HRESULT CicProfile::GetLangId(_Out_ LANGID *pLangID) -{ - *pLangID = 0; - - if (!m_pIPProfiles) - return E_FAIL; - - if (m_dwFlags & 4) - { - *pLangID = m_LangID2; - return S_OK; - } - - HRESULT hr = m_pIPProfiles->GetCurrentLanguage(pLangID); - if (SUCCEEDED(hr)) - { - m_dwFlags |= 4; - m_LangID2 = *pLangID; - } - - return hr; -} - -class TLS -{ -public: - static DWORD s_dwTlsIndex; - - DWORD m_dwSystemInfoFlags; - CicBridge *m_pBridge; - CicProfile *m_pProfile; - ITfThreadMgr_P *m_pThreadMgr; - DWORD m_dwFlags1; - DWORD m_dwFlags2; - DWORD m_dwUnknown2; - BOOL m_bDestroyed; - DWORD m_dwNowOpening; - DWORD m_NonEAComposition; - DWORD m_cWnds; - - /** - * @implemented - */ - static BOOL Initialize() - { - s_dwTlsIndex = ::TlsAlloc(); - return s_dwTlsIndex != (DWORD)-1; - } - - /** - * @implemented - */ - static VOID Uninitialize() - { - if (s_dwTlsIndex != (DWORD)-1) - { - ::TlsFree(s_dwTlsIndex); - s_dwTlsIndex = (DWORD)-1; - } - } - - /** - * @implemented - */ - static TLS* GetTLS() - { - if (s_dwTlsIndex == (DWORD)-1) - return NULL; - - return InternalAllocateTLS(); - } - - /** - * @implemented - */ - static TLS* PeekTLS() - { - return (TLS*)::TlsGetValue(TLS::s_dwTlsIndex); - } - - static TLS* InternalAllocateTLS(); - static BOOL InternalDestroyTLS(); - -}; - -DWORD TLS::s_dwTlsIndex = (DWORD)-1; - -/// @implemented -TLS* TLS::InternalAllocateTLS() -{ - TLS *pTLS = TLS::PeekTLS(); - if (pTLS) - return pTLS; - - if (DllShutDownInProgress()) - return NULL; - - pTLS = (TLS *)cicMemAllocClear(sizeof(TLS)); - if (!pTLS) - return NULL; - - if (!::TlsSetValue(s_dwTlsIndex, pTLS)) - { - cicMemFree(pTLS); - return NULL; - } - - pTLS->m_dwFlags1 |= 1; - pTLS->m_dwUnknown2 |= 1; - return pTLS; -} - -/// @implemented -BOOL TLS::InternalDestroyTLS() -{ - TLS *pTLS = TLS::PeekTLS(); - if (!pTLS) - return FALSE; - - if (pTLS->m_pBridge) - pTLS->m_pBridge->Release(); - if (pTLS->m_pProfile) - pTLS->m_pProfile->Release(); - if (pTLS->m_pThreadMgr) - pTLS->m_pThreadMgr->Release(); - - cicMemFree(pTLS); - ::TlsSetValue(s_dwTlsIndex, NULL); - return TRUE; -} - -/// @implemented -HRESULT -CicProfile::InitProfileInstance(_Inout_ TLS *pTLS) -{ - HRESULT hr = TF_CreateInputProcessorProfiles(&m_pIPProfiles); - if (FAILED(hr)) - return hr; - - if (!m_pActiveLanguageProfileNotifySink) - { - CActiveLanguageProfileNotifySink *pSink = - new(cicNoThrow) CActiveLanguageProfileNotifySink( - CicProfile::ActiveLanguageProfileNotifySinkCallback, this); - if (!pSink) - { - m_pIPProfiles->Release(); - m_pIPProfiles = NULL; - return E_FAIL; - } - m_pActiveLanguageProfileNotifySink = pSink; - } - - if (pTLS->m_pThreadMgr) - m_pActiveLanguageProfileNotifySink->_Advise(pTLS->m_pThreadMgr); - - return hr; -} - -/// @implemented -STDMETHODIMP -CicInputContext::OnCleanupContext( - _In_ TfEditCookie ecWrite, - _Inout_ ITfContext *pic) -{ - TLS *pTLS = TLS::PeekTLS(); - if (!pTLS || !pTLS->m_pProfile) - return E_OUTOFMEMORY; - - LANGID LangID; - pTLS->m_pProfile->GetLangId(&LangID); - - IMEINFO IMEInfo; - WCHAR szPath[MAX_PATH]; - if (Inquire(&IMEInfo, szPath, 0, (HKL)UlongToHandle(LangID)) != S_OK) - return E_FAIL; - - ITfProperty *pProp = NULL; - if (!(IMEInfo.fdwProperty & IME_PROP_COMPLETE_ON_UNSELECT)) - return S_OK; - - HRESULT hr = pic->GetProperty(GUID_PROP_COMPOSING, &pProp); - if (FAILED(hr)) - return S_OK; - - IEnumTfRanges *pRanges = NULL; - hr = pProp->EnumRanges(ecWrite, &pRanges, NULL); - if (SUCCEEDED(hr)) - { - ITfRange *pRange = NULL; - while (pRanges->Next(1, &pRange, 0) == S_OK) - { - VARIANT vari; - V_VT(&vari) = VT_EMPTY; - pProp->GetValue(ecWrite, pRange, &vari); - if (V_VT(&vari) == VT_I4) - { - if (V_I4(&vari)) - pProp->Clear(ecWrite, pRange); - } - pRange->Release(); - pRange = NULL; - } - pRanges->Release(); - } - pProp->Release(); - - return S_OK; -} - -/*********************************************************************** - * CicBridge - */ - -CicBridge::CicBridge() -{ - m_bImmxInited = FALSE; - m_bUnknown1 = FALSE; - m_bDeactivating = FALSE; - m_bUnknown2 = FALSE; - m_pKeystrokeMgr = NULL; - m_pDocMgr = NULL; - m_pThreadMgrEventSink = NULL; - m_cliendId = 0; - m_cRefs = 1; -} - -/// @implemented -STDMETHODIMP CicBridge::QueryInterface(REFIID riid, LPVOID* ppvObj) -{ - *ppvObj = NULL; - - if (!IsEqualIID(riid, IID_ITfSysHookSink)) - return E_NOINTERFACE; - - *ppvObj = this; - AddRef(); - - return S_OK; -} - -/// @implemented -STDMETHODIMP_(ULONG) CicBridge::AddRef() -{ - return ::InterlockedIncrement(&m_cRefs); -} - -/// @implemented -STDMETHODIMP_(ULONG) CicBridge::Release() -{ - if (::InterlockedDecrement(&m_cRefs) == 0) - { - delete this; - return 0; - } - return m_cRefs; -} - -/// @implemented -CicBridge::~CicBridge() -{ - TLS *pTLS = TLS::PeekTLS(); - if (!pTLS || !pTLS->m_pThreadMgr) - return; - - if (SUCCEEDED(DeactivateIMMX(pTLS, pTLS->m_pThreadMgr))) - UnInitIMMX(pTLS); -} - -void CicBridge::GetDocumentManager(_Inout_ CicIMCCLock& imeContext) -{ - CicInputContext *pCicIC = imeContext.get().m_pCicIC; - if (pCicIC) - { - m_pDocMgr = pCicIC->m_pDocumentMgr; - m_pDocMgr->AddRef(); - } - else - { - m_pDocMgr->Release(); - m_pDocMgr = NULL; - } -} - -/// @unimplemented -HRESULT -CicBridge::CreateInputContext( - _Inout_ TLS *pTLS, - _In_ HIMC hIMC) -{ - CicIMCLock imcLock(hIMC); - HRESULT hr = imcLock.m_hr; - if (!imcLock) - hr = E_FAIL; - if (FAILED(hr)) - return hr; - - if (!imcLock.get().hCtfImeContext) - { - HIMCC hCtfImeContext = ImmCreateIMCC(sizeof(CTFIMECONTEXT)); - if (!hCtfImeContext) - return E_OUTOFMEMORY; - imcLock.get().hCtfImeContext = hCtfImeContext; - } - - CicIMCCLock imeContext(imcLock.get().hCtfImeContext); - CicInputContext *pCicIC = imeContext.get().m_pCicIC; - if (!pCicIC) - { - pCicIC = new(cicNoThrow) CicInputContext(m_cliendId, &m_LibThread, hIMC); - if (!pCicIC) - { - imeContext.unlock(); - imcLock.unlock(); - DestroyInputContext(pTLS, hIMC); - return E_OUTOFMEMORY; - } - - if (!pTLS->m_pThreadMgr) - { - pCicIC->Release(); - imeContext.unlock(); - imcLock.unlock(); - DestroyInputContext(pTLS, hIMC); - return E_NOINTERFACE; - } - - imeContext.get().m_pCicIC = pCicIC; - } - - hr = pCicIC->CreateInputContext(pTLS->m_pThreadMgr, imcLock); - if (FAILED(hr)) - { - pCicIC->Release(); - imeContext.get().m_pCicIC = NULL; - } - else - { - if (imcLock.get().hWnd && imcLock.get().hWnd == ::GetFocus()) - { - GetDocumentManager(imeContext); - //FIXME - } - } - - return E_NOTIMPL; -} - -/// @implemented -HRESULT CicBridge::DestroyInputContext(TLS *pTLS, HIMC hIMC) -{ - CicIMCLock imcLock(hIMC); - HRESULT hr = imcLock.m_hr; - if (!imcLock) - hr = E_FAIL; - if (FAILED(hr)) - return hr; - - hr = E_FAIL; - CicIMCCLock imeContext(imcLock.get().hCtfImeContext); - if (imeContext) - hr = imeContext.m_hr; - - if (SUCCEEDED(hr) && !(imeContext.get().m_dwCicFlags & 1)) - { - imeContext.get().m_dwCicFlags |= 1; - - CicInputContext *pCicIC = imeContext.get().m_pCicIC; - if (pCicIC) - { - imeContext.get().m_pCicIC = NULL; - hr = pCicIC->DestroyInputContext(); - pCicIC->Release(); - imeContext.get().m_pCicIC = NULL; - } - } - - if (imcLock.get().hCtfImeContext) - { - ImmDestroyIMCC(imcLock.get().hCtfImeContext); - imcLock.get().hCtfImeContext = NULL; - hr = S_OK; - } - - return hr; -} - -ITfContext * -CicBridge::GetInputContext(CicIMCCLock& imeContext) -{ - CicInputContext *pCicIC = imeContext.get().m_pCicIC; - if (!pCicIC) - return NULL; - return pCicIC->m_pContext; -} - -/// @unimplemented -HRESULT CicBridge::OnSetOpenStatus( - TLS *pTLS, - ITfThreadMgr_P *pThreadMgr, - CicIMCLock& imcLock, - CicInputContext *pCicIC) -{ - return E_NOTIMPL; -} - -/// Selects the IME context. -/// @implemented -HRESULT -CicBridge::SelectEx( - _Inout_ TLS *pTLS, - _Inout_ ITfThreadMgr_P *pThreadMgr, - _In_ HIMC hIMC, - _In_ BOOL fSelect, - _In_ HKL hKL) -{ - CicIMCLock imcLock(hIMC); - if (FAILED(imcLock.m_hr)) - return imcLock.m_hr; - - CicIMCCLock imeContext(imcLock.get().hCtfImeContext); - if (!imeContext) - imeContext.m_hr = E_FAIL; - if (FAILED(imeContext.m_hr)) - return imeContext.m_hr; - - CicInputContext *pCicIC = imeContext.get().m_pCicIC; - if (pCicIC) - pCicIC->m_bSelecting = TRUE; - - if (fSelect) - { - if (pCicIC) - pCicIC->m_dwUnknown6[1] &= ~1; - if (imcLock.get().fOpen) - OnSetOpenStatus(pTLS, pThreadMgr, imcLock, pCicIC); - } - else - { - ITfContext *pContext = GetInputContext(imeContext); - pThreadMgr->RequestPostponedLock(pContext); - if (pCicIC) - pCicIC->m_bSelecting = FALSE; - if (pContext) - pContext->Release(); - } - - return imeContext.m_hr; -} - -/// Used in CicBridge::EnumCreateInputContextCallback and -/// CicBridge::EnumDestroyInputContextCallback. -typedef struct ENUM_CREATE_DESTROY_IC -{ - TLS *m_pTLS; - CicBridge *m_pBridge; -} ENUM_CREATE_DESTROY_IC, *PENUM_CREATE_DESTROY_IC; - -/// Creates input context for the current thread. -/// @implemented -BOOL CALLBACK CicBridge::EnumCreateInputContextCallback(HIMC hIMC, LPARAM lParam) -{ - PENUM_CREATE_DESTROY_IC pData = (PENUM_CREATE_DESTROY_IC)lParam; - pData->m_pBridge->CreateInputContext(pData->m_pTLS, hIMC); - return TRUE; -} - -/// Destroys input context for the current thread. -/// @implemented -BOOL CALLBACK CicBridge::EnumDestroyInputContextCallback(HIMC hIMC, LPARAM lParam) -{ - PENUM_CREATE_DESTROY_IC pData = (PENUM_CREATE_DESTROY_IC)lParam; - pData->m_pBridge->DestroyInputContext(pData->m_pTLS, hIMC); - return TRUE; -} - -/// @implemented -HRESULT -CicBridge::ActivateIMMX( - _Inout_ TLS *pTLS, - _Inout_ ITfThreadMgr_P *pThreadMgr) -{ - HRESULT hr = pThreadMgr->ActivateEx(&m_cliendId, 1); - if (hr != S_OK) - { - m_cliendId = 0; - return E_FAIL; - } - - if (m_cActivateLocks++ != 0) - return S_OK; - - ITfSourceSingle *pSource = NULL; - hr = pThreadMgr->QueryInterface(IID_ITfSourceSingle, (void**)&pSource); - if (FAILED(hr)) - { - DeactivateIMMX(pTLS, pThreadMgr); - return hr; - } - - CFunctionProvider *pProvider = new(cicNoThrow) CFunctionProvider(m_cliendId); - if (!pProvider) - { - hr = E_FAIL; - goto Finish; - } - - pSource->AdviseSingleSink(m_cliendId, IID_ITfFunctionProvider, pProvider); - pProvider->Release(); - - if (!m_pDocMgr) - { - hr = pThreadMgr->CreateDocumentMgr(&m_pDocMgr); - if (FAILED(hr)) - { - hr = E_FAIL; - goto Finish; - } - - SetCompartmentDWORD(m_cliendId, m_pDocMgr, GUID_COMPARTMENT_CTFIME_DIMFLAGS, TRUE, FALSE); - } - - pThreadMgr->SetSysHookSink(this); - - hr = S_OK; - if (pTLS->m_bDestroyed) - { - ENUM_CREATE_DESTROY_IC Data = { pTLS, this }; - ImmEnumInputContext(0, CicBridge::EnumCreateInputContextCallback, (LPARAM)&Data); - } - -Finish: - if (FAILED(hr)) - DeactivateIMMX(pTLS, pThreadMgr); - if (pSource) - pSource->Release(); - return hr; -} - -/// @implemented -HRESULT -CicBridge::DeactivateIMMX( - _Inout_ TLS *pTLS, - _Inout_ ITfThreadMgr_P *pThreadMgr) -{ - if (m_bDeactivating) - return TRUE; - - m_bDeactivating = TRUE; - - if (m_cliendId) - { - ENUM_CREATE_DESTROY_IC Data = { pTLS, this }; - ImmEnumInputContext(0, CicBridge::EnumDestroyInputContextCallback, (LPARAM)&Data); - pTLS->m_bDestroyed = TRUE; - - ITfSourceSingle *pSource = NULL; - if (pThreadMgr->QueryInterface(IID_ITfSourceSingle, (void **)&pSource) == S_OK) - pSource->UnadviseSingleSink(m_cliendId, IID_ITfFunctionProvider); - - m_cliendId = 0; - - while (m_cActivateLocks > 0) - { - --m_cActivateLocks; - pThreadMgr->Deactivate(); - } - - if (pSource) - pSource->Release(); - } - - if (m_pDocMgr) - { - m_pDocMgr->Release(); - m_pDocMgr = NULL; - } - - pThreadMgr->SetSysHookSink(NULL); - - m_bDeactivating = FALSE; - - return S_OK; -} - -/// @implemented -HRESULT -CicBridge::InitIMMX(_Inout_ TLS *pTLS) -{ - if (m_bImmxInited) - return S_OK; - - HRESULT hr = S_OK; - if (!pTLS->m_pThreadMgr) - { - ITfThreadMgr *pThreadMgr = NULL; - hr = TF_CreateThreadMgr(&pThreadMgr); - if (FAILED(hr)) - return E_FAIL; - - hr = pThreadMgr->QueryInterface(IID_ITfThreadMgr_P, (void **)&pTLS->m_pThreadMgr); - if (pThreadMgr) - pThreadMgr->Release(); - if (FAILED(hr)) - return E_FAIL; - } - - if (!m_pThreadMgrEventSink) - { - m_pThreadMgrEventSink = - new(cicNoThrow) CThreadMgrEventSink(CThreadMgrEventSink::DIMCallback, NULL, NULL); - if (!m_pThreadMgrEventSink) - { - UnInitIMMX(pTLS); - return E_FAIL; - } - } - - m_pThreadMgrEventSink->SetCallbackPV(m_pThreadMgrEventSink); - m_pThreadMgrEventSink->_Advise(pTLS->m_pThreadMgr); - - if (!pTLS->m_pProfile) - { - pTLS->m_pProfile = new(cicNoThrow) CicProfile(); - if (!pTLS->m_pProfile) - return E_OUTOFMEMORY; - - hr = pTLS->m_pProfile->InitProfileInstance(pTLS); - if (FAILED(hr)) - { - UnInitIMMX(pTLS); - return E_FAIL; - } - } - - hr = pTLS->m_pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr_P, (void **)&m_pKeystrokeMgr); - if (FAILED(hr)) - { - UnInitIMMX(pTLS); - return E_FAIL; - } - - hr = InitDisplayAttrbuteLib(&m_LibThread); - if (FAILED(hr)) - { - UnInitIMMX(pTLS); - return E_FAIL; - } - - m_bImmxInited = TRUE; - return S_OK; -} - -/// @implemented -BOOL CicBridge::UnInitIMMX(_Inout_ TLS *pTLS) -{ - UninitDisplayAttrbuteLib(&m_LibThread); - TFUninitLib_Thread(&m_LibThread); - - if (m_pKeystrokeMgr) - { - m_pKeystrokeMgr->Release(); - m_pKeystrokeMgr = NULL; - } - - if (pTLS->m_pProfile) - { - pTLS->m_pProfile->Release(); - pTLS->m_pProfile = NULL; - } - - if (m_pThreadMgrEventSink) - { - m_pThreadMgrEventSink->_Unadvise(); - m_pThreadMgrEventSink->Release(); - m_pThreadMgrEventSink = NULL; - } - - if (pTLS->m_pThreadMgr) - { - pTLS->m_pThreadMgr->Release(); - pTLS->m_pThreadMgr = NULL; - } - - m_bImmxInited = FALSE; - return TRUE; -} - -/// @implemented -STDMETHODIMP CicBridge::OnPreFocusDIM(HWND hwnd) -{ - return S_OK; -} - -/// @unimplemented -STDMETHODIMP CicBridge::OnSysKeyboardProc(UINT, LONG) -{ - return E_NOTIMPL; -} - -/// @implemented -STDMETHODIMP CicBridge::OnSysShellProc(INT, UINT, LONG) -{ - return S_OK; -} - -/// @implemented -void -CicBridge::PostTransMsg( - _In_ HWND hWnd, - _In_ INT cTransMsgs, - _In_ const TRANSMSG *pTransMsgs) -{ - for (INT i = 0; i < cTransMsgs; ++i, ++pTransMsgs) - { - ::PostMessageW(hWnd, pTransMsgs->message, pTransMsgs->wParam, pTransMsgs->lParam); - } -} - -/// @implemented -HRESULT -CicBridge::ConfigureGeneral( - _Inout_ TLS* pTLS, - _In_ ITfThreadMgr *pThreadMgr, - _In_ HKL hKL, - _In_ HWND hWnd) -{ - CicProfile *pProfile = pTLS->m_pProfile; - if (!pProfile) - return E_OUTOFMEMORY; - - TF_LANGUAGEPROFILE profile; - HRESULT hr = pProfile->GetActiveLanguageProfile(hKL, GUID_TFCAT_TIP_KEYBOARD, &profile); - if (FAILED(hr)) - return hr; - - ITfFunctionProvider *pProvider = NULL; - hr = pThreadMgr->GetFunctionProvider(profile.clsid, &pProvider); - if (FAILED(hr)) - return hr; - - ITfFnConfigure *pFnConfigure = NULL; - hr = pProvider->GetFunction(GUID_NULL, IID_ITfFnConfigure, (IUnknown**)&pFnConfigure); - if (FAILED(hr)) - { - pProvider->Release(); - return hr; - } - - hr = pFnConfigure->Show(hWnd, profile.langid, profile.guidProfile); - - pFnConfigure->Release(); - pProvider->Release(); - return hr; -} - -/// @implemented -HRESULT -CicBridge::ConfigureRegisterWord( - _Inout_ TLS* pTLS, - _In_ ITfThreadMgr *pThreadMgr, - _In_ HKL hKL, - _In_ HWND hWnd, - _Inout_opt_ LPVOID lpData) -{ - CicProfile *pProfile = pTLS->m_pProfile; - if (!pProfile) - return E_OUTOFMEMORY; - - TF_LANGUAGEPROFILE profile; - HRESULT hr = pProfile->GetActiveLanguageProfile(hKL, GUID_TFCAT_TIP_KEYBOARD, &profile); - if (FAILED(hr)) - return hr; - - ITfFunctionProvider *pProvider = NULL; - hr = pThreadMgr->GetFunctionProvider(profile.clsid, &pProvider); - if (FAILED(hr)) - return hr; - - ITfFnConfigureRegisterWord *pFunction = NULL; - hr = pProvider->GetFunction(GUID_NULL, IID_ITfFnConfigureRegisterWord, (IUnknown**)&pFunction); - if (FAILED(hr)) - { - pProvider->Release(); - return hr; - } - - REGISTERWORDW* pRegWord = (REGISTERWORDW*)lpData; - if (pRegWord) - { - if (pRegWord->lpWord) - { - hr = E_OUTOFMEMORY; - BSTR bstrWord = SysAllocString(pRegWord->lpWord); - if (bstrWord) - { - hr = pFunction->Show(hWnd, profile.langid, profile.guidProfile, bstrWord); - SysFreeString(bstrWord); - } - } - else - { - hr = pFunction->Show(hWnd, profile.langid, profile.guidProfile, NULL); - } - } - - pProvider->Release(); - pFunction->Release(); - return hr; -} - -/*********************************************************************** - * CicProfile - */ - -/// @unimplemented -HRESULT -CicProfile::GetActiveLanguageProfile( - _In_ HKL hKL, - _In_ REFGUID rguid, - _Out_ TF_LANGUAGEPROFILE *pProfile) -{ - return E_NOTIMPL; -} - /*********************************************************************** * ImeInquire (MSCTFIME.@) * @@ -3332,367 +858,6 @@ CtfImeThreadDetach(VOID) return S_OK; } -/*********************************************************************** - * UIComposition - */ -struct UIComposition -{ - void OnImeStartComposition(CicIMCLock& imcLock, HWND hUIWnd); - void OnImeCompositionUpdate(CicIMCLock& imcLock); - void OnImeEndComposition(); - void OnImeSetContext(CicIMCLock& imcLock, HWND hUIWnd, WPARAM wParam, LPARAM lParam); - void OnPaintTheme(WPARAM wParam); - void OnDestroy(); - - static LRESULT CALLBACK CompWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -}; - -/// @unimplemented -void UIComposition::OnImeStartComposition(CicIMCLock& imcLock, HWND hUIWnd) -{ - //FIXME -} - -/// @unimplemented -void UIComposition::OnImeCompositionUpdate(CicIMCLock& imcLock) -{ - //FIXME -} - -/// @unimplemented -void UIComposition::OnImeEndComposition() -{ - //FIXME -} - -/// @unimplemented -void UIComposition::OnImeSetContext(CicIMCLock& imcLock, HWND hUIWnd, WPARAM wParam, LPARAM lParam) -{ - //FIXME -} - -/// @unimplemented -void UIComposition::OnPaintTheme(WPARAM wParam) -{ - //FIXME -} - -/// @unimplemented -void UIComposition::OnDestroy() -{ - //FIXME -} - -/// @unimplemented -LRESULT CALLBACK -UIComposition::CompWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - if (uMsg == WM_CREATE) - return -1; // FIXME - return 0; -} - -/*********************************************************************** - * UI - */ -struct UI -{ - HWND m_hWnd; - UIComposition *m_pComp; - - UI(HWND hWnd); - virtual ~UI(); - - HRESULT _Create(); - void _Destroy(); - - static void OnCreate(HWND hWnd); - static void OnDestroy(HWND hWnd); - void OnImeSetContext(CicIMCLock& imcLock, WPARAM wParam, LPARAM lParam); -}; - -// For GetWindowLongPtr/SetWindowLongPtr -#define UIGWLP_HIMC 0 -#define UIGWLP_UI sizeof(HIMC) -#define UIGWLP_SIZE (UIGWLP_UI + sizeof(UI*)) - -/// @implemented -UI::UI(HWND hWnd) : m_hWnd(hWnd) -{ -} - -/// @implemented -UI::~UI() -{ - delete m_pComp; -} - -/// @unimplemented -HRESULT UI::_Create() -{ - m_pComp = new(cicNoThrow) UIComposition(); - if (!m_pComp) - return E_OUTOFMEMORY; - - SetWindowLongPtrW(m_hWnd, UIGWLP_UI, (LONG_PTR)this); - //FIXME - return S_OK; -} - -/// @implemented -void UI::_Destroy() -{ - m_pComp->OnDestroy(); - SetWindowLongPtrW(m_hWnd, UIGWLP_UI, 0); -} - -/// @implemented -void UI::OnCreate(HWND hWnd) -{ - UI *pUI = (UI*)GetWindowLongPtrW(hWnd, UIGWLP_UI); - if (pUI) - return; - pUI = new(cicNoThrow) UI(hWnd); - if (pUI) - pUI->_Create(); -} - -/// @implemented -void UI::OnDestroy(HWND hWnd) -{ - UI *pUI = (UI*)GetWindowLongPtrW(hWnd, UIGWLP_UI); - if (!pUI) - return; - - pUI->_Destroy(); - delete pUI; -} - -/// @implemented -void UI::OnImeSetContext(CicIMCLock& imcLock, WPARAM wParam, LPARAM lParam) -{ - m_pComp->OnImeSetContext(imcLock, m_hWnd, wParam, lParam); -} - -/*********************************************************************** - * CIMEUIWindowHandler - */ - -struct CIMEUIWindowHandler -{ - static LRESULT CALLBACK ImeUIMsImeHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); - static LRESULT CALLBACK ImeUIMsImeMouseHandler(HWND hWnd, WPARAM wParam, LPARAM lParam); - static LRESULT CALLBACK ImeUIMsImeModeBiasHandler(HWND hWnd, WPARAM wParam, LPARAM lParam); - static LRESULT CALLBACK ImeUIMsImeReconvertRequest(HWND hWnd, WPARAM wParam, LPARAM lParam); - static LRESULT CALLBACK ImeUIWndProcWorker(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -}; - -/// @unimplemented -LRESULT CALLBACK -CIMEUIWindowHandler::ImeUIMsImeMouseHandler(HWND hWnd, WPARAM wParam, LPARAM lParam) -{ - return 0; //FIXME -} - -/// @unimplemented -LRESULT CALLBACK -CIMEUIWindowHandler::ImeUIMsImeModeBiasHandler(HWND hWnd, WPARAM wParam, LPARAM lParam) -{ - return 0; //FIXME -} - -/// @unimplemented -LRESULT CALLBACK -CIMEUIWindowHandler::ImeUIMsImeReconvertRequest(HWND hWnd, WPARAM wParam, LPARAM lParam) -{ - return 0; //FIXME -} - -/// @implemented -LRESULT CALLBACK -CIMEUIWindowHandler::ImeUIMsImeHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - if (uMsg == WM_MSIME_MOUSE) - return ImeUIMsImeMouseHandler(hWnd, wParam, lParam); - if (uMsg == WM_MSIME_MODEBIAS) - return ImeUIMsImeModeBiasHandler(hWnd, wParam, lParam); - if (uMsg == WM_MSIME_RECONVERTREQUEST) - return ImeUIMsImeReconvertRequest(hWnd, wParam, lParam); - if (uMsg == WM_MSIME_SERVICE) - { - TLS *pTLS = TLS::GetTLS(); - if (pTLS && pTLS->m_pProfile) - { - LANGID LangID; - pTLS->m_pProfile->GetLangId(&LangID); - if (PRIMARYLANGID(LangID) == LANG_KOREAN) - return FALSE; - } - return TRUE; - } - return 0; -} - -/// @unimplemented -LRESULT CALLBACK -CIMEUIWindowHandler::ImeUIWndProcWorker(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - TLS *pTLS = TLS::GetTLS(); - if (pTLS && (pTLS->m_dwSystemInfoFlags & IME_SYSINFO_WINLOGON)) - { - if (uMsg == WM_CREATE) - return -1; - return DefWindowProcW(hWnd, uMsg, wParam, lParam); - } - - switch (uMsg) - { - case WM_CREATE: - { - UI::OnCreate(hWnd); - break; - } - case WM_DESTROY: - case WM_ENDSESSION: - { - UI::OnDestroy(hWnd); - break; - } - case WM_IME_STARTCOMPOSITION: - case WM_IME_COMPOSITION: - case WM_IME_ENDCOMPOSITION: - case WM_IME_SETCONTEXT: - case WM_IME_NOTIFY: - case WM_IME_SELECT: - case WM_TIMER: - { - HIMC hIMC = (HIMC)GetWindowLongPtrW(hWnd, UIGWLP_HIMC); - UI* pUI = (UI*)GetWindowLongPtrW(hWnd, UIGWLP_UI); - CicIMCLock imcLock(hIMC); - switch (uMsg) - { - case WM_IME_STARTCOMPOSITION: - { - pUI->m_pComp->OnImeStartComposition(imcLock, pUI->m_hWnd); - break; - } - case WM_IME_COMPOSITION: - { - if (lParam & GCS_COMPSTR) - { - pUI->m_pComp->OnImeCompositionUpdate(imcLock); - ::SetTimer(hWnd, 0, 10, NULL); - //FIXME - } - break; - } - case WM_IME_ENDCOMPOSITION: - { - ::KillTimer(hWnd, 0); - pUI->m_pComp->OnImeEndComposition(); - break; - } - case WM_IME_SETCONTEXT: - { - pUI->OnImeSetContext(imcLock, wParam, lParam); - ::KillTimer(hWnd, 1); - ::SetTimer(hWnd, 1, 300, NULL); - break; - } - case WM_TIMER: - { - //FIXME - ::KillTimer(hWnd, wParam); - break; - } - case WM_IME_NOTIFY: - case WM_IME_SELECT: - default: - { - pUI->m_pComp->OnPaintTheme(wParam); - break; - } - } - break; - } - default: - { - if (IsMsImeMessage(uMsg)) - return CIMEUIWindowHandler::ImeUIMsImeHandler(hWnd, uMsg, wParam, lParam); - return DefWindowProcW(hWnd, uMsg, wParam, lParam); - } - } - - return 0; -} - -/// @implemented -EXTERN_C LRESULT CALLBACK -UIWndProc( - _In_ HWND hWnd, - _In_ UINT uMsg, - _In_ WPARAM wParam, - _In_ LPARAM lParam) -{ - return CIMEUIWindowHandler::ImeUIWndProcWorker(hWnd, uMsg, wParam, lParam); -} - -/// @unimplemented -BOOL RegisterImeClass(VOID) -{ - WNDCLASSEXW wcx; - - if (!GetClassInfoExW(g_hInst, L"MSCTFIME UI", &wcx)) - { - ZeroMemory(&wcx, sizeof(wcx)); - wcx.cbSize = sizeof(WNDCLASSEXW); - wcx.cbWndExtra = UIGWLP_SIZE; - wcx.hIcon = LoadIconW(0, (LPCWSTR)IDC_ARROW); - wcx.hInstance = g_hInst; - wcx.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); - wcx.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); - wcx.style = CS_IME | CS_GLOBALCLASS; - wcx.lpfnWndProc = UIWndProc; - wcx.lpszClassName = L"MSCTFIME UI"; - if (!RegisterClassExW(&wcx)) - return FALSE; - } - - if (!GetClassInfoExW(g_hInst, L"MSCTFIME Composition", &wcx)) - { - ZeroMemory(&wcx, sizeof(wcx)); - wcx.cbSize = sizeof(WNDCLASSEXW); - wcx.cbWndExtra = sizeof(DWORD); - wcx.hIcon = NULL; - wcx.hInstance = g_hInst; - wcx.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_IBEAM); - wcx.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); - wcx.style = CS_IME | CS_HREDRAW | CS_VREDRAW; - wcx.lpfnWndProc = UIComposition::CompWndProc; - wcx.lpszClassName = L"MSCTFIME Composition"; - if (!RegisterClassExW(&wcx)) - return FALSE; - } - - return TRUE; -} - -/// @implemented -VOID UnregisterImeClass(VOID) -{ - WNDCLASSEXW wcx; - - GetClassInfoExW(g_hInst, L"MSCTFIME UI", &wcx); - UnregisterClassW(L"MSCTFIME UI", g_hInst); - DestroyIcon(wcx.hIcon); - DestroyIcon(wcx.hIconSm); - - GetClassInfoExW(g_hInst, L"MSCTFIME Composition", &wcx); - UnregisterClassW(L"MSCTFIME Composition", g_hInst); - DestroyIcon(wcx.hIcon); - DestroyIcon(wcx.hIconSm); -} - /// @implemented BOOL AttachIME(VOID) { diff --git a/dll/ime/msctfime/msctfime.h b/dll/ime/msctfime/msctfime.h index 040ecc11c96..f7ada8fbd6e 100644 --- a/dll/ime/msctfime/msctfime.h +++ b/dll/ime/msctfime/msctfime.h @@ -34,6 +34,23 @@ #include +EXTERN_C BOOLEAN WINAPI DllShutdownInProgress(VOID); + +HRESULT InitDisplayAttrbuteLib(PCIC_LIBTHREAD pLibThread); +HRESULT UninitDisplayAttrbuteLib(PCIC_LIBTHREAD pLibThread); + +DEFINE_GUID(GUID_COMPARTMENT_CTFIME_DIMFLAGS, 0xA94C5FD2, 0xC471, 0x4031, 0x95, 0x46, 0x70, 0x9C, 0x17, 0x30, 0x0C, 0xB9); +DEFINE_GUID(GUID_COMPARTMENT_CTFIME_CICINPUTCONTEXT, 0x85A688F7, 0x6DC8, 0x4F17, 0xA8, 0x3A, 0xB1, 0x1C, 0x09, 0xCD, 0xD7, 0xBF); + #include "resource.h" +#include "bridge.h" +#include "compartment.h" +#include "functions.h" +#include "inputcontext.h" +#include "profile.h" +#include "sinks.h" +#include "tls.h" +#include "ui.h" + extern HINSTANCE g_hInst; diff --git a/dll/ime/msctfime/profile.cpp b/dll/ime/msctfime/profile.cpp new file mode 100644 index 00000000000..9ec7cd12ff3 --- /dev/null +++ b/dll/ime/msctfime/profile.cpp @@ -0,0 +1,173 @@ +/* + * PROJECT: ReactOS msctfime.ime + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Profile of msctfime.ime + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +#include "msctfime.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msctfime); + +/// @implemented +CicProfile::CicProfile() +{ + m_dwFlags &= 0xFFFFFFF0; + m_cRefs = 1; + m_pIPProfiles = NULL; + m_pActiveLanguageProfileNotifySink = NULL; + m_LangID1 = 0; + m_nCodePage = CP_ACP; + m_LangID2 = 0; + m_dwUnknown1 = 0; +} + +/// @implemented +CicProfile::~CicProfile() +{ + if (m_pIPProfiles) + { + if (m_LangID1) + m_pIPProfiles->ChangeCurrentLanguage(m_LangID1); + + m_pIPProfiles->Release(); + m_pIPProfiles = NULL; + } + + if (m_pActiveLanguageProfileNotifySink) + { + m_pActiveLanguageProfileNotifySink->_Unadvise(); + m_pActiveLanguageProfileNotifySink->Release(); + m_pActiveLanguageProfileNotifySink = NULL; + } +} + +/// @implemented +STDMETHODIMP CicProfile::QueryInterface(REFIID riid, LPVOID* ppvObj) +{ + *ppvObj = NULL; + return E_NOINTERFACE; +} + +/// @implemented +STDMETHODIMP_(ULONG) CicProfile::AddRef() +{ + return ::InterlockedIncrement(&m_cRefs); +} + +/// @implemented +STDMETHODIMP_(ULONG) CicProfile::Release() +{ + if (::InterlockedDecrement(&m_cRefs) == 0) + { + delete this; + return 0; + } + return m_cRefs; +} + +/// @implemented +INT CALLBACK +CicProfile::ActiveLanguageProfileNotifySinkCallback( + REFGUID rguid1, + REFGUID rguid2, + BOOL fActivated, + LPVOID pUserData) +{ + CicProfile *pThis = (CicProfile *)pUserData; + pThis->m_dwFlags &= ~0xE; + return 0; +} + +/// @implemented +HRESULT CicProfile::GetCodePageA(_Out_ UINT *puCodePage) +{ + if (!puCodePage) + return E_INVALIDARG; + + if (m_dwFlags & 2) + { + *puCodePage = m_nCodePage; + return S_OK; + } + + *puCodePage = 0; + + LANGID LangID; + HRESULT hr = GetLangId(&LangID); + if (FAILED(hr)) + return E_FAIL; + + WCHAR szBuff[12]; + INT cch = ::GetLocaleInfoW(LangID, LOCALE_IDEFAULTANSICODEPAGE, szBuff, _countof(szBuff)); + if (cch) + { + szBuff[cch] = 0; + m_nCodePage = *puCodePage = wcstoul(szBuff, NULL, 10); + m_dwFlags |= 2; + } + + return S_OK; +} + +/// @implemented +HRESULT CicProfile::GetLangId(_Out_ LANGID *pLangID) +{ + *pLangID = 0; + + if (!m_pIPProfiles) + return E_FAIL; + + if (m_dwFlags & 4) + { + *pLangID = m_LangID2; + return S_OK; + } + + HRESULT hr = m_pIPProfiles->GetCurrentLanguage(pLangID); + if (SUCCEEDED(hr)) + { + m_dwFlags |= 4; + m_LangID2 = *pLangID; + } + + return hr; +} + +/// @implemented +HRESULT +CicProfile::InitProfileInstance(_Inout_ TLS *pTLS) +{ + HRESULT hr = TF_CreateInputProcessorProfiles(&m_pIPProfiles); + if (FAILED(hr)) + return hr; + + if (!m_pActiveLanguageProfileNotifySink) + { + CActiveLanguageProfileNotifySink *pSink = + new(cicNoThrow) CActiveLanguageProfileNotifySink( + CicProfile::ActiveLanguageProfileNotifySinkCallback, this); + if (!pSink) + { + m_pIPProfiles->Release(); + m_pIPProfiles = NULL; + return E_FAIL; + } + m_pActiveLanguageProfileNotifySink = pSink; + } + + if (pTLS->m_pThreadMgr) + m_pActiveLanguageProfileNotifySink->_Advise(pTLS->m_pThreadMgr); + + return hr; +} + +/// @unimplemented +HRESULT +CicProfile::GetActiveLanguageProfile( + _In_ HKL hKL, + _In_ REFGUID rguid, + _Out_ TF_LANGUAGEPROFILE *pProfile) +{ + return E_NOTIMPL; +} diff --git a/dll/ime/msctfime/profile.h b/dll/ime/msctfime/profile.h new file mode 100644 index 00000000000..2943750ad46 --- /dev/null +++ b/dll/ime/msctfime/profile.h @@ -0,0 +1,51 @@ +/* + * PROJECT: ReactOS msctfime.ime + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Profile of msctfime.ime + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +#pragma once + +#include "sinks.h" + +class CicProfile : public IUnknown +{ +protected: + ITfInputProcessorProfiles *m_pIPProfiles; + CActiveLanguageProfileNotifySink *m_pActiveLanguageProfileNotifySink; + LANGID m_LangID1; + WORD m_padding1; + DWORD m_dwFlags; + UINT m_nCodePage; + LANGID m_LangID2; + WORD m_padding2; + DWORD m_dwUnknown1; + LONG m_cRefs; + + static INT CALLBACK + ActiveLanguageProfileNotifySinkCallback( + REFGUID rguid1, + REFGUID rguid2, + BOOL fActivated, + LPVOID pUserData); + +public: + CicProfile(); + virtual ~CicProfile(); + + // IUnknown interface + STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + HRESULT + GetActiveLanguageProfile( + _In_ HKL hKL, + _In_ REFGUID rguid, + _Out_ TF_LANGUAGEPROFILE *pProfile); + HRESULT GetLangId(_Out_ LANGID *pLangID); + HRESULT GetCodePageA(_Out_ UINT *puCodePage); + + HRESULT InitProfileInstance(_Inout_ TLS *pTLS); +}; diff --git a/dll/ime/msctfime/sinks.cpp b/dll/ime/msctfime/sinks.cpp new file mode 100644 index 00000000000..235079c4d94 --- /dev/null +++ b/dll/ime/msctfime/sinks.cpp @@ -0,0 +1,539 @@ +/* + * PROJECT: ReactOS msctfime.ime + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: The sinks of msctfime.ime + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +#include "msctfime.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msctfime); + +/// @implemented +CCompartmentEventSink::CCompartmentEventSink(FN_EVENTSINK fnEventSink, LPVOID pUserData) + : m_array() + , m_cRefs(1) + , m_fnEventSink(fnEventSink) + , m_pUserData(pUserData) +{ +} + +/// @implemented +CCompartmentEventSink::~CCompartmentEventSink() +{ +} + +/// @implemented +STDMETHODIMP CCompartmentEventSink::QueryInterface(REFIID riid, LPVOID* ppvObj) +{ + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfCompartmentEventSink)) + { + *ppvObj = this; + AddRef(); + return S_OK; + } + + *ppvObj = NULL; + return E_NOINTERFACE; +} + +/// @implemented +STDMETHODIMP_(ULONG) CCompartmentEventSink::AddRef() +{ + return ::InterlockedIncrement(&m_cRefs); +} + +/// @implemented +STDMETHODIMP_(ULONG) CCompartmentEventSink::Release() +{ + if (::InterlockedDecrement(&m_cRefs) == 0) + { + delete this; + return 0; + } + return m_cRefs; +} + +/// @implemented +STDMETHODIMP CCompartmentEventSink::OnChange(REFGUID rguid) +{ + return m_fnEventSink(m_pUserData, rguid); +} + +/// @implemented +HRESULT +CCompartmentEventSink::_Advise(IUnknown *pUnknown, REFGUID rguid, BOOL bThread) +{ + CESMAP *pCesMap = m_array.Append(1); + if (!pCesMap) + return E_OUTOFMEMORY; + + ITfSource *pSource = NULL; + + HRESULT hr = GetCompartment(pUnknown, rguid, &pCesMap->m_pComp, bThread); + if (FAILED(hr)) + { + hr = pCesMap->m_pComp->QueryInterface(IID_ITfSource, (void **)&pSource); + if (FAILED(hr)) + { + hr = pSource->AdviseSink(IID_ITfCompartmentEventSink, this, &pCesMap->m_dwCookie); + if (FAILED(hr)) + { + if (pCesMap->m_pComp) + { + pCesMap->m_pComp->Release(); + pCesMap->m_pComp = NULL; + } + m_array.Remove(m_array.size() - 1, 1); + } + else + { + hr = S_OK; + } + } + } + + if (pSource) + pSource->Release(); + + return hr; +} + +/// @implemented +HRESULT CCompartmentEventSink::_Unadvise() +{ + CESMAP *pCesMap = m_array.data(); + size_t cItems = m_array.size(); + if (!cItems) + return S_OK; + + do + { + ITfSource *pSource = NULL; + HRESULT hr = pCesMap->m_pComp->QueryInterface(IID_ITfSource, (void **)&pSource); + if (SUCCEEDED(hr)) + pSource->UnadviseSink(pCesMap->m_dwCookie); + + if (pCesMap->m_pComp) + { + pCesMap->m_pComp->Release(); + pCesMap->m_pComp = NULL; + } + + if (pSource) + pSource->Release(); + + ++pCesMap; + --cItems; + } while (cItems); + + return S_OK; +} + +/***********************************************************************/ + +/// @implemented +CTextEventSink::CTextEventSink(FN_ENDEDIT fnEndEdit, LPVOID pCallbackPV) +{ + m_cRefs = 1; + m_pUnknown = NULL; + m_dwEditSinkCookie = (DWORD)-1; + m_dwLayoutSinkCookie = (DWORD)-1; + m_fnLayoutChange = NULL; + m_fnEndEdit = fnEndEdit; + m_pCallbackPV = pCallbackPV; +} + +/// @implemented +CTextEventSink::~CTextEventSink() +{ +} + +/// @implemented +STDMETHODIMP CTextEventSink::QueryInterface(REFIID riid, LPVOID* ppvObj) +{ + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfTextEditSink)) + { + *ppvObj = this; + AddRef(); + return S_OK; + } + if (IsEqualIID(riid, IID_ITfTextLayoutSink)) + { + *ppvObj = static_cast(this); + AddRef(); + return S_OK; + } + return E_NOINTERFACE; +} + +/// @implemented +STDMETHODIMP_(ULONG) CTextEventSink::AddRef() +{ + return ::InterlockedIncrement(&m_cRefs); +} + +/// @implemented +STDMETHODIMP_(ULONG) CTextEventSink::Release() +{ + if (::InterlockedDecrement(&m_cRefs) == 0) + { + delete this; + return 0; + } + return m_cRefs; +} + +struct TEXT_EVENT_SINK_END_EDIT +{ + TfEditCookie m_ecReadOnly; + ITfEditRecord *m_pEditRecord; + ITfContext *m_pContext; +}; + +/// @implemented +STDMETHODIMP CTextEventSink::OnEndEdit( + ITfContext *pic, + TfEditCookie ecReadOnly, + ITfEditRecord *pEditRecord) +{ + TEXT_EVENT_SINK_END_EDIT Data = { ecReadOnly, pEditRecord, pic }; + return m_fnEndEdit(1, m_pCallbackPV, (LPVOID)&Data); +} + +/// @implemented +STDMETHODIMP CTextEventSink::OnLayoutChange( + ITfContext *pContext, + TfLayoutCode lcode, + ITfContextView *pContextView) +{ + switch (lcode) + { + case TF_LC_CREATE: + return m_fnLayoutChange(3, m_fnEndEdit, pContextView); + case TF_LC_CHANGE: + return m_fnLayoutChange(2, m_fnEndEdit, pContextView); + case TF_LC_DESTROY: + return m_fnLayoutChange(4, m_fnEndEdit, pContextView); + default: + return E_INVALIDARG; + } +} + +/// @implemented +HRESULT CTextEventSink::_Advise(IUnknown *pUnknown, UINT uFlags) +{ + m_pUnknown = NULL; + m_uFlags = uFlags; + + ITfSource *pSource = NULL; + HRESULT hr = pUnknown->QueryInterface(IID_ITfSource, (void**)&pSource); + if (SUCCEEDED(hr)) + { + ITfTextEditSink *pSink = static_cast(this); + if (uFlags & 1) + hr = pSource->AdviseSink(IID_ITfTextEditSink, pSink, &m_dwEditSinkCookie); + if (SUCCEEDED(hr) && (uFlags & 2)) + hr = pSource->AdviseSink(IID_ITfTextLayoutSink, pSink, &m_dwLayoutSinkCookie); + + if (SUCCEEDED(hr)) + { + m_pUnknown = pUnknown; + pUnknown->AddRef(); + } + else + { + pSource->UnadviseSink(m_dwEditSinkCookie); + } + } + + if (pSource) + pSource->Release(); + + return hr; +} + +/// @implemented +HRESULT CTextEventSink::_Unadvise() +{ + if (!m_pUnknown) + return E_FAIL; + + ITfSource *pSource = NULL; + HRESULT hr = m_pUnknown->QueryInterface(IID_ITfSource, (void**)&pSource); + if (SUCCEEDED(hr)) + { + if (m_uFlags & 1) + hr = pSource->UnadviseSink(m_dwEditSinkCookie); + if (m_uFlags & 2) + hr = pSource->UnadviseSink(m_dwLayoutSinkCookie); + + pSource->Release(); + } + + m_pUnknown->Release(); + m_pUnknown = NULL; + + return E_NOTIMPL; +} + +/***********************************************************************/ + +/// @implemented +CThreadMgrEventSink::CThreadMgrEventSink( + _In_ FN_INITDOCMGR fnInit, + _In_ FN_PUSHPOP fnPushPop, + _Inout_ LPVOID pvCallbackPV) +{ + m_fnInit = fnInit; + m_fnPushPop = fnPushPop; + m_pCallbackPV = pvCallbackPV; + m_cRefs = 1; +} + +/// @implemented +STDMETHODIMP CThreadMgrEventSink::QueryInterface(REFIID riid, LPVOID* ppvObj) +{ + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfThreadMgrEventSink)) + { + *ppvObj = this; + AddRef(); + return S_OK; + } + *ppvObj = NULL; + return E_NOINTERFACE; +} + +/// @implemented +STDMETHODIMP_(ULONG) CThreadMgrEventSink::AddRef() +{ + return ::InterlockedIncrement(&m_cRefs); +} + +/// @implemented +STDMETHODIMP_(ULONG) CThreadMgrEventSink::Release() +{ + if (::InterlockedDecrement(&m_cRefs) == 0) + { + delete this; + return 0; + } + return m_cRefs; +} + +INT CALLBACK +CThreadMgrEventSink::DIMCallback( + UINT nCode, + ITfDocumentMgr *pDocMgr1, + ITfDocumentMgr *pDocMgr2, + LPVOID pUserData) +{ + return E_NOTIMPL; +} + +STDMETHODIMP CThreadMgrEventSink::OnInitDocumentMgr(ITfDocumentMgr *pdim) +{ + if (!m_fnInit) + return S_OK; + return m_fnInit(0, pdim, NULL, m_pCallbackPV); +} + +STDMETHODIMP CThreadMgrEventSink::OnUninitDocumentMgr(ITfDocumentMgr *pdim) +{ + if (!m_fnInit) + return S_OK; + return m_fnInit(1, pdim, NULL, m_pCallbackPV); +} + +STDMETHODIMP +CThreadMgrEventSink::OnSetFocus(ITfDocumentMgr *pdimFocus, ITfDocumentMgr *pdimPrevFocus) +{ + if (!m_fnInit) + return S_OK; + return m_fnInit(2, pdimFocus, pdimPrevFocus, m_pCallbackPV); +} + +STDMETHODIMP CThreadMgrEventSink::OnPushContext(ITfContext *pic) +{ + if (!m_fnPushPop) + return S_OK; + return m_fnPushPop(3, pic, m_pCallbackPV); +} + +STDMETHODIMP CThreadMgrEventSink::OnPopContext(ITfContext *pic) +{ + if (!m_fnPushPop) + return S_OK; + return m_fnPushPop(4, pic, m_pCallbackPV); +} + +void CThreadMgrEventSink::SetCallbackPV(_Inout_ LPVOID pv) +{ + if (!m_pCallbackPV) + m_pCallbackPV = pv; +} + +HRESULT CThreadMgrEventSink::_Advise(ITfThreadMgr *pThreadMgr) +{ + m_pThreadMgr = NULL; + + HRESULT hr = E_FAIL; + ITfSource *pSource = NULL; + if (pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) == S_OK && + pSource->AdviseSink(IID_ITfThreadMgrEventSink, this, &m_dwCookie) == S_OK) + { + m_pThreadMgr = pThreadMgr; + pThreadMgr->AddRef(); + hr = S_OK; + } + + if (pSource) + pSource->Release(); + + return hr; +} + +HRESULT CThreadMgrEventSink::_Unadvise() +{ + HRESULT hr = E_FAIL; + ITfSource *pSource = NULL; + + if (m_pThreadMgr) + { + if (m_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) == S_OK && + pSource->UnadviseSink(m_dwCookie) == S_OK) + { + hr = S_OK; + } + + if (pSource) + pSource->Release(); + } + + if (m_pThreadMgr) + { + m_pThreadMgr->Release(); + m_pThreadMgr = NULL; + } + + return hr; +} + +/***********************************************************************/ + +/// @implemented +CActiveLanguageProfileNotifySink::CActiveLanguageProfileNotifySink( + _In_ FN_COMPARE fnCompare, + _Inout_opt_ void *pUserData) +{ + m_dwConnection = (DWORD)-1; + m_fnCompare = fnCompare; + m_cRefs = 1; + m_pUserData = pUserData; +} + +/// @implemented +CActiveLanguageProfileNotifySink::~CActiveLanguageProfileNotifySink() +{ +} + +/// @implemented +STDMETHODIMP CActiveLanguageProfileNotifySink::QueryInterface(REFIID riid, LPVOID* ppvObj) +{ + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfActiveLanguageProfileNotifySink)) + { + *ppvObj = this; + AddRef(); + return S_OK; + } + *ppvObj = NULL; + return E_NOINTERFACE; +} + +/// @implemented +STDMETHODIMP_(ULONG) CActiveLanguageProfileNotifySink::AddRef() +{ + return ::InterlockedIncrement(&m_cRefs); +} + +/// @implemented +STDMETHODIMP_(ULONG) CActiveLanguageProfileNotifySink::Release() +{ + if (::InterlockedDecrement(&m_cRefs) == 0) + { + delete this; + return 0; + } + return m_cRefs; +} + +/// @implemented +STDMETHODIMP +CActiveLanguageProfileNotifySink::OnActivated( + REFCLSID clsid, + REFGUID guidProfile, + BOOL fActivated) +{ + if (!m_fnCompare) + return 0; + + return m_fnCompare(clsid, guidProfile, fActivated, m_pUserData); +} + +/// @implemented +HRESULT +CActiveLanguageProfileNotifySink::_Advise( + ITfThreadMgr *pThreadMgr) +{ + m_pThreadMgr = NULL; + + ITfSource *pSource = NULL; + HRESULT hr = pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource); + if (FAILED(hr)) + return E_FAIL; + + hr = pSource->AdviseSink(IID_ITfActiveLanguageProfileNotifySink, this, &m_dwConnection); + if (SUCCEEDED(hr)) + { + m_pThreadMgr = pThreadMgr; + pThreadMgr->AddRef(); + hr = S_OK; + } + else + { + hr = E_FAIL; + } + + if (pSource) + pSource->Release(); + + return hr; +} + +/// @implemented +HRESULT +CActiveLanguageProfileNotifySink::_Unadvise() +{ + if (!m_pThreadMgr) + return E_FAIL; + + ITfSource *pSource = NULL; + HRESULT hr = m_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource); + if (SUCCEEDED(hr)) + { + hr = pSource->UnadviseSink(m_dwConnection); + if (SUCCEEDED(hr)) + hr = S_OK; + } + + if (pSource) + pSource->Release(); + + if (m_pThreadMgr) + { + m_pThreadMgr->Release(); + m_pThreadMgr = NULL; + } + + return hr; +} diff --git a/dll/ime/msctfime/sinks.h b/dll/ime/msctfime/sinks.h new file mode 100644 index 00000000000..9d1fb7885c6 --- /dev/null +++ b/dll/ime/msctfime/sinks.h @@ -0,0 +1,166 @@ +/* + * PROJECT: ReactOS msctfime.ime + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: The sinks of msctfime.ime + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +#pragma once + +/***********************************************************************/ + +typedef struct CESMAP +{ + ITfCompartment *m_pComp; + DWORD m_dwCookie; +} CESMAP, *PCESMAP; + +typedef INT (CALLBACK *FN_EVENTSINK)(LPVOID, REFGUID); + +class CCompartmentEventSink : public ITfCompartmentEventSink +{ + CicArray m_array; + LONG m_cRefs; + FN_EVENTSINK m_fnEventSink; + LPVOID m_pUserData; + +public: + CCompartmentEventSink(FN_EVENTSINK fnEventSink, LPVOID pUserData); + virtual ~CCompartmentEventSink(); + + HRESULT _Advise(IUnknown *pUnknown, REFGUID rguid, BOOL bThread); + HRESULT _Unadvise(); + + // IUnknown interface + STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + // ITfCompartmentEventSink interface + STDMETHODIMP OnChange(REFGUID rguid) override; +}; + +/***********************************************************************/ + +typedef INT (CALLBACK *FN_ENDEDIT)(INT, LPVOID, LPVOID); +typedef INT (CALLBACK *FN_LAYOUTCHANGE)(UINT nType, FN_ENDEDIT fnEndEdit, ITfContextView *pView); + +class CTextEventSink : public ITfTextEditSink, ITfTextLayoutSink +{ +protected: + LONG m_cRefs; + IUnknown *m_pUnknown; + DWORD m_dwEditSinkCookie; + DWORD m_dwLayoutSinkCookie; + union + { + UINT m_uFlags; + FN_LAYOUTCHANGE m_fnLayoutChange; + }; + FN_ENDEDIT m_fnEndEdit; + LPVOID m_pCallbackPV; + +public: + CTextEventSink(FN_ENDEDIT fnEndEdit, LPVOID pCallbackPV); + virtual ~CTextEventSink(); + + HRESULT _Advise(IUnknown *pUnknown, UINT uFlags); + HRESULT _Unadvise(); + + // IUnknown interface + STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + // ITfTextEditSink interface + STDMETHODIMP OnEndEdit( + ITfContext *pic, + TfEditCookie ecReadOnly, + ITfEditRecord *pEditRecord) override; + + // ITfTextLayoutSink interface + STDMETHODIMP + OnLayoutChange( + ITfContext *pContext, + TfLayoutCode lcode, + ITfContextView *pContextView) override; +}; + +/***********************************************************************/ + +typedef INT (CALLBACK *FN_INITDOCMGR)(UINT, ITfDocumentMgr *, ITfDocumentMgr *, LPVOID); +typedef INT (CALLBACK *FN_PUSHPOP)(UINT, ITfContext *, LPVOID); + +class CThreadMgrEventSink : public ITfThreadMgrEventSink +{ +protected: + ITfThreadMgr *m_pThreadMgr; + DWORD m_dwCookie; + FN_INITDOCMGR m_fnInit; + FN_PUSHPOP m_fnPushPop; + DWORD m_dw; + LPVOID m_pCallbackPV; + LONG m_cRefs; + +public: + CThreadMgrEventSink( + _In_ FN_INITDOCMGR fnInit, + _In_ FN_PUSHPOP fnPushPop = NULL, + _Inout_ LPVOID pvCallbackPV = NULL); + virtual ~CThreadMgrEventSink() { } + + void SetCallbackPV(_Inout_ LPVOID pv); + HRESULT _Advise(ITfThreadMgr *pThreadMgr); + HRESULT _Unadvise(); + + // IUnknown interface + STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + // ITfThreadMgrEventSink interface + STDMETHODIMP OnInitDocumentMgr(ITfDocumentMgr *pdim) override; + STDMETHODIMP OnUninitDocumentMgr(ITfDocumentMgr *pdim) override; + STDMETHODIMP OnSetFocus(ITfDocumentMgr *pdimFocus, ITfDocumentMgr *pdimPrevFocus) override; + STDMETHODIMP OnPushContext(ITfContext *pic) override; + STDMETHODIMP OnPopContext(ITfContext *pic) override; + + static INT CALLBACK DIMCallback( + UINT nCode, + ITfDocumentMgr *pDocMgr1, + ITfDocumentMgr *pDocMgr2, + LPVOID pUserData); +}; + +/***********************************************************************/ + +class CActiveLanguageProfileNotifySink : public ITfActiveLanguageProfileNotifySink +{ +protected: + typedef INT (CALLBACK *FN_COMPARE)(REFGUID rguid1, REFGUID rguid2, BOOL fActivated, + LPVOID pUserData); + LONG m_cRefs; + ITfThreadMgr *m_pThreadMgr; + DWORD m_dwConnection; + FN_COMPARE m_fnCompare; + LPVOID m_pUserData; + +public: + CActiveLanguageProfileNotifySink(_In_ FN_COMPARE fnCompare, _Inout_opt_ void *pUserData); + virtual ~CActiveLanguageProfileNotifySink(); + + HRESULT _Advise(ITfThreadMgr *pThreadMgr); + HRESULT _Unadvise(); + + // IUnknown interface + STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppvObj) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + + // ITfActiveLanguageProfileNotifySink interface + STDMETHODIMP + OnActivated( + REFCLSID clsid, + REFGUID guidProfile, + BOOL fActivated) override; +}; diff --git a/dll/ime/msctfime/tls.cpp b/dll/ime/msctfime/tls.cpp new file mode 100644 index 00000000000..efb6f9726f5 --- /dev/null +++ b/dll/ime/msctfime/tls.cpp @@ -0,0 +1,56 @@ +/* + * PROJECT: ReactOS msctfime.ime + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Thread-local storage + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +#include "msctfime.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msctfime); + +DWORD TLS::s_dwTlsIndex = (DWORD)-1; + +/// @implemented +TLS* TLS::InternalAllocateTLS() +{ + TLS *pTLS = TLS::PeekTLS(); + if (pTLS) + return pTLS; + + if (DllShutdownInProgress()) + return NULL; + + pTLS = (TLS *)cicMemAllocClear(sizeof(TLS)); + if (!pTLS) + return NULL; + + if (!::TlsSetValue(s_dwTlsIndex, pTLS)) + { + cicMemFree(pTLS); + return NULL; + } + + pTLS->m_dwFlags1 |= 1; + pTLS->m_dwUnknown2 |= 1; + return pTLS; +} + +/// @implemented +BOOL TLS::InternalDestroyTLS() +{ + TLS *pTLS = TLS::PeekTLS(); + if (!pTLS) + return FALSE; + + if (pTLS->m_pBridge) + pTLS->m_pBridge->Release(); + if (pTLS->m_pProfile) + pTLS->m_pProfile->Release(); + if (pTLS->m_pThreadMgr) + pTLS->m_pThreadMgr->Release(); + + cicMemFree(pTLS); + ::TlsSetValue(s_dwTlsIndex, NULL); + return TRUE; +} diff --git a/dll/ime/msctfime/tls.h b/dll/ime/msctfime/tls.h new file mode 100644 index 00000000000..28f6d427177 --- /dev/null +++ b/dll/ime/msctfime/tls.h @@ -0,0 +1,74 @@ +/* + * PROJECT: ReactOS msctfime.ime + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Thread-local storage + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +#pragma once + +class TLS; + +class CicBridge; +class CicProfile; + +class TLS +{ +public: + static DWORD s_dwTlsIndex; + + DWORD m_dwSystemInfoFlags; + CicBridge *m_pBridge; + CicProfile *m_pProfile; + ITfThreadMgr_P *m_pThreadMgr; + DWORD m_dwFlags1; + DWORD m_dwFlags2; + DWORD m_dwUnknown2; + BOOL m_bDestroyed; + DWORD m_dwNowOpening; + DWORD m_NonEAComposition; + DWORD m_cWnds; + + /** + * @implemented + */ + static BOOL Initialize() + { + s_dwTlsIndex = ::TlsAlloc(); + return s_dwTlsIndex != (DWORD)-1; + } + + /** + * @implemented + */ + static VOID Uninitialize() + { + if (s_dwTlsIndex != (DWORD)-1) + { + ::TlsFree(s_dwTlsIndex); + s_dwTlsIndex = (DWORD)-1; + } + } + + /** + * @implemented + */ + static TLS* GetTLS() + { + if (s_dwTlsIndex == (DWORD)-1) + return NULL; + + return InternalAllocateTLS(); + } + + /** + * @implemented + */ + static TLS* PeekTLS() + { + return (TLS*)::TlsGetValue(TLS::s_dwTlsIndex); + } + + static TLS* InternalAllocateTLS(); + static BOOL InternalDestroyTLS(); +}; diff --git a/dll/ime/msctfime/ui.cpp b/dll/ime/msctfime/ui.cpp new file mode 100644 index 00000000000..74a8c733de0 --- /dev/null +++ b/dll/ime/msctfime/ui.cpp @@ -0,0 +1,393 @@ +/* + * PROJECT: ReactOS msctfime.ime + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: User Interface of msctfime.ime + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +#include "msctfime.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msctfime); + +UINT WM_MSIME_SERVICE = 0; +UINT WM_MSIME_UIREADY = 0; +UINT WM_MSIME_RECONVERTREQUEST = 0; +UINT WM_MSIME_RECONVERT = 0; +UINT WM_MSIME_DOCUMENTFEED = 0; +UINT WM_MSIME_QUERYPOSITION = 0; +UINT WM_MSIME_MODEBIAS = 0; +UINT WM_MSIME_SHOWIMEPAD = 0; +UINT WM_MSIME_MOUSE = 0; +UINT WM_MSIME_KEYMAP = 0; + +/// @implemented +BOOL IsMsImeMessage(_In_ UINT uMsg) +{ + return (uMsg == WM_MSIME_SERVICE || + uMsg == WM_MSIME_UIREADY || + uMsg == WM_MSIME_RECONVERTREQUEST || + uMsg == WM_MSIME_RECONVERT || + uMsg == WM_MSIME_DOCUMENTFEED || + uMsg == WM_MSIME_QUERYPOSITION || + uMsg == WM_MSIME_MODEBIAS || + uMsg == WM_MSIME_SHOWIMEPAD || + uMsg == WM_MSIME_MOUSE || + uMsg == WM_MSIME_KEYMAP); +} + +/// @implemented +BOOL RegisterMSIMEMessage(VOID) +{ + // Using ANSI (A) version here can reduce binary size. + WM_MSIME_SERVICE = RegisterWindowMessageA("MSIMEService"); + WM_MSIME_UIREADY = RegisterWindowMessageA("MSIMEUIReady"); + WM_MSIME_RECONVERTREQUEST = RegisterWindowMessageA("MSIMEReconvertRequest"); + WM_MSIME_RECONVERT = RegisterWindowMessageA("MSIMEReconvert"); + WM_MSIME_DOCUMENTFEED = RegisterWindowMessageA("MSIMEDocumentFeed"); + WM_MSIME_QUERYPOSITION = RegisterWindowMessageA("MSIMEQueryPosition"); + WM_MSIME_MODEBIAS = RegisterWindowMessageA("MSIMEModeBias"); + WM_MSIME_SHOWIMEPAD = RegisterWindowMessageA("MSIMEShowImePad"); + WM_MSIME_MOUSE = RegisterWindowMessageA("MSIMEMouseOperation"); + WM_MSIME_KEYMAP = RegisterWindowMessageA("MSIMEKeyMap"); + return (WM_MSIME_SERVICE && + WM_MSIME_UIREADY && + WM_MSIME_RECONVERTREQUEST && + WM_MSIME_RECONVERT && + WM_MSIME_DOCUMENTFEED && + WM_MSIME_QUERYPOSITION && + WM_MSIME_MODEBIAS && + WM_MSIME_SHOWIMEPAD && + WM_MSIME_MOUSE && + WM_MSIME_KEYMAP); +} + +// For GetWindowLongPtr/SetWindowLongPtr +#define UIGWLP_HIMC 0 +#define UIGWLP_UI sizeof(HIMC) +#define UIGWLP_SIZE (UIGWLP_UI + sizeof(UI*)) + +/// @unimplemented +void UIComposition::OnImeStartComposition(CicIMCLock& imcLock, HWND hUIWnd) +{ + //FIXME +} + +/// @unimplemented +void UIComposition::OnImeCompositionUpdate(CicIMCLock& imcLock) +{ + //FIXME +} + +/// @unimplemented +void UIComposition::OnImeEndComposition() +{ + //FIXME +} + +/// @unimplemented +void UIComposition::OnImeSetContext(CicIMCLock& imcLock, HWND hUIWnd, WPARAM wParam, LPARAM lParam) +{ + //FIXME +} + +/// @unimplemented +void UIComposition::OnPaintTheme(WPARAM wParam) +{ + //FIXME +} + +/// @unimplemented +void UIComposition::OnDestroy() +{ + //FIXME +} + +/// @unimplemented +LRESULT CALLBACK +UIComposition::CompWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (uMsg == WM_CREATE) + return -1; // FIXME + return 0; +} + +/***********************************************************************/ + +/// @implemented +UI::UI(HWND hWnd) : m_hWnd(hWnd) +{ +} + +/// @implemented +UI::~UI() +{ + delete m_pComp; +} + +/// @unimplemented +HRESULT UI::_Create() +{ + m_pComp = new(cicNoThrow) UIComposition(); + if (!m_pComp) + return E_OUTOFMEMORY; + + SetWindowLongPtrW(m_hWnd, UIGWLP_UI, (LONG_PTR)this); + //FIXME + return S_OK; +} + +/// @implemented +void UI::_Destroy() +{ + m_pComp->OnDestroy(); + SetWindowLongPtrW(m_hWnd, UIGWLP_UI, 0); +} + +/// @implemented +void UI::OnCreate(HWND hWnd) +{ + UI *pUI = (UI*)GetWindowLongPtrW(hWnd, UIGWLP_UI); + if (pUI) + return; + pUI = new(cicNoThrow) UI(hWnd); + if (pUI) + pUI->_Create(); +} + +/// @implemented +void UI::OnDestroy(HWND hWnd) +{ + UI *pUI = (UI*)GetWindowLongPtrW(hWnd, UIGWLP_UI); + if (!pUI) + return; + + pUI->_Destroy(); + delete pUI; +} + +/// @implemented +void UI::OnImeSetContext(CicIMCLock& imcLock, WPARAM wParam, LPARAM lParam) +{ + m_pComp->OnImeSetContext(imcLock, m_hWnd, wParam, lParam); +} + +/***********************************************************************/ + +struct CIMEUIWindowHandler +{ + static LRESULT CALLBACK ImeUIMsImeHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + static LRESULT CALLBACK ImeUIMsImeMouseHandler(HWND hWnd, WPARAM wParam, LPARAM lParam); + static LRESULT CALLBACK ImeUIMsImeModeBiasHandler(HWND hWnd, WPARAM wParam, LPARAM lParam); + static LRESULT CALLBACK ImeUIMsImeReconvertRequest(HWND hWnd, WPARAM wParam, LPARAM lParam); + static LRESULT CALLBACK ImeUIWndProcWorker(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +}; + +/// @unimplemented +LRESULT CALLBACK +CIMEUIWindowHandler::ImeUIMsImeMouseHandler(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + return 0; //FIXME +} + +/// @unimplemented +LRESULT CALLBACK +CIMEUIWindowHandler::ImeUIMsImeModeBiasHandler(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + return 0; //FIXME +} + +/// @unimplemented +LRESULT CALLBACK +CIMEUIWindowHandler::ImeUIMsImeReconvertRequest(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + return 0; //FIXME +} + +/// @implemented +LRESULT CALLBACK +CIMEUIWindowHandler::ImeUIMsImeHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (uMsg == WM_MSIME_MOUSE) + return ImeUIMsImeMouseHandler(hWnd, wParam, lParam); + if (uMsg == WM_MSIME_MODEBIAS) + return ImeUIMsImeModeBiasHandler(hWnd, wParam, lParam); + if (uMsg == WM_MSIME_RECONVERTREQUEST) + return ImeUIMsImeReconvertRequest(hWnd, wParam, lParam); + if (uMsg == WM_MSIME_SERVICE) + { + TLS *pTLS = TLS::GetTLS(); + if (pTLS && pTLS->m_pProfile) + { + LANGID LangID; + pTLS->m_pProfile->GetLangId(&LangID); + if (PRIMARYLANGID(LangID) == LANG_KOREAN) + return FALSE; + } + return TRUE; + } + return 0; +} + +/// @unimplemented +LRESULT CALLBACK +CIMEUIWindowHandler::ImeUIWndProcWorker(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + TLS *pTLS = TLS::GetTLS(); + if (pTLS && (pTLS->m_dwSystemInfoFlags & IME_SYSINFO_WINLOGON)) + { + if (uMsg == WM_CREATE) + return -1; + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + + switch (uMsg) + { + case WM_CREATE: + { + UI::OnCreate(hWnd); + break; + } + case WM_DESTROY: + case WM_ENDSESSION: + { + UI::OnDestroy(hWnd); + break; + } + case WM_IME_STARTCOMPOSITION: + case WM_IME_COMPOSITION: + case WM_IME_ENDCOMPOSITION: + case WM_IME_SETCONTEXT: + case WM_IME_NOTIFY: + case WM_IME_SELECT: + case WM_TIMER: + { + HIMC hIMC = (HIMC)GetWindowLongPtrW(hWnd, UIGWLP_HIMC); + UI* pUI = (UI*)GetWindowLongPtrW(hWnd, UIGWLP_UI); + CicIMCLock imcLock(hIMC); + switch (uMsg) + { + case WM_IME_STARTCOMPOSITION: + { + pUI->m_pComp->OnImeStartComposition(imcLock, pUI->m_hWnd); + break; + } + case WM_IME_COMPOSITION: + { + if (lParam & GCS_COMPSTR) + { + pUI->m_pComp->OnImeCompositionUpdate(imcLock); + ::SetTimer(hWnd, 0, 10, NULL); + //FIXME + } + break; + } + case WM_IME_ENDCOMPOSITION: + { + ::KillTimer(hWnd, 0); + pUI->m_pComp->OnImeEndComposition(); + break; + } + case WM_IME_SETCONTEXT: + { + pUI->OnImeSetContext(imcLock, wParam, lParam); + ::KillTimer(hWnd, 1); + ::SetTimer(hWnd, 1, 300, NULL); + break; + } + case WM_TIMER: + { + //FIXME + ::KillTimer(hWnd, wParam); + break; + } + case WM_IME_NOTIFY: + case WM_IME_SELECT: + default: + { + pUI->m_pComp->OnPaintTheme(wParam); + break; + } + } + break; + } + default: + { + if (IsMsImeMessage(uMsg)) + return CIMEUIWindowHandler::ImeUIMsImeHandler(hWnd, uMsg, wParam, lParam); + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + } + + return 0; +} + +/***********************************************************************/ + +/// @implemented +EXTERN_C LRESULT CALLBACK +UIWndProc( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam) +{ + return CIMEUIWindowHandler::ImeUIWndProcWorker(hWnd, uMsg, wParam, lParam); +} + +/***********************************************************************/ + +/// @unimplemented +BOOL RegisterImeClass(VOID) +{ + WNDCLASSEXW wcx; + + if (!GetClassInfoExW(g_hInst, L"MSCTFIME UI", &wcx)) + { + ZeroMemory(&wcx, sizeof(wcx)); + wcx.cbSize = sizeof(WNDCLASSEXW); + wcx.cbWndExtra = UIGWLP_SIZE; + wcx.hIcon = LoadIconW(0, (LPCWSTR)IDC_ARROW); + wcx.hInstance = g_hInst; + wcx.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); + wcx.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); + wcx.style = CS_IME | CS_GLOBALCLASS; + wcx.lpfnWndProc = UIWndProc; + wcx.lpszClassName = L"MSCTFIME UI"; + if (!RegisterClassExW(&wcx)) + return FALSE; + } + + if (!GetClassInfoExW(g_hInst, L"MSCTFIME Composition", &wcx)) + { + ZeroMemory(&wcx, sizeof(wcx)); + wcx.cbSize = sizeof(WNDCLASSEXW); + wcx.cbWndExtra = sizeof(DWORD); + wcx.hIcon = NULL; + wcx.hInstance = g_hInst; + wcx.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_IBEAM); + wcx.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); + wcx.style = CS_IME | CS_HREDRAW | CS_VREDRAW; + wcx.lpfnWndProc = UIComposition::CompWndProc; + wcx.lpszClassName = L"MSCTFIME Composition"; + if (!RegisterClassExW(&wcx)) + return FALSE; + } + + return TRUE; +} + +/// @implemented +VOID UnregisterImeClass(VOID) +{ + WNDCLASSEXW wcx; + + GetClassInfoExW(g_hInst, L"MSCTFIME UI", &wcx); + UnregisterClassW(L"MSCTFIME UI", g_hInst); + DestroyIcon(wcx.hIcon); + DestroyIcon(wcx.hIconSm); + + GetClassInfoExW(g_hInst, L"MSCTFIME Composition", &wcx); + UnregisterClassW(L"MSCTFIME Composition", g_hInst); + DestroyIcon(wcx.hIcon); + DestroyIcon(wcx.hIconSm); +} diff --git a/dll/ime/msctfime/ui.h b/dll/ime/msctfime/ui.h new file mode 100644 index 00000000000..e9433fc734c --- /dev/null +++ b/dll/ime/msctfime/ui.h @@ -0,0 +1,68 @@ +/* + * PROJECT: ReactOS msctfime.ime + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: User Interface of msctfime.ime + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +#pragma once + +/***********************************************************************/ + +extern UINT WM_MSIME_SERVICE; +extern UINT WM_MSIME_UIREADY; +extern UINT WM_MSIME_RECONVERTREQUEST; +extern UINT WM_MSIME_RECONVERT; +extern UINT WM_MSIME_DOCUMENTFEED; +extern UINT WM_MSIME_QUERYPOSITION; +extern UINT WM_MSIME_MODEBIAS; +extern UINT WM_MSIME_SHOWIMEPAD; +extern UINT WM_MSIME_MOUSE; +extern UINT WM_MSIME_KEYMAP; + +BOOL IsMsImeMessage(_In_ UINT uMsg); +BOOL RegisterMSIMEMessage(VOID); + +/***********************************************************************/ + +struct UIComposition +{ + void OnImeStartComposition(CicIMCLock& imcLock, HWND hUIWnd); + void OnImeCompositionUpdate(CicIMCLock& imcLock); + void OnImeEndComposition(); + void OnImeSetContext(CicIMCLock& imcLock, HWND hUIWnd, WPARAM wParam, LPARAM lParam); + void OnPaintTheme(WPARAM wParam); + void OnDestroy(); + + static LRESULT CALLBACK CompWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +}; + +/***********************************************************************/ + +struct UI +{ + HWND m_hWnd; + UIComposition *m_pComp; + + UI(HWND hWnd); + virtual ~UI(); + + HRESULT _Create(); + void _Destroy(); + + static void OnCreate(HWND hWnd); + static void OnDestroy(HWND hWnd); + void OnImeSetContext(CicIMCLock& imcLock, WPARAM wParam, LPARAM lParam); +}; + +/***********************************************************************/ + +EXTERN_C LRESULT CALLBACK +UIWndProc( + _In_ HWND hWnd, + _In_ UINT uMsg, + _In_ WPARAM wParam, + _In_ LPARAM lParam); + +BOOL RegisterImeClass(VOID); +VOID UnregisterImeClass(VOID);