xref: /reactos/dll/ime/msctfime/misc.cpp (revision c5e64563)
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 
10 WINE_DEFAULT_DEBUG_CHANNEL(msctfime);
11 
12 /// East-Asian language?
13 /// @implemented
IsEALang(_In_opt_ LANGID LangID)14 BOOL 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 
37 typedef 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
DllShutdownInProgress(VOID)42 BOOLEAN 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
IsInteractiveUserLogon(VOID)61 BOOL 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
GetCharsetFromLangId(_In_ DWORD dwValue)84 BYTE 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
GetActiveContext(VOID)94 HIMC GetActiveContext(VOID)
95 {
96     HWND hwndFocus = ::GetFocus();
97     if (!hwndFocus)
98         hwndFocus = ::GetActiveWindow();
99     return ::ImmGetContext(hwndFocus);
100 }
101 
102 // MSIMTF.dll!MsimtfIsGuidMapEnable
103 typedef BOOL (WINAPI *FN_MsimtfIsGuidMapEnable)(HIMC hIMC, LPBOOL pbValue);
104 HINSTANCE g_hMSIMTF = NULL;
105 
106 /// @implemented
MsimtfIsGuidMapEnable(_In_ HIMC hIMC,_Out_opt_ LPBOOL pbValue)107 BOOL 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
IsVKDBEKey(_In_ UINT uVirtKey)116 BOOL 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
GetUIMCat(PCIC_LIBTHREAD pLibThread)129 ITfCategoryMgr *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
146 static HRESULT
LibEnumItemsInCategory(PCIC_LIBTHREAD pLibThread,REFGUID rguid,IEnumGUID ** ppEnum)147 LibEnumItemsInCategory(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
InitDisplayAttrbuteLib(PCIC_LIBTHREAD pLibThread)156 HRESULT 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
UninitDisplayAttrbuteLib(PCIC_LIBTHREAD pLibThread)201 HRESULT 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
218 HRESULT
GetCompartment(IUnknown * pUnknown,REFGUID rguid,ITfCompartment ** ppComp,BOOL bThread)219 GetCompartment(
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
261 HRESULT
SetCompartmentDWORD(TfEditCookie cookie,IUnknown * pUnknown,REFGUID rguid,DWORD dwValue,BOOL bThread)262 SetCompartmentDWORD(
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
284 HRESULT
GetCompartmentDWORD(IUnknown * pUnknown,REFGUID rguid,LPDWORD pdwValue,BOOL bThread)285 GetCompartmentDWORD(
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
308 HRESULT
SetCompartmentUnknown(TfEditCookie cookie,IUnknown * pUnknown,REFGUID rguid,IUnknown * punkValue)309 SetCompartmentUnknown(
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
330 HRESULT
ClearCompartment(TfClientId tid,IUnknown * pUnknown,REFGUID rguid,BOOL bThread)331 ClearCompartment(
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 
372 struct MODEBIAS
373 {
374     REFGUID m_guid;
375     LONG m_bias;
376 };
377 
378 static 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
SetModeBias(REFGUID rguid)388 void CModeBias::SetModeBias(REFGUID rguid)
389 {
390     m_guid = rguid;
391 }
392 
393 /// @implemented
ConvertModeBias(LONG bias)394 GUID 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
ConvertModeBias(REFGUID guid)410 LONG 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
CFunctionProviderBase(_In_ TfClientId clientId)423 CFunctionProviderBase::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
~CFunctionProviderBase()432 CFunctionProviderBase::~CFunctionProviderBase()
433 {
434     if (!DllShutdownInProgress())
435         ::SysFreeString(m_bstr);
436 }
437 
438 /// @implemented
439 BOOL
Init(_In_ REFGUID rguid,_In_ LPCWSTR psz)440 CFunctionProviderBase::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
450 STDMETHODIMP
QueryInterface(_In_ REFIID riid,_Out_ LPVOID * ppvObj)451 CFunctionProviderBase::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
STDMETHODIMP_(ULONG)464 STDMETHODIMP_(ULONG) CFunctionProviderBase::AddRef()
465 {
466     return ::InterlockedIncrement(&m_cRefs);
467 }
468 
469 /// @implemented
STDMETHODIMP_(ULONG)470 STDMETHODIMP_(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
GetType(_Out_ GUID * guid)481 STDMETHODIMP CFunctionProviderBase::GetType(_Out_ GUID *guid)
482 {
483     *guid = m_guid;
484     return S_OK;
485 }
486 
487 /// @implemented
GetDescription(_Out_ BSTR * desc)488 STDMETHODIMP CFunctionProviderBase::GetDescription(_Out_ BSTR *desc)
489 {
490     *desc = ::SysAllocString(m_bstr);
491     return (*desc ? S_OK : E_OUTOFMEMORY);
492 }
493 
494 /***********************************************************************/
495 
496 /// @implemented
CFunctionProvider(_In_ TfClientId clientId)497 CFunctionProvider::CFunctionProvider(_In_ TfClientId clientId) : CFunctionProviderBase(clientId)
498 {
499     Init(CLSID_CAImmLayer, L"MSCTFIME::Function Provider");
500 }
501 
502 /// @implemented
503 STDMETHODIMP
GetFunction(_In_ REFGUID guid,_In_ REFIID riid,_Out_ IUnknown ** func)504 CFunctionProvider::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 
CFnDocFeed()524 CFnDocFeed::CFnDocFeed()
525 {
526     m_cRefs = 1;
527 }
528 
~CFnDocFeed()529 CFnDocFeed::~CFnDocFeed()
530 {
531 }
532 
533 /// @implemented
QueryInterface(_In_ REFIID riid,_Out_ LPVOID * ppvObj)534 STDMETHODIMP 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
STDMETHODIMP_(ULONG)545 STDMETHODIMP_(ULONG) CFnDocFeed::AddRef()
546 {
547     return ::InterlockedIncrement(&m_cRefs);
548 }
549 
550 /// @implemented
STDMETHODIMP_(ULONG)551 STDMETHODIMP_(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
DocFeed()562 STDMETHODIMP 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
ClearDocFeedBuffer()587 STDMETHODIMP 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
StartReconvert()610 STDMETHODIMP 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
StartUndoCompositionString()642 STDMETHODIMP 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 }
670