[MSCTFIME] Restructuring (#6505)
authorKatayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
Tue, 20 Feb 2024 12:11:08 +0000 (21:11 +0900)
committerGitHub <noreply@github.com>
Tue, 20 Feb 2024 12:11:08 +0000 (21:11 +0900)
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.

19 files changed:
dll/ime/msctfime/CMakeLists.txt
dll/ime/msctfime/bridge.cpp [new file with mode: 0644]
dll/ime/msctfime/bridge.h [new file with mode: 0644]
dll/ime/msctfime/compartment.cpp [new file with mode: 0644]
dll/ime/msctfime/compartment.h [new file with mode: 0644]
dll/ime/msctfime/functions.cpp [new file with mode: 0644]
dll/ime/msctfime/functions.h [new file with mode: 0644]
dll/ime/msctfime/inputcontext.cpp [new file with mode: 0644]
dll/ime/msctfime/inputcontext.h [new file with mode: 0644]
dll/ime/msctfime/msctfime.cpp
dll/ime/msctfime/msctfime.h
dll/ime/msctfime/profile.cpp [new file with mode: 0644]
dll/ime/msctfime/profile.h [new file with mode: 0644]
dll/ime/msctfime/sinks.cpp [new file with mode: 0644]
dll/ime/msctfime/sinks.h [new file with mode: 0644]
dll/ime/msctfime/tls.cpp [new file with mode: 0644]
dll/ime/msctfime/tls.h [new file with mode: 0644]
dll/ime/msctfime/ui.cpp [new file with mode: 0644]
dll/ime/msctfime/ui.h [new file with mode: 0644]

index 5a03439..288c14a 100644 (file)
@@ -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 (file)
index 0000000..de9e6ba
--- /dev/null
@@ -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 <katayama.hirofumi.mz@gmail.com>
+ */
+
+#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<CTFIMECONTEXT>& 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<CTFIMECONTEXT> 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<CTFIMECONTEXT> 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<CTFIMECONTEXT>& 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<CTFIMECONTEXT> 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 (file)
index 0000000..c8b8441
--- /dev/null
@@ -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 <katayama.hirofumi.mz@gmail.com>
+ */
+
+#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<CTFIMECONTEXT>& 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<CTFIMECONTEXT>& 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 (file)
index 0000000..d92b1b5
--- /dev/null
@@ -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 <katayama.hirofumi.mz@gmail.com>
+ */
+
+#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 (file)
index 0000000..8880556
--- /dev/null
@@ -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 <katayama.hirofumi.mz@gmail.com>
+ */
+
+#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 (file)
index 0000000..c9832f9
--- /dev/null
@@ -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 <katayama.hirofumi.mz@gmail.com>
+ */
+
+#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 (file)
index 0000000..6938162
--- /dev/null
@@ -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 <katayama.hirofumi.mz@gmail.com>
+ */
+
+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 (file)
index 0000000..1bdf64c
--- /dev/null
@@ -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 <katayama.hirofumi.mz@gmail.com>
+ */
+
+#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<ITfContextOwnerCompositionSink*>(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<CTFIMECONTEXT> 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 (file)
index 0000000..2273161
--- /dev/null
@@ -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 <katayama.hirofumi.mz@gmail.com>
+ */
+
+#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();
+};
index ccd4420..445998d 100644 (file)
@@ -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.
@@ -195,2533 +139,115 @@ HRESULT InitDisplayAttrbuteLib(PCIC_LIBTHREAD pLibThread)
     }
     ::LeaveCriticalSection(&g_csLock);
 
