#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 #include #include #include #include #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 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 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(-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 // "\t": 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 "\t\t\t" 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