- 快捷键页「翻页方式」新增「左 / 右(横排时移动候选项)」,与「上 / 下」各自独立,默认关闭, 只在候选窗为横排时生效。配置项 general.candidate_horizontal_arrow_navigation 已写入出厂模板。 - 光标在拼音串尾时右键移动高亮(到页末翻页),左键往回移高亮,高亮回到第一个候选后再按才移动光标; 光标在串中间时右键先把光标移回串尾,再按才移动高亮。Ctrl+左/右与 UILess 宿主不变。 - 新增 worker 消息 HorizontalArrowSelectionChanged(39)和按键标记 FanyImePipeFlags::HorizontalArrowSelection: 开关生效时 TSF 把候选态下的左右键交给 Server 决定,移光标时 Server 用 CompositionRestored 回光标, 移高亮时回 MoveSelection*,TSF 不再自己预测光标。旧 DLL 忽略新消息,保持原行为。 Closes #779 Closes #895
488 lines
25 KiB
C++
488 lines
25 KiB
C++
#pragma once
|
|
|
|
// Single wire contract for Windows TSF and Server. This header has no runtime
|
|
// dependency on the engine or the Windows SDK. UTF-16 and alignment are explicit
|
|
// so the same ABI assertions can run on every platform and on x86/x64 Windows.
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <vector>
|
|
#include "ipc_protocol_limits.h"
|
|
#ifdef _WIN32
|
|
using FanyImeWireChar = wchar_t;
|
|
#else
|
|
using FanyImeWireChar = char16_t;
|
|
#endif
|
|
|
|
inline const wchar_t *FANY_IME_SHARED_MEMORY = L"Local\\FanyImeSharedMemory";
|
|
inline const int BUFFER_SIZE = 4096;
|
|
|
|
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 const wchar_t *FANY_IME_TSF_DIAGNOSTIC_NAMED_PIPE = L"\\\\.\\pipe\\FanyImeTsfDiagnosticNamedPipe";
|
|
// Dedicated Tauri-to-Server voice lifecycle channel. It never carries TSF
|
|
// frames and cannot be confused with the legacy Aux endpoint.
|
|
inline const wchar_t *FANY_IME_VOICE_CONTROL_NAMED_PIPE = L"\\\\.\\pipe\\FanyImeVoiceControlNamedPipe";
|
|
inline constexpr uint32_t FANY_IME_TSF_DIAGNOSTIC_MAGIC = 0x474F4C54; // "TLOG"
|
|
inline constexpr uint32_t FANY_IME_TSF_DIAGNOSTIC_VERSION = 1;
|
|
inline constexpr size_t FANY_IME_TSF_DIAGNOSTIC_MAX_FRAME_BYTES = 16 * 1024;
|
|
// Optional local statistics channel: the TSF DLL is the client and the Server's
|
|
// statistics pipe listener is the server. Named pipes live in the machine-global
|
|
// namespace, so the pipe name must carry a session suffix (fast user switching
|
|
// and RDP sessions could otherwise collide); both sides append the decimal
|
|
// ProcessIdToSessionId(GetCurrentProcessId()) result.
|
|
inline const wchar_t *FANY_IME_STATS_PIPE_NAME_PREFIX = L"\\\\.\\pipe\\FanyImeStatsNamedPipe-";
|
|
inline constexpr uint32_t FANY_IME_STATS_MAGIC = 0x54415453; // "STAT"
|
|
inline constexpr uint32_t FANY_IME_STATS_VERSION = 1;
|
|
inline constexpr size_t FANY_IME_STATS_MAX_FRAME_BYTES = 16 * 1024;
|
|
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
|
|
L"FanyHideCandidateWndEvent", // Event sent to UI process to notify time to hide candidate window
|
|
L"FanyShowCandidateWndEvent", // Event sent to UI process to notify time to show candidate window
|
|
L"FanyMoveCandidateWndEvent", // Event sent to UI process to notify time to move candidate window
|
|
};
|
|
|
|
//
|
|
// modifiers:
|
|
// 0: non
|
|
// 1: shift
|
|
// 2: control
|
|
// 3: alt
|
|
// 4: win
|
|
// 5: to be supplemented
|
|
//
|
|
struct FanyImeSharedMemoryData
|
|
{
|
|
std::uint32_t keycode;
|
|
FanyImeWireChar wch;
|
|
std::uint32_t modifiers_down = 0;
|
|
int point[2] = {100, 100};
|
|
int pinyin_length = 0;
|
|
FanyImeWireChar pinyin_string[128];
|
|
FanyImeWireChar candidate_string[1024];
|
|
FanyImeWireChar selected_candiate_string[128];
|
|
};
|
|
|
|
//
|
|
// For uwp/metro apps, here we do not need candidate_string and selected_candiate_string,
|
|
// just let server process to handle them
|
|
//
|
|
// event_type
|
|
// 0: FanyImeKeyEvent
|
|
// 1: FanyHideCandidateWndEvent
|
|
// 2: FanyShowCandidateWndEvent
|
|
// 3: FanyMoveCandidateWndEvent
|
|
// 4: FanyLangbarRightClickEvent (legacy Main enum; tip now sends via Aux)
|
|
//
|
|
struct alignas(8) FanyImeNamedpipeData
|
|
{
|
|
std::uint32_t event_type;
|
|
uint64_t client_id = 0;
|
|
uint64_t request_id = 0;
|
|
std::uint32_t keycode;
|
|
FanyImeWireChar wch;
|
|
std::uint32_t modifiers_down = 0;
|
|
int point[2] = {100, 100};
|
|
int pinyin_length = 0;
|
|
FanyImeWireChar pinyin_string[128];
|
|
};
|
|
|
|
namespace FanyImePipeEventType
|
|
{
|
|
constexpr std::uint32_t KeyEvent = 0;
|
|
constexpr std::uint32_t HideCandidateWnd = 1;
|
|
constexpr std::uint32_t ShowCandidateWnd = 2;
|
|
constexpr std::uint32_t MoveCandidateWnd = 3;
|
|
constexpr std::uint32_t LangbarRightClick = 4; // Aux text protocol; not accepted on Main
|
|
constexpr std::uint32_t ClientHello = 10;
|
|
constexpr std::uint32_t ClientActivated = 11;
|
|
constexpr std::uint32_t ClientDeactivated = 12; // terminal TIP switch; toolbar hidden
|
|
constexpr std::uint32_t StatusSnapshot = 13; // wch: CapsLock 1/0
|
|
constexpr std::uint32_t ClientSuspended = 14; // temporary focus route reset; toolbar kept
|
|
// Same payload as StatusSnapshot, but additionally asserts that this client
|
|
// owns thread focus right now. Sent when document focus returns to a session
|
|
// the Server may have re-routed to another client meanwhile; without the
|
|
// ownership claim the Server would discard it as an inactive-client event.
|
|
constexpr std::uint32_t FocusRestored = 15;
|
|
constexpr std::uint32_t HideCaretState = 16; // focus-context boundary; never clears candidate state
|
|
// Caret-badge notifications (FanyImeProtocol::CaretStateIndicator). keycode is
|
|
// the resulting mode (1 = Chinese / Chinese punctuation / fullwidth); point[]
|
|
// is the physical caret anchor or {0, INVALID_Y} when none was resolved.
|
|
// IMESwitch: wch is the trigger (FanyImeCaretStateTrigger below) and
|
|
// modifiers_down carries the event-time Caps Lock snapshot (FanyImePipeFlags).
|
|
// PuncSwitch: wch is the IME open state (1 = Chinese) for the mode slot.
|
|
constexpr std::uint32_t IMESwitch = 7;
|
|
constexpr std::uint32_t PuncSwitch = 8;
|
|
constexpr std::uint32_t DoubleSingleByteSwitch = 9;
|
|
// Main, negotiated InputStateShortcuts: keycode is the stable input-state Action ID.
|
|
constexpr std::uint32_t InputStateAction = 17;
|
|
constexpr bool IsRouteDeactivation(std::uint32_t event_type)
|
|
{
|
|
return event_type == ClientDeactivated || event_type == ClientSuspended;
|
|
}
|
|
constexpr bool IsTerminalDeactivation(std::uint32_t event_type)
|
|
{
|
|
return event_type == ClientDeactivated;
|
|
}
|
|
} // namespace FanyImePipeEventType
|
|
|
|
// Why an IMESwitch badge was requested; carried in its wch field. Values are
|
|
// append-only. A Server that predates a value treats it as UserToggle.
|
|
namespace FanyImeCaretStateTrigger
|
|
{
|
|
constexpr std::uint32_t UserToggle = 0;
|
|
constexpr std::uint32_t CapsLockEdge = 0x14; // VK_CAPITAL, as first shipped
|
|
// Keyboard focus moved into another text field; keycode is the
|
|
// current mode, not a change. Shown only when the user opted in.
|
|
constexpr std::uint32_t FocusEntered = 1;
|
|
} // namespace FanyImeCaretStateTrigger
|
|
|
|
// OR'd into modifiers_down on Main-pipe packets. Server strips UiLess before
|
|
// any key-modifier policy runs. IMESwitch packets use the next two high bits
|
|
// as an append-only event-time Caps Lock snapshot; old Servers ignore them.
|
|
namespace FanyImePipeFlags
|
|
{
|
|
constexpr std::uint32_t UiLess = 0x80000000u;
|
|
constexpr std::uint32_t ImeSwitchCapsSnapshotPresent = 0x40000000u;
|
|
constexpr std::uint32_t ImeSwitchCapsSnapshotEnabled = 0x20000000u;
|
|
// KeyEvent only: the focused field declares a password or PIN input scope. The
|
|
// DLL checks only while the Server reports AiContextUploadChanged "1"; the Server
|
|
// then sends no AI request and keeps the committed text out of the AI context.
|
|
// The Server strips it before any key-modifier policy runs; old Servers never
|
|
// receive it because the DLL only sets it after the new opcode turned it on.
|
|
// 0x10000000..0x04000000 belong to FanyImeTrilingualInput.
|
|
constexpr std::uint32_t SensitiveField = 0x02000000u;
|
|
// KeyEvent only, plain Left / Right while candidates are shown: the DLL asks the Server to decide between moving
|
|
// the candidate highlight and moving the composition caret (horizontal candidate window, see
|
|
// HorizontalArrowSelectionChanged). The Server answers a highlight move with MoveSelectionPrevious / MoveSelectionNext
|
|
// and a caret move with CompositionRestored carrying the caret. The DLL only sets it after the Server turned the
|
|
// feature on and CompositionRestore was negotiated; the Server strips it before any key-modifier policy runs.
|
|
constexpr std::uint32_t HorizontalArrowSelection = 0x01000000u;
|
|
|
|
constexpr std::uint32_t EncodeImeSwitchCapsLockSnapshot(bool enabled)
|
|
{
|
|
return ImeSwitchCapsSnapshotPresent | (enabled ? ImeSwitchCapsSnapshotEnabled : 0u);
|
|
}
|
|
|
|
constexpr bool HasImeSwitchCapsLockSnapshot(std::uint32_t flags)
|
|
{
|
|
return (flags & ImeSwitchCapsSnapshotPresent) != 0;
|
|
}
|
|
|
|
constexpr bool ImeSwitchCapsLockSnapshotEnabled(std::uint32_t flags)
|
|
{
|
|
return HasImeSwitchCapsLockSnapshot(flags) && (flags & ImeSwitchCapsSnapshotEnabled) != 0;
|
|
}
|
|
|
|
constexpr std::optional<bool> DecodeImeSwitchCapsLockSnapshot(std::uint32_t flags)
|
|
{
|
|
if (!HasImeSwitchCapsLockSnapshot(flags))
|
|
return std::nullopt;
|
|
return ImeSwitchCapsLockSnapshotEnabled(flags);
|
|
}
|
|
} // namespace FanyImePipeFlags
|
|
|
|
namespace FanyImePipeRole
|
|
{
|
|
constexpr std::uint32_t Main = 0;
|
|
constexpr std::uint32_t ToTsf = 1;
|
|
constexpr std::uint32_t ToTsfWorkerThread = 2;
|
|
} // namespace FanyImePipeRole
|
|
|
|
// Tauri-to-Server voice lifecycle commands. Consumers must authenticate the
|
|
// client_id and activation epoch with the registered pipe lease; generation
|
|
// is a non-zero session value used to reject stale cancellation requests.
|
|
namespace FanyImeVoiceControl
|
|
{
|
|
constexpr std::uint32_t Start = 1;
|
|
constexpr std::uint32_t Stop = 2;
|
|
constexpr std::uint32_t Cancel = 3;
|
|
constexpr std::size_t MaxMessageChars = 96;
|
|
} // namespace FanyImeVoiceControl
|
|
|
|
struct alignas(8) FanyImePipeHello
|
|
{
|
|
uint64_t client_id = 0;
|
|
std::uint32_t pipe_role = 0;
|
|
};
|
|
|
|
//
|
|
// Data received from server end
|
|
//
|
|
// msg_type is a FanyImeReplyType opcode (declared below); candidate_string
|
|
// carries the reply's UTF-16 payload, which is empty for most opcodes.
|
|
//
|
|
struct alignas(8) FanyImeNamedpipeDataToTsf
|
|
{
|
|
std::uint32_t msg_type;
|
|
uint64_t request_id = 0;
|
|
FanyImeWireChar candidate_string[FanyImePipeLimits::CandidateTextCapacity];
|
|
};
|
|
|
|
//
|
|
// Data sent to tsf worker thread
|
|
//
|
|
// msg_type
|
|
// 0: IME switch to EN
|
|
// 1: IME switch to CN
|
|
//
|
|
// data
|
|
// Not used now.
|
|
//
|
|
//
|
|
struct FanyImeNamedpipeDataToTsfWorkerThread
|
|
{
|
|
std::uint32_t msg_type;
|
|
FanyImeWireChar data[FanyImePipeLimits::CandidateTextCapacity];
|
|
};
|
|
|
|
struct FanyImeTsfDiagnosticBatchHeader
|
|
{
|
|
uint32_t magic = FANY_IME_TSF_DIAGNOSTIC_MAGIC;
|
|
uint32_t version = FANY_IME_TSF_DIAGNOSTIC_VERSION;
|
|
uint32_t header_size = 28;
|
|
uint32_t payload_bytes = 0;
|
|
uint32_t record_count = 0;
|
|
uint32_t dropped_count = 0;
|
|
uint32_t source_process_id = 0;
|
|
};
|
|
|
|
// Statistics frames never carry text. The TSF DLL classifies committed text
|
|
// inside its own process and the pipe only sees per-class counts; local-day
|
|
// bucketing, active-time and every aggregate live in the stats tool. A frame is
|
|
// header + event_count * sizeof(FanyImeStatsEvent), written in one WriteFile.
|
|
// dropped_count covers the events lost since the previous frame (queue overflow
|
|
// plus failed sends). The tool drops any frame whose magic, version or sizes do
|
|
// not match instead of parsing it.
|
|
struct FanyImeStatsBatchHeader
|
|
{
|
|
uint32_t magic = FANY_IME_STATS_MAGIC;
|
|
uint32_t version = FANY_IME_STATS_VERSION;
|
|
uint32_t header_size = 28;
|
|
uint32_t payload_bytes = 0;
|
|
uint32_t event_count = 0;
|
|
uint32_t dropped_count = 0;
|
|
uint32_t source_process_id = 0;
|
|
};
|
|
|
|
// One committed-text event: when it committed and how many user-perceived
|
|
// characters of each class it contained (a UTF-16 surrogate pair counts once).
|
|
struct FanyImeStatsEvent
|
|
{
|
|
uint64_t timestamp_utc_ft = 0; // raw GetSystemTimeAsFileTime value, UTC 100 ns
|
|
uint16_t cjk = 0;
|
|
uint16_t latin = 0;
|
|
uint16_t digit = 0;
|
|
uint16_t punct = 0;
|
|
uint16_t other = 0;
|
|
// The struct is 24 bytes on the wire; naming the tail keeps the compiler
|
|
// from shipping indeterminate stack bytes across the process boundary and
|
|
// reserves the space for a later versioned field. Always written as zero.
|
|
uint16_t reserved[3] = {0, 0, 0};
|
|
};
|
|
|
|
static_assert(sizeof(FanyImeWireChar) == 2, "The IPC ABI requires 16-bit FanyImeWireChar.");
|
|
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);
|
|
static_assert(sizeof(FanyImeTsfDiagnosticBatchHeader) == 28);
|
|
static_assert(sizeof(FanyImeStatsBatchHeader) == 28);
|
|
static_assert(offsetof(FanyImeStatsBatchHeader, magic) == 0);
|
|
static_assert(offsetof(FanyImeStatsBatchHeader, version) == 4);
|
|
static_assert(offsetof(FanyImeStatsBatchHeader, header_size) == 8);
|
|
static_assert(offsetof(FanyImeStatsBatchHeader, payload_bytes) == 12);
|
|
static_assert(offsetof(FanyImeStatsBatchHeader, event_count) == 16);
|
|
static_assert(offsetof(FanyImeStatsBatchHeader, dropped_count) == 20);
|
|
static_assert(offsetof(FanyImeStatsBatchHeader, source_process_id) == 24);
|
|
static_assert(sizeof(FanyImeStatsEvent) == 24);
|
|
static_assert(offsetof(FanyImeStatsEvent, timestamp_utc_ft) == 0);
|
|
static_assert(offsetof(FanyImeStatsEvent, cjk) == 8);
|
|
static_assert(offsetof(FanyImeStatsEvent, latin) == 10);
|
|
static_assert(offsetof(FanyImeStatsEvent, digit) == 12);
|
|
static_assert(offsetof(FanyImeStatsEvent, punct) == 14);
|
|
static_assert(offsetof(FanyImeStatsEvent, other) == 16);
|
|
static_assert(offsetof(FanyImeStatsEvent, reserved) == 18);
|
|
|
|
namespace FanyImeReplyType
|
|
{
|
|
constexpr std::uint32_t Normal = 0;
|
|
constexpr std::uint32_t OutofRange = 1;
|
|
constexpr std::uint32_t NeedToCreateWord = 2;
|
|
constexpr std::uint32_t Preedit = 3;
|
|
constexpr std::uint32_t NavigationIgnored = 4;
|
|
constexpr std::uint32_t MoveSelectionPrevious = 5;
|
|
constexpr std::uint32_t MoveSelectionNext = 6;
|
|
constexpr std::uint32_t MovePagePrevious = 7;
|
|
constexpr std::uint32_t MovePageNext = 8;
|
|
// Reverse-pipe registration acknowledgement. This frame is consumed during
|
|
// the ToTsf pipe handshake and is never exposed as a key reply.
|
|
constexpr std::uint32_t PipeReady = 9;
|
|
// candidate_string is the complete text to commit; do not append punctuation.
|
|
constexpr std::uint32_t CommitExactText = 10;
|
|
// UILess hosts (games): candidate_string = preedit + L'\t' + cand1,cand2,...
|
|
// Plain words only — no helpcodes — so ITfCandidateListUIElement::GetString
|
|
// matches Microsoft IME behavior for host-drawn candidate UI.
|
|
constexpr std::uint32_t UiLessComposition = 11;
|
|
// Registration-only replies, consumed before a new client may send keys.
|
|
constexpr std::uint32_t ProtocolReady = 12;
|
|
constexpr std::uint32_t ProtocolMismatch = 13;
|
|
// The Server retracted or repositioned an in-progress word and restored its raw
|
|
// pinyin: this is the authoritative reply for the retraction (Backspace), the
|
|
// unit deletion (Ctrl+Backspace) and the unit caret move (Ctrl+Left /
|
|
// Ctrl+Right), and for a plain Left / Right sent with
|
|
// FanyImePipeFlags::HorizontalArrowSelection that moved the caret. candidate_string =
|
|
// remaining_raw \t committed_word \t display_preedit [\t caret]
|
|
// caret is an optional decimal offset into remaining_raw; a 3-field payload
|
|
// means "caret at the end". NeedToCreateWord and CompositionRestored share the
|
|
// payload parser on the TSF side.
|
|
constexpr std::uint32_t CompositionRestored = 14;
|
|
// Commit the already selected word prefix and remaining raw input before cycling languages.
|
|
// candidate_string = destination ("0" Chinese, "1" Japanese, "2" English) + \t + exact commit text.
|
|
// Only clients negotiating FanyImeProtocol::TrilingualCycle may receive this reply.
|
|
constexpr std::uint32_t TrilingualCycle = 15;
|
|
// Highest opcode a live Server may send. Local-only sentinels are larger and
|
|
// must never be accepted as a Server frame.
|
|
constexpr std::uint32_t MaxKnown = TrilingualCycle;
|
|
// Local-only result. It is never sent over the pipe and must never be
|
|
// interpreted as candidate text to commit.
|
|
constexpr std::uint32_t TransportUnavailable = static_cast<std::uint32_t>(-1);
|
|
} // namespace FanyImeReplyType
|
|
|
|
namespace FanyImeWorkerReplyType
|
|
{
|
|
constexpr std::uint32_t SwitchToEnglish = 0;
|
|
constexpr std::uint32_t SwitchToChinese = 1;
|
|
constexpr std::uint32_t SwitchToPuncEn = 2;
|
|
constexpr std::uint32_t SwitchToPuncCn = 3;
|
|
constexpr std::uint32_t SwitchToFullwidth = 4;
|
|
constexpr std::uint32_t SwitchToHalfwidth = 5;
|
|
constexpr std::uint32_t CommitCurCandidate = 6;
|
|
constexpr std::uint32_t PagingCommaPeriodChanged = 7;
|
|
constexpr std::uint32_t FocusSessionReady = 8;
|
|
// Worker-endpoint registration acknowledgement. It is consumed before the
|
|
// handle is published to IpcWorkerThread and before Main can be opened.
|
|
constexpr std::uint32_t PipeReady = 9;
|
|
// Unsolicited text insert (voice ASR, etc.). Does not finalize candidates.
|
|
constexpr std::uint32_t InsertText = 10;
|
|
// Master switch for the reversible Chinese/ASCII punctuation conversion:
|
|
// space after a committed Chinese punctuation converts it. Payload "0"/"1".
|
|
constexpr std::uint32_t SmartPunctuationChanged = 11;
|
|
// Auto-complete opening paired punctuation and leave the caret inside. Payload "0"/"1".
|
|
constexpr std::uint32_t PairedPunctuationChanged = 12;
|
|
// Whether ';' is an input key for the configured shuangpin profile (Microsoft, Sogou and
|
|
// Ziguang put the ing final there). The name predates the other profiles.
|
|
constexpr std::uint32_t MicrosoftShuangpinChanged = 13;
|
|
// Revert a converted ASCII punctuation back to Chinese when the same
|
|
// punctuation key is pressed within the revert window. Payload "0"/"1".
|
|
constexpr std::uint32_t SmartPunctuationRepeatToChineseChanged = 14;
|
|
// Streaming ASR: replace the inline composition with this full snapshot.
|
|
constexpr std::uint32_t UpdateVoiceComposition = 15;
|
|
// Streaming ASR: abort the voice composition without committing.
|
|
constexpr std::uint32_t CancelVoiceComposition = 16;
|
|
// Streaming ASR: replace the inline composition with this snapshot and commit.
|
|
constexpr std::uint32_t CommitVoiceComposition = 17;
|
|
// Payload "1" when the active CJK input mode is Japanese, otherwise "0".
|
|
constexpr std::uint32_t InputModeChanged = 18;
|
|
// Payload "1" when Caps Lock is on. Server is the source of truth.
|
|
constexpr std::uint32_t CapsLockChanged = 19;
|
|
// Enables buffered TSF diagnostics sent to Server. Payload "0"/"1".
|
|
constexpr std::uint32_t TsfDiagnosticLogChanged = 20;
|
|
// Payload "0" follow IME, "1" always Chinese punctuation, "2" always English punctuation.
|
|
constexpr std::uint32_t PunctuationLockChanged = 21;
|
|
// A space after a committed Chinese punctuation converts it to ASCII. Covers
|
|
// plain punctuation and individually-occurring paired symbols. Payload "0"/"1".
|
|
constexpr std::uint32_t SmartPunctuationSpaceConvertChanged = 22;
|
|
// 23 was the paired-symbol space conversion, removed because an auto-closed
|
|
// pair loses its caret position. The number stays unused: this branch already
|
|
// shipped test builds, so reusing it would let an old test build misinterpret
|
|
// a new frame.
|
|
// Direct ASCII punctuation output for ',' '.' ':' after ASCII digits. Payload "0"/"1".
|
|
// Opcode 24 used to cover letters and digits together. Keeping the number and narrowing
|
|
// it to digits is a semantic subset, not a reuse: a new DLL reading an old "1" frame
|
|
// still does the right thing (digits direct, letters not), while the old DLL ignores the
|
|
// new 25 because it filters everything above its MaxKnown.
|
|
constexpr std::uint32_t SmartPunctuationDirectDigitChanged = 24;
|
|
// Direct ASCII punctuation output for ',' '.' ':' after ASCII letters. Payload "0"/"1".
|
|
constexpr std::uint32_t SmartPunctuationDirectLetterChanged = 25;
|
|
// Local input statistics master switch. Payload "0"/"1". With it off the DLL
|
|
// classifies nothing, queues nothing and never opens the statistics pipe, so
|
|
// the opt-out costs the input path exactly one relaxed atomic load.
|
|
constexpr std::uint32_t StatisticsEnabledChanged = 26;
|
|
// Server-decided auto-commit with the characters it consumed. Payload
|
|
// "<consumed>\t<text>": consumed is the decimal number of leading characters this
|
|
// delivery replaces in the TSF keystroke buffer, text is the committed text (empty
|
|
// consumes without committing). TSF trims its own buffer with that count instead of
|
|
// applying a remainder the Server computed from a possibly stale view, so letters the
|
|
// user typed past the committed code survive as the next composition.
|
|
constexpr std::uint32_t CommitCandidateAndContinue = 27;
|
|
// Whether '`' can open a shuangpin mid-sentence helpcode block while composing. Payload "0"/"1".
|
|
// TSF still checks the block shape at its caret itself (FanyImeMidSentenceHelpcode::AcceptsMarkerAt)
|
|
// and the Server makes the final call; this flag only keeps TSF from eating '`' when the feature is off.
|
|
// The payload must stay a single "0"/"1": the worker drops any longer frame for this opcode.
|
|
constexpr std::uint32_t MidSentenceHelpcodeChanged = 28;
|
|
// Same as MidSentenceHelpcodeChanged for the optional ';' trigger. Payload "0"/"1". A separate
|
|
// opcode keeps 28 meaning "'`' is a trigger" for older DLLs, which ignore this one.
|
|
constexpr std::uint32_t MidSentenceHelpcodeSemicolonChanged = 29;
|
|
// Whether shuangpin direct helpcode (Wanxiang style, no guide key) is on. Payload "0" off, "1" on with '/' ending a
|
|
// four-key code, "2" on with '/' left as punctuation (FanyImeDirectHelpcode::PayloadFor). When on, TSF stops requiring
|
|
// an odd chunk before the ';' final (FanyImeDirectHelpcode::AcceptsSemicolonFinalAt); with "1" it also eats '/' after a
|
|
// four-key code (FanyImeDirectHelpcode::AcceptsSlashAt). Single-character payload, same rule as opcode 28; a DLL that
|
|
// only knows "0"/"1" drops "2" and keeps its previous state.
|
|
constexpr std::uint32_t DirectHelpcodeChanged = 30;
|
|
// Whether the uppercase trigger of shuangpin mid-sentence helpcode is in effect (mid-sentence helpcode on, uppercase
|
|
// trigger checked, shuangpin, direct helpcode off). Payload "0"/"1". TSF still eats letters as before; the flag only
|
|
// changes how it counts the chunk before '`' / ';' (FanyImeMidSentenceHelpcode::ChunkStart with uppercase blocks).
|
|
constexpr std::uint32_t MidSentenceHelpcodeUppercaseChanged = 31;
|
|
// Which prefix opens V mode (number to Chinese, arithmetic). Payload "0" off, "1" uppercase V only (shuangpin),
|
|
// "2" V or v (quanpin); see FanyImeVModeInput::Trigger. TSF then eats digits and . + - * / ( ) after the prefix
|
|
// (FanyImeVModeInput::AcceptsAt). Single-character payload, same rule as opcode 28.
|
|
constexpr std::uint32_t VModeChanged = 32;
|
|
// Whether language-switch hotkeys commit raw composition, then cycle Chinese/Japanese/English. Payload "0"/"1".
|
|
// Separate from InputModeChanged so old DLLs retain its active-language meaning and ignore this switch.
|
|
constexpr std::uint32_t TrilingualCycleChanged = 33;
|
|
// Whether the AI assistant uploads the committed-text context. Payload "0"/"1". Only while it is "1" does the DLL
|
|
// read the focused field's input scope and mark password fields with FanyImePipeFlags::SensitiveField, so with
|
|
// the assistant off the key path does no extra TSF work.
|
|
constexpr std::uint32_t AiContextUploadChanged = 34;
|
|
// UI-less hosts only: the Server replaced the candidate page outside a key reply (cloud/AI suggestion, reranking,
|
|
// mixed English). Payload has the UiLessComposition shape "<preedit>\t<page>\t<selection>\t<generation>" with an
|
|
// empty preedit; see FanyImeUiLessPage. Without it the host keeps drawing the page it read on the last key while
|
|
// digits commit from the new one.
|
|
constexpr std::uint32_t UiLessCandidatesChanged = 35;
|
|
// Temporary R-mode policy: one character 0..3, see temporary_japanese_input.h.
|
|
constexpr std::uint32_t TemporaryJapanesePolicyChanged = 36;
|
|
// Which keys open a shuangpin end helpcode block while the end helpcode is indirect (triggered by a guide key instead
|
|
// of being read off the input shape). Payload one character '0'..'7', see FanyImeEndHelpcode::PayloadFor: bit 1 '`',
|
|
// bit 2 ';', bit 4 uppercase. TSF eats '`' / ';' that can start a block (FanyImeMidSentenceHelpcode::
|
|
// AcceptsEndMarkerAt) and records them as FanyImeMidSentenceHelpcode::kEndMarker; with the uppercase bit it counts an
|
|
// uppercase end block as no keys of the chunk. Older DLLs ignore the opcode and keep treating the keys as punctuation.
|
|
constexpr std::uint32_t EndHelpcodeTriggersChanged = 37;
|
|
// Which keys produce the enumeration comma 、 under Chinese punctuation. Payload one character '1'..'3', see
|
|
// FanyImeEnumerationComma::PayloadFor: bit 1 '\', bit 2 '/'. A key whose bit is clear is not Chinese punctuation and
|
|
// passes through as itself. Older DLLs ignore the opcode and keep '\' only.
|
|
constexpr std::uint32_t EnumerationCommaKeysChanged = 38;
|
|
// Whether plain Left / Right move the candidate highlight while the candidate window is horizontal. Payload "0"/"1":
|
|
// "1" only when the setting is on and the configured layout is horizontal. With it on the DLL sends those keys with
|
|
// FanyImePipeFlags::HorizontalArrowSelection and applies the Server's answer instead of moving the caret itself.
|
|
// Older DLLs ignore the opcode and keep moving the caret.
|
|
constexpr std::uint32_t HorizontalArrowSelectionChanged = 39;
|
|
constexpr std::uint32_t MaxKnown = HorizontalArrowSelectionChanged;
|
|
// Source compatibility for the Server's historical spellings.
|
|
constexpr std::uint32_t SwitchToEn = SwitchToEnglish;
|
|
constexpr std::uint32_t SwitchToCn = SwitchToChinese;
|
|
constexpr std::uint32_t CommitCandidate = CommitCurCandidate;
|
|
} // namespace FanyImeWorkerReplyType
|