Files
msime-windows/engine/contracts/temporary_japanese_input.h
6072ae7784 fix(windows): 修复临时日语 R 模式无法输入长音符 (#1000)
* fix(windows): 修复临时日语 R 模式无法输入长音符

* fix(server,windows): 临时日语策略每键只算一次,并去掉多余的帧

- Server 在 HandleImeKey 开头按本次按键所属客户端算一次策略,不再每个谓词都查活动客户端、加锁、构造 payload 再解析回来。
- 普通激活只补发 TemporaryJapanesePolicyChanged;整套输入模式状态(含语言栏刷新)仍只在 FocusRestored 时发送。
- 配置重载时输入方案变了由 ApplyConfiguredInputScheme 统一广播,不再重复广播策略。
- TSF 的 WaitForProtocolReady 不再清零策略:worker 管道先于 Main 注册,可能已收到本次连接的策略;断开时已有清零路径。

---------

Co-authored-by: ranly <ran618ly210805@qq.com>
Co-authored-by: fanlusky <1730976608@qq.com>
2026-10-16 22:30:28 +08:00

91 lines
3.2 KiB
C++

#pragma once
#include <cstddef>
#include <cstdint>
// Shared TSF/Server classification of the display-only R prefix. The engine
// receives the body without R. Policy is supplied by the Server after negotiation.
namespace FanyImeTemporaryJapanese
{
using Policy = std::uint8_t;
inline constexpr Policy Allowed = 1;
inline constexpr Policy EnglishCandidates = 2;
constexpr bool IsValidPayload(wchar_t ch)
{
return ch >= L'0' && ch <= L'3';
}
constexpr Policy FromPayload(wchar_t ch)
{
return IsValidPayload(ch) ? Policy(ch - L'0') : 0;
}
constexpr wchar_t ToPayload(Policy policy)
{
return wchar_t(L'0' + (policy & 3));
}
constexpr Policy ToggleEnglish(Policy policy)
{
return policy ^ EnglishCandidates;
}
constexpr Policy LeaveEnglish(Policy policy)
{
return policy & ~EnglishCandidates;
}
template <typename Char> constexpr bool IsMode(bool japanese, Policy policy, const Char *raw, std::size_t length)
{
return japanese || ((policy & Allowed) != 0 && (policy & EnglishCandidates) == 0 && raw != nullptr && length > 0 &&
raw[0] == Char('R'));
}
// R is a display-only mode prefix. Caret positions in the romaji body start at 1.
template <typename Char>
constexpr std::size_t MinimumCaret(bool japanese, Policy policy, const Char *raw, std::size_t length)
{
return !japanese && IsMode(false, policy, raw, length) ? 1 : 0;
}
enum class KeyAction
{
None,
LongVowel,
Punctuation
};
// Virtual-key and modifier values are the existing Windows pipe contract values.
// Decide before candidate paging, word-to-character conversion and punctuation commit.
template <typename Char>
constexpr KeyAction ClassifyKey(std::uint32_t key, std::uint32_t character, std::uint32_t modifiers, bool japanese,
Policy policy, const Char *raw, std::size_t length, bool composition_active)
{
if ((modifiers & 6u) != 0 || (key != 0xBD && key != 0xBB) || !IsMode(japanese, policy, raw, length))
return KeyAction::None;
if (key == 0xBD && character == '-')
return KeyAction::LongVowel;
return length > 0 && composition_active ? KeyAction::Punctuation : KeyAction::None;
}
template <typename Char>
constexpr std::size_t ClampCaret(bool japanese, Policy policy, const Char *raw, std::size_t length, std::size_t caret)
{
const auto minimum = MinimumCaret(japanese, policy, raw, length);
return caret < minimum ? minimum : (caret > length ? length : caret);
}
template <typename Char>
constexpr bool CanBackspace(bool japanese, Policy policy, const Char *raw, std::size_t length, std::size_t caret)
{
const auto minimum = MinimumCaret(japanese, policy, raw, length);
return caret > 0 && caret <= length && !(caret == minimum && length > minimum);
}
template <typename Char>
constexpr bool CanMoveCaret(bool japanese, Policy policy, const Char *raw, std::size_t length, std::int64_t next)
{
return next >= static_cast<std::int64_t>(MinimumCaret(japanese, policy, raw, length)) &&
next <= static_cast<std::int64_t>(length);
}
template <typename Char> constexpr bool IsValidPolicyFrame(const Char *data, std::size_t capacity)
{
return data != nullptr && capacity >= 2 && IsValidPayload(data[0]) && data[1] == Char(0);
}
} // namespace FanyImeTemporaryJapanese