Reactos
1/*
2 * PROJECT: ReactOS msctfime.ime
3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4 * PURPOSE: Miscellaneous of msctfime.ime
5 * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6 */
7
8#include "msctfime.h"
9
10WINE_DEFAULT_DEBUG_CHANNEL(msctfime);
11
12/// East-Asian language?
13/// @implemented
14BOOL IsEALang(_In_opt_ LANGID LangID)
15{
16 if (LangID == 0)
17 {
18 TLS *pTLS = TLS::GetTLS();
19 if (!pTLS || !pTLS->m_pProfile)
20 return FALSE;
21
22 pTLS->m_pProfile->GetLangId(&LangID);
23 }
24
25 switch (PRIMARYLANGID(LangID))
26 {
27 case LANG_CHINESE:
28 case LANG_JAPANESE:
29 case LANG_KOREAN:
30 return TRUE;
31
32 default:
33 return FALSE;
34 }
35}
36
37typedef BOOLEAN (WINAPI *FN_DllShutdownInProgress)(VOID);
38
39/// This function calls ntdll!RtlDllShutdownInProgress.
40/// It can detect the system is shutting down or not.
41/// @implemented
42BOOLEAN DllShutdownInProgress(VOID)
43{
44 HMODULE hNTDLL;
45 static FN_DllShutdownInProgress s_fnDllShutdownInProgress = NULL;
46
47 if (s_fnDllShutdownInProgress)
48 return s_fnDllShutdownInProgress();
49
50 hNTDLL = cicGetSystemModuleHandle(L"ntdll.dll", FALSE);
51 s_fnDllShutdownInProgress =
52 (FN_DllShutdownInProgress)GetProcAddress(hNTDLL, "RtlDllShutdownInProgress");
53 if (!s_fnDllShutdownInProgress)
54 return FALSE;
55
56 return s_fnDllShutdownInProgress();
57}
58
59/// This function checks if the current user logon session is interactive.
60/// @implemented
61BOOL IsInteractiveUserLogon(VOID)
62{
63 BOOL bOK, IsMember = FALSE;
64 PSID pSid;
65 SID_IDENTIFIER_AUTHORITY IdentAuth = { SECURITY_NT_AUTHORITY };
66
67 if (!AllocateAndInitializeSid(&IdentAuth, 1, SECURITY_INTERACTIVE_RID,
68 0, 0, 0, 0, 0, 0, 0, &pSid))
69 {
70 ERR("Error: %ld\n", GetLastError());
71 return FALSE;
72 }
73
74 bOK = CheckTokenMembership(NULL, pSid, &IsMember);
75
76 if (pSid)
77 FreeSid(pSid);
78
79 return bOK && IsMember;
80}
81
82/// Gets the charset from a language ID.
83/// @implemented
84BYTE GetCharsetFromLangId(_In_ DWORD dwValue)
85{
86 CHARSETINFO info;
87 if (!::TranslateCharsetInfo((DWORD*)(DWORD_PTR)dwValue, &info, TCI_SRCLOCALE))
88 return 0;
89 return info.ciCharset;
90}
91
92/// Get the active input context.
93/// @implemented
94HIMC GetActiveContext(VOID)
95{
96 HWND hwndFocus = ::GetFocus();
97 if (!hwndFocus)
98 hwndFocus = ::GetActiveWindow();
99 return ::ImmGetContext(hwndFocus);
100}
101
102// MSIMTF.dll!MsimtfIsGuidMapEnable
103typedef BOOL (WINAPI *FN_MsimtfIsGuidMapEnable)(HIMC hIMC, LPBOOL pbValue);
104HINSTANCE g_hMSIMTF = NULL;
105
106/// @implemented
107BOOL MsimtfIsGuidMapEnable(_In_ HIMC hIMC, _Out_opt_ LPBOOL pbValue)
108{
109 static FN_MsimtfIsGuidMapEnable s_fn = NULL;
110 if (!cicGetFN(g_hMSIMTF, s_fn, L"msimtf.dll", "MsimtfIsGuidMapEnable"))
111 return FALSE;
112 return s_fn(hIMC, pbValue);
113}
114
115/// @implemented
116BOOL IsVKDBEKey(_In_ UINT uVirtKey)
117{
118 switch (uVirtKey)
119 {
120 case VK_KANJI:
121 case VK_CONVERT:
122 return TRUE;
123 default:
124 return (VK_OEM_ATTN <= uVirtKey && uVirtKey <= VK_PA1);
125 }
126}
127
128/// @implemented
129ITfCategoryMgr *GetUIMCat(PCIC_LIBTHREAD pLibThread)
130{
131 if (!pLibThread)
132 return NULL;
133
134 if (pLibThread->m_pCategoryMgr)
135 return pLibThread->m_pCategoryMgr;
136
137 if (FAILED(cicCoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER,
138 IID_ITfCategoryMgr, (void **)&pLibThread->m_pCategoryMgr)))
139 {
140 return NULL;
141 }
142 return pLibThread->m_pCategoryMgr;
143}
144
145/// @implemented
146static HRESULT
147LibEnumItemsInCategory(PCIC_LIBTHREAD pLibThread, REFGUID rguid, IEnumGUID **ppEnum)
148{
149 ITfCategoryMgr *pCat = GetUIMCat(pLibThread);
150 if (!pCat)
151 return E_FAIL;
152 return pCat->EnumItemsInCategory(rguid, ppEnum);
153}
154
155/// @implemented
156HRESULT InitDisplayAttrbuteLib(PCIC_LIBTHREAD pLibThread)
157{
158 if (!pLibThread)
159 return E_FAIL;
160
161 if (pLibThread->m_pDisplayAttrMgr)
162 {
163 pLibThread->m_pDisplayAttrMgr->Release();
164 pLibThread->m_pDisplayAttrMgr = NULL;
165 }
166
167 if (FAILED(cicCoCreateInstance(CLSID_TF_DisplayAttributeMgr, NULL, CLSCTX_INPROC_SERVER,
168 IID_ITfDisplayAttributeMgr,
169 (void **)&pLibThread->m_pDisplayAttrMgr)))
170 {
171 return E_FAIL;
172 }
173
174 IEnumGUID *pEnumGuid;
175 LibEnumItemsInCategory(pLibThread, GUID_TFCAT_DISPLAYATTRIBUTEPROPERTY, &pEnumGuid);
176
177 HRESULT hr = E_OUTOFMEMORY;
178
179 ::EnterCriticalSection(&g_csLock);
180 if (pEnumGuid && !g_pPropCache)
181 {
182 g_pPropCache = new(cicNoThrow) CDispAttrPropCache();
183 if (g_pPropCache)
184 {
185 g_pPropCache->Add(GUID_PROP_ATTRIBUTE);
186 GUID guid;
187 while (pEnumGuid->Next(1, &guid, NULL) == S_OK)
188 {
189 if (!IsEqualGUID(guid, GUID_PROP_ATTRIBUTE))
190 g_pPropCache->Add(guid);
191 }
192 hr = S_OK;
193 }
194 }
195 ::LeaveCriticalSection(&g_csLock);
196
197 return hr;
198}
199
200/// @implemented
201HRESULT UninitDisplayAttrbuteLib(PCIC_LIBTHREAD pLibThread)
202{
203 if (!pLibThread)
204 return E_FAIL;
205
206 if (pLibThread->m_pDisplayAttrMgr)
207 {
208 pLibThread->m_pDisplayAttrMgr->Release();
209 pLibThread->m_pDisplayAttrMgr = NULL;
210 }
211
212 return S_OK;
213}
214
215/***********************************************************************/
216
217/// @implemented
218HRESULT
219GetCompartment(
220 IUnknown *pUnknown,
221 REFGUID rguid,
222 ITfCompartment **ppComp,
223 BOOL bThread)
224{
225 *ppComp = NULL;
226
227 ITfThreadMgr *pThreadMgr = NULL;
228 ITfCompartmentMgr *pCompMgr = NULL;
229
230 HRESULT hr;
231 if (bThread)
232 {
233 hr = pUnknown->QueryInterface(IID_ITfThreadMgr, (void **)&pThreadMgr);
234 if (FAILED(hr))
235 return hr;
236
237 hr = pThreadMgr->GetGlobalCompartment(&pCompMgr);
238 }
239 else
240 {
241 hr = pUnknown->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompMgr);
242 }
243
244 if (SUCCEEDED(hr))
245 {
246 hr = E_FAIL;
247 if (pCompMgr)
248 {
249 hr = pCompMgr->GetCompartment(rguid, ppComp);
250 pCompMgr->Release();
251 }
252 }
253
254 if (pThreadMgr)
255 pThreadMgr->Release();
256
257 return hr;
258}
259
260/// @implemented
261HRESULT
262SetCompartmentDWORD(
263 TfEditCookie cookie,
264 IUnknown *pUnknown,
265 REFGUID rguid,
266 DWORD dwValue,
267 BOOL bThread)
268{
269 ITfCompartment *pComp = NULL;
270 HRESULT hr = GetCompartment(pUnknown, rguid, &pComp, bThread);
271 if (FAILED(hr))
272 return hr;
273
274 VARIANT vari;
275 V_I4(&vari) = dwValue;
276 V_VT(&vari) = VT_I4;
277 hr = pComp->SetValue(cookie, &vari);
278
279 pComp->Release();
280 return hr;
281}
282
283/// @implemented
284HRESULT
285GetCompartmentDWORD(
286 IUnknown *pUnknown,
287 REFGUID rguid,
288 LPDWORD pdwValue,
289 BOOL bThread)
290{
291 *pdwValue = 0;
292
293 ITfCompartment *pComp = NULL;
294 HRESULT hr = GetCompartment(pUnknown, rguid, &pComp, bThread);
295 if (FAILED(hr))
296 return hr;
297
298 VARIANT vari;
299 hr = pComp->GetValue(&vari);
300 if (hr == S_OK)
301 *pdwValue = V_I4(&vari);
302
303 pComp->Release();
304 return hr;
305}
306
307/// @implemented
308HRESULT
309SetCompartmentUnknown(
310 TfEditCookie cookie,
311 IUnknown *pUnknown,
312 REFGUID rguid,
313 IUnknown *punkValue)
314{
315 ITfCompartment *pComp = NULL;
316 HRESULT hr = GetCompartment(pUnknown, rguid, &pComp, FALSE);
317 if (FAILED(hr))
318 return hr;
319
320 VARIANT vari;
321 V_UNKNOWN(&vari) = punkValue;
322 V_VT(&vari) = VT_UNKNOWN;
323 hr = pComp->SetValue(cookie, &vari);
324
325 pComp->Release();
326 return hr;
327}
328
329/// @implemented
330HRESULT
331ClearCompartment(
332 TfClientId tid,
333 IUnknown *pUnknown,
334 REFGUID rguid,
335 BOOL bThread)
336{
337 ITfCompartmentMgr *pCompMgr = NULL;
338 ITfThreadMgr *pThreadMgr = NULL;
339
340 HRESULT hr;
341 if (bThread)
342 {
343 hr = pUnknown->QueryInterface(IID_ITfThreadMgr, (void **)&pThreadMgr);
344 if (FAILED(hr))
345 return hr;
346
347 hr = pThreadMgr->GetGlobalCompartment(&pCompMgr);
348 }
349 else
350 {
351 hr = pUnknown->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompMgr);
352 }
353
354 if (SUCCEEDED(hr))
355 {
356 hr = E_FAIL;
357 if (pCompMgr)
358 {
359 hr = pCompMgr->ClearCompartment(tid, rguid);
360 pCompMgr->Release();
361 }
362 }
363
364 if (pThreadMgr)
365 pThreadMgr->Release();
366
367 return hr;
368}
369
370/***********************************************************************/
371
372struct MODEBIAS
373{
374 REFGUID m_guid;
375 LONG m_bias;
376};
377
378static const MODEBIAS g_ModeBiasMap[] =
379{
380 { GUID_MODEBIAS_FILENAME, 0x00000001 },
381 { GUID_MODEBIAS_NUMERIC, 0x00000004 },
382 { GUID_MODEBIAS_URLHISTORY, 0x00010000 },
383 { GUID_MODEBIAS_DEFAULT, 0x00000000 },
384 { GUID_MODEBIAS_NONE, 0x00000000 },
385};
386
387/// @implemented
388void CModeBias::SetModeBias(REFGUID rguid)
389{
390 m_guid = rguid;
391}
392
393/// @implemented
394GUID CModeBias::ConvertModeBias(LONG bias)
395{
396 const GUID *pguid = &GUID_NULL;
397 for (auto& item : g_ModeBiasMap)
398 {
399 if (item.m_bias == bias)
400 {
401 pguid = &item.m_guid;
402 break;
403 }
404 }
405
406 return *pguid;
407}
408
409/// @implemented
410LONG CModeBias::ConvertModeBias(REFGUID guid)
411{
412 for (auto& item : g_ModeBiasMap)
413 {
414 if (IsEqualGUID(guid, item.m_guid))
415 return item.m_bias;
416 }
417 return 0;
418}
419
420/***********************************************************************/
421
422/// @implemented
423CFunctionProviderBase::CFunctionProviderBase(_In_ TfClientId clientId)
424{
425 m_clientId = clientId;
426 m_guid = GUID_NULL;
427 m_bstr = NULL;
428 m_cRefs = 1;
429}
430
431/// @implemented
432CFunctionProviderBase::~CFunctionProviderBase()
433{
434 if (!DllShutdownInProgress())
435 ::SysFreeString(m_bstr);
436}
437
438/// @implemented
439BOOL
440CFunctionProviderBase::Init(
441 _In_ REFGUID rguid,
442 _In_ LPCWSTR psz)
443{
444 m_bstr = ::SysAllocString(psz);
445 m_guid = rguid;
446 return (m_bstr != NULL);
447}
448
449/// @implemented
450STDMETHODIMP
451CFunctionProviderBase::QueryInterface(
452 _In_ REFIID riid,
453 _Out_ LPVOID* ppvObj)
454{
455 static const QITAB c_tab[] =
456 {
457 QITABENT(CFunctionProviderBase, ITfFunctionProvider),
458 { NULL }
459 };
460 return ::QISearch(this, c_tab, riid, ppvObj);
461}
462
463/// @implemented
464STDMETHODIMP_(ULONG) CFunctionProviderBase::AddRef()
465{
466 return ::InterlockedIncrement(&m_cRefs);
467}
468
469/// @implemented
470STDMETHODIMP_(ULONG) CFunctionProviderBase::Release()
471{
472 if (::InterlockedDecrement(&m_cRefs) == 0)
473 {
474 delete this;
475 return 0;
476 }
477 return m_cRefs;
478}
479
480/// @implemented
481STDMETHODIMP CFunctionProviderBase::GetType(_Out_ GUID *guid)
482{
483 *guid = m_guid;
484 return S_OK;
485}
486
487/// @implemented
488STDMETHODIMP CFunctionProviderBase::GetDescription(_Out_ BSTR *desc)
489{
490 *desc = ::SysAllocString(m_bstr);
491 return (*desc ? S_OK : E_OUTOFMEMORY);
492}
493
494/***********************************************************************/
495
496/// @implemented
497CFunctionProvider::CFunctionProvider(_In_ TfClientId clientId) : CFunctionProviderBase(clientId)
498{
499 Init(CLSID_CAImmLayer, L"MSCTFIME::Function Provider");
500}
501
502/// @implemented
503STDMETHODIMP
504CFunctionProvider::GetFunction(
505 _In_ REFGUID guid,
506 _In_ REFIID riid,
507 _Out_ IUnknown **func)
508{
509 *func = NULL;
510
511 if (IsEqualGUID(guid, GUID_NULL) &&
512 IsEqualIID(riid, IID_IAImmFnDocFeed))
513 {
514 *func = new(cicNoThrow) CFnDocFeed();
515 if (*func)
516 return S_OK;
517 }
518
519 return E_NOINTERFACE;
520}
521
522/***********************************************************************/
523
524CFnDocFeed::CFnDocFeed()
525{
526 m_cRefs = 1;
527}
528
529CFnDocFeed::~CFnDocFeed()
530{
531}
532
533/// @implemented
534STDMETHODIMP CFnDocFeed::QueryInterface(_In_ REFIID riid, _Out_ LPVOID* ppvObj)
535{
536 static const QITAB c_tab[] =
537 {
538 QITABENT(CFnDocFeed, IAImmFnDocFeed),
539 { NULL }
540 };
541 return ::QISearch(this, c_tab, riid, ppvObj);
542}
543
544/// @implemented
545STDMETHODIMP_(ULONG) CFnDocFeed::AddRef()
546{
547 return ::InterlockedIncrement(&m_cRefs);
548}
549
550/// @implemented
551STDMETHODIMP_(ULONG) CFnDocFeed::Release()
552{
553 if (::InterlockedDecrement(&m_cRefs) == 0)
554 {
555 delete this;
556 return 0;
557 }
558 return m_cRefs;
559}
560
561/// @implemented
562STDMETHODIMP CFnDocFeed::DocFeed()
563{
564 TLS *pTLS = TLS::GetTLS();
565 if (!pTLS)
566 return E_OUTOFMEMORY;
567
568 HIMC hIMC = GetActiveContext();
569 CicIMCLock imcLock(hIMC);
570 if (FAILED(imcLock.m_hr))
571 return imcLock.m_hr;
572
573 CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext);
574 if (FAILED(imeContext.m_hr))
575 return imeContext.m_hr;
576 CicInputContext *pCicIC = imeContext.get().m_pCicIC;
577 if (!pCicIC)
578 return E_FAIL;
579
580 UINT uCodePage = CP_ACP;
581 pTLS->m_pProfile->GetCodePageA(&uCodePage);
582 pCicIC->SetupDocFeedString(imcLock, uCodePage);
583 return S_OK;
584}
585
586/// @implemented
587STDMETHODIMP CFnDocFeed::ClearDocFeedBuffer()
588{
589 if (!TLS::GetTLS())
590 return E_OUTOFMEMORY;
591
592 HIMC hIMC = GetActiveContext();
593 CicIMCLock imcLock(hIMC);
594 if (FAILED(imcLock.m_hr))
595 return imcLock.m_hr;
596
597 CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext);
598 if (FAILED(imeContext.m_hr))
599 return imeContext.m_hr;
600
601 CicInputContext *pCicIC = imeContext.get().m_pCicIC;
602 if (!pCicIC)
603 return E_FAIL;
604
605 pCicIC->EscbClearDocFeedBuffer(imcLock, TRUE);
606 return S_OK;
607}
608
609/// @unimplemented
610STDMETHODIMP CFnDocFeed::StartReconvert()
611{
612 TLS *pTLS = TLS::GetTLS();
613 if (!pTLS)
614 return E_OUTOFMEMORY;
615 auto *pThreadMgr = pTLS->m_pThreadMgr;
616 if (!pThreadMgr)
617 return E_OUTOFMEMORY;
618
619 HIMC hIMC = GetActiveContext();
620 CicIMCLock imcLock(hIMC);
621 if (FAILED(imcLock.m_hr))
622 return imcLock.m_hr;
623
624 CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext);
625 if (FAILED(imeContext.m_hr))
626 return imeContext.m_hr;
627 CicInputContext *pCicIC = imeContext.get().m_pCicIC;
628 if (!pCicIC)
629 return E_FAIL;
630
631 UINT uCodePage = CP_ACP;
632 pTLS->m_pProfile->GetCodePageA(&uCodePage);
633
634 pCicIC->m_bReconverting = TRUE;
635 pCicIC->SetupReconvertString(imcLock, pThreadMgr, uCodePage, 0, 0);
636 pCicIC->EndReconvertString(imcLock);
637 pCicIC->m_bReconverting = FALSE;
638 return S_OK;
639}
640
641/// @implemented
642STDMETHODIMP CFnDocFeed::StartUndoCompositionString()
643{
644 TLS *pTLS = TLS::GetTLS();
645 if (!pTLS)
646 return E_OUTOFMEMORY;
647 auto *pThreadMgr = pTLS->m_pThreadMgr;
648 if (!pThreadMgr)
649 return E_OUTOFMEMORY;
650
651 HIMC hIMC = GetActiveContext();
652 CicIMCLock imcLock(hIMC);
653 if (FAILED(imcLock.m_hr))
654 return imcLock.m_hr;
655
656 CicIMCCLock<CTFIMECONTEXT> imeContext(imcLock.get().hCtfImeContext);
657 if (FAILED(imeContext.m_hr))
658 return imeContext.m_hr;
659 CicInputContext *pCicIC = imeContext.get().m_pCicIC;
660 if (!pCicIC)
661 return E_FAIL;
662
663 UINT uCodePage = CP_ACP;
664 pTLS->m_pProfile->GetCodePageA(&uCodePage);
665
666 pCicIC->SetupReconvertString(imcLock, pThreadMgr, uCodePage, 0, TRUE);
667 pCicIC->EndReconvertString(imcLock);
668 return S_OK;
669}