Fix the issue where the candidate window cannot be pulled up when switching back from Microsoft's emoji panel, and deeply optimize the stability of IPC connections.
This commit is contained in:
1 parent
6555913a72
commit
4f2fe01dc4
23 files changed
+4534
-1074
No files matched your search
@@ -190,8 +190,10 @@ HRESULT CCompartment::_ClearCompartment()
|
||||
// ctor
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
CCompartmentEventSink::CCompartmentEventSink(_In_ CESCALLBACK pfnCallback, _In_ void *pv)
|
||||
{
|
||||
CCompartmentEventSink::CCompartmentEventSink(_In_ CESCALLBACK pfnCallback, _In_ void *pv)
|
||||
{
|
||||
_pCompartment = nullptr;
|
||||
_dwCookie = TF_INVALID_COOKIE;
|
||||
_pfnCallback = pfnCallback;
|
||||
_pv = pv;
|
||||
_refCount = 1;
|
||||
@@ -280,8 +282,17 @@ STDAPI CCompartmentEventSink::OnChange(_In_ REFGUID guidCompartment)
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
HRESULT CCompartmentEventSink::_Advise(_In_ IUnknown *punk, _In_ REFGUID guidCompartment)
|
||||
{
|
||||
HRESULT CCompartmentEventSink::_Advise(_In_ IUnknown *punk, _In_ REFGUID guidCompartment)
|
||||
{
|
||||
if (!punk)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
if (_pCompartment)
|
||||
{
|
||||
_Unadvise();
|
||||
}
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
ITfCompartmentMgr *pCompartmentMgr = nullptr;
|
||||
ITfSource *pSource = nullptr;
|
||||
@@ -293,19 +304,28 @@ HRESULT CCompartmentEventSink::_Advise(_In_ IUnknown *punk, _In_ REFGUID guidCom
|
||||
}
|
||||
|
||||
hr = pCompartmentMgr->GetCompartment(guidCompartment, &_pCompartment);
|
||||
if (SUCCEEDED(hr))
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = _pCompartment->QueryInterface(IID_ITfSource, (void **)&pSource);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = pSource->AdviseSink(IID_ITfCompartmentEventSink, this, &_dwCookie);
|
||||
pSource->Release();
|
||||
}
|
||||
}
|
||||
hr = pSource->AdviseSink(IID_ITfCompartmentEventSink, this, &_dwCookie);
|
||||
pSource->Release();
|
||||
}
|
||||
}
|
||||
|
||||
pCompartmentMgr->Release();
|
||||
|
||||
return hr;
|
||||
if (FAILED(hr))
|
||||
{
|
||||
if (_pCompartment)
|
||||
{
|
||||
_pCompartment->Release();
|
||||
_pCompartment = nullptr;
|
||||
}
|
||||
_dwCookie = TF_INVALID_COOKIE;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
@@ -314,21 +334,30 @@ HRESULT CCompartmentEventSink::_Advise(_In_ IUnknown *punk, _In_ REFGUID guidCom
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
HRESULT CCompartmentEventSink::_Unadvise()
|
||||
{
|
||||
HRESULT CCompartmentEventSink::_Unadvise()
|
||||
{
|
||||
if (!_pCompartment)
|
||||
{
|
||||
_dwCookie = TF_INVALID_COOKIE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
ITfSource *pSource = nullptr;
|
||||
|
||||
hr = _pCompartment->QueryInterface(IID_ITfSource, (void **)&pSource);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = pSource->UnadviseSink(_dwCookie);
|
||||
pSource->Release();
|
||||
if (_dwCookie != TF_INVALID_COOKIE)
|
||||
{
|
||||
hr = _pCompartment->QueryInterface(IID_ITfSource, (void **)&pSource);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = pSource->UnadviseSink(_dwCookie);
|
||||
pSource->Release();
|
||||
}
|
||||
}
|
||||
|
||||
_pCompartment->Release();
|
||||
_pCompartment = nullptr;
|
||||
_dwCookie = 0;
|
||||
_dwCookie = TF_INVALID_COOKIE;
|
||||
|
||||
return hr;
|
||||
}
|
||||
@@ -5,8 +5,9 @@
|
||||
#include <debugapi.h>
|
||||
#include <fmt/xchar.h>
|
||||
#include <string>
|
||||
#include "FanyDefines.h"
|
||||
#include "../Utils/PerfTimer.h"
|
||||
#include "FanyDefines.h"
|
||||
#include "Ipc.h"
|
||||
#include "../Utils/PerfTimer.h"
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
@@ -16,39 +17,66 @@
|
||||
// someone other than this service ends a composition.
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
STDAPI CMetasequoiaIME::OnCompositionTerminated(TfEditCookie ecWrite, _In_ ITfComposition *pComposition)
|
||||
{
|
||||
PerfTimer timer;
|
||||
// Clear dummy composition
|
||||
PerfTimer removeDummyTimer;
|
||||
_RemoveDummyCompositionForComposing(ecWrite, pComposition);
|
||||
double removeDummyElapsedMs = removeDummyTimer.ElapsedMs();
|
||||
|
||||
// Clear display attribute and end composition, _EndComposition will release composition for us
|
||||
ITfContext *pContext = _pContext;
|
||||
const bool hadContext = (pContext != nullptr);
|
||||
if (pContext)
|
||||
{
|
||||
pContext->AddRef();
|
||||
}
|
||||
|
||||
PerfTimer endCompositionTimer;
|
||||
_EndComposition(_pContext);
|
||||
double endCompositionElapsedMs = endCompositionTimer.ElapsedMs();
|
||||
|
||||
PerfTimer deleteCandidateTimer;
|
||||
_DeleteCandidateList(FALSE, pContext);
|
||||
double deleteCandidateElapsedMs = deleteCandidateTimer.ElapsedMs();
|
||||
|
||||
if (pContext)
|
||||
{
|
||||
pContext->Release();
|
||||
pContext = nullptr;
|
||||
}
|
||||
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
STDAPI CMetasequoiaIME::OnCompositionTerminated(TfEditCookie ecWrite, _In_ ITfComposition *pComposition)
|
||||
{
|
||||
PerfTimer timer;
|
||||
if (pComposition == nullptr || !_IsCompositionCurrent(pComposition))
|
||||
{
|
||||
// A delayed termination callback for an older composition must never
|
||||
// delete the current candidate presenter or release newer ownership.
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// The callback already carries a write cookie and the host has already
|
||||
// ended this exact composition. Detach ownership before making COM calls,
|
||||
// so a re-entrant/stale callback cannot observe it as current or tear down
|
||||
// a composition created later.
|
||||
ITfComposition *terminatedComposition = pComposition;
|
||||
terminatedComposition->AddRef();
|
||||
_pComposition->Release();
|
||||
_pComposition = nullptr;
|
||||
|
||||
ITfContext *ownerContext = _pContext;
|
||||
if (ownerContext)
|
||||
{
|
||||
ownerContext->AddRef();
|
||||
_pContext->Release();
|
||||
_pContext = nullptr;
|
||||
}
|
||||
|
||||
uint64_t nextEpoch =
|
||||
_compositionEpoch.fetch_add(1, std::memory_order_acq_rel) + 1;
|
||||
if (nextEpoch == 0)
|
||||
{
|
||||
_compositionEpoch.fetch_add(1, std::memory_order_acq_rel);
|
||||
}
|
||||
|
||||
// Detach and end the old candidate/session before the COM cleanup calls
|
||||
// below can re-enter and create a presenter for a newer composition.
|
||||
_DeleteCandidateList(FALSE, ownerContext);
|
||||
if (Global::g_connected)
|
||||
{
|
||||
// EndCandidateUiSession is intentionally idempotent, but a presenter
|
||||
// can exist before its UI session becomes active. Always send one
|
||||
// exact routed clear so the Server cannot retain that composition.
|
||||
SendHideCandidateWndEventToUIProcess();
|
||||
}
|
||||
|
||||
_RemoveDummyCompositionForComposing(ecWrite, terminatedComposition);
|
||||
if (ownerContext)
|
||||
{
|
||||
_ClearCompositionDisplayAttributes(ecWrite, ownerContext,
|
||||
terminatedComposition);
|
||||
}
|
||||
terminatedComposition->Release();
|
||||
|
||||
if (ownerContext)
|
||||
{
|
||||
ownerContext->Release();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
@@ -67,10 +95,15 @@ BOOL CMetasequoiaIME::_IsComposing()
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void CMetasequoiaIME::_SetComposition(_In_ ITfComposition *pComposition)
|
||||
{
|
||||
_pComposition = pComposition;
|
||||
}
|
||||
void CMetasequoiaIME::_SetComposition(_In_ ITfComposition *pComposition)
|
||||
{
|
||||
_pComposition = pComposition;
|
||||
uint64_t nextEpoch = _compositionEpoch.fetch_add(1, std::memory_order_acq_rel) + 1;
|
||||
if (nextEpoch == 0)
|
||||
{
|
||||
_compositionEpoch.fetch_add(1, std::memory_order_acq_rel);
|
||||
}
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
@@ -110,8 +110,10 @@ CCompositionProcessorEngine::CCompositionProcessorEngine()
|
||||
_pCompartmentConversion = nullptr;
|
||||
_pCompartmentKeyboardOpenEventSink = nullptr;
|
||||
_pCompartmentConversionEventSink = nullptr;
|
||||
_pCompartmentDoubleSingleByteEventSink = nullptr;
|
||||
_pCompartmentPunctuationEventSink = nullptr;
|
||||
_pCompartmentDoubleSingleByteEventSink = nullptr;
|
||||
_pCompartmentPunctuationEventSink = nullptr;
|
||||
_pOwnerThreadMgr = nullptr;
|
||||
_ownerMsgWndHandle = nullptr;
|
||||
|
||||
_hasWildcardIncludedInKeystrokeBuffer = FALSE;
|
||||
|
||||
@@ -176,12 +178,18 @@ CCompositionProcessorEngine::~CCompositionProcessorEngine()
|
||||
delete _pCompartmentDoubleSingleByteEventSink;
|
||||
_pCompartmentDoubleSingleByteEventSink = nullptr;
|
||||
}
|
||||
if (_pCompartmentPunctuationEventSink)
|
||||
if (_pCompartmentPunctuationEventSink)
|
||||
{
|
||||
_pCompartmentPunctuationEventSink->_Unadvise();
|
||||
delete _pCompartmentPunctuationEventSink;
|
||||
_pCompartmentPunctuationEventSink = nullptr;
|
||||
}
|
||||
_pCompartmentPunctuationEventSink = nullptr;
|
||||
}
|
||||
if (_pOwnerThreadMgr)
|
||||
{
|
||||
_pOwnerThreadMgr->Release();
|
||||
_pOwnerThreadMgr = nullptr;
|
||||
}
|
||||
_ownerMsgWndHandle = nullptr;
|
||||
|
||||
}
|
||||
|
||||
@@ -214,7 +222,17 @@ BOOL CCompositionProcessorEngine::SetupLanguageProfile(LANGID langid, REFGUID gu
|
||||
_isComLessMode = isComLessMode;
|
||||
_langid = langid;
|
||||
_guidProfile = guidLanguageProfile;
|
||||
_tfClientId = tfClientId;
|
||||
_tfClientId = tfClientId;
|
||||
if (_pOwnerThreadMgr != pThreadMgr)
|
||||
{
|
||||
if (_pOwnerThreadMgr)
|
||||
{
|
||||
_pOwnerThreadMgr->Release();
|
||||
}
|
||||
_pOwnerThreadMgr = pThreadMgr;
|
||||
_pOwnerThreadMgr->AddRef();
|
||||
}
|
||||
_ownerMsgWndHandle = Global::msgWndHandle;
|
||||
|
||||
SetupPreserved(pThreadMgr, tfClientId);
|
||||
InitializeMetasequoiaIMECompartment(pThreadMgr, tfClientId);
|
||||
@@ -836,22 +854,64 @@ BOOL CCompositionProcessorEngine::CheckShiftKeyOnly(_In_ CMetasequoiaImeArray<TF
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
// OnPreservedKey
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void CCompositionProcessorEngine::OnPreservedKey( //
|
||||
// OnPreservedKey
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
BOOL CCompositionProcessorEngine::IsPreservedKeyEligible(REFGUID rguid)
|
||||
{
|
||||
if (IsEqualGUID(rguid, _PreservedKey_IMEMode.Guid))
|
||||
{
|
||||
return CheckShiftKeyOnly(&_PreservedKey_IMEMode.TSFPreservedKeyTable);
|
||||
}
|
||||
if (IsEqualGUID(rguid, _PreservedKey_IMEMode02.Guid))
|
||||
{
|
||||
return CheckShiftKeyOnly(&_PreservedKey_IMEMode02.TSFPreservedKeyTable);
|
||||
}
|
||||
if (IsEqualGUID(rguid, _PreservedKey_DoubleSingleByte.Guid))
|
||||
{
|
||||
return CheckShiftKeyOnly(&_PreservedKey_DoubleSingleByte.TSFPreservedKeyTable);
|
||||
}
|
||||
if (IsEqualGUID(rguid, _PreservedKey_Punctuation.Guid))
|
||||
{
|
||||
return CheckShiftKeyOnly(&_PreservedKey_Punctuation.TSFPreservedKeyTable);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CCompositionProcessorEngine::PreservedKeyAction
|
||||
CCompositionProcessorEngine::GetPreservedKeyAction(REFGUID rguid) const
|
||||
{
|
||||
if (IsEqualGUID(rguid, _PreservedKey_IMEMode.Guid) ||
|
||||
IsEqualGUID(rguid, _PreservedKey_IMEMode02.Guid))
|
||||
{
|
||||
return PreservedKeyAction::ToggleImeMode;
|
||||
}
|
||||
if (IsEqualGUID(rguid, _PreservedKey_DoubleSingleByte.Guid))
|
||||
{
|
||||
return PreservedKeyAction::ToggleDoubleSingleByteMode;
|
||||
}
|
||||
if (IsEqualGUID(rguid, _PreservedKey_Punctuation.Guid))
|
||||
{
|
||||
return PreservedKeyAction::TogglePunctuationMode;
|
||||
}
|
||||
return PreservedKeyAction::None;
|
||||
}
|
||||
|
||||
void CCompositionProcessorEngine::OnPreservedKey( //
|
||||
ITfContext *pContext, //
|
||||
REFGUID rguid, //
|
||||
_Out_ BOOL *pIsEaten, //
|
||||
_In_ ITfThreadMgr *pThreadMgr, //
|
||||
TfClientId tfClientId, //
|
||||
BOOL *pNeedToggleIMEMode //
|
||||
)
|
||||
{
|
||||
if (IsEqualGUID(rguid, _PreservedKey_IMEMode.Guid))
|
||||
{
|
||||
if (!CheckShiftKeyOnly(&_PreservedKey_IMEMode.TSFPreservedKeyTable))
|
||||
_In_ ITfThreadMgr *pThreadMgr, //
|
||||
TfClientId tfClientId, //
|
||||
BOOL *pNeedToggleIMEMode, //
|
||||
BOOL isPrevalidated //
|
||||
)
|
||||
{
|
||||
if (IsEqualGUID(rguid, _PreservedKey_IMEMode.Guid))
|
||||
{
|
||||
if (!isPrevalidated &&
|
||||
!CheckShiftKeyOnly(&_PreservedKey_IMEMode.TSFPreservedKeyTable))
|
||||
{
|
||||
*pIsEaten = FALSE;
|
||||
return;
|
||||
@@ -887,9 +947,10 @@ void CCompositionProcessorEngine::OnPreservedKey( //
|
||||
SendKeyEventToUIProcess();
|
||||
ClearNamedpipeDataIfExists();
|
||||
}
|
||||
else if (IsEqualGUID(rguid, _PreservedKey_IMEMode02.Guid))
|
||||
{
|
||||
if (!CheckShiftKeyOnly(&_PreservedKey_IMEMode02.TSFPreservedKeyTable))
|
||||
else if (IsEqualGUID(rguid, _PreservedKey_IMEMode02.Guid))
|
||||
{
|
||||
if (!isPrevalidated &&
|
||||
!CheckShiftKeyOnly(&_PreservedKey_IMEMode02.TSFPreservedKeyTable))
|
||||
{
|
||||
*pIsEaten = FALSE;
|
||||
return;
|
||||
@@ -926,9 +987,10 @@ void CCompositionProcessorEngine::OnPreservedKey( //
|
||||
SendKeyEventToUIProcess();
|
||||
ClearNamedpipeDataIfExists();
|
||||
}
|
||||
else if (IsEqualGUID(rguid, _PreservedKey_DoubleSingleByte.Guid))
|
||||
{
|
||||
if (!CheckShiftKeyOnly(&_PreservedKey_DoubleSingleByte.TSFPreservedKeyTable))
|
||||
else if (IsEqualGUID(rguid, _PreservedKey_DoubleSingleByte.Guid))
|
||||
{
|
||||
if (!isPrevalidated &&
|
||||
!CheckShiftKeyOnly(&_PreservedKey_DoubleSingleByte.TSFPreservedKeyTable))
|
||||
{
|
||||
*pIsEaten = FALSE;
|
||||
return;
|
||||
@@ -940,9 +1002,10 @@ void CCompositionProcessorEngine::OnPreservedKey( //
|
||||
CompartmentDoubleSingleByte._SetCompartmentBOOL(isDouble ? FALSE : TRUE);
|
||||
*pIsEaten = TRUE;
|
||||
}
|
||||
else if (IsEqualGUID(rguid, _PreservedKey_Punctuation.Guid))
|
||||
{
|
||||
if (!CheckShiftKeyOnly(&_PreservedKey_Punctuation.TSFPreservedKeyTable))
|
||||
else if (IsEqualGUID(rguid, _PreservedKey_Punctuation.Guid))
|
||||
{
|
||||
if (!isPrevalidated &&
|
||||
!CheckShiftKeyOnly(&_PreservedKey_Punctuation.TSFPreservedKeyTable))
|
||||
{
|
||||
*pIsEaten = FALSE;
|
||||
return;
|
||||
@@ -1264,13 +1327,13 @@ HRESULT CCompositionProcessorEngine::CompartmentCallback(_In_ void *pv, REFGUID
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
ITfThreadMgr *pThreadMgr = nullptr;
|
||||
HRESULT hr =
|
||||
CoCreateInstance(CLSID_TF_ThreadMgr, nullptr, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, (void **)&pThreadMgr);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
ITfThreadMgr *pThreadMgr = fakeThis->_pOwnerThreadMgr;
|
||||
if (!pThreadMgr)
|
||||
{
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
pThreadMgr->AddRef();
|
||||
const HWND ownerWindow = fakeThis->_ownerMsgWndHandle;
|
||||
|
||||
if (IsEqualGUID(guidCompartment, Global::MetasequoiaIMEGuidCompartmentDoubleSingleByte))
|
||||
{
|
||||
@@ -1280,7 +1343,10 @@ HRESULT CCompositionProcessorEngine::CompartmentCallback(_In_ void *pv, REFGUID
|
||||
CompartmentDoubleSingleByte._GetCompartmentBOOL(isDoubleSingleByte);
|
||||
// 0: halfwidth, 1: fullwidth
|
||||
// SendDoubleSingleByteSwitchEventToUIProcessViaNamedPipe(isDoubleSingleByte ? 1 : 0);
|
||||
PostMessage(Global::msgWndHandle, WM_UpdateDoubleSingleByte, (WPARAM)(isDoubleSingleByte ? 1 : 0), 0);
|
||||
if (ownerWindow && IsWindow(ownerWindow))
|
||||
{
|
||||
PostMessage(ownerWindow, WM_UpdateDoubleSingleByte, (WPARAM)(isDoubleSingleByte ? 1 : 0), 0);
|
||||
}
|
||||
fakeThis->PrivateCompartmentsUpdated(pThreadMgr);
|
||||
}
|
||||
else if (IsEqualGUID(guidCompartment, Global::MetasequoiaIMEGuidCompartmentPunctuation))
|
||||
@@ -1290,7 +1356,10 @@ HRESULT CCompositionProcessorEngine::CompartmentCallback(_In_ void *pv, REFGUID
|
||||
Global::MetasequoiaIMEGuidCompartmentPunctuation);
|
||||
CompartmentPunctuation._GetCompartmentBOOL(isPunctuation);
|
||||
// SendPuncSwitchEventToUIProcessViaNamedPipe(isPunctuation ? 1 : 0);
|
||||
PostMessage(Global::msgWndHandle, WM_UpdatePuncMode, (WPARAM)(isPunctuation ? 1 : 0), 0);
|
||||
if (ownerWindow && IsWindow(ownerWindow))
|
||||
{
|
||||
PostMessage(ownerWindow, WM_UpdatePuncMode, (WPARAM)(isPunctuation ? 1 : 0), 0);
|
||||
}
|
||||
fakeThis->PrivateCompartmentsUpdated(pThreadMgr);
|
||||
}
|
||||
else if (IsEqualGUID(guidCompartment, GUID_COMPARTMENT_KEYBOARD_INPUTMODE_CONVERSION) ||
|
||||
@@ -1319,7 +1388,10 @@ HRESULT CCompositionProcessorEngine::CompartmentCallback(_In_ void *pv, REFGUID
|
||||
}
|
||||
|
||||
// SendIMESwitchEventToUIProcessViaNamedPipe(isOpen ? 1 : 0);
|
||||
PostMessage(Global::msgWndHandle, WM_UpdateIMEStatus, (WPARAM)(isOpen ? 1 : 0), 0);
|
||||
if (ownerWindow && IsWindow(ownerWindow))
|
||||
{
|
||||
PostMessage(ownerWindow, WM_UpdateIMEStatus, (WPARAM)(isOpen ? 1 : 0), 0);
|
||||
}
|
||||
|
||||
fakeThis->KeyboardOpenCompartmentUpdated(pThreadMgr);
|
||||
}
|
||||
@@ -1742,11 +1814,52 @@ void CCompositionProcessorEngine::SetDefaultCandidateTextFont()
|
||||
//
|
||||
// CCompositionProcessorEngine
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
// CCompositionProcessorEngine::IsVirtualKeyNeed
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
BOOL CCompositionProcessorEngine::IsVirtualKeyNeedForFreshComposition(
|
||||
UINT uCode, _In_reads_(1) WCHAR *pwch,
|
||||
_Out_opt_ _KEYSTROKE_STATE *pKeyState)
|
||||
{
|
||||
if (pKeyState)
|
||||
{
|
||||
pKeyState->Category = CATEGORY_NONE;
|
||||
pKeyState->Function = FUNCTION_NONE;
|
||||
}
|
||||
|
||||
// Classify against an actually empty composition. This path is used while
|
||||
// the old focus session is still being cancelled, so none of its candidate
|
||||
// mode, wildcard flags, or virtual-key buffer may affect the first key in
|
||||
// the replacement session.
|
||||
if (IsManualPinyinSeparatorInComposition(pwch ? *pwch : 0, FALSE,
|
||||
CANDIDATE_NONE, 0))
|
||||
{
|
||||
if (pKeyState)
|
||||
{
|
||||
pKeyState->Category = CATEGORY_COMPOSING;
|
||||
pKeyState->Function = FUNCTION_INPUT;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
if (IsVirtualKeyKeystrokeComposition(uCode, pKeyState, FUNCTION_INPUT))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
if (pwch && IsWildcard() && IsWildcardChar(*pwch) &&
|
||||
!IsDisableWildcardAtFirst())
|
||||
{
|
||||
if (pKeyState)
|
||||
{
|
||||
pKeyState->Category = CATEGORY_COMPOSING;
|
||||
pKeyState->Function = FUNCTION_INPUT;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
// CCompositionProcessorEngine::IsVirtualKeyNeed
|
||||
//
|
||||
// Test virtual key code need to the Composition Processor Engine.
|
||||
// param
|
||||
|
||||
@@ -7,11 +7,19 @@
|
||||
#include "Compartment.h"
|
||||
#include "define.h"
|
||||
|
||||
class CCompositionProcessorEngine
|
||||
{
|
||||
class CCompositionProcessorEngine
|
||||
{
|
||||
friend class CMetasequoiaIME;
|
||||
|
||||
public:
|
||||
public:
|
||||
enum class PreservedKeyAction
|
||||
{
|
||||
None,
|
||||
ToggleImeMode,
|
||||
ToggleDoubleSingleByteMode,
|
||||
TogglePunctuationMode
|
||||
};
|
||||
|
||||
CCompositionProcessorEngine(void);
|
||||
~CCompositionProcessorEngine(void);
|
||||
|
||||
@@ -30,8 +38,10 @@ class CCompositionProcessorEngine
|
||||
return MAKELCID(_langid, SORT_DEFAULT);
|
||||
}
|
||||
|
||||
BOOL IsVirtualKeyNeed(UINT uCode, _In_reads_(1) WCHAR *pwch, BOOL fComposing, CANDIDATE_MODE candidateMode,
|
||||
BOOL hasCandidateWithWildcard, _Out_opt_ _KEYSTROKE_STATE *pKeyState);
|
||||
BOOL IsVirtualKeyNeed(UINT uCode, _In_reads_(1) WCHAR *pwch, BOOL fComposing, CANDIDATE_MODE candidateMode,
|
||||
BOOL hasCandidateWithWildcard, _Out_opt_ _KEYSTROKE_STATE *pKeyState);
|
||||
BOOL IsVirtualKeyNeedForFreshComposition(UINT uCode, _In_reads_(1) WCHAR *pwch,
|
||||
_Out_opt_ _KEYSTROKE_STATE *pKeyState);
|
||||
|
||||
BOOL AddVirtualKey(WCHAR wch);
|
||||
void RemoveVirtualKey(DWORD_PTR dwIndex);
|
||||
@@ -64,9 +74,12 @@ class CCompositionProcessorEngine
|
||||
BOOL isIncrementalWordSearch, BOOL isWildcardSearch //
|
||||
);
|
||||
|
||||
// Preserved key handler
|
||||
void OnPreservedKey(ITfContext *pContext, REFGUID rguid, _Out_ BOOL *pIsEaten, _In_ ITfThreadMgr *pThreadMgr,
|
||||
TfClientId tfClientId, BOOL *pNeedToggleIMEMode);
|
||||
// Preserved key handler
|
||||
BOOL IsPreservedKeyEligible(REFGUID rguid);
|
||||
PreservedKeyAction GetPreservedKeyAction(REFGUID rguid) const;
|
||||
void OnPreservedKey(ITfContext *pContext, REFGUID rguid, _Out_ BOOL *pIsEaten, _In_ ITfThreadMgr *pThreadMgr,
|
||||
TfClientId tfClientId, BOOL *pNeedToggleIMEMode,
|
||||
BOOL isPrevalidated = FALSE);
|
||||
|
||||
// Toggle IME Mode
|
||||
void ToggleIMEMode(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId);
|
||||
@@ -228,8 +241,10 @@ class CCompositionProcessorEngine
|
||||
CCompartment *_pCompartmentConversion;
|
||||
CCompartmentEventSink *_pCompartmentConversionEventSink;
|
||||
CCompartmentEventSink *_pCompartmentKeyboardOpenEventSink;
|
||||
CCompartmentEventSink *_pCompartmentDoubleSingleByteEventSink;
|
||||
CCompartmentEventSink *_pCompartmentPunctuationEventSink;
|
||||
CCompartmentEventSink *_pCompartmentDoubleSingleByteEventSink;
|
||||
CCompartmentEventSink *_pCompartmentPunctuationEventSink;
|
||||
ITfThreadMgr *_pOwnerThreadMgr;
|
||||
HWND _ownerMsgWndHandle;
|
||||
|
||||
// Configuration data
|
||||
BOOL _isWildcard : 1;
|
||||
|
||||
@@ -23,17 +23,37 @@
|
||||
class CEndCompositionEditSession : public CEditSessionBase
|
||||
{
|
||||
public:
|
||||
CEndCompositionEditSession(_In_ CMetasequoiaIME *pTextService, _In_ ITfContext *pContext)
|
||||
: CEditSessionBase(pTextService, pContext)
|
||||
CEndCompositionEditSession(_In_ CMetasequoiaIME *pTextService, _In_ ITfContext *pContext,
|
||||
_In_ ITfComposition *expectedComposition, uint64_t focusToken,
|
||||
bool bypassFocusValidation)
|
||||
: CEditSessionBase(pTextService, pContext), _expectedComposition(expectedComposition),
|
||||
_focusToken(focusToken), _bypassFocusValidation(bypassFocusValidation)
|
||||
{
|
||||
_expectedComposition->AddRef();
|
||||
}
|
||||
|
||||
~CEndCompositionEditSession() override
|
||||
{
|
||||
_expectedComposition->Release();
|
||||
}
|
||||
|
||||
// ITfEditSession
|
||||
STDMETHODIMP DoEditSession(TfEditCookie ec)
|
||||
{
|
||||
if (!_pTextService->_IsCompositionCurrent(_expectedComposition) ||
|
||||
(!_bypassFocusValidation &&
|
||||
!_pTextService->_IsFocusSessionCurrent(_focusToken, _pContext)))
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
_pTextService->_TerminateComposition(ec, _pContext, TRUE);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
ITfComposition *_expectedComposition;
|
||||
uint64_t _focusToken;
|
||||
bool _bypassFocusValidation;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@@ -55,13 +75,27 @@ void CMetasequoiaIME::_TerminateComposition(TfEditCookie ec, _In_ ITfContext *pC
|
||||
|
||||
if (_pComposition != nullptr)
|
||||
{
|
||||
// Every operation below crosses COM and can re-enter
|
||||
// OnCompositionTerminated. Hold exact local references and only clear
|
||||
// service ownership if the same composition is still current after
|
||||
// the call returns.
|
||||
ITfComposition *terminatingComposition = _pComposition;
|
||||
terminatingComposition->AddRef();
|
||||
ITfContext *ownerContext = _pContext;
|
||||
if (ownerContext)
|
||||
{
|
||||
ownerContext->AddRef();
|
||||
}
|
||||
|
||||
// remove the display attribute from the composition range.
|
||||
PerfTimer clearDisplayAttrTimer;
|
||||
_ClearCompositionDisplayAttributes(ec, pContext);
|
||||
_ClearCompositionDisplayAttributes(ec, pContext,
|
||||
terminatingComposition);
|
||||
double clearDisplayAttrElapsedMs = clearDisplayAttrTimer.ElapsedMs();
|
||||
|
||||
PerfTimer endCompositionTimer;
|
||||
if (FAILED(_pComposition->EndComposition(ec)))
|
||||
const HRESULT endResult = terminatingComposition->EndComposition(ec);
|
||||
if (FAILED(endResult) && _pComposition == terminatingComposition)
|
||||
{
|
||||
// if we fail to EndComposition, then we need to close the reverse reading window.
|
||||
_DeleteCandidateList(TRUE, pContext);
|
||||
@@ -69,19 +103,37 @@ void CMetasequoiaIME::_TerminateComposition(TfEditCookie ec, _In_ ITfContext *pC
|
||||
double endCompositionElapsedMs = endCompositionTimer.ElapsedMs();
|
||||
|
||||
PerfTimer releaseCompositionTimer;
|
||||
_pComposition->Release();
|
||||
_pComposition = nullptr;
|
||||
double releaseCompositionElapsedMs = releaseCompositionTimer.ElapsedMs();
|
||||
|
||||
double releaseContextElapsedMs = 0;
|
||||
if (_pContext)
|
||||
if (_pComposition == terminatingComposition)
|
||||
{
|
||||
PerfTimer releaseContextTimer;
|
||||
_pContext->Release();
|
||||
_pContext = nullptr;
|
||||
releaseContextElapsedMs = releaseContextTimer.ElapsedMs();
|
||||
}
|
||||
_pComposition->Release();
|
||||
_pComposition = nullptr;
|
||||
uint64_t nextEpoch =
|
||||
_compositionEpoch.fetch_add(1, std::memory_order_acq_rel) + 1;
|
||||
if (nextEpoch == 0)
|
||||
{
|
||||
_compositionEpoch.fetch_add(1, std::memory_order_acq_rel);
|
||||
}
|
||||
|
||||
// Clear the context only as part of releasing this exact
|
||||
// composition. A re-entrant callback may already have installed a
|
||||
// newer composition on the same ITfContext pointer.
|
||||
if (_pContext == ownerContext && _pContext)
|
||||
{
|
||||
PerfTimer releaseContextTimer;
|
||||
_pContext->Release();
|
||||
_pContext = nullptr;
|
||||
double releaseContextElapsedMs =
|
||||
releaseContextTimer.ElapsedMs();
|
||||
}
|
||||
}
|
||||
double releaseCompositionElapsedMs =
|
||||
releaseCompositionTimer.ElapsedMs();
|
||||
|
||||
if (ownerContext)
|
||||
{
|
||||
ownerContext->Release();
|
||||
}
|
||||
terminatingComposition->Release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,17 +143,36 @@ void CMetasequoiaIME::_TerminateComposition(TfEditCookie ec, _In_ ITfContext *pC
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void CMetasequoiaIME::_EndComposition(_In_opt_ ITfContext *pContext)
|
||||
HRESULT CMetasequoiaIME::_EndComposition(_In_opt_ ITfContext *pContext,
|
||||
_In_opt_ ITfComposition *expectedComposition,
|
||||
bool bypassFocusValidation)
|
||||
{
|
||||
CEndCompositionEditSession *pEditSession = new (std::nothrow) CEndCompositionEditSession(this, pContext);
|
||||
ITfComposition *target = expectedComposition ? expectedComposition : _pComposition;
|
||||
if (pContext == nullptr || target == nullptr)
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
CEndCompositionEditSession *pEditSession =
|
||||
new (std::nothrow) CEndCompositionEditSession(this, pContext, target,
|
||||
_CaptureFocusSessionToken(), bypassFocusValidation);
|
||||
HRESULT hr = S_OK;
|
||||
PerfTimer timer;
|
||||
|
||||
if (nullptr != pEditSession)
|
||||
{
|
||||
PerfTimer requestTimer;
|
||||
pContext->RequestEditSession(_tfClientId, pEditSession, TF_ES_ASYNCDONTCARE | TF_ES_READWRITE, &hr);
|
||||
HRESULT editSessionHr = E_FAIL;
|
||||
const HRESULT requestHr =
|
||||
pContext->RequestEditSession(_tfClientId, pEditSession,
|
||||
TF_ES_ASYNCDONTCARE | TF_ES_READWRITE,
|
||||
&editSessionHr);
|
||||
hr = FAILED(requestHr) ? requestHr : editSessionHr;
|
||||
double requestElapsedMs = requestTimer.ElapsedMs();
|
||||
pEditSession->Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = E_OUTOFMEMORY;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
@@ -8,13 +8,21 @@
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void CMetasequoiaIME::_ClearCompositionDisplayAttributes(TfEditCookie ec, _In_ ITfContext *pContext)
|
||||
{
|
||||
ITfRange *pRangeComposition = nullptr;
|
||||
ITfProperty *pDisplayAttributeProperty = nullptr;
|
||||
|
||||
// get the compositon range.
|
||||
if (FAILED(_pComposition->GetRange(&pRangeComposition)))
|
||||
void CMetasequoiaIME::_ClearCompositionDisplayAttributes(
|
||||
TfEditCookie ec, _In_ ITfContext *pContext,
|
||||
_In_opt_ ITfComposition *expectedComposition)
|
||||
{
|
||||
ITfRange *pRangeComposition = nullptr;
|
||||
ITfProperty *pDisplayAttributeProperty = nullptr;
|
||||
ITfComposition *composition =
|
||||
expectedComposition ? expectedComposition : _pComposition;
|
||||
if (composition == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// get the compositon range.
|
||||
if (FAILED(composition->GetRange(&pRangeComposition)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "Private.h"
|
||||
#include "Globals.h"
|
||||
#include "MetasequoiaIME.h"
|
||||
#include "Globals.h"
|
||||
#include "MetasequoiaIME.h"
|
||||
#include "Ipc.h"
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
@@ -9,10 +10,14 @@
|
||||
// Called by the system whenever anyone releases a write-access document lock.
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
STDAPI CMetasequoiaIME::OnEndEdit(__RPC__in_opt ITfContext *pContext, TfEditCookie ecReadOnly,
|
||||
__RPC__in_opt ITfEditRecord *pEditRecord)
|
||||
{
|
||||
BOOL isSelectionChanged;
|
||||
STDAPI CMetasequoiaIME::OnEndEdit(__RPC__in_opt ITfContext *pContext, TfEditCookie ecReadOnly,
|
||||
__RPC__in_opt ITfEditRecord *pEditRecord)
|
||||
{
|
||||
if (!IsNamedpipeFocusStateOwner(this) || !Global::g_connected)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
BOOL isSelectionChanged;
|
||||
|
||||
//
|
||||
// did the selection change?
|
||||
@@ -49,7 +54,10 @@ STDAPI CMetasequoiaIME::OnEndEdit(__RPC__in_opt ITfContext *pContext, TfEditCook
|
||||
{
|
||||
if (!_IsRangeCovered(ecReadOnly, tfSelection.range, pRangeComposition))
|
||||
{
|
||||
_EndComposition(pContext);
|
||||
if (FAILED(_EndComposition(pContext)))
|
||||
{
|
||||
MarkNamedpipeSessionDirtyForOwner(this);
|
||||
}
|
||||
}
|
||||
|
||||
pRangeComposition->Release();
|
||||
@@ -70,7 +78,7 @@ STDAPI CMetasequoiaIME::OnEndEdit(__RPC__in_opt ITfContext *pContext, TfEditCook
|
||||
// Always release any previous sink.
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
BOOL CMetasequoiaIME::_InitTextEditSink(_In_ ITfDocumentMgr *pDocMgr)
|
||||
BOOL CMetasequoiaIME::_InitTextEditSink(_In_opt_ ITfDocumentMgr *pDocMgr)
|
||||
{
|
||||
ITfSource *pSource = nullptr;
|
||||
BOOL ret = TRUE;
|
||||
|
||||
@@ -9,7 +9,7 @@ inline std::wstring ZEN_BROWSER = L"zen.exe";
|
||||
// inline std::unordered_set<std::wstring> VSCodeSeries = {L"Code.exe", L"Code - Insiders.exe", L"VSCodium.exe"};
|
||||
// inline bool IsVSCodeLike = false;
|
||||
inline LONG INVALID_Y = -100000;
|
||||
inline float DpiScale = 1.0f;
|
||||
inline thread_local float DpiScale = 1.0f;
|
||||
} // namespace Global
|
||||
|
||||
namespace GlobalSettings
|
||||
@@ -27,25 +27,29 @@ constexpr std::string_view Pinyin = "pinyin";
|
||||
constexpr std::string_view Cand = "cand";
|
||||
} // namespace TsfPreeditStyle
|
||||
|
||||
inline std::string &tsfPreeditStyleStorage()
|
||||
{
|
||||
static std::string style = std::string(TsfPreeditStyle::Raw); // 默认的原始按键序列
|
||||
return style;
|
||||
}
|
||||
|
||||
inline const std::string &getTsfPreeditStyle()
|
||||
{
|
||||
static const std::string style = std::string(TsfPreeditStyle::Raw); // 默认的原始按键序列
|
||||
return style;
|
||||
return tsfPreeditStyleStorage();
|
||||
}
|
||||
|
||||
inline void setTsfPreeditStyle(std::string_view newStyle)
|
||||
{
|
||||
static std::string currentStyle = std::string(TsfPreeditStyle::Raw);
|
||||
currentStyle = std::string(newStyle);
|
||||
tsfPreeditStyleStorage() = std::string(newStyle);
|
||||
}
|
||||
} // namespace GlobalSettings
|
||||
|
||||
namespace GlobalIme
|
||||
{
|
||||
inline std::wstring word_for_creating_word = L"";
|
||||
inline thread_local std::wstring word_for_creating_word = L"";
|
||||
}
|
||||
|
||||
namespace Global
|
||||
{
|
||||
inline HWND msgWndHandle = nullptr;
|
||||
}
|
||||
inline thread_local HWND msgWndHandle = nullptr;
|
||||
}
|
||||
@@ -364,12 +364,12 @@ BOOL CheckModifiers(UINT modCurrent, UINT mod)
|
||||
// [31] Transition state
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
USHORT ModifiersValue = 0;
|
||||
BOOL IsShiftKeyDownOnly = FALSE;
|
||||
BOOL IsControlKeyDownOnly = FALSE;
|
||||
BOOL IsAltKeyDownOnly = FALSE;
|
||||
BOOL PureShiftKeyDown = FALSE;
|
||||
BOOL PureShiftKeyUp = FALSE;
|
||||
thread_local USHORT ModifiersValue = 0;
|
||||
thread_local BOOL IsShiftKeyDownOnly = FALSE;
|
||||
thread_local BOOL IsControlKeyDownOnly = FALSE;
|
||||
thread_local BOOL IsAltKeyDownOnly = FALSE;
|
||||
thread_local BOOL PureShiftKeyDown = FALSE;
|
||||
thread_local BOOL PureShiftKeyUp = FALSE;
|
||||
|
||||
BOOL UpdateModifiers(WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
|
||||
@@ -114,12 +114,12 @@ LRESULT CALLBACK ThreadKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
|
||||
BOOL CheckModifiers(UINT uModCurrent, UINT uMod);
|
||||
BOOL UpdateModifiers(WPARAM wParam, LPARAM lParam);
|
||||
|
||||
extern USHORT ModifiersValue;
|
||||
extern BOOL IsShiftKeyDownOnly;
|
||||
extern BOOL IsControlKeyDownOnly;
|
||||
extern BOOL IsAltKeyDownOnly;
|
||||
extern BOOL PureShiftKeyDown;
|
||||
extern BOOL PureShiftKeyUp;
|
||||
extern thread_local USHORT ModifiersValue;
|
||||
extern thread_local BOOL IsShiftKeyDownOnly;
|
||||
extern thread_local BOOL IsControlKeyDownOnly;
|
||||
extern thread_local BOOL IsAltKeyDownOnly;
|
||||
extern thread_local BOOL PureShiftKeyDown;
|
||||
extern thread_local BOOL PureShiftKeyUp;
|
||||
|
||||
extern const GUID MetasequoiaIMEGuidCompartmentDoubleSingleByte;
|
||||
extern const GUID MetasequoiaIMEGuidCompartmentPunctuation;
|
||||
|
||||
+1225
-222
File diff suppressed because it is too large.
Load diff
+202
-32
@@ -3,7 +3,9 @@
|
||||
#include "KeyHandlerEditSession.h"
|
||||
#include "MetasequoiaIMEBaseStructure.h"
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
@@ -27,6 +29,10 @@ const DWORD WM_AsyncFinalizeCandidate = WM_USER + 11;
|
||||
const DWORD WM_AsyncPunctuationCommit = WM_USER + 12;
|
||||
const DWORD WM_AsyncNumberCandidateCommit = WM_USER + 13;
|
||||
const DWORD WM_AsyncServerCandidateKey = WM_USER + 14;
|
||||
const DWORD WM_IpcWorkerDisconnected = WM_USER + 15;
|
||||
const DWORD WM_IpcReconnect = WM_USER + 16;
|
||||
const DWORD WM_IpcSessionDirty = WM_USER + 17;
|
||||
const DWORD WM_DrainDeferredKeyDown = WM_USER + 18;
|
||||
LRESULT CALLBACK CMetasequoiaIME_WindowProc(HWND wndHandle, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
class CMetasequoiaIME : public ITfTextInputProcessorEx,
|
||||
@@ -128,20 +134,24 @@ class CMetasequoiaIME : public ITfTextInputProcessorEx,
|
||||
HRESULT _HandleToogleIMEMode(TfEditCookie ec, _In_ ITfContext *pContext);
|
||||
|
||||
// key event handlers for composition object.
|
||||
HRESULT _HandleCompositionInput(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch);
|
||||
HRESULT _HandleCompositionInput(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch, uint64_t requestId);
|
||||
HRESULT _HandleCompositionFinalize(TfEditCookie ec, _In_ ITfContext *pContext, BOOL fCandidateList);
|
||||
HRESULT _HandleCompositionConvert(TfEditCookie ec, _In_ ITfContext *pContext, BOOL isWildcardSearch);
|
||||
HRESULT _HandleCompositionBackspace(TfEditCookie ec, _In_ ITfContext *pContext);
|
||||
HRESULT _HandleCompositionBackspace(TfEditCookie ec, _In_ ITfContext *pContext, uint64_t requestId);
|
||||
HRESULT _HandleCompositionArrowKey(TfEditCookie ec, _In_ ITfContext *pContext, KEYSTROKE_FUNCTION keyFunction);
|
||||
HRESULT _HandleCompositionPunctuation(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch);
|
||||
HRESULT _HandleCompositionPunctuation(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch, uint64_t requestId,
|
||||
const std::wstring &prefetchedText);
|
||||
HRESULT _HandleCompositionDoubleSingleByte(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch);
|
||||
|
||||
// key event handlers for candidate object.
|
||||
HRESULT _HandleCandidateFinalize(TfEditCookie ec, _In_ ITfContext *pContext);
|
||||
HRESULT _HandleCandidateFinalize(TfEditCookie ec, _In_ ITfContext *pContext, uint64_t requestId,
|
||||
const std::wstring &prefetchedText);
|
||||
HRESULT _HandleCandidateFinalizeForVKReturn(TfEditCookie ec, _In_ ITfContext *pContext);
|
||||
HRESULT _HandleCandidateConvert(TfEditCookie ec, _In_ ITfContext *pContext);
|
||||
HRESULT _HandleCandidateConvert(TfEditCookie ec, _In_ ITfContext *pContext, uint64_t requestId,
|
||||
const std::wstring &prefetchedText);
|
||||
HRESULT _HandleCandidateArrowKey(TfEditCookie ec, _In_ ITfContext *pContext, _In_ KEYSTROKE_FUNCTION keyFunction);
|
||||
HRESULT _HandleCandidateSelectByNumber(TfEditCookie ec, _In_ ITfContext *pContext, _In_ UINT uCode);
|
||||
HRESULT _HandleCandidateSelectByNumber(TfEditCookie ec, _In_ ITfContext *pContext, _In_ UINT uCode,
|
||||
uint64_t requestId, const std::wstring &prefetchedText);
|
||||
|
||||
BOOL _IsSecureMode(void)
|
||||
{
|
||||
@@ -156,10 +166,26 @@ class CMetasequoiaIME : public ITfTextInputProcessorEx,
|
||||
return (_dwActivateFlags & TF_TMF_IMMERSIVEMODE) ? TRUE : FALSE;
|
||||
};
|
||||
|
||||
CCompositionProcessorEngine *GetCompositionProcessorEngine()
|
||||
{
|
||||
return (_pCompositionProcessorEngine);
|
||||
};
|
||||
CCompositionProcessorEngine *GetCompositionProcessorEngine()
|
||||
{
|
||||
return (_pCompositionProcessorEngine);
|
||||
};
|
||||
|
||||
// Async TSF edit sessions may be granted after a focus transition. Bind
|
||||
// every text-producing session to the exact focus token that scheduled it
|
||||
// so an old worker/key message cannot commit into the next document.
|
||||
uint64_t _CaptureFocusSessionToken() const;
|
||||
bool _IsFocusSessionCurrent(uint64_t focusToken, _In_opt_ ITfContext *expectedContext = nullptr) const;
|
||||
uint64_t _CaptureCompositionEpoch() const;
|
||||
bool _IsCompositionEpochCurrent(uint64_t compositionEpoch) const;
|
||||
bool _IsCompositionCurrent(_In_opt_ ITfComposition *expectedComposition) const;
|
||||
bool _IsLocalSessionResetCurrent(UINT resetToken) const;
|
||||
void _CompleteLocalSessionReset(UINT resetToken);
|
||||
bool _IsDeferredKeyReplayCurrent(uint64_t replayToken,
|
||||
uint64_t focusGeneration,
|
||||
_In_opt_ ITfContext *expectedContext) const;
|
||||
void _CompleteDeferredKeyReplay(uint64_t replayToken);
|
||||
void _RetryDeferredKeyReplay(uint64_t replayToken);
|
||||
|
||||
// comless helpers
|
||||
static HRESULT CMetasequoiaIME::CreateInstance(REFCLSID rclsid, REFIID riid, _Outptr_result_maybenull_ LPVOID *ppv,
|
||||
@@ -171,25 +197,126 @@ class CMetasequoiaIME : public ITfTextInputProcessorEx,
|
||||
DWORD cchPath);
|
||||
|
||||
static void IpcWorkerThread(CMetasequoiaIME *pIME);
|
||||
void _QueuePendingCommitCandidate(_In_z_ const WCHAR *pCommitString);
|
||||
std::wstring _TakePendingCommitCandidate();
|
||||
void _QueuePendingServerCandidate(UINT msgType, _In_z_ const WCHAR *pCandidateString);
|
||||
bool _TakePendingServerCandidate(_Out_ UINT *pMsgType, _Out_ std::wstring *pCandidateString);
|
||||
void _QueuePendingPunctuationCommitText(_In_z_ const WCHAR *pCommitString);
|
||||
std::wstring _TakePendingPunctuationCommitText();
|
||||
void _ScheduleCandidatePresenterCleanup(_In_ CCandidateListUIPresenter *pPresenter);
|
||||
void _DrainPendingCandidatePresenterCleanup();
|
||||
|
||||
private:
|
||||
// functions for the composition object.
|
||||
HRESULT _HandleCompositionInputWorker(_In_ CCompositionProcessorEngine *pCompositionProcessorEngine,
|
||||
TfEditCookie ec, _In_ ITfContext *pContext);
|
||||
HRESULT _HandleCompositionInputWorker(_In_ CCompositionProcessorEngine *pCompositionProcessorEngine,
|
||||
TfEditCookie ec, _In_ ITfContext *pContext, uint64_t requestId);
|
||||
HRESULT _CreateAndStartCandidate(_In_ CCompositionProcessorEngine *pCompositionProcessorEngine, TfEditCookie ec,
|
||||
_In_ ITfContext *pContext);
|
||||
HRESULT _HandleCandidateWorker(TfEditCookie ec, _In_ ITfContext *pContext);
|
||||
|
||||
void _StartComposition(_In_ ITfContext *pContext);
|
||||
void _EndComposition(_In_opt_ ITfContext *pContext);
|
||||
HRESULT _HandleCandidateWorker(TfEditCookie ec, _In_ ITfContext *pContext, uint64_t requestId,
|
||||
const std::wstring &prefetchedText);
|
||||
|
||||
struct AsyncKeyRequest
|
||||
{
|
||||
UINT message = 0;
|
||||
UINT code = 0;
|
||||
WCHAR wch = L'\0';
|
||||
uint64_t requestId = UINT64_MAX;
|
||||
uint64_t focusToken = 0;
|
||||
uint64_t compositionEpoch = 0;
|
||||
uint64_t deferredReplayToken = 0;
|
||||
std::wstring prefetchedText;
|
||||
};
|
||||
bool _PostAsyncKeyRequest(UINT message, UINT code, WCHAR wch, uint64_t requestId,
|
||||
std::wstring prefetchedText = {},
|
||||
uint64_t expectedFocusToken = 0,
|
||||
uint64_t expectedCompositionEpoch = 0,
|
||||
uint64_t deferredReplayToken = 0);
|
||||
bool _TakeAsyncKeyRequest(UINT message, UINT token, _Out_ AsyncKeyRequest &request);
|
||||
struct WorkerCandidateCommit
|
||||
{
|
||||
std::wstring text;
|
||||
uint64_t focusToken = 0;
|
||||
uint64_t compositionEpoch = 0;
|
||||
};
|
||||
bool _PostServerCandidateCommit(_In_z_ const WCHAR *candidateText);
|
||||
bool _TakeServerCandidateCommit(UINT token, _Out_ WorkerCandidateCommit &request);
|
||||
struct WorkerCompartmentSwitch
|
||||
{
|
||||
UINT messageType = 0;
|
||||
uint64_t focusToken = 0;
|
||||
uint64_t compositionEpoch = 0;
|
||||
};
|
||||
bool _PostWorkerCompartmentSwitch(UINT messageType, uint64_t focusToken);
|
||||
bool _TakeWorkerCompartmentSwitch(UINT token, _Out_ WorkerCompartmentSwitch &request);
|
||||
void _ClearAsyncKeyRequests();
|
||||
void _ClearPendingIpcRequests();
|
||||
void _RequestLocalSessionReset(_In_opt_ ITfContext *preferredContext, UINT resetToken);
|
||||
bool _CaptureWindowsTextInputHostFocusLoss();
|
||||
|
||||
struct DeferredKeyDown
|
||||
{
|
||||
enum class Kind
|
||||
{
|
||||
KeyDown,
|
||||
PreservedKey,
|
||||
ApplicationText
|
||||
};
|
||||
|
||||
Kind kind = Kind::KeyDown;
|
||||
ITfContext *context = nullptr;
|
||||
WPARAM wParam = 0;
|
||||
LPARAM lParam = 0;
|
||||
WCHAR translatedWch = L'\0';
|
||||
UINT modifiersDown = 0;
|
||||
_KEYSTROKE_STATE keyState = {};
|
||||
GUID preservedKey = {};
|
||||
uint64_t focusGeneration = 0;
|
||||
UINT replayAttempts = 0;
|
||||
bool preservedApplied = false;
|
||||
};
|
||||
enum class KeyDownDispatchResult
|
||||
{
|
||||
Complete,
|
||||
Retry,
|
||||
AwaitingCompletion
|
||||
};
|
||||
bool _HasDeferredKeyBarrier() const;
|
||||
bool _DeferredKeyQueueHasCapacity() const;
|
||||
void _EnsureDeferredKeyProjection();
|
||||
void _ApplyDeferredKeyProjection(const _KEYSTROKE_STATE &keyState);
|
||||
void _ApplyDeferredPreservedKeyProjection(REFGUID preservedKey);
|
||||
bool _RefreshDeferredRecoveryPrefix(_In_ ITfContext *pContext);
|
||||
void _ArmDeferredRecoveryForTransport(_In_opt_ ITfContext *pContext);
|
||||
bool _ClassifyDeferredKeyDown(_In_ ITfContext *pContext, WPARAM wParam,
|
||||
_In_opt_ const WCHAR *translatedWch,
|
||||
_In_opt_ const UINT *modifiersDown,
|
||||
_Out_ WCHAR *classifiedWch,
|
||||
_Out_ UINT *classifiedCode,
|
||||
_Out_ _KEYSTROKE_STATE *keyState);
|
||||
bool _QueueDeferredKeyDown(_In_ ITfContext *pContext, WPARAM wParam, LPARAM lParam,
|
||||
WCHAR translatedWch, UINT modifiersDown,
|
||||
const _KEYSTROKE_STATE &keyState);
|
||||
bool _QueueDeferredPreservedKey(_In_ ITfContext *pContext, REFGUID preservedKey);
|
||||
void _ClearDeferredKeyDowns();
|
||||
void _ScheduleDeferredKeyDownDrain();
|
||||
void _DrainOneDeferredKeyDown();
|
||||
HRESULT _RequestDeferredApplicationTextEditSession(
|
||||
_In_ ITfContext *pContext, WCHAR wch, uint64_t expectedFocusToken,
|
||||
uint64_t expectedFocusGeneration, uint64_t deferredReplayToken);
|
||||
KeyDownDispatchResult _DispatchKeyDown(_In_ ITfContext *pContext, WPARAM wParam,
|
||||
LPARAM lParam, _Out_ BOOL *pIsEaten,
|
||||
_In_opt_ const WCHAR *translatedWch,
|
||||
_In_opt_ const UINT *modifiersDown,
|
||||
_In_opt_ const _KEYSTROKE_STATE *prevalidatedKeyState,
|
||||
bool canDefer,
|
||||
uint64_t expectedFocusGeneration,
|
||||
uint64_t deferredReplayToken = 0);
|
||||
void _DispatchPreservedKey(_In_ ITfContext *pContext, REFGUID preservedKey,
|
||||
_Out_ BOOL *pIsEaten,
|
||||
uint64_t expectedFocusGeneration,
|
||||
bool isPrevalidated,
|
||||
uint64_t deferredReplayToken = 0);
|
||||
|
||||
void _StartComposition(_In_ ITfContext *pContext);
|
||||
HRESULT _EndComposition(_In_opt_ ITfContext *pContext,
|
||||
_In_opt_ ITfComposition *expectedComposition = nullptr,
|
||||
bool bypassFocusValidation = false);
|
||||
BOOL _IsComposing();
|
||||
BOOL _IsKeyboardDisabled();
|
||||
|
||||
@@ -209,24 +336,36 @@ class CMetasequoiaIME : public ITfTextInputProcessorEx,
|
||||
HRESULT _RemoveDummyCompositionForComposing(TfEditCookie ec, _In_ ITfComposition *pComposition);
|
||||
|
||||
// Invoke key handler edit session
|
||||
HRESULT _InvokeKeyHandler(_In_ ITfContext *pContext, UINT code, WCHAR wch, DWORD flags, _KEYSTROKE_STATE keyState);
|
||||
HRESULT _RequestDirectPunctuationEditSession(_In_ ITfContext *pContext, UINT code, WCHAR wch);
|
||||
HRESULT _InvokeKeyHandler(_In_ ITfContext *pContext, UINT code, WCHAR wch, DWORD flags,
|
||||
_KEYSTROKE_STATE keyState, uint64_t requestId,
|
||||
std::wstring prefetchedText = {}, UINT localResetToken = 0,
|
||||
uint64_t expectedCompositionEpoch = 0,
|
||||
uint64_t expectedFocusToken = 0,
|
||||
uint64_t deferredReplayToken = 0);
|
||||
HRESULT _RequestDirectPunctuationEditSession(_In_ ITfContext *pContext, UINT code, WCHAR wch,
|
||||
uint64_t requestId, std::wstring prefetchedText,
|
||||
uint64_t expectedFocusToken = 0,
|
||||
uint64_t expectedCompositionEpoch = 0,
|
||||
uint64_t deferredReplayToken = 0);
|
||||
|
||||
// function for the language property
|
||||
BOOL _SetCompositionLanguage(TfEditCookie ec, _In_ ITfContext *pContext);
|
||||
|
||||
// function for the display attribute
|
||||
void _ClearCompositionDisplayAttributes(TfEditCookie ec, _In_ ITfContext *pContext);
|
||||
void _ClearCompositionDisplayAttributes(
|
||||
TfEditCookie ec, _In_ ITfContext *pContext,
|
||||
_In_opt_ ITfComposition *expectedComposition = nullptr);
|
||||
BOOL _SetCompositionDisplayAttributes(TfEditCookie ec, _In_ ITfContext *pContext, TfGuidAtom gaDisplayAttribute);
|
||||
BOOL _SetCompositionDisplayAttributesForRange(TfEditCookie ec, _In_ ITfContext *pContext,
|
||||
_In_ ITfRange *pRangeComposition,
|
||||
TfGuidAtom gaDisplayAttribute);
|
||||
BOOL _InitDisplayAttributeGuidAtom();
|
||||
|
||||
BOOL _InitThreadMgrEventSink();
|
||||
void _UninitThreadMgrEventSink();
|
||||
|
||||
BOOL _InitTextEditSink(_In_ ITfDocumentMgr *pDocMgr);
|
||||
BOOL _InitThreadMgrEventSink();
|
||||
void _UninitThreadMgrEventSink();
|
||||
void _HandleFocusedContextStackChange(_In_opt_ ITfContext *changedContext);
|
||||
|
||||
BOOL _InitTextEditSink(_In_opt_ ITfDocumentMgr *pDocMgr);
|
||||
|
||||
void _UpdateLanguageBarOnSetFocus(_In_ ITfDocumentMgr *pDocMgrFocus);
|
||||
|
||||
@@ -236,8 +375,10 @@ class CMetasequoiaIME : public ITfTextInputProcessorEx,
|
||||
BOOL _InitActiveLanguageProfileNotifySink();
|
||||
void _UninitActiveLanguageProfileNotifySink();
|
||||
|
||||
BOOL _IsKeyEaten(_In_ ITfContext *pContext, UINT codeIn, _Out_ UINT *pCodeOut, _Out_writes_(1) WCHAR *pwch,
|
||||
_Out_opt_ _KEYSTROKE_STATE *pKeyState);
|
||||
BOOL _IsKeyEaten(_In_ ITfContext *pContext, UINT codeIn, _Out_ UINT *pCodeOut, _Out_writes_(1) WCHAR *pwch,
|
||||
_Out_opt_ _KEYSTROKE_STATE *pKeyState,
|
||||
_In_opt_ const WCHAR *translatedWch = nullptr,
|
||||
bool freshCompositionState = false);
|
||||
|
||||
BOOL _IsRangeCovered(TfEditCookie ec, _In_ ITfRange *pRangeTest, _In_ ITfRange *pRangeCover);
|
||||
VOID _DeleteCandidateList(BOOL fForce, _In_opt_ ITfContext *pContext);
|
||||
@@ -299,15 +440,44 @@ class CMetasequoiaIME : public ITfTextInputProcessorEx,
|
||||
|
||||
HWND _msgWndHandle;
|
||||
std::thread *_pIpcThread;
|
||||
HANDLE _hToTsfWorkerThreadPipe;
|
||||
std::atomic<HANDLE> _hToTsfWorkerThreadPipe;
|
||||
std::atomic<UINT> _workerPipeGeneration;
|
||||
HANDLE _ipcStopEvent;
|
||||
std::atomic<bool> _shouldStopIpcThread;
|
||||
UINT _ipcReconnectDelayMs;
|
||||
std::mutex _pendingCommitCandidateMutex;
|
||||
std::wstring _pendingCommitCandidate;
|
||||
std::wstring _pendingPunctuationCommitText;
|
||||
std::map<UINT, WorkerCandidateCommit> _pendingServerCommitMessages;
|
||||
std::map<UINT, WorkerCompartmentSwitch> _pendingWorkerSwitchMessages;
|
||||
std::map<UINT, AsyncKeyRequest> _pendingAsyncKeyMessages;
|
||||
std::atomic<bool> _workerCommitReady;
|
||||
std::atomic<uint64_t> _expectedWorkerFocusToken;
|
||||
std::atomic<uint64_t> _acknowledgedWorkerFocusToken;
|
||||
std::atomic<uint64_t> _compositionEpoch;
|
||||
std::atomic<bool> _localSessionResetPending;
|
||||
std::atomic<UINT> _localSessionResetToken;
|
||||
bool _localResetEditSessionQueued;
|
||||
UINT _queuedLocalResetToken;
|
||||
bool _focusResetPending;
|
||||
bool _activationRequired;
|
||||
bool _focusLostToWindowsTextInputHost;
|
||||
bool _hasPendingServerCandidate;
|
||||
UINT _pendingServerCandidateMsgType;
|
||||
std::wstring _pendingServerCandidateString;
|
||||
std::deque<CCandidateListUIPresenter *> _pendingCandidatePresenterCleanup;
|
||||
std::deque<DeferredKeyDown> _deferredKeyDowns;
|
||||
std::deque<DeferredKeyDown> _deferredAppliedPrefix;
|
||||
DeferredKeyDown _deferredKeyInFlight;
|
||||
bool _hasDeferredKeyInFlight;
|
||||
uint64_t _deferredKeyReplayToken;
|
||||
uint64_t _nextDeferredKeyReplayToken;
|
||||
bool _deferredKeyProjectionValid;
|
||||
bool _deferredProjectedImeOpen;
|
||||
bool _deferredProjectedPunctuationOpen;
|
||||
bool _deferredProjectedDoubleSingleByteOpen;
|
||||
size_t _deferredProjectedInputLength;
|
||||
bool _deferredProjectedCandidateActive;
|
||||
uint64_t _deferredKeyFocusGeneration;
|
||||
bool _deferredKeyDrainPosted;
|
||||
|
||||
LONG _refCount;
|
||||
|
||||
|
||||
+926
-388
File diff suppressed because it is too large.
Load diff
+75
-16
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -12,6 +13,8 @@ inline const wchar_t *FANY_IME_NAMED_PIPE = L"\\\\.\\pipe\\FanyImeNamedPipe";
|
||||
inline const wchar_t *FANY_IME_TO_TSF_NAMED_PIPE = L"\\\\.\\pipe\\FanyImeToTsfNamedPipe";
|
||||
inline const wchar_t *FANY_IME_TO_TSF_WORKER_THREAD_NAMED_PIPE = L"\\\\.\\pipe\\FanyImeToTsfWorkerThreadNamedPipe";
|
||||
inline const wchar_t *FANY_IME_AUX_NAMED_PIPE = L"\\\\.\\pipe\\FanyImeAuxNamedPipe";
|
||||
inline constexpr uint64_t FANY_IME_UNSOLICITED_REQUEST_ID = 0;
|
||||
inline constexpr uint64_t FANY_IME_NO_REQUEST_ID = UINT64_MAX;
|
||||
|
||||
inline const std::vector<std::wstring> FANY_IME_EVENT_ARRAY = {
|
||||
L"FanyImeKeyEvent", // Event sent to UI process to notify time to update UI by new pinyin_string
|
||||
@@ -56,6 +59,7 @@ struct FanyImeNamedpipeData
|
||||
{
|
||||
UINT event_type;
|
||||
uint64_t client_id = 0;
|
||||
uint64_t request_id = 0;
|
||||
UINT keycode;
|
||||
WCHAR wch;
|
||||
UINT modifiers_down = 0;
|
||||
@@ -73,7 +77,9 @@ constexpr UINT MoveCandidateWnd = 3;
|
||||
constexpr UINT LangbarRightClick = 4;
|
||||
constexpr UINT ClientHello = 10;
|
||||
constexpr UINT ClientActivated = 11;
|
||||
constexpr UINT ClientDeactivated = 12;
|
||||
constexpr UINT ClientDeactivated = 12; // terminal TIP switch; toolbar hidden
|
||||
constexpr UINT StatusSnapshot = 13;
|
||||
constexpr UINT ClientSuspended = 14; // temporary focus route reset; toolbar kept
|
||||
constexpr UINT IMESwitch = 7;
|
||||
constexpr UINT PuncSwitch = 8;
|
||||
constexpr UINT DoubleSingleByteSwitch = 9;
|
||||
@@ -102,6 +108,7 @@ struct FanyImePipeHello
|
||||
struct FanyImeNamedpipeDataToTsf
|
||||
{
|
||||
UINT msg_type;
|
||||
uint64_t request_id = 0;
|
||||
wchar_t candidate_string[200]; // 200 chars at most
|
||||
};
|
||||
|
||||
@@ -122,13 +129,53 @@ struct FanyImeNamedpipeDataToTsfWorkerThread
|
||||
wchar_t data[200];
|
||||
};
|
||||
|
||||
static_assert(sizeof(WCHAR) == 2, "The IPC ABI requires 16-bit WCHAR.");
|
||||
static_assert(offsetof(FanyImeNamedpipeData, client_id) == 8);
|
||||
static_assert(offsetof(FanyImeNamedpipeData, request_id) == 16);
|
||||
static_assert(offsetof(FanyImeNamedpipeData, keycode) == 24);
|
||||
static_assert(offsetof(FanyImeNamedpipeData, pinyin_string) == 48);
|
||||
static_assert(sizeof(FanyImeNamedpipeData) == 304);
|
||||
static_assert(offsetof(FanyImeNamedpipeDataToTsf, request_id) == 8);
|
||||
static_assert(offsetof(FanyImeNamedpipeDataToTsf, candidate_string) == 16);
|
||||
static_assert(sizeof(FanyImeNamedpipeDataToTsf) == 416);
|
||||
static_assert(sizeof(FanyImePipeHello) == 16);
|
||||
static_assert(sizeof(FanyImeNamedpipeDataToTsfWorkerThread) == 404);
|
||||
|
||||
enum class KeyEventSendResult
|
||||
{
|
||||
Sent,
|
||||
DefinitelyNotSent,
|
||||
DeliveryAmbiguous,
|
||||
};
|
||||
|
||||
int InitIpc();
|
||||
int InitNamedpipe();
|
||||
int ConnectToAllNamedpipe();
|
||||
int ConnectToTsfNamedpipe();
|
||||
int CloseIpc();
|
||||
int CloseNamedpipe();
|
||||
void ResetNamedpipeReplyState();
|
||||
HANDLE GetToTsfWorkerThreadNamedpipe();
|
||||
void BindNamedpipeFocusState(_In_ const void *owner,
|
||||
_In_opt_ bool *focusResetPending, _In_opt_ bool *activationRequired,
|
||||
_In_opt_ std::atomic<uint64_t> *expectedWorkerFocusToken,
|
||||
_In_opt_ std::atomic<bool> *localSessionResetPending,
|
||||
_In_opt_ std::atomic<UINT> *localSessionResetToken,
|
||||
_In_opt_ std::atomic<bool> *workerCommitReady,
|
||||
_In_opt_ std::atomic<uint64_t> *acknowledgedWorkerFocusToken,
|
||||
_In_opt_ std::atomic<HANDLE> *workerPipeHandle,
|
||||
_In_opt_ std::atomic<UINT> *workerPipeGeneration);
|
||||
void UnbindNamedpipeFocusState(_In_ const void *owner);
|
||||
bool IsNamedpipeFocusStateOwner(_In_ const void *owner);
|
||||
UINT BeginNamedpipeLocalSessionReset();
|
||||
void InvalidateNamedpipeWorkerGeneration();
|
||||
void MarkNamedpipeFocusLost();
|
||||
void RequireNamedpipeFocusActivation();
|
||||
void MarkNamedpipeSessionDirty();
|
||||
bool MarkNamedpipeSessionDirtyForOwner(_In_ const void *owner);
|
||||
bool EnsureNamedpipeFocusSessionActivated();
|
||||
bool FlushNamedpipeFocusSessionReset();
|
||||
bool FlushNamedpipeImeDeactivation();
|
||||
|
||||
//
|
||||
// For shared memory
|
||||
@@ -142,15 +189,17 @@ int WriteDataToSharedMemory( //
|
||||
const std::wstring &pinyin_string, //
|
||||
UINT write_flag //
|
||||
);
|
||||
int SendKeyEventToUIProcess();
|
||||
KeyEventSendResult SendKeyEventToUIProcess(_Out_opt_ uint64_t *requestId = nullptr);
|
||||
int SendHideCandidateWndEventToUIProcess();
|
||||
int SendShowCandidateWndEventToUIProcess();
|
||||
int SendMoveCandidateWndEventToUIProcess();
|
||||
int SendLangbarRightClickEventToUIProcess(const RECT *prcArea);
|
||||
int SendIMEActivationEventToUIProcessViaNamedPipe();
|
||||
int SendIMEDeactivationEventToUIProcessViaNamedPipe();
|
||||
int SendClientActivatedEventToServerViaNamedPipe();
|
||||
int SendClientActivatedEventToServerViaNamedPipe(uint64_t focusToken);
|
||||
int SendClientDeactivatedEventToServerViaNamedPipe();
|
||||
int SendClientSuspendedEventToServerViaNamedPipe();
|
||||
int SendIMEStatusSnapshotToUIProcessViaNamedPipe(bool kbdIsOpen, bool fullwidthIsOpen, bool puncIsOpen);
|
||||
int SendIMEStatusEventToUIProcessViaNamedPipe(bool kbdIsOpen, bool fullwidthIsOpen, bool puncIsOpen);
|
||||
int SendIMESwitchEventToUIProcessViaNamedPipe(UINT uImeStatus);
|
||||
int SendPuncSwitchEventToUIProcessViaNamedPipe(BOOL isPunc);
|
||||
@@ -170,14 +219,14 @@ int WriteDataToNamedPipe( //
|
||||
const std::wstring &pinyin_string, //
|
||||
UINT write_flag //
|
||||
);
|
||||
int SendKeyEventToUIProcessViaNamedPipe();
|
||||
KeyEventSendResult SendKeyEventToUIProcessViaNamedPipe(_Out_opt_ uint64_t *requestId = nullptr);
|
||||
int SendHideCandidateWndEventToUIProcessViaNamedPipe();
|
||||
int SendShowCandidateWndEventToUIProcessViaNamedPipe();
|
||||
int SendMoveCandidateWndEventToUIProcessViaNamedPipe();
|
||||
int SendLangbarRightClickEventToUIProcessViaNamedPipe(const RECT *prcArea);
|
||||
void ClearNamedpipeDataIfExists(bool force = false);
|
||||
struct FanyImeNamedpipeDataToTsf *TryReadDataFromServerPipeWithTimeout();
|
||||
struct FanyImeNamedpipeDataToTsf *ReadDataFromServerViaNamedPipe();
|
||||
struct FanyImeNamedpipeDataToTsf *TryReadDataFromServerPipeWithTimeout(uint64_t expectedRequestId);
|
||||
struct FanyImeNamedpipeDataToTsf *ReadDataFromServerViaNamedPipe(uint64_t expectedRequestId);
|
||||
|
||||
//
|
||||
// Modifiers:
|
||||
@@ -188,17 +237,17 @@ struct FanyImeNamedpipeDataToTsf *ReadDataFromServerViaNamedPipe();
|
||||
//
|
||||
namespace Global
|
||||
{
|
||||
inline UINT Keycode = 0;
|
||||
inline WCHAR wch = L'\0';
|
||||
inline UINT ModifiersDown = 0;
|
||||
inline int Point[2] = {100, 100};
|
||||
inline int PinyinLength = 0;
|
||||
inline std::wstring PinyinString = L"";
|
||||
inline thread_local UINT Keycode = 0;
|
||||
inline thread_local WCHAR wch = L'\0';
|
||||
inline thread_local UINT ModifiersDown = 0;
|
||||
inline thread_local int Point[2] = {100, 100};
|
||||
inline thread_local int PinyinLength = 0;
|
||||
inline thread_local std::wstring PinyinString = L"";
|
||||
|
||||
inline int firefox_like_cnt = 0; // Apps like firefox, e.g. firefox, zen...
|
||||
inline std::wstring current_process_name = L"";
|
||||
inline thread_local int firefox_like_cnt = 0; // Apps like firefox, e.g. firefox, zen...
|
||||
inline thread_local std::wstring current_process_name = L"";
|
||||
|
||||
inline wchar_t app_name[512] = {0};
|
||||
inline thread_local wchar_t app_name[512] = {0};
|
||||
|
||||
namespace DataFromServerMsgType
|
||||
{
|
||||
@@ -211,6 +260,12 @@ constexpr UINT MoveSelectionPrevious = 5;
|
||||
constexpr UINT MoveSelectionNext = 6;
|
||||
constexpr UINT MovePagePrevious = 7;
|
||||
constexpr UINT MovePageNext = 8;
|
||||
// Reverse-pipe registration acknowledgement. This frame is consumed during
|
||||
// the ToTsf pipe handshake and is never exposed as a key reply.
|
||||
constexpr UINT PipeReady = 9;
|
||||
// Local-only result. It is never sent over the pipe and must never be
|
||||
// interpreted as candidate text to commit.
|
||||
constexpr UINT TransportUnavailable = static_cast<UINT>(-1);
|
||||
} // namespace DataFromServerMsgType
|
||||
|
||||
namespace DataToTsfWorkerThreadMsgType
|
||||
@@ -223,9 +278,13 @@ constexpr UINT SwitchToFullwidth = 4;
|
||||
constexpr UINT SwitchToHalfwidth = 5;
|
||||
constexpr UINT CommitCurCandidate = 6;
|
||||
constexpr UINT PagingCommaPeriodChanged = 7;
|
||||
constexpr UINT FocusSessionReady = 8;
|
||||
// Worker-endpoint registration acknowledgement. It is consumed before the
|
||||
// handle is published to IpcWorkerThread and before Main can be opened.
|
||||
constexpr UINT PipeReady = 9;
|
||||
} // namespace DataToTsfWorkerThreadMsgType
|
||||
|
||||
inline std::atomic_bool PagingCommaPeriodEnabled{false};
|
||||
inline bool g_connected = false;
|
||||
inline thread_local bool g_connected = false;
|
||||
|
||||
} // namespace Global
|
||||
+1236
-68
File diff suppressed because it is too large.
Load diff
+66
-67
@@ -16,13 +16,7 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
std::wstring g_toggleImeFallbackBuffer;
|
||||
|
||||
bool IsTimeoutSentinelCandidate(UINT msgType, const WCHAR *pCandidateString)
|
||||
{
|
||||
return msgType == Global::DataFromServerMsgType::Normal && pCandidateString != nullptr &&
|
||||
wcscmp(pCandidateString, L"T") == 0;
|
||||
}
|
||||
thread_local std::wstring g_toggleImeFallbackBuffer;
|
||||
|
||||
DWORD_PTR MapRawCaretToPreedit(const CStringRange &raw, DWORD_PTR rawCaret, const std::wstring &preedit,
|
||||
size_t prefixLength)
|
||||
@@ -102,9 +96,12 @@ VOID CMetasequoiaIME::_DeleteCandidateList(BOOL isForce, _In_opt_ ITfContext *pC
|
||||
|
||||
CCompositionProcessorEngine *pCompositionProcessorEngine = nullptr;
|
||||
pCompositionProcessorEngine = _pCompositionProcessorEngine;
|
||||
PerfTimer purgeTimer;
|
||||
pCompositionProcessorEngine->PurgeVirtualKey();
|
||||
double purgeElapsedMs = purgeTimer.ElapsedMs();
|
||||
if (pCompositionProcessorEngine)
|
||||
{
|
||||
PerfTimer purgeTimer;
|
||||
pCompositionProcessorEngine->PurgeVirtualKey();
|
||||
double purgeElapsedMs = purgeTimer.ElapsedMs();
|
||||
}
|
||||
|
||||
double endCandidateElapsedMs = 0;
|
||||
if (_pCandidateListUIPresenter)
|
||||
@@ -147,7 +144,6 @@ HRESULT CMetasequoiaIME::_HandleComplete(TfEditCookie ec, _In_ ITfContext *pCont
|
||||
_TerminateComposition(ec, pContext);
|
||||
double terminateElapsedMs = terminateTimer.ElapsedMs();
|
||||
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -191,7 +187,6 @@ HRESULT CMetasequoiaIME::_HandleCancel(TfEditCookie ec, _In_ ITfContext *pContex
|
||||
_TerminateComposition(ec, pContext);
|
||||
double terminateElapsedMs = terminateTimer.ElapsedMs();
|
||||
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -242,8 +237,10 @@ HRESULT CMetasequoiaIME::_HandleToogleIMEMode(TfEditCookie ec, _In_ ITfContext *
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
HRESULT CMetasequoiaIME::_HandleCompositionInput(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch)
|
||||
HRESULT CMetasequoiaIME::_HandleCompositionInput(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch,
|
||||
uint64_t requestId)
|
||||
{
|
||||
HRESULT workerResult = S_OK;
|
||||
ITfRange *pRangeComposition = nullptr;
|
||||
TF_SELECTION tfSelection;
|
||||
ULONG fetched = 0;
|
||||
@@ -286,11 +283,12 @@ HRESULT CMetasequoiaIME::_HandleCompositionInput(TfEditCookie ec, _In_ ITfContex
|
||||
pCompositionProcessorEngine->AddVirtualKey(wch);
|
||||
g_toggleImeFallbackBuffer.push_back(wch);
|
||||
|
||||
_HandleCompositionInputWorker(pCompositionProcessorEngine, ec, pContext);
|
||||
workerResult = _HandleCompositionInputWorker(
|
||||
pCompositionProcessorEngine, ec, pContext, requestId);
|
||||
|
||||
Exit:
|
||||
tfSelection.range->Release();
|
||||
return S_OK;
|
||||
return workerResult;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
@@ -302,7 +300,8 @@ Exit:
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
HRESULT CMetasequoiaIME::_HandleCompositionInputWorker(_In_ CCompositionProcessorEngine *pCompositionProcessorEngine,
|
||||
TfEditCookie ec, _In_ ITfContext *pContext)
|
||||
TfEditCookie ec, _In_ ITfContext *pContext,
|
||||
uint64_t requestId)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
PerfTimer timer;
|
||||
@@ -339,11 +338,17 @@ HRESULT CMetasequoiaIME::_HandleCompositionInputWorker(_In_ CCompositionProcesso
|
||||
// readingStr = L"";
|
||||
curReadingStr.Set(readingStr.c_str(), readingStr.length());
|
||||
|
||||
if (GlobalSettings::getTsfPreeditStyle() == GlobalSettings::TsfPreeditStyle::Pinyin)
|
||||
if (GlobalSettings::getTsfPreeditStyle() == GlobalSettings::TsfPreeditStyle::Pinyin &&
|
||||
requestId != FANY_IME_NO_REQUEST_ID)
|
||||
{
|
||||
PerfTimer preeditPipeTimer;
|
||||
struct FanyImeNamedpipeDataToTsf *receivedData = TryReadDataFromServerPipeWithTimeout();
|
||||
struct FanyImeNamedpipeDataToTsf *receivedData = TryReadDataFromServerPipeWithTimeout(requestId);
|
||||
preeditPipeElapsedMs += preeditPipeTimer.ElapsedMs();
|
||||
if (receivedData->msg_type ==
|
||||
Global::DataFromServerMsgType::TransportUnavailable)
|
||||
{
|
||||
return HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE);
|
||||
}
|
||||
if (receivedData->msg_type == Global::DataFromServerMsgType::Preedit)
|
||||
{
|
||||
curReadingStr.Set(receivedData->candidate_string, wcslen(receivedData->candidate_string));
|
||||
@@ -516,9 +521,6 @@ HRESULT CMetasequoiaIME::_HandleCompositionFinalize(TfEditCookie ec, _In_ ITfCon
|
||||
HRESULT hr = S_OK;
|
||||
PerfTimer timer;
|
||||
double finalizeCandidateElapsedMs = 0;
|
||||
double getSelectionElapsedMs = 0;
|
||||
double getRangeElapsedMs = 0;
|
||||
double endCompositionElapsedMs = 0;
|
||||
|
||||
if (isCandidateList && _pCandidateListUIPresenter)
|
||||
{
|
||||
@@ -543,39 +545,10 @@ HRESULT CMetasequoiaIME::_HandleCompositionFinalize(TfEditCookie ec, _In_ ITfCon
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Finalize current text store strings
|
||||
if (_IsComposing())
|
||||
{
|
||||
ULONG fetched = 0;
|
||||
TF_SELECTION tfSelection;
|
||||
|
||||
PerfTimer getSelectionTimer;
|
||||
if (FAILED(pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched)) || fetched != 1)
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
getSelectionElapsedMs = getSelectionTimer.ElapsedMs();
|
||||
|
||||
ITfRange *pRangeComposition = nullptr;
|
||||
PerfTimer getRangeTimer;
|
||||
if (SUCCEEDED(_pComposition->GetRange(&pRangeComposition)))
|
||||
{
|
||||
getRangeElapsedMs = getRangeTimer.ElapsedMs();
|
||||
if (_IsRangeCovered(ec, tfSelection.range, pRangeComposition))
|
||||
{
|
||||
PerfTimer endCompositionTimer;
|
||||
_EndComposition(pContext);
|
||||
endCompositionElapsedMs = endCompositionTimer.ElapsedMs();
|
||||
}
|
||||
|
||||
pRangeComposition->Release();
|
||||
}
|
||||
|
||||
tfSelection.range->Release();
|
||||
}
|
||||
}
|
||||
// For the non-candidate path, the current composition text is already in
|
||||
// the text store. _HandleCancel below owns the exact write cookie and
|
||||
// terminates it synchronously; requesting a nested edit session here can
|
||||
// legitimately fail with TF_E_SYNCHRONOUS and is redundant.
|
||||
|
||||
PerfTimer cancelTimer;
|
||||
_HandleCancel(ec, pContext);
|
||||
@@ -682,8 +655,10 @@ HRESULT CMetasequoiaIME::_HandleCompositionConvert(TfEditCookie ec, _In_ ITfCont
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
HRESULT CMetasequoiaIME::_HandleCompositionBackspace(TfEditCookie ec, _In_ ITfContext *pContext)
|
||||
HRESULT CMetasequoiaIME::_HandleCompositionBackspace(TfEditCookie ec, _In_ ITfContext *pContext,
|
||||
uint64_t requestId)
|
||||
{
|
||||
HRESULT workerResult = S_OK;
|
||||
ITfRange *pRangeComposition = nullptr;
|
||||
TF_SELECTION tfSelection;
|
||||
ULONG fetched = 0;
|
||||
@@ -733,7 +708,8 @@ HRESULT CMetasequoiaIME::_HandleCompositionBackspace(TfEditCookie ec, _In_ ITfCo
|
||||
|
||||
if (pCompositionProcessorEngine->GetVirtualKeyLength())
|
||||
{
|
||||
_HandleCompositionInputWorker(pCompositionProcessorEngine, ec, pContext);
|
||||
workerResult = _HandleCompositionInputWorker(
|
||||
pCompositionProcessorEngine, ec, pContext, requestId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -743,7 +719,7 @@ HRESULT CMetasequoiaIME::_HandleCompositionBackspace(TfEditCookie ec, _In_ ITfCo
|
||||
|
||||
Exit:
|
||||
tfSelection.range->Release();
|
||||
return S_OK;
|
||||
return workerResult;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
@@ -836,7 +812,9 @@ Exit:
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
HRESULT CMetasequoiaIME::_HandleCompositionPunctuation(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch)
|
||||
HRESULT CMetasequoiaIME::_HandleCompositionPunctuation(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch,
|
||||
uint64_t requestId,
|
||||
const std::wstring &prefetchedText)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
PerfTimer timer;
|
||||
@@ -846,7 +824,7 @@ HRESULT CMetasequoiaIME::_HandleCompositionPunctuation(TfEditCookie ec, _In_ ITf
|
||||
CCompositionProcessorEngine *pCompositionProcessorEngine = nullptr;
|
||||
pCompositionProcessorEngine = _pCompositionProcessorEngine;
|
||||
|
||||
std::wstring pendingPunctuationCommitText = _TakePendingPunctuationCommitText();
|
||||
std::wstring pendingPunctuationCommitText = prefetchedText;
|
||||
const bool hasPendingPunctuationCommitText = !pendingPunctuationCommitText.empty();
|
||||
std::wstring punctuationStr;
|
||||
if (hasPendingPunctuationCommitText)
|
||||
@@ -869,13 +847,13 @@ HRESULT CMetasequoiaIME::_HandleCompositionPunctuation(TfEditCookie ec, _In_ ITf
|
||||
{
|
||||
/* 这里我们不需要考虑下标超出范围,因为我们总是可以取到第一个候选词 */
|
||||
PerfTimer pipeReadTimer;
|
||||
struct FanyImeNamedpipeDataToTsf *receivedData = TryReadDataFromServerPipeWithTimeout();
|
||||
struct FanyImeNamedpipeDataToTsf *receivedData = TryReadDataFromServerPipeWithTimeout(requestId);
|
||||
pipeReadElapsedMs = pipeReadTimer.ElapsedMs();
|
||||
if (IsTimeoutSentinelCandidate(receivedData->msg_type, receivedData->candidate_string))
|
||||
|
||||
if (receivedData->msg_type ==
|
||||
Global::DataFromServerMsgType::TransportUnavailable)
|
||||
{
|
||||
PerfTimer retryTimer;
|
||||
receivedData = TryReadDataFromServerPipeWithTimeout();
|
||||
pipeReadElapsedMs += retryTimer.ElapsedMs();
|
||||
return HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE);
|
||||
}
|
||||
|
||||
// The Server is authoritative for configurable candidate-navigation
|
||||
@@ -980,7 +958,11 @@ HRESULT CMetasequoiaIME::_HandleCompositionDoubleSingleByte(TfEditCookie ec, _In
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
HRESULT CMetasequoiaIME::_InvokeKeyHandler(_In_ ITfContext *pContext, UINT code, WCHAR wch, DWORD flags,
|
||||
_KEYSTROKE_STATE keyState)
|
||||
_KEYSTROKE_STATE keyState, uint64_t requestId,
|
||||
std::wstring prefetchedText, UINT localResetToken,
|
||||
uint64_t expectedCompositionEpoch,
|
||||
uint64_t expectedFocusToken,
|
||||
uint64_t deferredReplayToken)
|
||||
{
|
||||
flags;
|
||||
|
||||
@@ -992,10 +974,22 @@ HRESULT CMetasequoiaIME::_InvokeKeyHandler(_In_ ITfContext *pContext, UINT code,
|
||||
LARGE_INTEGER requestStartQpc;
|
||||
QueryPerformanceCounter(&requestStartQpc);
|
||||
PerfTimer allocTimer;
|
||||
pEditSession = new (std::nothrow) CKeyHandlerEditSession(this, pContext, code, wch, keyState, requestStartQpc);
|
||||
pEditSession = new (std::nothrow) CKeyHandlerEditSession(this, pContext, code, wch, keyState, requestId,
|
||||
requestStartQpc, std::move(prefetchedText),
|
||||
localResetToken == 0
|
||||
? (expectedFocusToken != 0
|
||||
? expectedFocusToken
|
||||
: _CaptureFocusSessionToken())
|
||||
: 0,
|
||||
localResetToken, expectedCompositionEpoch,
|
||||
deferredReplayToken);
|
||||
double allocElapsedMs = allocTimer.ElapsedMs();
|
||||
if (pEditSession == nullptr)
|
||||
{
|
||||
if (deferredReplayToken != 0)
|
||||
{
|
||||
_RetryDeferredKeyReplay(deferredReplayToken);
|
||||
}
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
@@ -1009,7 +1003,12 @@ HRESULT CMetasequoiaIME::_InvokeKeyHandler(_In_ ITfContext *pContext, UINT code,
|
||||
HRESULT requestHr = pContext->RequestEditSession(_tfClientId, pEditSession, TF_ES_ASYNCDONTCARE | TF_ES_READWRITE,
|
||||
&editSessionHr);
|
||||
double requestCallElapsedMs = requestCallTimer.ElapsedMs();
|
||||
hr = requestHr;
|
||||
hr = FAILED(requestHr) ? requestHr : editSessionHr;
|
||||
if ((FAILED(requestHr) || FAILED(editSessionHr)) &&
|
||||
deferredReplayToken != 0)
|
||||
{
|
||||
_RetryDeferredKeyReplay(deferredReplayToken);
|
||||
}
|
||||
|
||||
PerfTimer releaseTimer;
|
||||
pEditSession->Release();
|
||||
|
||||
@@ -23,6 +23,50 @@
|
||||
|
||||
STDAPI CKeyHandlerEditSession::DoEditSession(TfEditCookie ec)
|
||||
{
|
||||
struct DeferredReplayCompletion
|
||||
{
|
||||
CMetasequoiaIME *textService;
|
||||
uint64_t token;
|
||||
bool applied = false;
|
||||
~DeferredReplayCompletion()
|
||||
{
|
||||
if (textService && token != 0)
|
||||
{
|
||||
if (applied)
|
||||
{
|
||||
textService->_CompleteDeferredKeyReplay(token);
|
||||
}
|
||||
else
|
||||
{
|
||||
textService->_RetryDeferredKeyReplay(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
} deferredReplayCompletion{_pTextService, _deferredReplayToken, false};
|
||||
|
||||
const bool isLocalReset = _localResetToken != 0;
|
||||
if (isLocalReset)
|
||||
{
|
||||
if (!_pTextService->_IsLocalSessionResetCurrent(_localResetToken))
|
||||
{
|
||||
// A newer focus/reset token may supersede this granted session.
|
||||
// It must still retire its exact queue slot so Complete can queue
|
||||
// the newer token; returning without bookkeeping would gate every
|
||||
// subsequent key forever.
|
||||
_pTextService->_CompleteLocalSessionReset(_localResetToken);
|
||||
return S_FALSE;
|
||||
}
|
||||
}
|
||||
else if (!_pTextService->_IsFocusSessionCurrent(_focusToken, _pContext))
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
if (!isLocalReset && _expectedCompositionEpoch != 0 &&
|
||||
!_pTextService->_IsCompositionEpochCurrent(_expectedCompositionEpoch))
|
||||
{
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT hResult = S_OK;
|
||||
PerfTimer doEditSessionTimer;
|
||||
LARGE_INTEGER freq, nowQpc;
|
||||
@@ -32,18 +76,28 @@ STDAPI CKeyHandlerEditSession::DoEditSession(TfEditCookie ec)
|
||||
static_cast<double>(freq.QuadPart);
|
||||
|
||||
CKeyStateCategoryFactory *pKeyStateCategoryFactory = CKeyStateCategoryFactory::Instance();
|
||||
CKeyStateCategory *pKeyStateCategory =
|
||||
pKeyStateCategoryFactory->MakeKeyStateCategory(_KeyState.Category, _pTextService);
|
||||
CKeyStateCategory *pKeyStateCategory = pKeyStateCategoryFactory
|
||||
? pKeyStateCategoryFactory->MakeKeyStateCategory(
|
||||
_KeyState.Category, _pTextService)
|
||||
: nullptr;
|
||||
|
||||
if (pKeyStateCategory)
|
||||
{
|
||||
KeyHandlerEditSessionDTO keyHandlerEditSessioDTO(ec, _pContext, _uCode, _wch, _KeyState.Function);
|
||||
KeyHandlerEditSessionDTO keyHandlerEditSessioDTO(ec, _pContext, _uCode, _wch, _KeyState.Function,
|
||||
_requestId, _prefetchedText);
|
||||
hResult = pKeyStateCategory->KeyStateHandler(_KeyState.Function, keyHandlerEditSessioDTO);
|
||||
deferredReplayCompletion.applied = hResult == S_OK;
|
||||
|
||||
pKeyStateCategory->Release();
|
||||
}
|
||||
if (pKeyStateCategoryFactory)
|
||||
{
|
||||
pKeyStateCategoryFactory->Release();
|
||||
}
|
||||
|
||||
|
||||
if (isLocalReset)
|
||||
{
|
||||
_pTextService->_CompleteLocalSessionReset(_localResetToken);
|
||||
}
|
||||
return hResult;
|
||||
}
|
||||
@@ -2,17 +2,29 @@
|
||||
|
||||
#include "EditSession.h"
|
||||
#include "Globals.h"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
class CKeyHandlerEditSession : public CEditSessionBase
|
||||
{
|
||||
public:
|
||||
CKeyHandlerEditSession(CMetasequoiaIME *pTextService, ITfContext *pContext, UINT uCode, WCHAR wch,
|
||||
_KEYSTROKE_STATE keyState, LARGE_INTEGER requestStartQpc = {})
|
||||
_KEYSTROKE_STATE keyState, uint64_t requestId, LARGE_INTEGER requestStartQpc = {},
|
||||
std::wstring prefetchedText = {}, uint64_t focusToken = 0,
|
||||
UINT localResetToken = 0, uint64_t expectedCompositionEpoch = 0,
|
||||
uint64_t deferredReplayToken = 0)
|
||||
: CEditSessionBase(pTextService, pContext)
|
||||
{
|
||||
_uCode = uCode;
|
||||
_wch = wch;
|
||||
_KeyState = keyState;
|
||||
_requestId = requestId;
|
||||
_focusToken = focusToken;
|
||||
_localResetToken = localResetToken;
|
||||
_expectedCompositionEpoch = expectedCompositionEpoch;
|
||||
_deferredReplayToken = deferredReplayToken;
|
||||
_prefetchedText = std::move(prefetchedText);
|
||||
_requestStartQpc = requestStartQpc;
|
||||
if (_requestStartQpc.QuadPart == 0)
|
||||
{
|
||||
@@ -27,5 +39,11 @@ class CKeyHandlerEditSession : public CEditSessionBase
|
||||
UINT _uCode; // virtual key code
|
||||
WCHAR _wch; // character code
|
||||
_KEYSTROKE_STATE _KeyState; // key function regarding virtual key
|
||||
uint64_t _requestId;
|
||||
uint64_t _focusToken;
|
||||
UINT _localResetToken;
|
||||
uint64_t _expectedCompositionEpoch;
|
||||
uint64_t _deferredReplayToken;
|
||||
std::wstring _prefetchedText;
|
||||
LARGE_INTEGER _requestStartQpc;
|
||||
};
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "MetasequoiaIMEBaseStructure.h"
|
||||
#include <debugapi.h>
|
||||
|
||||
CKeyStateCategoryFactory *CKeyStateCategoryFactory::_instance;
|
||||
thread_local CKeyStateCategoryFactory *CKeyStateCategoryFactory::_instance = nullptr;
|
||||
|
||||
CKeyStateCategoryFactory::CKeyStateCategoryFactory()
|
||||
{
|
||||
@@ -248,13 +248,13 @@ CKeyStateComposing::CKeyStateComposing(_In_ CMetasequoiaIME *pTextService) : CKe
|
||||
|
||||
HRESULT CKeyStateComposing::HandleKeyInput(KeyHandlerEditSessionDTO dto)
|
||||
{
|
||||
return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch);
|
||||
return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch, dto.requestId);
|
||||
}
|
||||
|
||||
HRESULT CKeyStateComposing::HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto)
|
||||
{
|
||||
_pTextService->_HandleCompositionFinalize(dto.ec, dto.pContext, FALSE);
|
||||
return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch);
|
||||
return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch, dto.requestId);
|
||||
}
|
||||
|
||||
HRESULT CKeyStateComposing::HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto)
|
||||
@@ -265,7 +265,7 @@ HRESULT CKeyStateComposing::HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO
|
||||
HRESULT CKeyStateComposing::HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto)
|
||||
{
|
||||
_pTextService->_HandleCompositionFinalize(dto.ec, dto.pContext, TRUE);
|
||||
return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch);
|
||||
return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch, dto.requestId);
|
||||
}
|
||||
|
||||
HRESULT CKeyStateComposing::HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto)
|
||||
@@ -275,9 +275,10 @@ HRESULT CKeyStateComposing::HandleKeyFinalizeCandidatelist(KeyHandlerEditSession
|
||||
|
||||
HRESULT CKeyStateComposing::HandleKeyConvert(KeyHandlerEditSessionDTO dto)
|
||||
{
|
||||
if (Global::Keycode == VK_SPACE)
|
||||
if (dto.code == VK_SPACE)
|
||||
{
|
||||
return _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext);
|
||||
return _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext, dto.requestId,
|
||||
dto.prefetchedText);
|
||||
}
|
||||
// VK_SPACE
|
||||
return _pTextService->_HandleCompositionConvert(dto.ec, dto.pContext, FALSE);
|
||||
@@ -300,7 +301,7 @@ HRESULT CKeyStateComposing::HandleKeyToogleIMEMode(KeyHandlerEditSessionDTO dto)
|
||||
|
||||
HRESULT CKeyStateComposing::HandleKeyBackspace(KeyHandlerEditSessionDTO dto)
|
||||
{
|
||||
return _pTextService->_HandleCompositionBackspace(dto.ec, dto.pContext);
|
||||
return _pTextService->_HandleCompositionBackspace(dto.ec, dto.pContext, dto.requestId);
|
||||
}
|
||||
|
||||
HRESULT CKeyStateComposing::HandleKeyArrow(KeyHandlerEditSessionDTO dto)
|
||||
@@ -315,7 +316,8 @@ HRESULT CKeyStateComposing::HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO d
|
||||
|
||||
HRESULT CKeyStateComposing::HandleKeyPunctuation(KeyHandlerEditSessionDTO dto)
|
||||
{
|
||||
return _pTextService->_HandleCompositionPunctuation(dto.ec, dto.pContext, dto.wch);
|
||||
return _pTextService->_HandleCompositionPunctuation(dto.ec, dto.pContext, dto.wch, dto.requestId,
|
||||
dto.prefetchedText);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -328,7 +330,8 @@ CKeyStateCandidate::CKeyStateCandidate(_In_ CMetasequoiaIME *pTextService) : CKe
|
||||
// _HandleCandidateInput
|
||||
HRESULT CKeyStateCandidate::HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto)
|
||||
{
|
||||
return _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext);
|
||||
return _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext, dto.requestId,
|
||||
dto.prefetchedText);
|
||||
}
|
||||
|
||||
// _HandleCandidateInput
|
||||
@@ -340,19 +343,21 @@ HRESULT CKeyStateCandidate::HandleKeyFinalizeCandidatelistForVKReturn(KeyHandler
|
||||
// HandleKeyFinalizeCandidatelistAndInput
|
||||
HRESULT CKeyStateCandidate::HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto)
|
||||
{
|
||||
_pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext);
|
||||
return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch);
|
||||
_pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext, dto.requestId, dto.prefetchedText);
|
||||
return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch, dto.requestId);
|
||||
}
|
||||
|
||||
//_HandleCandidateConvert
|
||||
HRESULT CKeyStateCandidate::HandleKeyConvert(KeyHandlerEditSessionDTO dto)
|
||||
{
|
||||
if (Global::Keycode == VK_SPACE)
|
||||
if (dto.code == VK_SPACE)
|
||||
{
|
||||
return _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext);
|
||||
return _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext, dto.requestId,
|
||||
dto.prefetchedText);
|
||||
}
|
||||
// Send candidate string to client when pressing VK_SPACE
|
||||
return _pTextService->_HandleCandidateConvert(dto.ec, dto.pContext);
|
||||
return _pTextService->_HandleCandidateConvert(dto.ec, dto.pContext, dto.requestId,
|
||||
dto.prefetchedText);
|
||||
}
|
||||
|
||||
//_HandleCancel
|
||||
@@ -371,6 +376,7 @@ HRESULT CKeyStateCandidate::HandleKeyArrow(KeyHandlerEditSessionDTO dto)
|
||||
HRESULT CKeyStateCandidate::HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto)
|
||||
{
|
||||
// return _pTextService->_HandleCandidateSelectByNumber(dto.ec, dto.pContext, dto.code);
|
||||
return _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext);
|
||||
return _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext, dto.requestId,
|
||||
dto.prefetchedText);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
#include "Globals.h"
|
||||
#include "Private.h"
|
||||
#include "MetasequoiaIME.h"
|
||||
#include "MetasequoiaIME.h"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
class CKeyStateCategory;
|
||||
|
||||
@@ -16,26 +18,32 @@ class CKeyStateCategoryFactory
|
||||
CKeyStateCategoryFactory();
|
||||
|
||||
private:
|
||||
static CKeyStateCategoryFactory *_instance;
|
||||
static thread_local CKeyStateCategoryFactory *_instance;
|
||||
};
|
||||
|
||||
typedef struct KeyHandlerEditSessionDTO
|
||||
{
|
||||
KeyHandlerEditSessionDTO::KeyHandlerEditSessionDTO(TfEditCookie tFEC, _In_ ITfContext *pTfContext, UINT virualCode,
|
||||
WCHAR inputChar, KEYSTROKE_FUNCTION arrowKeyFunction)
|
||||
KeyHandlerEditSessionDTO::KeyHandlerEditSessionDTO(TfEditCookie tFEC, _In_ ITfContext *pTfContext, UINT virualCode,
|
||||
WCHAR inputChar, KEYSTROKE_FUNCTION arrowKeyFunction,
|
||||
uint64_t pipeRequestId,
|
||||
const std::wstring &sessionPrefetchedText)
|
||||
{
|
||||
ec = tFEC;
|
||||
pContext = pTfContext;
|
||||
code = virualCode;
|
||||
wch = inputChar;
|
||||
arrowKey = arrowKeyFunction;
|
||||
arrowKey = arrowKeyFunction;
|
||||
requestId = pipeRequestId;
|
||||
prefetchedText = sessionPrefetchedText;
|
||||
}
|
||||
|
||||
TfEditCookie ec;
|
||||
ITfContext *pContext;
|
||||
UINT code;
|
||||
WCHAR wch;
|
||||
KEYSTROKE_FUNCTION arrowKey;
|
||||
KEYSTROKE_FUNCTION arrowKey;
|
||||
uint64_t requestId;
|
||||
std::wstring prefetchedText;
|
||||
} KeyHandlerEditSessionDTO;
|
||||
|
||||
class CKeyStateCategory
|
||||
|
||||
@@ -2,8 +2,57 @@
|
||||
#include "Private.h"
|
||||
#include "MetasequoiaIME.h"
|
||||
#include "CandidateListUIPresenter.h"
|
||||
#include <debugapi.h>
|
||||
#include "fmt/xchar.h"
|
||||
#include <debugapi.h>
|
||||
#include <cwchar>
|
||||
#include "fmt/xchar.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
bool IsWindowsTextInputHostWindow(HWND window)
|
||||
{
|
||||
if (!window)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD processId = 0;
|
||||
GetWindowThreadProcessId(window, &processId);
|
||||
if (processId == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
|
||||
processId);
|
||||
if (!process)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
WCHAR imagePath[32768] = {};
|
||||
DWORD imagePathLength = ARRAYSIZE(imagePath);
|
||||
const bool queried =
|
||||
QueryFullProcessImageNameW(process, 0, imagePath, &imagePathLength) !=
|
||||
FALSE;
|
||||
CloseHandle(process);
|
||||
if (!queried || imagePathLength == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const WCHAR *fileName = wcsrchr(imagePath, L'\\');
|
||||
fileName = fileName ? fileName + 1 : imagePath;
|
||||
return _wcsicmp(fileName, L"TextInputHost.exe") == 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool CMetasequoiaIME::_CaptureWindowsTextInputHostFocusLoss()
|
||||
{
|
||||
_focusLostToWindowsTextInputHost =
|
||||
_focusLostToWindowsTextInputHost ||
|
||||
IsWindowsTextInputHostWindow(GetForegroundWindow());
|
||||
return _focusLostToWindowsTextInputHost;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
@@ -13,7 +62,19 @@
|
||||
|
||||
STDAPI CMetasequoiaIME::OnSetThreadFocus()
|
||||
{
|
||||
PostMessage(_msgWndHandle, WM_ThreadFocus, 0, 0);
|
||||
if (!IsNamedpipeFocusStateOwner(this))
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
// Do not consume this marker here. TextInputHost owns a separate TSF
|
||||
// client, so the authoritative document OnSetFocus must use it to force a
|
||||
// new Server activation even when our local pipe handles still look live.
|
||||
// Match bcaf34e: thread focus is observational. Document OnSetFocus owns
|
||||
// disconnect/reconnect; this callback only refreshes status/presenter.
|
||||
if (_msgWndHandle && IsWindow(_msgWndHandle))
|
||||
{
|
||||
PostMessage(_msgWndHandle, WM_ThreadFocus, 0, 0);
|
||||
}
|
||||
if (_pCandidateListUIPresenter)
|
||||
{
|
||||
ITfDocumentMgr *pCandidateListDocumentMgr = nullptr;
|
||||
@@ -26,11 +87,11 @@ STDAPI CMetasequoiaIME::OnSetThreadFocus()
|
||||
_pCandidateListUIPresenter->OnSetThreadFocus();
|
||||
}
|
||||
|
||||
pCandidateListDocumentMgr->Release();
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
pCandidateListDocumentMgr->Release();
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
@@ -40,28 +101,42 @@ STDAPI CMetasequoiaIME::OnSetThreadFocus()
|
||||
|
||||
STDAPI CMetasequoiaIME::OnKillThreadFocus()
|
||||
{
|
||||
if (!IsNamedpipeFocusStateOwner(this) || !Global::g_connected)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
_focusLostToWindowsTextInputHost = false;
|
||||
(void)_CaptureWindowsTextInputHostFocusLoss();
|
||||
|
||||
// ITfThreadFocusSink reports temporary auxiliary UI transitions as well
|
||||
// as real application focus changes. The known-good implementation did
|
||||
// not use this callback as an IPC/session boundary: document/top-context
|
||||
// changes are already handled by ITfThreadMgrEventSink, and TIP switching
|
||||
// is handled by Deactivate. Keep the current composition and pipe epoch
|
||||
// intact here, especially for TextInputHost.exe / Win+..
|
||||
if (_pCandidateListUIPresenter)
|
||||
{
|
||||
ITfDocumentMgr *pCandidateListDocumentMgr = nullptr;
|
||||
ITfContext *pTfContext = _pCandidateListUIPresenter->_GetContextDocument();
|
||||
|
||||
if ((nullptr != pTfContext) && SUCCEEDED(pTfContext->GetDocumentMgr(&pCandidateListDocumentMgr)))
|
||||
{
|
||||
if ((nullptr != pTfContext) && SUCCEEDED(pTfContext->GetDocumentMgr(&pCandidateListDocumentMgr)))
|
||||
{
|
||||
if (_pDocMgrLastFocused)
|
||||
{
|
||||
_pDocMgrLastFocused->Release();
|
||||
_pDocMgrLastFocused = nullptr;
|
||||
}
|
||||
_pDocMgrLastFocused = pCandidateListDocumentMgr;
|
||||
if (_pDocMgrLastFocused)
|
||||
{
|
||||
_pDocMgrLastFocused->AddRef();
|
||||
}
|
||||
}
|
||||
_pCandidateListUIPresenter->OnKillThreadFocus();
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
// GetDocumentMgr already returned one owned reference. Transfer it
|
||||
// directly to _pDocMgrLastFocused; adding another reference here
|
||||
// leaks one on every Win+. / thread-focus round trip.
|
||||
_pDocMgrLastFocused = pCandidateListDocumentMgr;
|
||||
pCandidateListDocumentMgr = nullptr;
|
||||
|
||||
}
|
||||
_pCandidateListUIPresenter->OnKillThreadFocus();
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
BOOL CMetasequoiaIME::_InitThreadFocusSink()
|
||||
{
|
||||
|
||||
@@ -42,22 +42,39 @@ STDAPI CMetasequoiaIME::OnUninitDocumentMgr(_In_ ITfDocumentMgr *pDocMgr)
|
||||
// focus document, or now no document holds the input focus.
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
STDAPI CMetasequoiaIME::OnSetFocus(_In_ ITfDocumentMgr *pDocMgrFocus, _In_ ITfDocumentMgr *pDocMgrPrevFocus)
|
||||
{
|
||||
pDocMgrPrevFocus;
|
||||
|
||||
if (pDocMgrFocus && !Global::g_connected)
|
||||
{
|
||||
Global::g_connected = true;
|
||||
PostMessage(_msgWndHandle, WM_ConnectNamedpipe, 0, 0);
|
||||
}
|
||||
else if (!pDocMgrFocus && Global::g_connected)
|
||||
{
|
||||
Global::g_connected = false;
|
||||
PostMessage(_msgWndHandle, WM_DisconnectNamedpipe, 0, 0);
|
||||
}
|
||||
|
||||
_InitTextEditSink(pDocMgrFocus);
|
||||
STDAPI CMetasequoiaIME::OnSetFocus(_In_ ITfDocumentMgr *pDocMgrFocus, _In_ ITfDocumentMgr *pDocMgrPrevFocus)
|
||||
{
|
||||
if (!IsNamedpipeFocusStateOwner(this))
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
pDocMgrPrevFocus;
|
||||
|
||||
const bool windowsTextInputHostTransition =
|
||||
_focusLostToWindowsTextInputHost ||
|
||||
_CaptureWindowsTextInputHostFocusLoss();
|
||||
|
||||
// bcaf34e used document focus as the authoritative reconnect boundary.
|
||||
// The modern protocol preserves that behavior with a new activation epoch
|
||||
// on the existing healthy transport; EnsureNamedpipeFocusSessionActivated
|
||||
// physically reopens a channel as well when its health check fails.
|
||||
if (pDocMgrFocus &&
|
||||
(!Global::g_connected || windowsTextInputHostTransition))
|
||||
{
|
||||
Global::g_connected = true;
|
||||
_workerCommitReady.store(false, std::memory_order_release);
|
||||
RequireNamedpipeFocusActivation();
|
||||
PostMessage(_msgWndHandle, WM_ConnectNamedpipe, 0, 0);
|
||||
_focusLostToWindowsTextInputHost = false;
|
||||
}
|
||||
else if (!pDocMgrFocus && Global::g_connected)
|
||||
{
|
||||
MarkNamedpipeFocusLost();
|
||||
Global::g_connected = false;
|
||||
PostMessage(_msgWndHandle, WM_DisconnectNamedpipe, 0, 0);
|
||||
}
|
||||
|
||||
_InitTextEditSink(pDocMgrFocus);
|
||||
|
||||
_UpdateLanguageBarOnSetFocus(pDocMgrFocus);
|
||||
|
||||
@@ -92,13 +109,13 @@ STDAPI CMetasequoiaIME::OnSetFocus(_In_ ITfDocumentMgr *pDocMgrFocus, _In_ ITfDo
|
||||
|
||||
_pDocMgrLastFocused = pDocMgrFocus;
|
||||
|
||||
if (_pDocMgrLastFocused)
|
||||
{
|
||||
_pDocMgrLastFocused->AddRef();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
if (_pDocMgrLastFocused)
|
||||
{
|
||||
_pDocMgrLastFocused->AddRef();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
@@ -107,11 +124,10 @@ STDAPI CMetasequoiaIME::OnSetFocus(_In_ ITfDocumentMgr *pDocMgrFocus, _In_ ITfDo
|
||||
// Sink called by the framework when a context is pushed.
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
STDAPI CMetasequoiaIME::OnPushContext(_In_ ITfContext *pContext)
|
||||
{
|
||||
pContext;
|
||||
|
||||
return E_NOTIMPL;
|
||||
STDAPI CMetasequoiaIME::OnPushContext(_In_ ITfContext *pContext)
|
||||
{
|
||||
_HandleFocusedContextStackChange(pContext);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
@@ -121,12 +137,78 @@ STDAPI CMetasequoiaIME::OnPushContext(_In_ ITfContext *pContext)
|
||||
// Sink called by the framework when a context is popped.
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
STDAPI CMetasequoiaIME::OnPopContext(_In_ ITfContext *pContext)
|
||||
{
|
||||
pContext;
|
||||
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
STDAPI CMetasequoiaIME::OnPopContext(_In_ ITfContext *pContext)
|
||||
{
|
||||
_HandleFocusedContextStackChange(pContext);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void CMetasequoiaIME::_HandleFocusedContextStackChange(_In_opt_ ITfContext *changedContext)
|
||||
{
|
||||
if (!IsNamedpipeFocusStateOwner(this) || changedContext == nullptr ||
|
||||
_pThreadMgr == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ITfDocumentMgr *changedDocument = nullptr;
|
||||
ITfDocumentMgr *focusedDocument = nullptr;
|
||||
if (FAILED(changedContext->GetDocumentMgr(&changedDocument)) ||
|
||||
changedDocument == nullptr ||
|
||||
FAILED(_pThreadMgr->GetFocus(&focusedDocument)) ||
|
||||
focusedDocument == nullptr || changedDocument != focusedDocument)
|
||||
{
|
||||
if (changedDocument)
|
||||
{
|
||||
changedDocument->Release();
|
||||
}
|
||||
if (focusedDocument)
|
||||
{
|
||||
focusedDocument->Release();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ITfContext *newTopContext = nullptr;
|
||||
if (FAILED(focusedDocument->GetTop(&newTopContext)) || newTopContext == nullptr)
|
||||
{
|
||||
focusedDocument->Release();
|
||||
changedDocument->Release();
|
||||
return;
|
||||
}
|
||||
const bool topContextChanged = newTopContext != _pTextEditSinkContext;
|
||||
newTopContext->Release();
|
||||
if (!topContextChanged)
|
||||
{
|
||||
focusedDocument->Release();
|
||||
changedDocument->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
// Capture the old composition owner before rebinding the text-edit sink.
|
||||
// A pushed modal/context (or a popped editor context) must never inherit
|
||||
// replies or queued edit sessions that belonged to the previous top.
|
||||
ITfContext *resetContext = _pContext ? _pContext : _pTextEditSinkContext;
|
||||
if (resetContext)
|
||||
{
|
||||
resetContext->AddRef();
|
||||
}
|
||||
else
|
||||
{
|
||||
resetContext = changedContext;
|
||||
resetContext->AddRef();
|
||||
}
|
||||
|
||||
_ClearDeferredKeyDowns();
|
||||
MarkNamedpipeSessionDirtyForOwner(this);
|
||||
const UINT resetToken = _localSessionResetToken.load(std::memory_order_acquire);
|
||||
_RequestLocalSessionReset(resetContext, resetToken);
|
||||
_InitTextEditSink(focusedDocument);
|
||||
|
||||
resetContext->Release();
|
||||
focusedDocument->Release();
|
||||
changedDocument->Release();
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
@@ -26,21 +26,15 @@ const int MOVEDOWN_ONE = 1;
|
||||
const int MOVETO_TOP = 0;
|
||||
const int MOVETO_BOTTOM = -1;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool IsTimeoutSentinelCandidate(UINT msgType, const std::wstring &candidateString)
|
||||
{
|
||||
return msgType == Global::DataFromServerMsgType::Normal && candidateString == L"T";
|
||||
}
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
// _HandleCandidateFinalize
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
HRESULT CMetasequoiaIME::_HandleCandidateFinalize(TfEditCookie ec, _In_ ITfContext *pContext)
|
||||
HRESULT CMetasequoiaIME::_HandleCandidateFinalize(TfEditCookie ec, _In_ ITfContext *pContext,
|
||||
uint64_t requestId,
|
||||
const std::wstring &prefetchedText)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
PerfTimer finalizeTimer;
|
||||
@@ -49,7 +43,7 @@ HRESULT CMetasequoiaIME::_HandleCandidateFinalize(TfEditCookie ec, _In_ ITfConte
|
||||
DWORD_PTR keystrokeBufLen = keyStrokebuffer.GetLength();
|
||||
DWORD_PTR candidateLen = keystrokeBufLen;
|
||||
CStringRange candidateString(keyStrokebuffer);
|
||||
std::wstring pendingCommitCandidate = _TakePendingCommitCandidate();
|
||||
const std::wstring &pendingCommitCandidate = prefetchedText;
|
||||
|
||||
|
||||
// _pCandidateListUIPresenter would be null in uwp/metro apps
|
||||
@@ -62,7 +56,6 @@ HRESULT CMetasequoiaIME::_HandleCandidateFinalize(TfEditCookie ec, _In_ ITfConte
|
||||
{
|
||||
if (!pendingCommitCandidate.empty())
|
||||
{
|
||||
ClearNamedpipeDataIfExists(true);
|
||||
candidateString.Set(pendingCommitCandidate.c_str(), pendingCommitCandidate.length());
|
||||
PerfTimer insertTextTimer;
|
||||
hr = _InsertTextToComposition(ec, pContext, &candidateString);
|
||||
@@ -84,29 +77,21 @@ HRESULT CMetasequoiaIME::_HandleCandidateFinalize(TfEditCookie ec, _In_ ITfConte
|
||||
std::wstring serverCandidateString;
|
||||
const bool hasPrefetchedServerCandidate =
|
||||
_TakePendingServerCandidate(&serverMsgType, &serverCandidateString);
|
||||
if (hasPrefetchedServerCandidate)
|
||||
{
|
||||
if (IsTimeoutSentinelCandidate(serverMsgType, serverCandidateString))
|
||||
{
|
||||
struct FanyImeNamedpipeDataToTsf *retryData = TryReadDataFromServerPipeWithTimeout();
|
||||
serverMsgType = retryData->msg_type;
|
||||
serverCandidateString = retryData->candidate_string;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!hasPrefetchedServerCandidate)
|
||||
{
|
||||
PerfTimer pipeReadTimer;
|
||||
struct FanyImeNamedpipeDataToTsf *receivedData = TryReadDataFromServerPipeWithTimeout();
|
||||
struct FanyImeNamedpipeDataToTsf *receivedData = TryReadDataFromServerPipeWithTimeout(requestId);
|
||||
serverMsgType = receivedData->msg_type;
|
||||
serverCandidateString = receivedData->candidate_string;
|
||||
if (IsTimeoutSentinelCandidate(serverMsgType, serverCandidateString))
|
||||
{
|
||||
struct FanyImeNamedpipeDataToTsf *retryData = TryReadDataFromServerPipeWithTimeout();
|
||||
serverMsgType = retryData->msg_type;
|
||||
serverCandidateString = retryData->candidate_string;
|
||||
}
|
||||
}
|
||||
|
||||
if (serverMsgType == Global::DataFromServerMsgType::TransportUnavailable)
|
||||
{
|
||||
// Transport state is never candidate text. Propagate failure so
|
||||
// the exact deferred key remains owned and is replayed only after
|
||||
// the replacement focus/session fence is ready.
|
||||
return HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE);
|
||||
}
|
||||
if (serverMsgType == Global::DataFromServerMsgType::OutofRange) // Candidate index out of range
|
||||
{
|
||||
return hr;
|
||||
@@ -156,7 +141,8 @@ HRESULT CMetasequoiaIME::_HandleCandidateFinalize(TfEditCookie ec, _In_ ITfConte
|
||||
|
||||
if (pCompositionProcessorEngine->GetVirtualKeyLength())
|
||||
{
|
||||
_HandleCompositionInputWorker(pCompositionProcessorEngine, ec, pContext);
|
||||
_HandleCompositionInputWorker(pCompositionProcessorEngine, ec, pContext,
|
||||
FANY_IME_NO_REQUEST_ID);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -218,9 +204,11 @@ NoPresenter:
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
HRESULT CMetasequoiaIME::_HandleCandidateConvert(TfEditCookie ec, _In_ ITfContext *pContext)
|
||||
HRESULT CMetasequoiaIME::_HandleCandidateConvert(TfEditCookie ec, _In_ ITfContext *pContext,
|
||||
uint64_t requestId,
|
||||
const std::wstring &prefetchedText)
|
||||
{
|
||||
return _HandleCandidateWorker(ec, pContext);
|
||||
return _HandleCandidateWorker(ec, pContext, requestId, prefetchedText);
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
@@ -229,7 +217,9 @@ HRESULT CMetasequoiaIME::_HandleCandidateConvert(TfEditCookie ec, _In_ ITfContex
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
HRESULT CMetasequoiaIME::_HandleCandidateWorker(TfEditCookie ec, _In_ ITfContext *pContext)
|
||||
HRESULT CMetasequoiaIME::_HandleCandidateWorker(TfEditCookie ec, _In_ ITfContext *pContext,
|
||||
uint64_t requestId,
|
||||
const std::wstring &prefetchedText)
|
||||
{
|
||||
HRESULT hrReturn = E_FAIL;
|
||||
DWORD_PTR candidateLen = 0;
|
||||
@@ -250,7 +240,7 @@ HRESULT CMetasequoiaIME::_HandleCandidateWorker(TfEditCookie ec, _In_ ITfContext
|
||||
}
|
||||
|
||||
candidateString.Set(pCandidateString, candidateLen);
|
||||
hrReturn = _HandleCandidateFinalize(ec, pContext);
|
||||
hrReturn = _HandleCandidateFinalize(ec, pContext, requestId, prefetchedText);
|
||||
|
||||
Exit:
|
||||
return hrReturn;
|
||||
@@ -296,7 +286,9 @@ HRESULT CMetasequoiaIME::_HandleCandidateArrowKey( //
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
HRESULT CMetasequoiaIME::_HandleCandidateSelectByNumber(TfEditCookie ec, _In_ ITfContext *pContext, _In_ UINT uCode)
|
||||
HRESULT CMetasequoiaIME::_HandleCandidateSelectByNumber(TfEditCookie ec, _In_ ITfContext *pContext, _In_ UINT uCode,
|
||||
uint64_t requestId,
|
||||
const std::wstring &prefetchedText)
|
||||
{
|
||||
int iSelectAsNumber = _pCompositionProcessorEngine->GetCandidateListIndexRange()->GetIndex(uCode);
|
||||
if (iSelectAsNumber == -1)
|
||||
@@ -308,7 +300,7 @@ HRESULT CMetasequoiaIME::_HandleCandidateSelectByNumber(TfEditCookie ec, _In_ IT
|
||||
{
|
||||
if (_pCandidateListUIPresenter->_SetSelectionInPage(iSelectAsNumber))
|
||||
{
|
||||
return _HandleCandidateConvert(ec, pContext);
|
||||
return _HandleCandidateConvert(ec, pContext, requestId, prefetchedText);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -802,6 +794,10 @@ void CCandidateListUIPresenter::_EndCandidateList()
|
||||
void CCandidateListUIPresenter::_PrepareForAsyncCleanup()
|
||||
{
|
||||
_asyncCleanupPending = TRUE;
|
||||
// Hide and clear the exact Server-side candidate session now. Deferring
|
||||
// this until the presenter destructor lets an old cleanup message race a
|
||||
// newly started composition and clear its candidates.
|
||||
EndCandidateUiSession();
|
||||
}
|
||||
|
||||
void CCandidateListUIPresenter::_NotifyUI()
|
||||
@@ -1095,7 +1091,10 @@ HRESULT CCandidateListUIPresenter::_CandidateChangeNotification(_In_ enum CANDWN
|
||||
}
|
||||
|
||||
CKeyHandlerEditSession *pEditSession =
|
||||
new (std::nothrow) CKeyHandlerEditSession(_pTextService, pContext, 0, 0, KeyState);
|
||||
new (std::nothrow) CKeyHandlerEditSession(_pTextService, pContext, 0, 0, KeyState,
|
||||
FANY_IME_NO_REQUEST_ID, {}, {},
|
||||
_pTextService->_CaptureFocusSessionToken(), 0,
|
||||
_pTextService->_CaptureCompositionEpoch());
|
||||
if (nullptr != pEditSession)
|
||||
{
|
||||
HRESULT hrSession = S_OK;
|
||||
@@ -1279,7 +1278,7 @@ void CCandidateListUIPresenter::WriteCandidateUiPayload(_In_ UINT writeFlag)
|
||||
Global::ModifiersDown, //
|
||||
Global::Point, //
|
||||
Global::PinyinLength, //
|
||||
Global::PinyinString, //
|
||||
pinyinString, //
|
||||
writeFlag //
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user