Files
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

86 lines
2.9 KiB
C++

#pragma once
#include <cstdint>
#include <string>
#include <string_view>
namespace FanyImeTrilingualInput
{
enum class Mode
{
Chinese = 0,
Japanese = 1,
English = 2
};
constexpr Mode Next(bool ime_enabled, bool japanese)
{
return !ime_enabled ? Mode::Chinese : japanese ? Mode::English : Mode::Japanese;
}
// OR'd into modifiers_down of the Main-pipe KeyEvent that requests a cycle.
// Only a client that negotiated FanyImeProtocol::TrilingualCycle sets them, and
// only on the VK_SHIFT it normalizes from an already matched language hotkey.
// The binary language toggle still sends a plain VK_SHIFT, so the Server never
// infers a cycle from the key alone. The two state bits carry the client's own
// OPENCLOSE and native language: with ime_mode_scope=app every host has its own
// state, and the Server must cycle from the requester's state, not its global one.
// The Server strips these bits before any key-modifier policy runs.
constexpr std::uint32_t CycleRequestFlag = 0x10000000u;
constexpr std::uint32_t CycleFromImeOpenFlag = 0x08000000u;
constexpr std::uint32_t CycleFromJapaneseFlag = 0x04000000u;
constexpr std::uint32_t CycleRequestMask = CycleRequestFlag | CycleFromImeOpenFlag | CycleFromJapaneseFlag;
constexpr std::uint32_t EncodeCycleRequest(bool ime_enabled, bool japanese)
{
return CycleRequestFlag | (ime_enabled ? CycleFromImeOpenFlag : 0u) | (japanese ? CycleFromJapaneseFlag : 0u);
}
constexpr bool IsCycleRequest(std::uint32_t key, std::uint32_t modifiers)
{
return key == 0x10u && (modifiers & CycleRequestFlag) != 0 && (modifiers & 7u) == 0;
}
constexpr bool CycleRequestImeEnabled(std::uint32_t modifiers)
{
return (modifiers & CycleFromImeOpenFlag) != 0;
}
constexpr bool CycleRequestJapanese(std::uint32_t modifiers)
{
return (modifiers & CycleFromJapaneseFlag) != 0;
}
// Destination the Server answers for a cycle request. When the switch was turned
// off after the client queued the request, the request still gets its reply, but
// as the ordinary binary toggle: English, or back to the configured language.
constexpr Mode Destination(bool ime_enabled, bool japanese, bool cycle_enabled, bool configured_japanese)
{
if (cycle_enabled)
{
return Next(ime_enabled, japanese);
}
return ime_enabled ? Mode::English : configured_japanese ? Mode::Japanese : Mode::Chinese;
}
inline std::wstring BuildPayload(Mode mode, std::wstring_view text)
{
std::wstring payload(1, static_cast<wchar_t>(L'0' + static_cast<int>(mode)));
payload.push_back(L'\t');
payload.append(text);
return payload;
}
inline bool ParsePayload(std::wstring_view payload, Mode &mode, std::wstring &text)
{
if (payload.size() < 2 || payload[0] < L'0' || payload[0] > L'2' || payload[1] != L'\t' ||
payload.find(L'\0') != std::wstring_view::npos)
{
return false;
}
mode = static_cast<Mode>(payload[0] - L'0');
text.assign(payload.substr(2));
return true;
}
} // namespace FanyImeTrilingualInput