Something went wrong. Try again.
Reactos
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399/* * PROJECT: ReactOS CTF * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) * PURPOSE: ITfThreadMgr implementation * COPYRIGHT: Copyright 2008 Aric Stewart, CodeWeavers * Copyright 2025 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> */
#include "precomp.h"
#include <wine/debug.h>WINE_DEFAULT_DEBUG_CHANNEL(msctf);
////////////////////////////////////////////////////////////////////////////
typedef struct tagPreservedKey{ struct list entry; GUID guid; TF_PRESERVEDKEY prekey; LPWSTR description; TfClientId tid;} PreservedKey;
typedef struct tagDocumentMgrs{ struct list entry; ITfDocumentMgr *docmgr;} DocumentMgrEntry;
typedef struct tagAssociatedWindow{ struct list entry; HWND hwnd; ITfDocumentMgr *docmgr;} AssociatedWindow;
////////////////////////////////////////////////////////////////////////////
class CThreadMgr : public ITfThreadMgrEx , public ITfSource , public ITfKeystrokeMgr , public ITfMessagePump , public ITfClientId // , public ITfConfigureSystemKeystrokeFeed // , public ITfLangBarItemMgr , public ITfUIElementMgr , public ITfSourceSingle , public ITfThreadMgrEventSink{public: CThreadMgr(); virtual ~CThreadMgr();
static HRESULT CreateInstance(IUnknown *pUnkOuter, CThreadMgr **ppOut); void OnDocumentMgrDestruction(ITfDocumentMgr *mgr);
// ** IUnknown methods ** STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppvObject) override; STDMETHODIMP_(ULONG) AddRef() override; STDMETHODIMP_(ULONG) Release() override;
// ** ITfThreadMgr methods ** STDMETHODIMP Activate(_Out_ TfClientId *ptid) override; STDMETHODIMP Deactivate() override; STDMETHODIMP CreateDocumentMgr(_Out_ ITfDocumentMgr **ppdim) override; STDMETHODIMP EnumDocumentMgrs(_Out_ IEnumTfDocumentMgrs **ppEnum) override; STDMETHODIMP GetFocus(_Out_ ITfDocumentMgr **ppdimFocus) override; STDMETHODIMP SetFocus(_In_ ITfDocumentMgr *pdimFocus) override; STDMETHODIMP AssociateFocus( _In_ HWND hwnd, _In_ ITfDocumentMgr *pdimNew, _Out_ ITfDocumentMgr **ppdimPrev) override; STDMETHODIMP IsThreadFocus(_Out_ BOOL *pfThreadFocus) override; STDMETHODIMP GetFunctionProvider( _In_ REFCLSID clsid, _Out_ ITfFunctionProvider **ppFuncProv) override; STDMETHODIMP EnumFunctionProviders(_Out_ IEnumTfFunctionProviders **ppEnum) override; STDMETHODIMP GetGlobalCompartment(_Out_ ITfCompartmentMgr **ppCompMgr) override;
// ** ITfThreadMgrEx methods ** STDMETHODIMP ActivateEx( _Out_ TfClientId *id, _In_ DWORD flags) override; STDMETHODIMP GetActiveFlags(_Out_ DWORD *flags) override;
// ** ITfSource methods ** STDMETHODIMP AdviseSink( _In_ REFIID riid, _In_ IUnknown *punk, _Out_ DWORD *pdwCookie) override; STDMETHODIMP UnadviseSink(_In_ DWORD dwCookie) override;
// ** ITfKeystrokeMgr methods ** STDMETHODIMP AdviseKeyEventSink( _In_ TfClientId tid, _In_ ITfKeyEventSink *pSink, _In_ BOOL fForeground) override; STDMETHODIMP UnadviseKeyEventSink(_In_ TfClientId tid) override; STDMETHODIMP GetForeground(_Out_ CLSID *pclsid) override; STDMETHODIMP TestKeyDown( _In_ WPARAM wParam, _In_ LPARAM lParam, _Out_ BOOL *pfEaten) override; STDMETHODIMP TestKeyUp( _In_ WPARAM wParam, _In_ LPARAM lParam, _Out_ BOOL *pfEaten) override; STDMETHODIMP KeyDown( _In_ WPARAM wParam, _In_ LPARAM lParam, _Out_ BOOL *pfEaten) override; STDMETHODIMP KeyUp( _In_ WPARAM wParam, _In_ LPARAM lParam, _Out_ BOOL *pfEaten) override; STDMETHODIMP GetPreservedKey( _In_ ITfContext *pic, _In_ const TF_PRESERVEDKEY *pprekey, _Out_ GUID *pguid) override; STDMETHODIMP IsPreservedKey( _In_ REFGUID rguid, _In_ const TF_PRESERVEDKEY *pprekey, _Out_ BOOL *pfRegistered) override; STDMETHODIMP PreserveKey( _In_ TfClientId tid, _In_ REFGUID rguid, _In_ const TF_PRESERVEDKEY *prekey, _In_ const WCHAR *pchDesc, _In_ ULONG cchDesc) override; STDMETHODIMP UnpreserveKey( _In_ REFGUID rguid, _In_ const TF_PRESERVEDKEY *pprekey) override; STDMETHODIMP SetPreservedKeyDescription( _In_ REFGUID rguid, _In_ const WCHAR *pchDesc, _In_ ULONG cchDesc) override; STDMETHODIMP GetPreservedKeyDescription( _In_ REFGUID rguid, _Out_ BSTR *pbstrDesc) override; STDMETHODIMP SimulatePreservedKey( _In_ ITfContext *pic, _In_ REFGUID rguid, _Out_ BOOL *pfEaten) override;
// ** ITfMessagePump methods ** STDMETHODIMP PeekMessageA( _Out_ LPMSG pMsg, _In_ HWND hwnd, _In_ UINT wMsgFilterMin, _In_ UINT wMsgFilterMax, _In_ UINT wRemoveMsg, _Out_ BOOL *pfResult) override; STDMETHODIMP GetMessageA( _Out_ LPMSG pMsg, _In_ HWND hwnd, _In_ UINT wMsgFilterMin, _In_ UINT wMsgFilterMax, _Out_ BOOL *pfResult) override; STDMETHODIMP PeekMessageW( _Out_ LPMSG pMsg, _In_ HWND hwnd, _In_ UINT wMsgFilterMin, _In_ UINT wMsgFilterMax, _In_ UINT wRemoveMsg, _Out_ BOOL *pfResult) override; STDMETHODIMP GetMessageW( _Out_ LPMSG pMsg, _In_ HWND hwnd, _In_ UINT wMsgFilterMin, _In_ UINT wMsgFilterMax, _Out_ BOOL *pfResult) override;
// ** ITfClientId methods ** STDMETHODIMP GetClientId( _In_ REFCLSID rclsid, _Out_ TfClientId *ptid) override;
// ** ITfUIElementMgr methods ** STDMETHODIMP BeginUIElement( _In_ ITfUIElement *element, _Inout_ BOOL *show, _Out_ DWORD *id) override; STDMETHODIMP UpdateUIElement(_In_ DWORD id) override; STDMETHODIMP EndUIElement(_In_ DWORD id) override; STDMETHODIMP GetUIElement( _In_ DWORD id, _Out_ ITfUIElement **element) override; STDMETHODIMP EnumUIElements(_Out_ IEnumTfUIElements **enum_elements) override;
// ** ITfSourceSingle methods ** STDMETHODIMP AdviseSingleSink( _In_ TfClientId tid, _In_ REFIID riid, _In_ IUnknown *punk) override; STDMETHODIMP UnadviseSingleSink( _In_ TfClientId tid, _In_ REFIID riid) override;
// ** ITfThreadMgrEventSink methods ** STDMETHODIMP OnInitDocumentMgr(_In_ ITfDocumentMgr *pdim) override; STDMETHODIMP OnUninitDocumentMgr(_In_ ITfDocumentMgr *pdim) override; STDMETHODIMP OnSetFocus( _In_ ITfDocumentMgr *pdimFocus, _In_ ITfDocumentMgr *pdimPrevFocus) override; STDMETHODIMP OnPushContext(_In_ ITfContext *pic) override; STDMETHODIMP OnPopContext(_In_ ITfContext *pic) override;
protected: LONG m_cRefs;
/* Aggregation */ ITfCompartmentMgr *m_CompartmentMgr;
ITfDocumentMgr *m_focus; LONG m_activationCount;
ITfKeyEventSink *m_foregroundKeyEventSink; CLSID m_foregroundTextService;
struct list m_CurrentPreservedKeys; struct list m_CreatedDocumentMgrs;
struct list m_AssociatedFocusWindows; HHOOK m_focusHook;
/* kept as separate lists to reduce unnecessary iterations */ struct list m_ActiveLanguageProfileNotifySink; struct list m_DisplayAttributeNotifySink; struct list m_KeyTraceEventSink; struct list m_PreservedKeyNotifySink; struct list m_ThreadFocusSink; struct list m_ThreadMgrEventSink; struct list m_UIElementSink; struct list m_InputProcessorProfileActivationSink;
static LRESULT CALLBACK ThreadFocusHookProc(INT nCode, WPARAM wParam, LPARAM lParam); LRESULT _ThreadFocusHookProc(INT nCode, WPARAM wParam, LPARAM lParam);
HRESULT SetupWindowsHook();};
////////////////////////////////////////////////////////////////////////////
class CEnumTfDocumentMgr : public IEnumTfDocumentMgrs{public: CEnumTfDocumentMgr(); virtual ~CEnumTfDocumentMgr();
static HRESULT CreateInstance(struct list* head, CEnumTfDocumentMgr **ppOut);
// ** IUnknown methods ** STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppvObject) override; STDMETHODIMP_(ULONG) AddRef() override; STDMETHODIMP_(ULONG) Release() override;
// ** IEnumTfDocumentMgrs methods ** STDMETHODIMP Clone(_Out_ IEnumTfDocumentMgrs **ppEnum) override; STDMETHODIMP Next( _In_ ULONG ulCount, _Out_ ITfDocumentMgr **rgDocumentMgr, _Out_ ULONG *pcFetched) override; STDMETHODIMP Reset() override; STDMETHODIMP Skip(_In_ ULONG ulCount) override;
protected: LONG m_cRefs; struct list *m_index; struct list *m_head;};
////////////////////////////////////////////////////////////////////////////
CThreadMgr::CThreadMgr() : m_cRefs(1) , m_CompartmentMgr(NULL) , m_focus(NULL) , m_activationCount(0) , m_foregroundKeyEventSink(NULL){ m_foregroundTextService = GUID_NULL;
list_init(&m_CurrentPreservedKeys); list_init(&m_CreatedDocumentMgrs);
list_init(&m_AssociatedFocusWindows); m_focusHook = NULL;
/* kept as separate lists to reduce unnecessary iterations */ list_init(&m_ActiveLanguageProfileNotifySink); list_init(&m_DisplayAttributeNotifySink); list_init(&m_KeyTraceEventSink); list_init(&m_PreservedKeyNotifySink); list_init(&m_ThreadFocusSink); list_init(&m_ThreadMgrEventSink); list_init(&m_UIElementSink); list_init(&m_InputProcessorProfileActivationSink);}
CThreadMgr::~CThreadMgr(){ struct list *cursor, *cursor2;
/* unhook right away */ if (m_focusHook) UnhookWindowsHookEx(m_focusHook);
TlsSetValue(g_dwTLSIndex, NULL); TRACE("destroying %p\n", this);
if (m_focus) m_focus->Release();
free_sinks(&m_ActiveLanguageProfileNotifySink); free_sinks(&m_DisplayAttributeNotifySink); free_sinks(&m_KeyTraceEventSink); free_sinks(&m_PreservedKeyNotifySink); free_sinks(&m_ThreadFocusSink); free_sinks(&m_ThreadMgrEventSink); free_sinks(&m_UIElementSink); free_sinks(&m_InputProcessorProfileActivationSink);
LIST_FOR_EACH_SAFE(cursor, cursor2, &m_CurrentPreservedKeys) { PreservedKey* key = LIST_ENTRY(cursor, PreservedKey, entry); list_remove(cursor); cicMemFree(key->description); cicMemFree(key); }
LIST_FOR_EACH_SAFE(cursor, cursor2, &m_CreatedDocumentMgrs) { DocumentMgrEntry *mgr = LIST_ENTRY(cursor, DocumentMgrEntry, entry); list_remove(cursor); FIXME("Left Over ITfDocumentMgr. Should we do something with it?\n"); cicMemFree(mgr); }
LIST_FOR_EACH_SAFE(cursor, cursor2, &m_AssociatedFocusWindows) { AssociatedWindow *wnd = LIST_ENTRY(cursor, AssociatedWindow, entry); list_remove(cursor); cicMemFree(wnd); }
m_CompartmentMgr->Release();}
STDMETHODIMP CThreadMgr::QueryInterface(REFIID iid, LPVOID *ppvObject){ *ppvObject = NULL;
IUnknown *pUnk = NULL; if (iid == IID_IUnknown || iid == IID_ITfThreadMgr || iid == IID_ITfThreadMgrEx) pUnk = static_cast<ITfThreadMgrEx *>(this); else if (iid == IID_ITfSource) pUnk = static_cast<ITfSource *>(this); else if (iid == IID_ITfKeystrokeMgr) pUnk = static_cast<ITfKeystrokeMgr *>(this); else if (iid == IID_ITfMessagePump) pUnk = static_cast<ITfMessagePump *>(this); else if (iid == IID_ITfClientId) pUnk = static_cast<ITfClientId *>(this); else if (iid == IID_ITfCompartmentMgr) pUnk = m_CompartmentMgr; else if (iid == IID_ITfUIElementMgr) pUnk = static_cast<ITfUIElementMgr *>(this); else if (iid == IID_ITfSourceSingle) pUnk = static_cast<ITfSourceSingle *>(this);
if (pUnk) { *ppvObject = pUnk; pUnk->AddRef(); return S_OK; }
WARN("unsupported interface: %s\n", debugstr_guid(&iid)); return E_NOINTERFACE;}
STDMETHODIMP_(ULONG) CThreadMgr::AddRef(){ return ::InterlockedIncrement(&m_cRefs);}
STDMETHODIMP_(ULONG) CThreadMgr::Release(){ ULONG ret = ::InterlockedDecrement(&m_cRefs); if (!ret) delete this; return ret;}
STDMETHODIMP CThreadMgr::Activate(_Out_ TfClientId *ptid){ TRACE("(%p) %p\n", this, ptid); return ActivateEx(ptid, 0);}
STDMETHODIMP CThreadMgr::Deactivate(){ TRACE("(%p)\n", this);
if (m_activationCount == 0) return E_UNEXPECTED;
--m_activationCount;
if (m_activationCount == 0) { if (m_focus) { OnSetFocus(NULL, m_focus); m_focus->Release(); m_focus = NULL; } }
deactivate_textservices(); return S_OK;}
STDMETHODIMP CThreadMgr::CreateDocumentMgr(_Out_ ITfDocumentMgr **ppdim){ TRACE("(%p)\n", this);
if (!ppdim) return E_INVALIDARG;
DocumentMgrEntry *mgrentry = (DocumentMgrEntry *)cicMemAlloc(sizeof(DocumentMgrEntry)); if (!mgrentry) return E_OUTOFMEMORY;
HRESULT hr = DocumentMgr_Constructor(this, ppdim); if (SUCCEEDED(hr)) { mgrentry->docmgr = *ppdim; list_add_head(&m_CreatedDocumentMgrs, &mgrentry->entry); } else { cicMemFree(mgrentry); }
return hr;}
STDMETHODIMP CThreadMgr::EnumDocumentMgrs(_Out_ IEnumTfDocumentMgrs **ppEnum){ TRACE("(%p) %p\n", this, ppEnum);
if (!ppEnum) return E_INVALIDARG;
return CEnumTfDocumentMgr::CreateInstance(&m_CreatedDocumentMgrs, (CEnumTfDocumentMgr **)ppEnum);}
STDMETHODIMP CThreadMgr::GetFocus(_Out_ ITfDocumentMgr **ppdimFocus){ TRACE("(%p)\n", this);
if (!ppdimFocus) return E_INVALIDARG;
*ppdimFocus = m_focus;
TRACE("->%p\n", m_focus);
if (!m_focus) return S_FALSE;
m_focus->AddRef(); return S_OK;}
STDMETHODIMP CThreadMgr::SetFocus(_In_ ITfDocumentMgr *pdimFocus){ ITfDocumentMgr *check;
TRACE("(%p) %p\n", this, pdimFocus);
if (!pdimFocus) check = NULL; else if (FAILED(pdimFocus->QueryInterface(IID_ITfDocumentMgr, (LPVOID*)&check))) return E_INVALIDARG;
OnSetFocus(check, m_focus);
if (m_focus) m_focus->Release();
m_focus = check; return S_OK;}
LRESULT CThreadMgr::_ThreadFocusHookProc(INT nCode, WPARAM wParam, LPARAM lParam){ if (!m_focusHook) { ERR("Hook proc but no ThreadMgr focus Hook. Serious Error\n"); return 0; }
if (nCode == HCBT_SETFOCUS) /* focus change within our thread */ { struct list *cursor;
LIST_FOR_EACH(cursor, &m_AssociatedFocusWindows) { AssociatedWindow *wnd = LIST_ENTRY(cursor, AssociatedWindow, entry); if (wnd->hwnd == (HWND)wParam) { TRACE("Triggering Associated window focus\n"); if (m_focus != wnd->docmgr) SetFocus(wnd->docmgr); break; } } }
return CallNextHookEx(m_focusHook, nCode, wParam, lParam);}
LRESULT CALLBACK CThreadMgr::ThreadFocusHookProc(INT nCode, WPARAM wParam, LPARAM lParam){ CThreadMgr *This = (CThreadMgr *)TlsGetValue(g_dwTLSIndex); if (!This) { ERR("Hook proc but no ThreadMgr for this thread. Serious Error\n"); return 0; } return This->_ThreadFocusHookProc(nCode, wParam, lParam);}
HRESULT CThreadMgr::SetupWindowsHook(){ if (!m_focusHook) { m_focusHook = SetWindowsHookExW(WH_CBT, ThreadFocusHookProc, 0, GetCurrentThreadId()); if (!m_focusHook) { ERR("Unable to set focus hook\n"); return E_FAIL; } return S_OK; } return S_FALSE;}
STDMETHODIMP CThreadMgr::AssociateFocus( _In_ HWND hwnd, _In_ ITfDocumentMgr *pdimNew, _Out_ ITfDocumentMgr **ppdimPrev){ struct list *cursor, *cursor2; AssociatedWindow *wnd;
TRACE("(%p) %p %p %p\n", this, hwnd, pdimNew, ppdimPrev);
if (!ppdimPrev) return E_INVALIDARG;
*ppdimPrev = NULL;
LIST_FOR_EACH_SAFE(cursor, cursor2, &m_AssociatedFocusWindows) { wnd = LIST_ENTRY(cursor, AssociatedWindow, entry); if (wnd->hwnd == hwnd) { if (wnd->docmgr) wnd->docmgr->AddRef(); *ppdimPrev = wnd->docmgr; wnd->docmgr = pdimNew; if (::GetFocus() == hwnd) SetFocus(pdimNew); return S_OK; } }
wnd = (AssociatedWindow *)cicMemAlloc(sizeof(AssociatedWindow)); wnd->hwnd = hwnd; wnd->docmgr = pdimNew; list_add_head(&m_AssociatedFocusWindows, &wnd->entry);
if (::GetFocus() == hwnd) SetFocus(pdimNew);
this->SetupWindowsHook(); return S_OK;}
STDMETHODIMP CThreadMgr::IsThreadFocus(_Out_ BOOL *pfThreadFocus){ TRACE("(%p) %p\n", this, pfThreadFocus);
if (!pfThreadFocus) return E_INVALIDARG;
HWND focus = ::GetFocus(); *pfThreadFocus = !focus; return S_OK;}
STDMETHODIMP CThreadMgr::GetFunctionProvider( _In_ REFCLSID clsid, _Out_ ITfFunctionProvider **ppFuncProv){ FIXME("STUB:(%p)\n", this); return E_NOTIMPL;}
STDMETHODIMP CThreadMgr::EnumFunctionProviders(_Out_ IEnumTfFunctionProviders **ppEnum){ FIXME("STUB:(%p)\n", this); return E_NOTIMPL;}
STDMETHODIMP CThreadMgr::GetGlobalCompartment(_Out_ ITfCompartmentMgr **ppCompMgr){ HRESULT hr; TRACE("(%p) %p\n", this, ppCompMgr);
if (!ppCompMgr) return E_INVALIDARG;
if (!g_globalCompartmentMgr) { hr = CompartmentMgr_Constructor(NULL, IID_ITfCompartmentMgr, (IUnknown **)&g_globalCompartmentMgr); if (FAILED(hr)) return hr; }
g_globalCompartmentMgr->AddRef(); *ppCompMgr = g_globalCompartmentMgr; return S_OK;}
STDMETHODIMP CThreadMgr::ActivateEx( _Out_ TfClientId *id, _In_ DWORD flags){ TRACE("(%p) %p, %#x\n", this, id, flags);
if (!id) return E_INVALIDARG;
if (flags) FIXME("Unimplemented flags %#x\n", flags);
if (!g_processId) { GUID guid; CoCreateGuid(&guid); GetClientId(guid, &g_processId); }
activate_textservices(this); ++m_activationCount; *id = g_processId; return S_OK;}
STDMETHODIMP CThreadMgr::GetActiveFlags(_Out_ DWORD *flags){ FIXME("STUB:(%p)\n", this); return E_NOTIMPL;}
STDMETHODIMP CThreadMgr::AdviseSink( _In_ REFIID riid, _In_ IUnknown *punk, _Out_ DWORD *pdwCookie){ TRACE("(%p) %s %p %p\n", this, debugstr_guid(&riid), punk, pdwCookie);
if (cicIsNullPtr(&riid) || !punk || !pdwCookie) return E_INVALIDARG;
if (riid == IID_ITfThreadMgrEventSink) return advise_sink(&m_ThreadMgrEventSink, IID_ITfThreadMgrEventSink, COOKIE_MAGIC_TMSINK, punk, pdwCookie);
if (riid == IID_ITfThreadFocusSink) { WARN("semi-stub for ITfThreadFocusSink: sink won't be used.\n"); return advise_sink(&m_ThreadFocusSink, IID_ITfThreadFocusSink, COOKIE_MAGIC_THREADFOCUSSINK, punk, pdwCookie); }
if (riid == IID_ITfActiveLanguageProfileNotifySink) { WARN("semi-stub for ITfActiveLanguageProfileNotifySink: sink won't be used.\n"); return advise_sink(&m_ActiveLanguageProfileNotifySink, IID_ITfActiveLanguageProfileNotifySink, COOKIE_MAGIC_ACTIVELANGSINK, punk, pdwCookie); }
if (riid == IID_ITfKeyTraceEventSink) { WARN("semi-stub for ITfKeyTraceEventSink: sink won't be used.\n"); return advise_sink(&m_KeyTraceEventSink, IID_ITfKeyTraceEventSink, COOKIE_MAGIC_KEYTRACESINK, punk, pdwCookie); }
if (riid == IID_ITfUIElementSink) { WARN("semi-stub for ITfUIElementSink: sink won't be used.\n"); return advise_sink(&m_UIElementSink, IID_ITfUIElementSink, COOKIE_MAGIC_UIELEMENTSINK, punk, pdwCookie); }
if (riid == IID_ITfInputProcessorProfileActivationSink) { WARN("semi-stub for ITfInputProcessorProfileActivationSink: sink won't be used.\n"); return advise_sink(&m_InputProcessorProfileActivationSink, IID_ITfInputProcessorProfileActivationSink, COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK, punk, pdwCookie); }
FIXME("(%p) Unhandled Sink: %s\n", this, debugstr_guid(&riid)); return E_NOTIMPL;}
STDMETHODIMP CThreadMgr::UnadviseSink(_In_ DWORD dwCookie){ DWORD magic;
TRACE("(%p) %x\n", this, dwCookie);
magic = get_Cookie_magic(dwCookie); if (magic != COOKIE_MAGIC_TMSINK && magic != COOKIE_MAGIC_THREADFOCUSSINK && magic != COOKIE_MAGIC_KEYTRACESINK && magic != COOKIE_MAGIC_UIELEMENTSINK && magic != COOKIE_MAGIC_INPUTPROCESSORPROFILEACTIVATIONSINK) { return E_INVALIDARG; }
return unadvise_sink(dwCookie);}
STDMETHODIMP CThreadMgr::AdviseKeyEventSink( _In_ TfClientId tid, _In_ ITfKeyEventSink *pSink, _In_ BOOL fForeground){ CLSID textservice; ITfKeyEventSink *check = NULL;
TRACE("(%p) %x %p %i\n", this, tid, pSink, fForeground);
if (!tid || !pSink) return E_INVALIDARG;
textservice = get_textservice_clsid(tid); if (GUID_NULL == textservice) return E_INVALIDARG;
get_textservice_sink(tid, IID_ITfKeyEventSink, (IUnknown **)&check); if (check) return CONNECT_E_ADVISELIMIT;
if (FAILED(pSink->QueryInterface(IID_ITfKeyEventSink, (LPVOID*)&check))) return E_INVALIDARG;
set_textservice_sink(tid, IID_ITfKeyEventSink, check);
if (fForeground) { if (m_foregroundKeyEventSink) { m_foregroundKeyEventSink->OnSetFocus(FALSE); m_foregroundKeyEventSink->Release(); } check->AddRef(); check->OnSetFocus(TRUE); m_foregroundKeyEventSink = check; m_foregroundTextService = textservice; } return S_OK;}
STDMETHODIMP CThreadMgr::UnadviseKeyEventSink(_In_ TfClientId tid){ CLSID textservice; ITfKeyEventSink *check = NULL; TRACE("(%p) %x\n", this, tid);
if (!tid) return E_INVALIDARG;
textservice = get_textservice_clsid(tid); if (GUID_NULL == textservice) return E_INVALIDARG;
get_textservice_sink(tid, IID_ITfKeyEventSink, (IUnknown **)&check);
if (!check) return CONNECT_E_NOCONNECTION;
set_textservice_sink(tid, IID_ITfKeyEventSink, NULL); check->Release();
if (m_foregroundKeyEventSink == check) { m_foregroundKeyEventSink->Release(); m_foregroundKeyEventSink = NULL; m_foregroundTextService = GUID_NULL; } return S_OK;}
STDMETHODIMP CThreadMgr::GetForeground(_Out_ CLSID *pclsid){ TRACE("(%p) %p\n", this, pclsid); if (!pclsid) return E_INVALIDARG;
if (m_foregroundTextService == GUID_NULL) return S_FALSE;
*pclsid = m_foregroundTextService; return S_OK;}
STDMETHODIMP CThreadMgr::TestKeyDown( _In_ WPARAM wParam, _In_ LPARAM lParam, _Out_ BOOL *pfEaten){ FIXME("STUB:(%p)\n", this); if (!pfEaten) return E_INVALIDARG; *pfEaten = FALSE; return S_OK;}
STDMETHODIMP CThreadMgr::TestKeyUp( _In_ WPARAM wParam, _In_ LPARAM lParam, _Out_ BOOL *pfEaten){ FIXME("STUB:(%p)\n", this); if (!pfEaten) return E_INVALIDARG; *pfEaten = FALSE; return S_OK;}
STDMETHODIMP CThreadMgr::KeyDown( _In_ WPARAM wParam, _In_ LPARAM lParam, _Out_ BOOL *pfEaten){ FIXME("STUB:(%p)\n", this); if (!pfEaten) return E_INVALIDARG; *pfEaten = FALSE; return E_NOTIMPL;}
STDMETHODIMP CThreadMgr::KeyUp( _In_ WPARAM wParam, _In_ LPARAM lParam, _Out_ BOOL *pfEaten){ FIXME("STUB:(%p)\n", this); if (!pfEaten) return E_INVALIDARG; *pfEaten = FALSE; return E_NOTIMPL;}
STDMETHODIMP CThreadMgr::GetPreservedKey( _In_ ITfContext *pic, _In_ const TF_PRESERVEDKEY *pprekey, _Out_ GUID *pguid){ FIXME("STUB:(%p)\n", this); return E_NOTIMPL;}
STDMETHODIMP CThreadMgr::IsPreservedKey( _In_ REFGUID rguid, _In_ const TF_PRESERVEDKEY *pprekey, _Out_ BOOL *pfRegistered){ struct list *cursor;
TRACE("(%p) %s (%x %x) %p\n", this, debugstr_guid(&rguid), (pprekey ? pprekey->uVKey : 0), (pprekey ? pprekey->uModifiers : 0), pfRegistered);
if (cicIsNullPtr(&rguid) || !pprekey || !pfRegistered) return E_INVALIDARG;
LIST_FOR_EACH(cursor, &m_CurrentPreservedKeys) { PreservedKey* key = LIST_ENTRY(cursor, PreservedKey, entry); if (rguid == key->guid && pprekey->uVKey == key->prekey.uVKey && pprekey->uModifiers == key->prekey.uModifiers) { *pfRegistered = TRUE; return S_OK; } }
*pfRegistered = FALSE; return S_FALSE;}
STDMETHODIMP CThreadMgr::PreserveKey( _In_ TfClientId tid, _In_ REFGUID rguid, _In_ const TF_PRESERVEDKEY *prekey, _In_ const WCHAR *pchDesc, _In_ ULONG cchDesc){ struct list *cursor;
TRACE("(%p) %x %s (%x,%x) %s\n", this, tid, debugstr_guid(&rguid), (prekey ? prekey->uVKey : 0), (prekey ? prekey->uModifiers : 0), debugstr_wn(pchDesc, cchDesc));
if (!tid || cicIsNullPtr(&rguid) || !prekey || (cchDesc && !pchDesc)) return E_INVALIDARG;
LIST_FOR_EACH(cursor, &m_CurrentPreservedKeys) { PreservedKey* key = LIST_ENTRY(cursor, PreservedKey, entry); if (rguid == key->guid && prekey->uVKey == key->prekey.uVKey && prekey->uModifiers == key->prekey.uModifiers) return TF_E_ALREADY_EXISTS; }
PreservedKey *newkey = (PreservedKey *)cicMemAlloc(sizeof(PreservedKey)); if (!newkey) return E_OUTOFMEMORY;
newkey->guid = rguid; newkey->prekey = *prekey; newkey->tid = tid; newkey->description = NULL; if (cchDesc) { newkey->description = (LPWSTR)cicMemAlloc((cchDesc + 1) * sizeof(WCHAR)); if (!newkey->description) { cicMemFree(newkey); return E_OUTOFMEMORY; } CopyMemory(newkey->description, pchDesc, cchDesc * sizeof(WCHAR)); newkey->description[cchDesc] = UNICODE_NULL; }
list_add_head(&m_CurrentPreservedKeys, &newkey->entry); return S_OK;}
STDMETHODIMP CThreadMgr::UnpreserveKey( _In_ REFGUID rguid, _In_ const TF_PRESERVEDKEY *pprekey){ PreservedKey* key = NULL; struct list *cursor; TRACE("(%p) %s (%x %x)\n", this, debugstr_guid(&rguid), (pprekey ? pprekey->uVKey : 0), (pprekey ? pprekey->uModifiers : 0));
if (!pprekey || cicIsNullPtr(&rguid)) return E_INVALIDARG;
LIST_FOR_EACH(cursor, &m_CurrentPreservedKeys) { key = LIST_ENTRY(cursor, PreservedKey, entry); if (rguid == key->guid && pprekey->uVKey == key->prekey.uVKey && pprekey->uModifiers == key->prekey.uModifiers) break; key = NULL; }
if (!key) return CONNECT_E_NOCONNECTION;
list_remove(&key->entry); cicMemFree(key->description); cicMemFree(key);
return S_OK;}
STDMETHODIMP CThreadMgr::SetPreservedKeyDescription( _In_ REFGUID rguid, _In_ const WCHAR *pchDesc, _In_ ULONG cchDesc){ FIXME("STUB:(%p)\n", this); return E_NOTIMPL;}
STDMETHODIMP CThreadMgr::GetPreservedKeyDescription( _In_ REFGUID rguid, _Out_ BSTR *pbstrDesc){ FIXME("STUB:(%p)\n", this); return E_NOTIMPL;}
STDMETHODIMP CThreadMgr::SimulatePreservedKey( _In_ ITfContext *pic, _In_ REFGUID rguid, _Out_ BOOL *pfEaten){ FIXME("STUB:(%p)\n", this); if (!pfEaten) return E_INVALIDARG; *pfEaten = FALSE; return E_NOTIMPL;}
STDMETHODIMP CThreadMgr::PeekMessageA( _Out_ LPMSG pMsg, _In_ HWND hwnd, _In_ UINT wMsgFilterMin, _In_ UINT wMsgFilterMax, _In_ UINT wRemoveMsg, _Out_ BOOL *pfResult){ if (!pfResult) return E_INVALIDARG; *pfResult = ::PeekMessageA(pMsg, hwnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg); return S_OK;}
STDMETHODIMP CThreadMgr::GetMessageA( _Out_ LPMSG pMsg, _In_ HWND hwnd, _In_ UINT wMsgFilterMin, _In_ UINT wMsgFilterMax, _Out_ BOOL *pfResult){ if (!pfResult) return E_INVALIDARG; *pfResult = ::GetMessageA(pMsg, hwnd, wMsgFilterMin, wMsgFilterMax); return S_OK;}
STDMETHODIMP CThreadMgr::PeekMessageW( _Out_ LPMSG pMsg, _In_ HWND hwnd, _In_ UINT wMsgFilterMin, _In_ UINT wMsgFilterMax, _In_ UINT wRemoveMsg, _Out_ BOOL *pfResult){ if (!pfResult) return E_INVALIDARG; *pfResult = ::PeekMessageW(pMsg, hwnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg); return S_OK;}
STDMETHODIMP CThreadMgr::GetMessageW( _Out_ LPMSG pMsg, _In_ HWND hwnd, _In_ UINT wMsgFilterMin, _In_ UINT wMsgFilterMax, _Out_ BOOL *pfResult){ if (!pfResult) return E_INVALIDARG; *pfResult = ::GetMessageW(pMsg, hwnd, wMsgFilterMin, wMsgFilterMax); return S_OK;}
STDMETHODIMP CThreadMgr::GetClientId( _In_ REFCLSID rclsid, _Out_ TfClientId *ptid){ HRESULT hr; ITfCategoryMgr *catmgr;
TRACE("(%p) %s\n", this, debugstr_guid(&rclsid));
CategoryMgr_Constructor(NULL, (IUnknown **)&catmgr); hr = catmgr->RegisterGUID(rclsid, ptid); catmgr->Release();
return hr;}
STDMETHODIMP CThreadMgr::OnInitDocumentMgr(_In_ ITfDocumentMgr *pdim){ ITfThreadMgrEventSink *sink; struct list *cursor;
TRACE("(%p) %p\n", this, pdim);
SINK_FOR_EACH(cursor, &m_ThreadMgrEventSink, ITfThreadMgrEventSink, sink) { sink->OnInitDocumentMgr(pdim); }
return S_OK;}
STDMETHODIMP CThreadMgr::OnUninitDocumentMgr(_In_ ITfDocumentMgr *pdim){ ITfThreadMgrEventSink *sink; struct list *cursor;
TRACE("(%p) %p\n", this, pdim);
SINK_FOR_EACH(cursor, &m_ThreadMgrEventSink, ITfThreadMgrEventSink, sink) { sink->OnUninitDocumentMgr(pdim); }
return S_OK;}
STDMETHODIMP CThreadMgr::OnSetFocus( _In_ ITfDocumentMgr *pdimFocus, _In_ ITfDocumentMgr *pdimPrevFocus){ ITfThreadMgrEventSink *sink; struct list *cursor;
TRACE("(%p) %p %p\n", this, pdimFocus, pdimPrevFocus);
SINK_FOR_EACH(cursor, &m_ThreadMgrEventSink, ITfThreadMgrEventSink, sink) { sink->OnSetFocus(pdimFocus, pdimPrevFocus); }
return S_OK;}
STDMETHODIMP CThreadMgr::OnPushContext(_In_ ITfContext *pic){ ITfThreadMgrEventSink *sink; struct list *cursor;
TRACE("(%p) %p\n", this, pic);
SINK_FOR_EACH(cursor, &m_ThreadMgrEventSink, ITfThreadMgrEventSink, sink) { sink->OnPushContext(pic); }
return S_OK;}
STDMETHODIMP CThreadMgr::OnPopContext(_In_ ITfContext *pic){ ITfThreadMgrEventSink *sink; struct list *cursor;
TRACE("(%p) %p\n", this, pic);
SINK_FOR_EACH(cursor, &m_ThreadMgrEventSink, ITfThreadMgrEventSink, sink) { sink->OnPopContext(pic); }
return S_OK;}
STDMETHODIMP CThreadMgr::BeginUIElement( _In_ ITfUIElement *element, _Inout_ BOOL *show, _Out_ DWORD *id){ FIXME("STUB:(%p)\n", this); return E_NOTIMPL;}
STDMETHODIMP CThreadMgr::UpdateUIElement(_In_ DWORD id){ FIXME("STUB:(%p)\n", this); return E_NOTIMPL;}
STDMETHODIMP CThreadMgr::EndUIElement(_In_ DWORD id){ FIXME("STUB:(%p)\n", this); return E_NOTIMPL;}
STDMETHODIMP CThreadMgr::GetUIElement( _In_ DWORD id, _Out_ ITfUIElement **element){ FIXME("STUB:(%p)\n", this); return E_NOTIMPL;}
STDMETHODIMP CThreadMgr::EnumUIElements(_Out_ IEnumTfUIElements **enum_elements){ FIXME("STUB:(%p)\n", this); return E_NOTIMPL;}
STDMETHODIMP CThreadMgr::AdviseSingleSink( _In_ TfClientId tid, _In_ REFIID riid, _In_ IUnknown *punk){ FIXME("STUB:(%p) %i %s %p\n", this, tid, debugstr_guid(&riid), punk); return E_NOTIMPL;}
STDMETHODIMP CThreadMgr::UnadviseSingleSink( _In_ TfClientId tid, _In_ REFIID riid){ FIXME("STUB:(%p) %i %s\n", this, tid, debugstr_guid(&riid)); return E_NOTIMPL;}
HRESULT CThreadMgr::CreateInstance(IUnknown *pUnkOuter, CThreadMgr **ppOut){ if (pUnkOuter) return CLASS_E_NOAGGREGATION;
/* Only 1 ThreadMgr is created per thread */ CThreadMgr *This = (CThreadMgr *)TlsGetValue(g_dwTLSIndex); if (This) { This->AddRef(); *ppOut = This; return S_OK; }
This = new(cicNoThrow) CThreadMgr(); if (!This) return E_OUTOFMEMORY;
TlsSetValue(g_dwTLSIndex, This);
ITfCompartmentMgr *pCompMgr = NULL; CompartmentMgr_Constructor(static_cast<ITfThreadMgrEx *>(This), IID_IUnknown, (IUnknown **)&pCompMgr); This->m_CompartmentMgr = pCompMgr;
TRACE("returning %p\n", This); *ppOut = This; return S_OK;}
void CThreadMgr::OnDocumentMgrDestruction(ITfDocumentMgr *mgr){ struct list *cursor; LIST_FOR_EACH(cursor, &m_CreatedDocumentMgrs) { DocumentMgrEntry *mgrentry = LIST_ENTRY(cursor, DocumentMgrEntry, entry); if (mgrentry->docmgr == mgr) { list_remove(cursor); cicMemFree(mgrentry); return; } } FIXME("ITfDocumentMgr %p not found in this thread\n", mgr);}
////////////////////////////////////////////////////////////////////////////
CEnumTfDocumentMgr::CEnumTfDocumentMgr() : m_cRefs(1) , m_index(NULL) , m_head(NULL){}
CEnumTfDocumentMgr::~CEnumTfDocumentMgr(){ TRACE("destroying %p\n", this);}
STDMETHODIMP CEnumTfDocumentMgr::QueryInterface(REFIID iid, LPVOID *ppvObject){ *ppvObject = NULL;
if (iid == IID_IUnknown || iid == IID_IEnumTfDocumentMgrs) *ppvObject = static_cast<IEnumTfDocumentMgrs *>(this);
if (*ppvObject) { AddRef(); return S_OK; }
WARN("unsupported interface: %s\n", debugstr_guid(&iid)); return E_NOINTERFACE;}
STDMETHODIMP_(ULONG) CEnumTfDocumentMgr::AddRef(){ return InterlockedIncrement(&m_cRefs);}
STDMETHODIMP_(ULONG) CEnumTfDocumentMgr::Release(){ ULONG ret = InterlockedDecrement(&m_cRefs); if (!ret) delete this; return ret;}
STDMETHODIMP CEnumTfDocumentMgr::Clone(_Out_ IEnumTfDocumentMgrs **ppEnum){ TRACE("(%p)\n", this);
if (!ppEnum) return E_POINTER;
*ppEnum = NULL;
CEnumTfDocumentMgr *cloned; HRESULT hr = CEnumTfDocumentMgr::CreateInstance(m_head, &cloned); if (SUCCEEDED(hr)) { *ppEnum = static_cast<IEnumTfDocumentMgrs *>(cloned); cloned->m_index = m_index; }
return hr;}
STDMETHODIMP CEnumTfDocumentMgr::Next( _In_ ULONG ulCount, _Out_ ITfDocumentMgr **rgDocumentMgr, _Out_ ULONG *pcFetched){ ULONG fetched = 0;
TRACE("(%p)\n", this);
if (!rgDocumentMgr) return E_POINTER;
while (fetched < ulCount) { DocumentMgrEntry *mgrentry; if (!m_index) break;
mgrentry = LIST_ENTRY(m_index, DocumentMgrEntry, entry); if (!mgrentry) break;
*rgDocumentMgr = mgrentry->docmgr; (*rgDocumentMgr)->AddRef();
m_index = list_next(m_head, m_index); ++fetched; ++rgDocumentMgr; }
if (pcFetched) *pcFetched = fetched; return fetched == ulCount ? S_OK : S_FALSE;}
STDMETHODIMP CEnumTfDocumentMgr::Reset(){ TRACE("(%p)\n", this); m_index = list_head(m_head); return S_OK;}
STDMETHODIMP CEnumTfDocumentMgr::Skip(_In_ ULONG ulCount){ TRACE("(%p)\n", this); for (ULONG i = 0; i < ulCount && m_index; ++i) m_index = list_next(m_head, m_index); return S_OK;}
HRESULT CEnumTfDocumentMgr::CreateInstance(struct list* head, CEnumTfDocumentMgr **ppOut){ CEnumTfDocumentMgr *This = new(cicNoThrow) CEnumTfDocumentMgr(); if (!This) return E_OUTOFMEMORY;
This->m_head = head; This->m_index = list_head(This->m_head);
*ppOut = This; TRACE("returning %p\n", *ppOut); return S_OK;}
////////////////////////////////////////////////////////////////////////////
EXTERN_CHRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut){ return CThreadMgr::CreateInstance(pUnkOuter, (CThreadMgr **)ppOut);}
EXTERN_Cvoid ThreadMgr_OnDocumentMgrDestruction(ITfThreadMgr *iface, ITfDocumentMgr *mgr){ CThreadMgr *This = static_cast<CThreadMgr *>(iface); This->OnDocumentMgrDestruction(mgr);}