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