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