-    return hr;
-}
-
-HIMC GetActiveContext(VOID)
-{
-    HWND hwndFocus = ::GetFocus();
-    if (!hwndFocus)
-        hwndFocus = ::GetActiveWindow();
-    return ::ImmGetContext(hwndFocus);
-}
-
-/// @implemented
-HRESULT UninitDisplayAttrbuteLib(PCIC_LIBTHREAD pLibThread)
-{
-    if (!pLibThread)
-        return E_FAIL;
-
-    if (pLibThread->m_pDisplayAttrMgr)
-    {
-        pLibThread->m_pDisplayAttrMgr->Release();
-        pLibThread->m_pDisplayAttrMgr = NULL;
-    }
-
-    return S_OK;
-}
-
-/// Gets the charset from a language ID.
-/// @implemented
-BYTE GetCharsetFromLangId(_In_ DWORD dwValue)
-{
-    CHARSETINFO info;
-    if (!::TranslateCharsetInfo((DWORD*)(DWORD_PTR)dwValue, &info, TCI_SRCLOCALE))
-        return 0;
-    return info.ciCharset;
-}
-
-/// Selects or unselects the input context.
-/// @implemented
-HRESULT
-InternalSelectEx(
-    _In_ HIMC hIMC,
-    _In_ BOOL fSelect,
-    _In_ LANGID LangID)
-{
-    CicIMCLock imcLock(hIMC);
-    if (!imcLock)
-        imcLock.m_hr = E_FAIL;
-    if (FAILED(imcLock.m_hr))
-        return imcLock.m_hr;
-
-    if (PRIMARYLANGID(LangID) == LANG_CHINESE)
-    {
-        imcLock.get().cfCandForm[0].dwStyle = 0;
-        imcLock.get().cfCandForm[0].dwIndex = (DWORD)-1;
-    }
-
-    if (!fSelect)
-    {
-        imcLock.get().fdwInit &= ~INIT_GUIDMAP;
-        return imcLock.m_hr;
-    }
-
-    if (!imcLock.ClearCand())
-        return imcLock.m_hr;
-
-    // Populate conversion mode
-    if (!(imcLock.get().fdwInit & INIT_CONVERSION))
-    {
-        DWORD dwConv = (imcLock.get().fdwConversion & IME_CMODE_SOFTKBD);
-        if (LangID)
-        {
-            if (PRIMARYLANGID(LangID) == LANG_JAPANESE)
-            {
-                dwConv |= IME_CMODE_ROMAN | IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE;
-            }
-            else if (PRIMARYLANGID(LangID) != LANG_KOREAN)
-            {
-                dwConv |= IME_CMODE_NATIVE;
-            }
-        }
-        imcLock.get().fdwConversion |= dwConv;
-        imcLock.get().fdwInit |= INIT_CONVERSION;
-    }
-
-    // Populate sentence mode
-    imcLock.get().fdwSentence |= IME_SMODE_PHRASEPREDICT;
-
-    // Populate LOGFONT
-    if (!(imcLock.get().fdwInit & INIT_LOGFONT))
-    {
-        // Get logical font
-        LOGFONTW lf;
-        HDC hDC = ::GetDC(imcLock.get().hWnd);
-        HGDIOBJ hFont = GetCurrentObject(hDC, OBJ_FONT);
-        ::GetObjectW(hFont, sizeof(LOGFONTW), &lf);
-        ::ReleaseDC(imcLock.get().hWnd, hDC);
-
-        imcLock.get().lfFont.W = lf;
-        imcLock.get().fdwInit |= INIT_LOGFONT;
-    }
-    imcLock.get().lfFont.W.lfCharSet = GetCharsetFromLangId(LangID);
-
-    imcLock.InitContext();
-
-    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<CESMAP> 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<ITfTextLayoutSink*>(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<ITfTextEditSink*>(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<ITfContextOwnerCompositionSink*>(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<CTFIMECONTEXT> 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<CTFIMECONTEXT>& 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<CTFIMECONTEXT>& 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<CTFIMECONTEXT>& 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<CTFIMECONTEXT> 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<CTFIMECONTEXT> 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<CTFIMECONTEXT>& 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<CTFIMECONTEXT> 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;
+    return hr;
 }
 
-/// @implemented
-STDMETHODIMP CicBridge::OnSysShellProc(INT, UINT, LONG)
+HIMC GetActiveContext(VOID)
 {
-    return S_OK;
+    HWND hwndFocus = ::GetFocus();
+    if (!hwndFocus)
+        hwndFocus = ::GetActiveWindow();
+    return ::ImmGetContext(hwndFocus);
 }
 
 /// @implemented
-void
-CicBridge::PostTransMsg(
-    _In_ HWND hWnd,
-    _In_ INT cTransMsgs,
-    _In_ const TRANSMSG *pTransMsgs)
+HRESULT UninitDisplayAttrbuteLib(PCIC_LIBTHREAD pLibThread)
 {
-    for (INT i = 0; i < cTransMsgs; ++i, ++pTransMsgs)
+    if (!pLibThread)
+        return E_FAIL;
+
+    if (pLibThread->m_pDisplayAttrMgr)
     {
-        ::PostMessageW(hWnd, pTransMsgs->message, pTransMsgs->wParam, pTransMsgs->lParam);
+        pLibThread->m_pDisplayAttrMgr->Release();
+        pLibThread->m_pDisplayAttrMgr = NULL;
     }
+
+    return S_OK;
 }
 
+/// Gets the charset from a language ID.
 /// @implemented
-HRESULT
-CicBridge::ConfigureGeneral(
-    _Inout_ TLS* pTLS,
-    _In_ ITfThreadMgr *pThreadMgr,
-    _In_ HKL hKL,
-    _In_ HWND hWnd)
+BYTE GetCharsetFromLangId(_In_ DWORD dwValue)
 {
-    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;
+    CHARSETINFO info;
+    if (!::TranslateCharsetInfo((DWORD*)(DWORD_PTR)dwValue, &info, TCI_SRCLOCALE))
+        return 0;
+    return info.ciCharset;
 }
 
+/// Selects or unselects the input context.
 /// @implemented
 HRESULT
-CicBridge::ConfigureRegisterWord(
-    _Inout_ TLS* pTLS,
-    _In_ ITfThreadMgr *pThreadMgr,
-    _In_ HKL hKL,
-    _In_ HWND hWnd,
-    _Inout_opt_ LPVOID lpData)
+InternalSelectEx(
+    _In_ HIMC hIMC,
+    _In_ BOOL fSelect,
+    _In_ LANGID LangID)
 {
-    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;
+    CicIMCLock imcLock(hIMC);
+    if (!imcLock)
+        imcLock.m_hr = E_FAIL;
+    if (FAILED(imcLock.m_hr))
+        return imcLock.m_hr;
 
-    ITfFunctionProvider *pProvider = NULL;
-    hr = pThreadMgr->GetFunctionProvider(profile.clsid, &pProvider);
-    if (FAILED(hr))
-        return hr;
+    if (PRIMARYLANGID(LangID) == LANG_CHINESE)
+    {
+        imcLock.get().cfCandForm[0].dwStyle = 0;
+        imcLock.get().cfCandForm[0].dwIndex = (DWORD)-1;
+    }
 
-    ITfFnConfigureRegisterWord *pFunction = NULL;
-    hr = pProvider->GetFunction(GUID_NULL, IID_ITfFnConfigureRegisterWord, (IUnknown**)&pFunction);
-    if (FAILED(hr))
+    if (!fSelect)
     {
-        pProvider->Release();
-        return hr;
+        imcLock.get().fdwInit &= ~INIT_GUIDMAP;
+        return imcLock.m_hr;
     }
 
-    REGISTERWORDW* pRegWord = (REGISTERWORDW*)lpData;
-    if (pRegWord)
+    if (!imcLock.ClearCand())
+        return imcLock.m_hr;
+
+    // Populate conversion mode
+    if (!(imcLock.get().fdwInit & INIT_CONVERSION))
     {
-        if (pRegWord->lpWord)
+        DWORD dwConv = (imcLock.get().fdwConversion & IME_CMODE_SOFTKBD);
+        if (LangID)
         {
-            hr = E_OUTOFMEMORY;
-            BSTR bstrWord = SysAllocString(pRegWord->lpWord);
-            if (bstrWord)
+            if (PRIMARYLANGID(LangID) == LANG_JAPANESE)
             {
-                hr = pFunction->Show(hWnd, profile.langid, profile.guidProfile, bstrWord);
-                SysFreeString(bstrWord);
+                dwConv |= IME_CMODE_ROMAN | IME_CMODE_FULLSHAPE | IME_CMODE_NATIVE;
+            }
+            else if (PRIMARYLANGID(LangID) != LANG_KOREAN)
+            {
+                dwConv |= IME_CMODE_NATIVE;
             }
         }
-        else
-        {
-            hr = pFunction->Show(hWnd, profile.langid, profile.guidProfile, NULL);
-        }
+        imcLock.get().fdwConversion |= dwConv;
+        imcLock.get().fdwInit |= INIT_CONVERSION;
     }
 
-    pProvider->Release();
-    pFunction->Release();
-    return hr;
-}
+    // Populate sentence mode
+    imcLock.get().fdwSentence |= IME_SMODE_PHRASEPREDICT;
 
-/***********************************************************************
- *      CicProfile
- */
+    // Populate LOGFONT
+    if (!(imcLock.get().fdwInit & INIT_LOGFONT))
+    {
+        // Get logical font
+        LOGFONTW lf;
+        HDC hDC = ::GetDC(imcLock.get().hWnd);
+        HGDIOBJ hFont = GetCurrentObject(hDC, OBJ_FONT);
+        ::GetObjectW(hFont, sizeof(LOGFONTW), &lf);
+        ::ReleaseDC(imcLock.get().hWnd, hDC);
 
-/// @unimplemented
-HRESULT
-CicProfile::GetActiveLanguageProfile(
-    _In_ HKL hKL,
-    _In_ REFGUID rguid,
-    _Out_ TF_LANGUAGEPROFILE *pProfile)
-{
-    return E_NOTIMPL;
+        imcLock.get().lfFont.W = lf;
+        imcLock.get().fdwInit |= INIT_LOGFONT;
+    }
+    imcLock.get().lfFont.W.lfCharSet = GetCharsetFromLangId(LangID);
+
+    imcLock.InitContext();
+
+    return imcLock.m_hr;
 }
 
+class TLS;
+
 /***********************************************************************
  *      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)
 {
index 040ecc1..f7ada8f 100644 (file)
 
 #include <wine/debug.h>
 
+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 (file)
index 0000000..9ec7cd1
--- /dev/null
@@ -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 <katayama.hirofumi.mz@gmail.com>
+ */
+
+#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 (file)
index 0000000..2943750
--- /dev/null
@@ -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 <katayama.hirofumi.mz@gmail.com>
+ */
+
+#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 (file)
index 0000000..235079c
--- /dev/null
@@ -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 <katayama.hirofumi.mz@gmail.com>
+ */
+
+#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<ITfTextLayoutSink*>(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<ITfTextEditSink*>(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 (file)
index 0000000..9d1fb78
--- /dev/null
@@ -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 <katayama.hirofumi.mz@gmail.com>
+ */
+
+#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<CESMAP> 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 (file)
index 0000000..efb6f97
--- /dev/null
@@ -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 <katayama.hirofumi.mz@gmail.com>
+ */
+
+#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 (file)
index 0000000..28f6d42
--- /dev/null
@@ -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 <katayama.hirofumi.mz@gmail.com>
+ */
+
+#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 (file)
index 0000000..74a8c73
--- /dev/null
@@ -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 <katayama.hirofumi.mz@gmail.com>
+ */
+
+#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 (file)
index 0000000..e9433fc
--- /dev/null
@@ -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 <katayama.hirofumi.mz@gmail.com>
+ */
+
+#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);