* feat(windows): 添加中日英三语轮换 * fix(windows): 三语轮换改为快捷键开关并修复两端状态不同步 - 开关移到「快捷键 → 中英文切换」下的「允许中日英循环切换」,配置键改为 [keybindings] trilingual_cycle;输入模式恢复为中文/日文,二者互不影响 - 循环请求带显式标记位和请求方自身的中英/日文状态,Server 据此计算目标语言; 普通 VK_SHIFT 仍是中英切换,不再被误当成循环 - Server 开关在请求途中被关闭时仍回复,按中英切换处理,TSF 只丢弃后续排队按键, 不再超时重连 - 已发出的循环回复未被应用(含 Superseded)时走完整重连,避免日文状态两端不一致 - 循环切换补上光标处状态提示和标点同步 - 切换语言时复用暂存的另一语言会话,不在按键路径上重建引擎和打开 msime.db - 配置读取的语言与开关改为原子量;TrilingualCycleChanged 的能力过滤移到发送方 - 中日切换不再重发 TrilingualCycleChanged;结束组合被拒绝时不计入统计 --------- Co-authored-by: fanlusky <1730976608@qq.com>
115 lines
4.9 KiB
C++
115 lines
4.9 KiB
C++
#include "Key/DeferredKeyFailurePolicy.h"
|
|
|
|
namespace
|
|
{
|
|
using Kind = DeferredKeyFailureKind;
|
|
using Reason = DeferredKeyFailureReason;
|
|
|
|
bool Resolves(Reason reason, bool offline, Kind expected)
|
|
{
|
|
return ResolveDeferredKeyFailure(reason, offline) == expected;
|
|
}
|
|
} // namespace
|
|
|
|
int main()
|
|
{
|
|
// A superseded key never starts another reset, online or offline.
|
|
if (!Resolves(Reason::Superseded, false, Kind::Stale))
|
|
return 1;
|
|
if (!Resolves(Reason::Superseded, true, Kind::Stale))
|
|
return 2;
|
|
|
|
// Host quirks resync the composition over the healthy pipes.
|
|
if (!Resolves(Reason::HostEditRejected, false, Kind::Resync))
|
|
return 3;
|
|
if (!Resolves(Reason::EditSessionRequestFailed, false, Kind::Resync))
|
|
return 4;
|
|
if (!Resolves(Reason::AsyncPostFailed, false, Kind::Resync))
|
|
return 5;
|
|
|
|
// Offline, the local composition is the only authority: nothing to resync.
|
|
if (!Resolves(Reason::HostEditRejected, true, Kind::Offline))
|
|
return 6;
|
|
if (!Resolves(Reason::EditSessionRequestFailed, true, Kind::Offline))
|
|
return 7;
|
|
if (!Resolves(Reason::AsyncPostFailed, true, Kind::Offline))
|
|
return 8;
|
|
|
|
// A broken or ambiguous transport always takes the full reset.
|
|
if (!Resolves(Reason::TransportBroken, false, Kind::Transport))
|
|
return 9;
|
|
if (!Resolves(Reason::TransportBroken, true, Kind::Transport))
|
|
return 10;
|
|
if (!Resolves(Reason::DeliveryAmbiguous, false, Kind::Transport))
|
|
return 11;
|
|
if (!Resolves(Reason::DeliveryAmbiguous, true, Kind::Transport))
|
|
return 12;
|
|
|
|
// Edit-session outcomes: validation beats the HRESULT, ambiguity beats a
|
|
// plain transport error, and anything else is a host rejection.
|
|
if (ClassifyEditSessionFailure(true, true, true) != Reason::Superseded)
|
|
return 13;
|
|
if (ClassifyEditSessionFailure(false, true, true) != Reason::DeliveryAmbiguous)
|
|
return 14;
|
|
if (ClassifyEditSessionFailure(false, false, true) != Reason::TransportBroken)
|
|
return 15;
|
|
if (ClassifyEditSessionFailure(false, false, false) != Reason::HostEditRejected)
|
|
return 16;
|
|
|
|
// A failed cycle reply must refresh native mode, which the Server changed
|
|
// before replying. Ordinary host failures still use the same-token resync.
|
|
const Reason cycleFailures[] = {Reason::HostEditRejected, Reason::EditSessionRequestFailed,
|
|
Reason::AsyncPostFailed};
|
|
for (const auto reason : cycleFailures)
|
|
{
|
|
if (ResolveDeferredKeyFailure(reason, false, true) != Kind::Transport)
|
|
return 19;
|
|
if (ResolveDeferredKeyFailure(reason, false) != Kind::Resync)
|
|
return 20;
|
|
// An offline cycle never reached the Server: discard its dependent
|
|
// queue through Offline while keeping the local raw composition.
|
|
if (ResolveDeferredKeyFailure(reason, true, true) != Kind::Offline)
|
|
return 22;
|
|
}
|
|
// The Server already switched language and left the requester out of the
|
|
// broadcast: an unapplied cycle reply needs a fresh snapshot even when a
|
|
// newer token superseded it.
|
|
if (ResolveDeferredKeyFailure(Reason::Superseded, false, true) != Kind::Transport)
|
|
return 21;
|
|
if (ResolveDeferredKeyFailure(Reason::Superseded, false) != Kind::Stale)
|
|
return 23;
|
|
// A cycle answered as the binary toggle was applied on both sides; only
|
|
// the keys queued behind it are wrong.
|
|
if (ResolveDeferredKeyFailure(Reason::ProjectionInvalidated, false, true) != Kind::Resync)
|
|
return 24;
|
|
if (ResolveDeferredKeyFailure(Reason::ProjectionInvalidated, true, true) != Kind::Offline)
|
|
return 25;
|
|
if (ClassifyEditSessionFailure(false, false, false, true) != Reason::ProjectionInvalidated)
|
|
return 26;
|
|
if (ClassifyEditSessionFailure(false, false, true, true) != Reason::TransportBroken)
|
|
return 27;
|
|
|
|
// Paging and highlight moves are acknowledgement-only.
|
|
const unsigned int navigationKeys[] = {0x09 /*Tab*/, 0x21 /*PageUp*/, 0x22 /*PageDown*/, 0x26 /*Up*/,
|
|
0x28 /*Down*/};
|
|
for (const unsigned int key : navigationKeys)
|
|
{
|
|
if (!IsCandidateNavigationOnlyKey(key))
|
|
return 17;
|
|
}
|
|
// Keys the Server may turn into a commit keep the ambiguous-commit rule:
|
|
// configurable paging punctuation (, . [ ] - =), Enter (Ctrl+Enter
|
|
// translation), Space and the selection digits. Composition edit keys are
|
|
// not candidate keys at all.
|
|
const unsigned int commitCapableKeys[] = {0xBC /*,*/, 0xBE /*.*/, 0xDB /*[*/, 0xDD /*]*/, 0xBD /*-*/,
|
|
0xBB /*=*/, 0x0D /*Enter*/, 0x20 /*Space*/, '1', '9',
|
|
0x24 /*Home*/, 0x23 /*End*/, 0x25 /*Left*/, 0x27 /*Right*/};
|
|
for (const unsigned int key : commitCapableKeys)
|
|
{
|
|
if (IsCandidateNavigationOnlyKey(key))
|
|
return 18;
|
|
}
|
|
|
|
return 0;
|
|
}
|