Files
msime-windows/windows/tests/deferred_key_failure_policy.cpp
AkaneIwasaandfanlusky 568d07f513 feat(windows): 添加中日英三语轮换 (#717)
* feat(windows): 添加中日英三语轮换

* fix(windows): 三语轮换改为快捷键开关并修复两端状态不同步

- 开关移到「快捷键 → 中英文切换」下的「允许中日英循环切换」,配置键改为
  [keybindings] trilingual_cycle;输入模式恢复为中文/日文,二者互不影响
- 循环请求带显式标记位和请求方自身的中英/日文状态,Server 据此计算目标语言;
  普通 VK_SHIFT 仍是中英切换,不再被误当成循环
- Server 开关在请求途中被关闭时仍回复,按中英切换处理,TSF 只丢弃后续排队按键,
  不再超时重连
- 已发出的循环回复未被应用(含 Superseded)时走完整重连,避免日文状态两端不一致
- 循环切换补上光标处状态提示和标点同步
- 切换语言时复用暂存的另一语言会话,不在按键路径上重建引擎和打开 msime.db
- 配置读取的语言与开关改为原子量;TrilingualCycleChanged 的能力过滤移到发送方
- 中日切换不再重发 TrilingualCycleChanged;结束组合被拒绝时不计入统计

---------

Co-authored-by: fanlusky <1730976608@qq.com>
2026-10-09 15:44:41 +08:00

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;
}