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