[PATCH 0/2] MR10996: msctf: Add a stub implementation of ITfFnReconversion.
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/msctf/threadmgr.c | 60 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c index 11c79c030ff..67cf4c99750 100644 --- a/dlls/msctf/threadmgr.c +++ b/dlls/msctf/threadmgr.c @@ -70,6 +70,7 @@ typedef struct tagACLMulti { /* const ITfLangBarItemMgrVtbl *LangBarItemMgrVtbl; */ ITfUIElementMgr ITfUIElementMgr_iface; ITfSourceSingle ITfSourceSingle_iface; + ITfFunctionProvider ITfFunctionProvider_iface; LONG refCount; /* Aggregation */ @@ -150,11 +151,62 @@ static inline ThreadMgr *impl_from_ITfSourceSingle(ITfSourceSingle *iface) return CONTAINING_RECORD(iface, ThreadMgr, ITfSourceSingle_iface); } +static ThreadMgr *impl_from_ITfFunctionProvider(ITfFunctionProvider *iface) +{ + return CONTAINING_RECORD(iface, ThreadMgr, ITfFunctionProvider_iface); +} + static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMgrs *iface) { return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); } +static HRESULT WINAPI func_provider_QueryInterface(ITfFunctionProvider *iface, REFIID iid, LPVOID *ppvOut) +{ + ThreadMgr *This = impl_from_ITfFunctionProvider(iface); + return ITfThreadMgrEx_QueryInterface(&This->ITfThreadMgrEx_iface, iid, ppvOut); +} + +static ULONG WINAPI func_provider_AddRef(ITfFunctionProvider *iface) +{ + ThreadMgr *This = impl_from_ITfFunctionProvider(iface); + return ITfThreadMgrEx_AddRef(&This->ITfThreadMgrEx_iface); +} + +static ULONG WINAPI func_provider_Release(ITfFunctionProvider *iface) +{ + ThreadMgr *This = impl_from_ITfFunctionProvider(iface); + return ITfThreadMgrEx_Release(&This->ITfThreadMgrEx_iface); +} + +static HRESULT WINAPI func_provider_GetType(ITfFunctionProvider *iface, GUID *guid) +{ + FIXME("(%p) %p stub.\n", iface, guid); + return E_NOTIMPL; +} + +static HRESULT WINAPI func_provider_GetDescription(ITfFunctionProvider *iface, BSTR *desc) +{ + FIXME("(%p) %p stub.\n", iface, desc); + return E_NOTIMPL; +} + +static HRESULT WINAPI func_provider_GetFunction(ITfFunctionProvider *iface, REFGUID guid, REFIID riid, IUnknown **func) +{ + FIXME("(%p) %s %s %p stub.\n", iface, debugstr_guid(guid), debugstr_guid(riid), func); + return E_NOTIMPL; +} + +static const ITfFunctionProviderVtbl TfFunctionProviderVtbl = +{ + func_provider_QueryInterface, + func_provider_AddRef, + func_provider_Release, + func_provider_GetType, + func_provider_GetDescription, + func_provider_GetFunction, +}; + static void ThreadMgr_Destructor(ThreadMgr *This) { struct list *cursor, *cursor2; @@ -493,8 +545,11 @@ static HRESULT WINAPI ThreadMgr_GetFunctionProvider(ITfThreadMgrEx *iface, REFCL ITfFunctionProvider **ppFuncProv) { ThreadMgr *This = impl_from_ITfThreadMgrEx(iface); - FIXME("STUB:(%p)\n",This); - return E_NOTIMPL; + + TRACE("(%p)\n",This); + *ppFuncProv = &This->ITfFunctionProvider_iface; + ITfFunctionProvider_AddRef(*ppFuncProv); + return S_OK; } static HRESULT WINAPI ThreadMgr_EnumFunctionProviders(ITfThreadMgrEx *iface, @@ -1366,6 +1421,7 @@ HRESULT ThreadMgr_Constructor(IUnknown *pUnkOuter, IUnknown **ppOut) This->ITfThreadMgrEventSink_iface.lpVtbl = &ThreadMgrEventSinkVtbl; This->ITfUIElementMgr_iface.lpVtbl = &ThreadMgrUIElementMgrVtbl; This->ITfSourceSingle_iface.lpVtbl = &SourceSingleVtbl; + This->ITfFunctionProvider_iface.lpVtbl = &TfFunctionProviderVtbl; This->refCount = 1; TlsSetValue(tlsIndex,This); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10996
From: Hans Leidekker <hans@codeweavers.com> Based on a patch by Paul Gofman. --- dlls/msctf/tests/Makefile.in | 2 +- dlls/msctf/tests/inputprocessor.c | 25 +++++ dlls/msctf/threadmgr.c | 156 +++++++++++++++++++++++++++++- include/Makefile.in | 1 + include/ctffunc.idl | 80 +++++++++++++++ include/msctf.idl | 10 ++ 6 files changed, 272 insertions(+), 2 deletions(-) create mode 100644 include/ctffunc.idl diff --git a/dlls/msctf/tests/Makefile.in b/dlls/msctf/tests/Makefile.in index dc572d4d9c1..029e30de451 100644 --- a/dlls/msctf/tests/Makefile.in +++ b/dlls/msctf/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = msctf.dll -IMPORTS = ole32 user32 advapi32 +IMPORTS = oleaut32 ole32 user32 advapi32 SOURCES = \ inputprocessor.c \ diff --git a/dlls/msctf/tests/inputprocessor.c b/dlls/msctf/tests/inputprocessor.c index 5038b733b55..b912ecda697 100644 --- a/dlls/msctf/tests/inputprocessor.c +++ b/dlls/msctf/tests/inputprocessor.c @@ -29,6 +29,7 @@ #include "shlguid.h" #include "comcat.h" #include "msctf.h" +#include "ctffunc.h" #include "olectl.h" static ITfInputProcessorProfiles* g_ipp; @@ -1082,6 +1083,7 @@ DEFINE_GUID(GUID_COMPARTMENT_SPEECH_GLOBALSTATE, 0x2a54fe8e,0x0d08,0x460c,0xa DEFINE_GUID(GUID_COMPARTMENT_PERSISTMENUENABLED, 0x575f3783,0x70c8,0x47c8,0xae,0x5d,0x91,0xa0,0x1a,0x1f,0x75,0x92); DEFINE_GUID(GUID_COMPARTMENT_EMPTYCONTEXT, 0xd7487dbf,0x804e,0x41c5,0x89,0x4d,0xad,0x96,0xfd,0x4e,0xea,0x13); DEFINE_GUID(GUID_COMPARTMENT_TIPUISTATUS, 0x148ca3ec,0x0366,0x401c,0x8d,0x75,0xed,0x97,0x8d,0x85,0xfb,0xc9); +DEFINE_GUID(GUID_SYSTEM_FUNCTIONPROVIDER, 0x9a698bb0,0x0f21,0x11d3,0x8d,0xf1,0x00,0x10,0x5a,0x27,0x99,0xb5); static HRESULT initialize(void) { @@ -2627,6 +2629,28 @@ static void test_MultiThreadApartment(void) CloseHandle(thread); } +static void test_function_provider(void) +{ + ITfFunctionProvider *provider; + ITfFnReconversion *reconv; + BSTR name; + HRESULT hr; + + hr = ITfThreadMgr_GetFunctionProvider(g_tm, &GUID_SYSTEM_FUNCTIONPROVIDER, &provider); + ok(hr == S_OK, "got %lx\n", hr); + + hr = ITfFunctionProvider_GetFunction(provider, &GUID_NULL, &IID_ITfFnReconversion, (IUnknown **)&reconv); + ok(hr == S_OK, "got %lx\n", hr); + + hr = ITfFnReconversion_GetDisplayName(reconv, &name); + ok(hr == S_OK, "got %lx\n", hr); + ok(!wcscmp(name, L"Reconversion"), "got %s\n", debugstr_w(name)); + SysFreeString(name); + + ITfFnReconversion_Release(reconv); + ITfFunctionProvider_Release(provider); +} + START_TEST(inputprocessor) { if (SUCCEEDED(initialize())) @@ -2657,6 +2681,7 @@ START_TEST(inputprocessor) test_Unregister(); test_profile_mgr(); test_MultiThreadApartment(); + test_function_provider(); ITextStoreACPSink_Release(ACPSink); ITfDocumentMgr_Release(g_dm); diff --git a/dlls/msctf/threadmgr.c b/dlls/msctf/threadmgr.c index 67cf4c99750..252b2fef940 100644 --- a/dlls/msctf/threadmgr.c +++ b/dlls/msctf/threadmgr.c @@ -35,6 +35,9 @@ #include "msctf.h" #include "msctf_internal.h" +#include "initguid.h" +#include "ctffunc.h" + WINE_DEFAULT_DEBUG_CHANNEL(msctf); typedef struct tagPreservedKey @@ -161,6 +164,153 @@ static inline EnumTfDocumentMgr *impl_from_IEnumTfDocumentMgrs(IEnumTfDocumentMg return CONTAINING_RECORD(iface, EnumTfDocumentMgr, IEnumTfDocumentMgrs_iface); } +struct function +{ + ITfFunction ITfFunction_iface; + ITfFnReconversion ITfFnReconversion_iface; + LONG ref; + WCHAR *name; +}; + +static inline struct function *impl_from_ITfFunction(ITfFunction *iface) +{ + return CONTAINING_RECORD(iface, struct function, ITfFunction_iface); +} + +static HRESULT WINAPI func_QueryInterface(ITfFunction *iface, REFIID iid, void **out) +{ + TRACE("(%p) %s, %p.\n", iface, debugstr_guid(iid), out); + + *out = NULL; + if (IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_ITfFunction) + || IsEqualIID(iid, &IID_ITfFnReconversion)) + { + *out = iface; + ITfFunction_AddRef(iface); + return S_OK; + } + + WARN("unsupported interface: %s\n", debugstr_guid(iid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI func_AddRef(ITfFunction *iface) +{ + struct function *func = impl_from_ITfFunction(iface); + return InterlockedIncrement(&func->ref); +} + +static ULONG WINAPI func_Release(ITfFunction *iface) +{ + struct function *func = impl_from_ITfFunction(iface); + ULONG ret; + + if (!(ret = InterlockedDecrement(&func->ref))) free(func); + return ret; +} + +static HRESULT WINAPI func_GetDisplayName(ITfFunction *iface, BSTR *name) +{ + struct function *func = impl_from_ITfFunction(iface); + BSTR str; + + TRACE("(%p) %p\n", iface, name); + + if (!(str = SysAllocString(func->name))) return E_OUTOFMEMORY; + *name = str; + return S_OK; +} + +static ITfFunctionVtbl func_vtbl = +{ + func_QueryInterface, + func_AddRef, + func_Release, + func_GetDisplayName, +}; + +static inline struct function *impl_from_ITfFnReconversion(ITfFnReconversion *iface) +{ + return CONTAINING_RECORD(iface, struct function, ITfFunction_iface); +} + +static HRESULT WINAPI reconv_QueryInterface(ITfFnReconversion *iface, REFIID iid, void **out) +{ + struct function *func = impl_from_ITfFnReconversion(iface); + return ITfFunction_QueryInterface(&func->ITfFunction_iface, iid, out); +} + +static ULONG WINAPI reconv_AddRef(ITfFnReconversion *iface) +{ + struct function *func = impl_from_ITfFnReconversion(iface); + return ITfFunction_AddRef(&func->ITfFunction_iface); +} + +static ULONG WINAPI reconv_Release(ITfFnReconversion *iface) +{ + struct function *func = impl_from_ITfFnReconversion(iface); + return ITfFunction_Release(&func->ITfFunction_iface); +} + +static HRESULT WINAPI reconv_GetDisplayName(ITfFnReconversion *iface, BSTR *name) +{ + struct function *func = impl_from_ITfFnReconversion(iface); + return ITfFunction_GetDisplayName(&func->ITfFunction_iface, name); +} + +static HRESULT WINAPI reconv_QueryRange(ITfFnReconversion *iface, ITfRange *range, ITfRange **new_range, + BOOL *convertable) +{ + FIXME("(%p) %p %p %p stub.\n", iface, range, new_range, convertable); + + *convertable = FALSE; + *new_range = NULL; + return E_NOTIMPL; +} + +static HRESULT WINAPI reconv_GetReconversion(ITfFnReconversion *iface, ITfRange *range, ITfCandidateList **cand_list) +{ + FIXME("(%p) %p %p stub.\n", iface, range, cand_list); + + *cand_list = NULL; + return E_NOTIMPL; +} + +static HRESULT WINAPI reconv_Reconvert(ITfFnReconversion *iface, ITfRange *range) +{ + FIXME("(%p) %p stub.\n", iface, range); + return E_NOTIMPL; +} + +static ITfFnReconversionVtbl reconv_vtbl = +{ + reconv_QueryInterface, + reconv_AddRef, + reconv_Release, + reconv_GetDisplayName, + reconv_QueryRange, + reconv_GetReconversion, + reconv_Reconvert, +}; + +static HRESULT ITfFunction_Constructor(const WCHAR *name, IUnknown **out) +{ + struct function *func; + + if (!(func = calloc(1, sizeof(*func) + (wcslen(name) + 1) * sizeof(WCHAR) ))) return E_OUTOFMEMORY; + + func->ITfFunction_iface.lpVtbl= &func_vtbl; + func->ITfFnReconversion_iface.lpVtbl= &reconv_vtbl; + func->ref = 1; + + func->name = (WCHAR *)(func + 1); + wcscpy( func->name, name ); + + TRACE("returning %p\n", &func->ITfFunction_iface); + *out = (IUnknown *)&func->ITfFunction_iface; + return S_OK; +} + static HRESULT WINAPI func_provider_QueryInterface(ITfFunctionProvider *iface, REFIID iid, LPVOID *ppvOut) { ThreadMgr *This = impl_from_ITfFunctionProvider(iface); @@ -193,6 +343,10 @@ static HRESULT WINAPI func_provider_GetDescription(ITfFunctionProvider *iface, B static HRESULT WINAPI func_provider_GetFunction(ITfFunctionProvider *iface, REFGUID guid, REFIID riid, IUnknown **func) { + TRACE("(%p) %s %s %p\n", iface, debugstr_guid(guid), debugstr_guid(riid), func); + + if (IsEqualIID(riid, &IID_ITfFnReconversion)) return ITfFunction_Constructor(L"Reconversion", func); + FIXME("(%p) %s %s %p stub.\n", iface, debugstr_guid(guid), debugstr_guid(riid), func); return E_NOTIMPL; } @@ -546,7 +700,7 @@ ITfFunctionProvider **ppFuncProv) { ThreadMgr *This = impl_from_ITfThreadMgrEx(iface); - TRACE("(%p)\n",This); + TRACE("(%p) %s\n", This, debugstr_guid(clsid)); *ppFuncProv = &This->ITfFunctionProvider_iface; ITfFunctionProvider_AddRef(*ppFuncProv); return S_OK; diff --git a/include/Makefile.in b/include/Makefile.in index 9e4ef779ae5..eda503972d7 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -108,6 +108,7 @@ SOURCES = \ cryptdlg.h \ cryptuiapi.h \ cryptxml.h \ + ctffunc.idl \ ctfutb.idl \ ctxtcall.idl \ custcntl.h \ diff --git a/include/ctffunc.idl b/include/ctffunc.idl new file mode 100644 index 00000000000..c308c5377c7 --- /dev/null +++ b/include/ctffunc.idl @@ -0,0 +1,80 @@ +/* + * Copyright 2026 Paul Gofman for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef DO_NO_IMPORTS +import "oaidl.idl"; +import "msctf.idl"; +#endif + +interface IEnumTfCandidates; + +[ + object, + uuid(581f317e-fd9d-443f-b972-ed00467c5d40), + pointer_default(unique) +] +interface ITfCandidateString : IUnknown +{ + HRESULT GetString([out] BSTR *bstr); + HRESULT GetIndex([out] ULONG *index); +} + +[ + object, + uuid(defb1926-6c80-4ce8-87d4-d6b72b812bde), + pointer_default(unique) +] +interface IEnumTfCandidates : IUnknown +{ + HRESULT Clone([out] IEnumTfCandidates **ret_enum); + HRESULT Next([in] ULONG count, [out] ITfCandidateString **cand, [out] ULONG *fetched); + HRESULT Reset(); + HRESULT Skip([in] ULONG count); +} + +typedef [uuid(baa898f2-0207-4643-92ca-f3f7b0cf6f80)] enum +{ + CAND_FINALIZED = 0x0, + CAND_SELECTED = 0x1, + CAND_CANCELED = 0x2, +} TfCandidateResult; + +[ + object, + uuid(a3ad50fb-9bdb-49e3-a843-6c76520fbf5d), + pointer_default(unique) +] +interface ITfCandidateList : IUnknown +{ + HRESULT EnumCandidates([out] IEnumTfCandidates **ret_enum); + HRESULT GetCandidate([in] ULONG index, [out] ITfCandidateString **cand); + HRESULT GetCandidateNum([out] ULONG *count); + HRESULT SetResult([in] ULONG nIndex, [in] TfCandidateResult imcr); +} + +[ + object, + uuid(4cea93c0-0a58-11d3-8df0-00105a2799b5), + pointer_default(unique) +] +interface ITfFnReconversion : ITfFunction +{ + HRESULT QueryRange([in] ITfRange *range, ITfRange **new_range, BOOL *convertable); + HRESULT GetReconversion(ITfRange *range, ITfCandidateList **cand_list); + HRESULT Reconvert([in] ITfRange *range); +} diff --git a/include/msctf.idl b/include/msctf.idl index 6b0c84bdd53..a1c40e944e4 100644 --- a/include/msctf.idl +++ b/include/msctf.idl @@ -172,6 +172,16 @@ typedef [uuid(d678c645-eb6a-45c9-b4ee-0f3e3a991348)] struct TF_PROPERTYVAL VARIANT varValue; } TF_PROPERTYVAL; +[ + object, + uuid(db593490-098f-11d3-8df0-00105a2799b5), + pointer_default(unique) +] +interface ITfFunction : IUnknown +{ + HRESULT GetDisplayName([out] BSTR *name); +} + [ object, uuid(101d6610-0990-11d3-8df0-00105a2799b5), -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10996
This merge request was approved by Hans Leidekker. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10996
FWIW I didn't sent those stubs right away because I was thinking it needs at least some minor beginnings of systematic handling of those function providers and functions. An application can register function providers itself while builtin function provider can be queried GUID_SYSTEM_FUNCTIONPROVIDER. I am not sure where system provider functions reside, in msctf itself or there is another one (perhaps can be checked by looking what dlls are loaded in test?). I am not sure how much of details are needed for the empty stubs but I'd guess we'd at least want to check for provider GUID and only return ours for GUID_SYSTEM_FUNCTIONPROVIDER. And then introduce a table with guid / constructor for builin functions so that can be extended in a straightforward way. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10996#note_141338
participants (4)
-
Hans Leidekker -
Hans Leidekker (@hans) -
Paul Gofman -
Paul Gofman (@gofman)