@@ -27,9 +27,45 @@ static void TfPropertyvalClose(TF_PROPERTYVAL* val)
2727}
2828using unique_tf_propertyval = wil::unique_struct<TF_PROPERTYVAL, decltype (&TfPropertyvalClose), &TfPropertyvalClose>;
2929
30+ // The flags passed to ActivateEx don't replace the flags during previous calls.
31+ // Instead, they're additive. So, if we pass flags that some concurrently running
32+ // TSF clients don't expect we may blow them up accidentally.
33+ //
34+ // Such is the case with WPF (and TSF, which is actually at fault there).
35+ // If you pass TF_TMAE_CONSOLE it'll instantly crash on Windows 10 on first text input.
36+ // On Windows 11 it'll at least not crash but still make emoji input completely non-functional.
37+ //
38+ // --------
39+ //
40+ // In any case, we pass the same flags as conhost v1:
41+ // - TF_TMAE_UIELEMENTENABLEDONLY: TSF activates only text services that are
42+ // categorized in GUID_TFCAT_TIPCAP_UIELEMENTENABLED.
43+ // - TF_TMAE_NOACTIVATEKEYBOARDLAYOUT: TSF does not sync the current keyboard layout
44+ // while this method is called. The keyboard layout will be adjusted when the
45+ // calling thread gets focus. This flag must be used with TF_TMAE_NOACTIVATETIP.
46+ // - TF_TMAE_CONSOLE: A text service is activated for console usage.
47+ // Some IMEs are known to use this as a hint. Particularly a Korean IME can benefit
48+ // from this, because Korean relies on "recomposing" previously finished compositions.
49+ // That can't work in a terminal, since we submit composed text to the shell immediately.
50+ //
51+ // I'm not sure what TF_TMAE_UIELEMENTENABLEDONLY does. I tried to figure it out but failed.
52+ //
53+ // For TF_TMAE_NOACTIVATEKEYBOARDLAYOUT, I'm 99% sure it doesn't do anything, including in
54+ // conhost v1. This is because IMM will be initialized on WM_ACTIVATE, which calls ActivateEx(0).
55+ // Any subsequent ActivateEx() calls will update the flags, _except_ for this one and
56+ // TF_TMAE_NOACTIVATETIP which are explicitly filtered out.
57+ //
58+ // TF_TMAE_NOACTIVATETIP however is important. Without it, TIPs are immediately initialized.
59+ static std::atomic<DWORD> s_activationFlags{ TF_TMAE_NOACTIVATETIP | TF_TMAE_UIELEMENTENABLEDONLY | TF_TMAE_NOACTIVATEKEYBOARDLAYOUT | TF_TMAE_CONSOLE };
60+ void Implementation::AvoidBuggyTSFConsoleFlags () noexcept
61+ {
62+ s_activationFlags.fetch_and (~static_cast <DWORD>(TF_TMAE_CONSOLE), std::memory_order_relaxed);
63+ }
64+
65+ static std::atomic<bool > s_wantsAnsiInputScope{ false };
3066void Implementation::SetDefaultScopeAlphanumericHalfWidth (bool enable) noexcept
3167{
32- _wantsAnsiInputScope .store (enable, std::memory_order_relaxed);
68+ s_wantsAnsiInputScope .store (enable, std::memory_order_relaxed);
3369}
3470
3571void Implementation::Initialize ()
@@ -40,7 +76,7 @@ void Implementation::Initialize()
4076 // There's no point in calling TF_GetThreadMgr. ITfThreadMgr is a per-thread singleton.
4177 _threadMgrEx = wil::CoCreateInstance<ITfThreadMgrEx>(CLSID_TF_ThreadMgr, CLSCTX_INPROC_SERVER);
4278
43- THROW_IF_FAILED (_threadMgrEx->ActivateEx (&_clientId, TF_TMAE_CONSOLE ));
79+ THROW_IF_FAILED (_threadMgrEx->ActivateEx (&_clientId, s_activationFlags. load (std::memory_order_relaxed) ));
4480 THROW_IF_FAILED (_threadMgrEx->CreateDocumentMgr (_documentMgr.addressof ()));
4581
4682 TfEditCookie ecTextStore;
@@ -319,7 +355,7 @@ STDMETHODIMP Implementation::GetWnd(HWND* phwnd) noexcept
319355
320356STDMETHODIMP Implementation::GetAttribute (REFGUID rguidAttribute, VARIANT* pvarValue) noexcept
321357{
322- if (_wantsAnsiInputScope .load (std::memory_order_relaxed) && IsEqualGUID (rguidAttribute, GUID_PROP_INPUTSCOPE))
358+ if (s_wantsAnsiInputScope .load (std::memory_order_relaxed) && IsEqualGUID (rguidAttribute, GUID_PROP_INPUTSCOPE))
323359 {
324360 _ansiInputScope.AddRef ();
325361 pvarValue->vt = VT_UNKNOWN;
0 commit comments