diff --git a/windows/src/Composition/CompositionProcessorEngine_PreservedKeys.cpp b/windows/src/Composition/CompositionProcessorEngine_PreservedKeys.cpp index 7bf051a7..77550590 100644 --- a/windows/src/Composition/CompositionProcessorEngine_PreservedKeys.cpp +++ b/windows/src/Composition/CompositionProcessorEngine_PreservedKeys.cpp @@ -356,7 +356,6 @@ void CCompositionProcessorEngine::OnPreservedKey( // { WriteDataToSharedMemory(Global::Keycode, L'\0', Global::ModifiersDown, nullptr, 0, L"", 0b000111); SendKeyEventToUIProcess(); - ClearNamedpipeDataIfExists(); } } else if (IsEqualGUID(rguid, _PreservedKey_DoubleSingleByte.Guid)) diff --git a/windows/src/IME/MetasequoiaIME.h b/windows/src/IME/MetasequoiaIME.h index f8df2161..c76c644c 100644 --- a/windows/src/IME/MetasequoiaIME.h +++ b/windows/src/IME/MetasequoiaIME.h @@ -547,8 +547,10 @@ class CMetasequoiaIME : public ITfTextInputProcessorEx, // caller already proved the keyboard is live through a non-zero // _IsKeyEaten out-char; otherwise the disabled compartment is queried here. void _NotePassthroughStatistics(UINT virtualKey, WCHAR wch, bool keyboardKnownEnabled); + // scheduleDrain is false only when the caller drains synchronously right + // after queueing and schedules a drain itself if the key stays queued. bool _QueueDeferredKeyDown(_In_ ITfContext *pContext, WPARAM wParam, LPARAM lParam, WCHAR translatedWch, - UINT modifiersDown, const _KEYSTROKE_STATE &keyState); + UINT modifiersDown, const _KEYSTROKE_STATE &keyState, bool scheduleDrain = true); bool _QueueDeferredPreservedKey(_In_ ITfContext *pContext, REFGUID preservedKey); void _ClearDeferredKeyDowns(); void _ScheduleDeferredKeyDownDrain(); diff --git a/windows/src/IPC/Ipc.cpp b/windows/src/IPC/Ipc.cpp index bb13096f..a2bc9df3 100644 --- a/windows/src/IPC/Ipc.cpp +++ b/windows/src/IPC/Ipc.cpp @@ -1267,38 +1267,6 @@ bool SendToNamedpipe(bool *deliveryAmbiguous = nullptr) return false; } -/** - * @brief Clear namedpipe data if exists, cause sometimes there may be some useless data sent by last key event from - * server - * - */ -void ClearNamedpipeDataIfExists(bool force) -{ - // Request IDs make destructive draining both unnecessary and incorrect: - // an async edit session may still own any frame currently in this pipe. - // Mismatched replies are retained by TryReadData... in pendingReplies. - if (force) - { - // Compatibility path for Server-initiated candidate clicks: legacy - // Server builds deliver the same candidate both on the worker pipe and - // as one unsolicited (request_id == 0) reply. The worker payload has - // already been consumed by the caller, so discard exactly that one - // duplicate. TryRead caches every nonzero request reply it encounters - // and ignores PipeReady, preserving all edit-session-owned frames. - (void)TryReadDataFromServerPipeWithTimeout(FANY_IME_UNSOLICITED_REQUEST_ID); - } -} - -/** - * @brief Try to read selected candiate string data from server pipe with timeout - * - * @return struct FanyImeNamedpipeDataToTsf* - */ -struct FanyImeNamedpipeDataToTsf *TryReadDataFromServerPipeWithTimeout(uint64_t expectedRequestId) -{ - return TryReadDataFromServerPipeWithTimeout(expectedRequestId, true); -} - static struct FanyImeNamedpipeDataToTsf *TryReadDataFromServerPipeWithDeadline(uint64_t expectedRequestId, bool abortTransportOnTimeout, DWORD timeoutMs); @@ -1448,18 +1416,6 @@ static struct FanyImeNamedpipeDataToTsf *TryReadDataFromServerPipeWithDeadline(u return transportUnavailable(); } -/** - * @brief Read data sent by server - * - * TODO: Cancel when time exceed, we should set a timeout - * - * @return struct FanyImeNamedpipeDataToTsf* - */ -struct FanyImeNamedpipeDataToTsf *ReadDataFromServerViaNamedPipe(uint64_t expectedRequestId) -{ - return TryReadDataFromServerPipeWithTimeout(expectedRequestId); -} - /** * @brief 即用即断,不要求一直连接,但是,要让 Server 端的管道一直在 connect 状态阻塞住等待连接 * diff --git a/windows/src/IPC/Ipc.h b/windows/src/IPC/Ipc.h index 5bf7d62e..d29aee80 100644 --- a/windows/src/IPC/Ipc.h +++ b/windows/src/IPC/Ipc.h @@ -103,12 +103,10 @@ int SendHideCaretStateEventToUIProcessViaNamedPipe(); int SendShowCandidateWndEventToUIProcessViaNamedPipe(); int SendMoveCandidateWndEventToUIProcessViaNamedPipe(); int SendLangbarRightClickEventToUIProcessViaNamedPipe(const RECT *prcArea); -void ClearNamedpipeDataIfExists(bool force = false); // Best-effort read of the Server-published current candidate page (comma- // separated). Used in UILess mode so ITfCandidateListUIElement::GetString can // return real candidates after PrepareCandidateList has written shared memory. bool TryReadCandidatePageFromSharedMemory(_Out_ std::wstring *candidatePage); -struct FanyImeNamedpipeDataToTsf *TryReadDataFromServerPipeWithTimeout(uint64_t expectedRequestId); // When abortTransportOnTimeout is false, a missed reply leaves the pipe up and // returns a non-TransportUnavailable empty frame for the caller to fall back. struct FanyImeNamedpipeDataToTsf *TryReadDataFromServerPipeWithTimeout(uint64_t expectedRequestId, @@ -132,7 +130,6 @@ inline bool IsDeliveredServerRequestId(uint64_t requestId) // request was delivered. The key is dropped with the composition and never // sent again (DeferredKeyFailureReason::DeliveryAmbiguous). constexpr HRESULT FANY_E_COMMIT_REPLY_AMBIGUOUS = __HRESULT_FROM_WIN32(ERROR_TIMEOUT); -struct FanyImeNamedpipeDataToTsf *ReadDataFromServerViaNamedPipe(uint64_t expectedRequestId); // // Modifiers: diff --git a/windows/src/Key/KeyEventSink.cpp b/windows/src/Key/KeyEventSink.cpp index 40908be1..d7fba8b1 100644 --- a/windows/src/Key/KeyEventSink.cpp +++ b/windows/src/Key/KeyEventSink.cpp @@ -1070,8 +1070,8 @@ CMetasequoiaIME::KeyDownDispatchResult CMetasequoiaIME::_DispatchKeyDown( // a write success followed by a reply/edit failure takes the same // _FailDeferredKey path as a key that arrived behind a barrier. const bool healthyImmediateDispatch = _deferredKeyDowns.empty() && !_hasDeferredKeyInFlight; - const bool queued = - _QueueDeferredKeyDown(pContext, wParam, lParam, wch, capturedModifiers, KeystrokeState) != FALSE; + const bool queued = _QueueDeferredKeyDown(pContext, wParam, lParam, wch, capturedModifiers, KeystrokeState, + /*scheduleDrain=*/!healthyImmediateDispatch) != FALSE; *pIsEaten = queued ? TRUE : FALSE; DebugTsfIssue47(queued ? L"keydown-owned-queued" : L"keydown-owned-queue-failed", FANY_IME_NO_REQUEST_ID, code, wch, KeystrokeState.Category, KeystrokeState.Function, queued ? 1 : 0, _IsComposing(), @@ -1091,6 +1091,12 @@ CMetasequoiaIME::KeyDownDispatchResult CMetasequoiaIME::_DispatchKeyDown( // not ready, _DrainOneDeferredKeyDown leaves the key queued for the // ordinary asynchronous drain. _DrainOneDeferredKeyDown(); + // The drain is posted only when the synchronous one left the key + // queued (no connected focus session, or a reset opened). A key it + // dispatched is retired by its own completion, which schedules the + // next drain, so a WM_DrainDeferredKeyDown posted up front would + // only ever find the key in flight or gone. + _ScheduleDeferredKeyDownDrain(); } return KeyDownDispatchResult::Complete; } diff --git a/windows/src/Key/KeyEventSink_DeferredKeys.cpp b/windows/src/Key/KeyEventSink_DeferredKeys.cpp index 7463b689..9c447013 100644 --- a/windows/src/Key/KeyEventSink_DeferredKeys.cpp +++ b/windows/src/Key/KeyEventSink_DeferredKeys.cpp @@ -636,7 +636,8 @@ bool CMetasequoiaIME::_ClassifyDeferredKeyDown(_In_ ITfContext *pContext, WPARAM } bool CMetasequoiaIME::_QueueDeferredKeyDown(_In_ ITfContext *pContext, WPARAM wParam, LPARAM lParam, - WCHAR translatedWch, UINT modifiersDown, const _KEYSTROKE_STATE &keyState) + WCHAR translatedWch, UINT modifiersDown, const _KEYSTROKE_STATE &keyState, + bool scheduleDrain) { // Repeats are owned but do not enqueue another global configuration toggle. if (keyState.Function == FUNCTION_TOGGLE_CHARACTER_SET && (lParam & 0x40000000) != 0) @@ -668,7 +669,10 @@ bool CMetasequoiaIME::_QueueDeferredKeyDown(_In_ ITfContext *pContext, WPARAM wP { _EnsureDeferredKeyProjection(); } - _ScheduleDeferredKeyDownDrain(); + if (scheduleDrain) + { + _ScheduleDeferredKeyDownDrain(); + } return true; }