Files
msime-windows/server/src/config/ime_config_input.cpp
T
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

895 lines
25 KiB
C++

// 输入方案与输入行为配置:方案、中日文模式、简繁、中英切换键、双拼与五笔方案、上屏样式、辅助码、全拼纠错、模糊音和调频。
#include "config/ime_config_internal.h"
#include <cstdint>
#include <iterator>
#include <string>
#include <vector>
#include "global/globals.h"
#include "defines/defines.h"
#include "engine/common/helpcode_utils.h"
#include "engine/contracts/direct_helpcode.h"
#include "engine/core/data_path.h"
#include "engine/shuangpin/shuangpin_profile.h"
using namespace ime_config_detail;
SchemeType GetConfiguredInputScheme()
{
return g_input_scheme;
}
SchemeType GetConfiguredActiveInputScheme()
{
return GetActiveInputMode() == "japanese" ? SchemeType::JapaneseRomaji : g_input_scheme;
}
std::string GetConfiguredInputSchemeName()
{
switch (g_input_scheme)
{
case SchemeType::Quanpin:
return "quanpin";
case SchemeType::Shuangpin:
return "shuangpin";
case SchemeType::Wubi:
return "wubi";
default:
return "shuangpin";
}
}
bool SetConfiguredInputScheme(const std::string &scheme)
{
if (scheme != "quanpin" && scheme != "shuangpin" && scheme != "wubi")
{
return false;
}
if (!WriteConfiguredValue("input", "schema", EscapeTomlBasicString(scheme)))
{
return false;
}
g_input_scheme = ParseScheme(scheme);
RefreshEffectiveTsfPreeditStyle();
NotifyImeServerInputSchemeChanged();
return true;
}
const std::string &GetConfiguredCharacterSet()
{
return g_character_set;
}
bool SetConfiguredCharacterSet(const std::string &character_set)
{
if (character_set != "simplified" && character_set != "traditional")
{
return false;
}
if (!WriteConfiguredValue("input", "character_set", EscapeTomlBasicString(character_set)))
{
return false;
}
g_character_set = character_set;
// Marshal WebView/native toolbar refreshes to their owner thread. Settings
// in a separate process reaches the same refresh through ConfigChanged.
NotifyImeServer(WM_REFRESH_CHARACTER_SET, L"ConfigChanged");
return true;
}
bool GetConfiguredCharacterSetShortcutEnabled()
{
return g_character_set_shortcut_enabled;
}
bool SetConfiguredCharacterSetShortcutEnabled(bool enabled)
{
if (!WriteConfiguredValue("keybindings", "toggle_character_set_ctrl_shift_f", enabled ? "true" : "false"))
return false;
g_character_set_shortcut_enabled = enabled;
return true;
}
const std::string &GetConfiguredDefaultImeMode()
{
return g_default_ime_mode;
}
bool SetConfiguredDefaultImeMode(const std::string &mode)
{
if (mode != "chinese" && mode != "english")
{
return false;
}
if (!WriteConfiguredValue("input", "default_ime_mode", EscapeTomlBasicString(mode)))
{
return false;
}
g_default_ime_mode = mode;
return true;
}
const std::string &GetConfiguredImeModeScope()
{
return g_ime_mode_scope;
}
bool SetConfiguredImeModeScope(const std::string &scope)
{
if (scope != "app" && scope != "global")
{
return false;
}
if (!WriteConfiguredValue("input", "ime_mode_scope", EscapeTomlBasicString(scope)))
{
return false;
}
g_ime_mode_scope = scope;
return true;
}
bool IsConfiguredImeModeScopeGlobal()
{
return g_ime_mode_scope == "global";
}
bool GetConfiguredSwitchLanguageShiftEnabled()
{
return g_switch_language_shift_enabled;
}
bool SetConfiguredSwitchLanguageShiftEnabled(bool enabled)
{
if (!WriteConfiguredValue("keybindings", "switch_language_shift", enabled ? "true" : "false"))
{
return false;
}
g_switch_language_shift_enabled = enabled;
return true;
}
bool GetConfiguredSwitchLanguageCtrlEnabled()
{
return g_switch_language_ctrl_enabled;
}
bool SetConfiguredSwitchLanguageCtrlEnabled(bool enabled)
{
if (!WriteConfiguredValue("keybindings", "switch_language_ctrl", enabled ? "true" : "false"))
{
return false;
}
g_switch_language_ctrl_enabled = enabled;
return true;
}
bool GetConfiguredSwitchLanguageCtrlAltSpaceEnabled()
{
return g_switch_language_ctrl_alt_space_enabled;
}
bool SetConfiguredSwitchLanguageCtrlAltSpaceEnabled(bool enabled)
{
if (!WriteConfiguredValue("keybindings", "switch_language_ctrl_alt_space", enabled ? "true" : "false"))
{
return false;
}
g_switch_language_ctrl_alt_space_enabled = enabled;
return true;
}
const std::string &GetConfiguredShuangpinSchema()
{
return g_shuangpin_schema;
}
bool IsConfiguredShuangpinSemicolonFinal()
{
return ShuangpinProfileUsesSemicolonFinal(GetShuangpinProfile(g_shuangpin_schema));
}
bool SetConfiguredShuangpinSchema(const std::string &schema)
{
if (!LoadShuangpinSchema(schema))
{
return false;
}
if (!WriteConfiguredValue("input", "shuangpin_schema", EscapeTomlBasicString(schema)))
{
return false;
}
g_shuangpin_schema = schema;
return true;
}
const std::string &GetConfiguredWubiSchema()
{
return g_wubi_schema;
}
bool SetConfiguredWubiSchema(const std::string &schema)
{
if (schema != "wubi86")
{
return false;
}
if (!WriteConfiguredValue("input", "wubi_schema", EscapeTomlBasicString(schema)))
{
return false;
}
g_wubi_schema = schema;
return true;
}
const std::string &GetConfiguredWubiZMode()
{
return g_wubi_z_mode;
}
bool SetConfiguredWubiZMode(const std::string &mode)
{
if (mode != "off" && mode != "wildcard")
{
return false;
}
if (!WriteConfiguredValue("input", "wubi_z_mode", EscapeTomlBasicString(mode)))
{
return false;
}
g_wubi_z_mode = mode;
return true;
}
bool GetConfiguredWubiMixedPinyin()
{
return g_wubi_mixed_pinyin;
}
bool SetConfiguredWubiMixedPinyin(bool enabled)
{
if (!WriteConfiguredValue("input", "wubi_mixed_pinyin", enabled ? "true" : "false"))
{
return false;
}
g_wubi_mixed_pinyin = enabled;
return true;
}
bool GetConfiguredWubiFourCodeAutoCommit()
{
return g_wubi_four_code_auto_commit;
}
bool SetConfiguredWubiFourCodeAutoCommit(bool enabled)
{
if (!WriteConfiguredValue("input", "wubi_four_code_auto_commit", enabled ? "true" : "false"))
{
return false;
}
g_wubi_four_code_auto_commit = enabled;
return true;
}
bool GetConfiguredWubiFifthCodeTopCommit()
{
return g_wubi_fifth_code_top_commit;
}
bool SetConfiguredWubiFifthCodeTopCommit(bool enabled)
{
if (!WriteConfiguredValue("input", "wubi_fifth_code_top_commit", enabled ? "true" : "false"))
{
return false;
}
g_wubi_fifth_code_top_commit = enabled;
return true;
}
bool GetConfiguredEscapeKeepsSelectedWord()
{
return g_escape_keeps_selected_word;
}
bool SetConfiguredEscapeKeepsSelectedWord(bool enabled)
{
if (!WriteConfiguredValue("input", "escape_keeps_selected_word", enabled ? "true" : "false"))
{
return false;
}
g_escape_keeps_selected_word = enabled;
return true;
}
bool GetConfiguredEnterLearnsEnglishWord()
{
return g_enter_learns_english_word;
}
bool SetConfiguredEnterLearnsEnglishWord(bool enabled)
{
if (!WriteConfiguredValue("input", "enter_learns_english_word", enabled ? "true" : "false"))
{
return false;
}
g_enter_learns_english_word = enabled;
return true;
}
const std::string &GetConfiguredShuangpinPreeditMode()
{
return g_shuangpin_preedit_mode;
}
const std::string &GetConfiguredTsfPreeditStyle()
{
return g_tsf_preedit_style;
}
bool SetConfiguredTsfPreeditStyle(const std::string &style)
{
if (!GlobalSettings::isKnownTsfPreeditStyle(style))
{
return false;
}
if (!WriteConfiguredValue("appearance", "tsf_preedit_style", EscapeTomlBasicString(style)))
{
return false;
}
g_tsf_preedit_style = style;
RefreshEffectiveTsfPreeditStyle();
return true;
}
bool GetConfiguredTsfPreeditShuangpinQuanpin()
{
return g_tsf_preedit_shuangpin_quanpin;
}
bool SetConfiguredTsfPreeditShuangpinQuanpin(bool enabled)
{
if (!WriteConfiguredValue("appearance", "tsf_preedit_shuangpin_quanpin", enabled ? "true" : "false"))
{
return false;
}
g_tsf_preedit_shuangpin_quanpin = enabled;
RefreshEffectiveTsfPreeditStyle();
return true;
}
namespace ime_config_detail
{
void RefreshEffectiveTsfPreeditStyle()
{
// 原始按键样式下 TSF 自己显示按键缓冲,从不等 Server 的预编辑;双拼要显示转换后的全拼就得
// 让它按分词样式等回包,回包里再按用户选的样式去掉分词符号(见 BuildTsfPreedit)。
const bool shuangpin_quanpin = g_tsf_preedit_shuangpin_quanpin && g_tsf_preedit_style == "raw" &&
GetConfiguredActiveInputScheme() == SchemeType::Shuangpin;
GlobalSettings::setTsfPreeditStyle(shuangpin_quanpin ? std::string(GlobalSettings::TsfPreeditStyle::Pinyin)
: g_tsf_preedit_style);
}
} // namespace ime_config_detail
bool GetConfiguredShuangpinHelpcodeEnabled()
{
return g_shuangpin_helpcode_enabled;
}
bool SetConfiguredShuangpinHelpcodeEnabled(bool enabled)
{
if (!WriteConfiguredValue("helpcode", "shuangpin_helpcode", enabled ? "true" : "false"))
{
return false;
}
g_shuangpin_helpcode_enabled = enabled;
return true;
}
bool GetConfiguredShuangpinMidSentenceHelpcodeEnabled()
{
return g_shuangpin_mid_sentence_helpcode_enabled;
}
bool SetConfiguredShuangpinMidSentenceHelpcodeEnabled(bool enabled)
{
// 句中辅助码与直接辅助码互斥:开这个就先关掉那个,设置页随回推的配置快照一起更新。先关后开,
// 写到一半失败时最多两个都关着,不会出现两个都开着。
if (enabled && g_shuangpin_direct_helpcode_enabled)
{
if (!WriteConfiguredValue("helpcode", "shuangpin_direct_helpcode", "false"))
{
return false;
}
g_shuangpin_direct_helpcode_enabled = false;
}
if (!WriteConfiguredValue("helpcode", "shuangpin_mid_sentence_helpcode", enabled ? "true" : "false"))
{
return false;
}
g_shuangpin_mid_sentence_helpcode_enabled = enabled;
return true;
}
bool GetConfiguredShuangpinMidSentenceHelpcodeBacktick()
{
return g_shuangpin_mid_sentence_helpcode_backtick;
}
bool SetConfiguredShuangpinMidSentenceHelpcodeBacktick(bool enabled)
{
if (!WriteConfiguredValue("helpcode", "shuangpin_mid_sentence_helpcode_backtick", enabled ? "true" : "false"))
{
return false;
}
g_shuangpin_mid_sentence_helpcode_backtick = enabled;
return true;
}
bool GetConfiguredShuangpinMidSentenceHelpcodeSemicolon()
{
return g_shuangpin_mid_sentence_helpcode_semicolon;
}
bool SetConfiguredShuangpinMidSentenceHelpcodeSemicolon(bool enabled)
{
if (!WriteConfiguredValue("helpcode", "shuangpin_mid_sentence_helpcode_semicolon", enabled ? "true" : "false"))
{
return false;
}
g_shuangpin_mid_sentence_helpcode_semicolon = enabled;
return true;
}
bool GetConfiguredShuangpinMidSentenceHelpcodeUppercase()
{
return g_shuangpin_mid_sentence_helpcode_uppercase;
}
bool SetConfiguredShuangpinMidSentenceHelpcodeUppercase(bool enabled)
{
if (!WriteConfiguredValue("helpcode", "shuangpin_mid_sentence_helpcode_uppercase", enabled ? "true" : "false"))
{
return false;
}
g_shuangpin_mid_sentence_helpcode_uppercase = enabled;
return true;
}
bool IsConfiguredMidSentenceHelpcodeUppercaseTrigger()
{
// 与反引号、分号同一套门控:直接辅助码接管辅码时句中辅助码的触发键都不生效。
return g_shuangpin_mid_sentence_helpcode_enabled && g_shuangpin_mid_sentence_helpcode_uppercase &&
!g_shuangpin_direct_helpcode_enabled;
}
std::wstring FormatMidSentenceHelpcodeUppercaseWorkerPayload()
{
return IsConfiguredMidSentenceHelpcodeUppercaseTrigger() &&
GetConfiguredActiveInputScheme() == SchemeType::Shuangpin
? L"1"
: L"0";
}
bool GetConfiguredShuangpinDirectHelpcodeEnabled()
{
return g_shuangpin_direct_helpcode_enabled;
}
bool SetConfiguredShuangpinDirectHelpcodeEnabled(bool enabled)
{
// 与句中辅助码互斥,规则同 SetConfiguredShuangpinMidSentenceHelpcodeEnabled。
if (enabled && g_shuangpin_mid_sentence_helpcode_enabled)
{
if (!WriteConfiguredValue("helpcode", "shuangpin_mid_sentence_helpcode", "false"))
{
return false;
}
g_shuangpin_mid_sentence_helpcode_enabled = false;
}
if (!WriteConfiguredValue("helpcode", "shuangpin_direct_helpcode", enabled ? "true" : "false"))
{
return false;
}
g_shuangpin_direct_helpcode_enabled = enabled;
return true;
}
bool GetConfiguredShuangpinDirectHelpcodeSlash()
{
return g_shuangpin_direct_helpcode_slash;
}
bool SetConfiguredShuangpinDirectHelpcodeSlash(bool enabled)
{
// 至少留一个:设置页取消不掉最后一个,这里同样不收。
if (!enabled && !g_shuangpin_direct_helpcode_uppercase)
{
return false;
}
if (!WriteConfiguredValue("helpcode", "shuangpin_direct_helpcode_slash", enabled ? "true" : "false"))
{
return false;
}
g_shuangpin_direct_helpcode_slash = enabled;
return true;
}
bool GetConfiguredShuangpinDirectHelpcodeUppercase()
{
return g_shuangpin_direct_helpcode_uppercase;
}
bool SetConfiguredShuangpinDirectHelpcodeUppercase(bool enabled)
{
if (!enabled && !g_shuangpin_direct_helpcode_slash)
{
return false;
}
if (!WriteConfiguredValue("helpcode", "shuangpin_direct_helpcode_uppercase", enabled ? "true" : "false"))
{
return false;
}
g_shuangpin_direct_helpcode_uppercase = enabled;
return true;
}
std::wstring FormatDirectHelpcodeWorkerPayload()
{
const bool enabled =
g_shuangpin_direct_helpcode_enabled && GetConfiguredActiveInputScheme() == SchemeType::Shuangpin;
return std::wstring(1, FanyImeDirectHelpcode::PayloadFor(enabled, g_shuangpin_direct_helpcode_slash));
}
bool IsConfiguredMidSentenceHelpcodeTrigger(wchar_t ch)
{
// 直接辅助码开着时引擎不收反引号段(两套辅码会把同一个音节约束两遍),触发键跟着失效。
if (!g_shuangpin_mid_sentence_helpcode_enabled || g_shuangpin_direct_helpcode_enabled)
{
return false;
}
return (ch == L'`' && g_shuangpin_mid_sentence_helpcode_backtick) ||
(ch == L';' && g_shuangpin_mid_sentence_helpcode_semicolon);
}
std::wstring FormatMidSentenceHelpcodeWorkerPayload()
{
// TSF 只需要知道反引号此刻有没有可能是编码键:开关开着、勾了反引号且正在用双拼。
return IsConfiguredMidSentenceHelpcodeTrigger(L'`') && GetConfiguredActiveInputScheme() == SchemeType::Shuangpin
? L"1"
: L"0";
}
std::wstring FormatMidSentenceHelpcodeSemicolonWorkerPayload()
{
return IsConfiguredMidSentenceHelpcodeTrigger(L';') && GetConfiguredActiveInputScheme() == SchemeType::Shuangpin
? L"1"
: L"0";
}
const std::string &GetConfiguredShuangpinHelpcodeSchema()
{
return g_shuangpin_helpcode_schema;
}
bool SetConfiguredShuangpinHelpcodeSchema(const std::string &schema)
{
if (!IsHelpcodeSchemaAvailable(schema))
return false;
if (!WriteConfiguredValue("helpcode", "shuangpin_helpcode_schema", EscapeTomlBasicString(schema)))
return false;
g_shuangpin_helpcode_schema = schema;
return true;
}
bool GetConfiguredQuanpinHelpcodeEnabled()
{
return g_quanpin_helpcode_enabled;
}
bool SetConfiguredQuanpinHelpcodeEnabled(bool enabled)
{
if (!WriteConfiguredValue("helpcode", "quanpin_helpcode", enabled ? "true" : "false"))
{
return false;
}
g_quanpin_helpcode_enabled = enabled;
return true;
}
bool GetConfiguredQuanpinAutocorrectTransposition()
{
return g_quanpin_autocorrect_transposition;
}
bool SetConfiguredQuanpinAutocorrectTransposition(bool enabled)
{
if (!WriteConfiguredValue("quanpin", "autocorrect_transposition", enabled ? "true" : "false"))
{
return false;
}
g_quanpin_autocorrect_transposition = enabled;
return true;
}
bool GetConfiguredQuanpinAutocorrectNeighbor()
{
return g_quanpin_autocorrect_neighbor;
}
bool SetConfiguredQuanpinAutocorrectNeighbor(bool enabled)
{
if (!WriteConfiguredValue("quanpin", "autocorrect_neighbor", enabled ? "true" : "false"))
{
return false;
}
g_quanpin_autocorrect_neighbor = enabled;
return true;
}
bool GetConfiguredQuanpinAutocorrectMarker()
{
return g_quanpin_autocorrect_marker;
}
bool SetConfiguredQuanpinAutocorrectMarker(bool enabled)
{
if (!WriteConfiguredValue("quanpin", "autocorrect_marker", enabled ? "true" : "false"))
{
return false;
}
g_quanpin_autocorrect_marker = enabled;
return true;
}
bool GetConfiguredFuzzyPinyinEnabled()
{
return g_fuzzy_pinyin_enabled;
}
bool SetConfiguredFuzzyPinyinEnabled(bool enabled)
{
// 首次启用播种:出厂态第一次开总开关,11 条规则全部置 true 并持久化;之后总开关的
// 任何翻动只写总开关一个键,用户修剪过的选择在临时停用/恢复间原样保留。不能拿
// 「规则位全零」当首次信号——用户故意全部取消勾选后位图同样是零,无标记会把每次
// 开启都误判成首次启用,反复改写用户的空选择。
if (enabled && !g_fuzzy_seeded)
{
std::vector<ConfigValueUpdate> updates;
updates.reserve(std::size(kFuzzyPinyinRuleKeys) + 2);
updates.push_back({"input", "fuzzy_pinyin", "true"});
updates.push_back({"input", "fuzzy_seeded", "true"});
for (const auto &entry : kFuzzyPinyinRuleKeys)
updates.push_back({"input", entry.key, "true"});
// 一次批量写:WriteConfiguredValues 走单文件临时改名,无部分失败态。
if (!WriteConfiguredValues(updates))
return false;
g_fuzzy_pinyin_enabled = true;
g_fuzzy_seeded = true;
for (const auto &entry : kFuzzyPinyinRuleKeys)
g_fuzzy_pinyin_rules |= static_cast<std::uint32_t>(entry.rule);
return true;
}
if (!WriteConfiguredValue("input", "fuzzy_pinyin", enabled ? "true" : "false"))
return false;
g_fuzzy_pinyin_enabled = enabled;
return true;
}
// The master switch is gated here and nowhere else: sessions see all-zero rules while it is
// off, and the cached rule bits survive the toggle so re-enabling restores the prior choice.
metasequoia::FuzzyPinyinOptions GetConfiguredFuzzyPinyinOptions()
{
metasequoia::FuzzyPinyinOptions options;
if (g_fuzzy_pinyin_enabled)
options.rules = g_fuzzy_pinyin_rules;
return options;
}
metasequoia::FuzzyPinyinOptions GetConfiguredFuzzyPinyinRuleStates()
{
metasequoia::FuzzyPinyinOptions options;
options.rules = g_fuzzy_pinyin_rules;
return options;
}
bool SetConfiguredFuzzyPinyinRule(const std::string &key, bool enabled)
{
const FuzzyPinyinRuleKey *entry = nullptr;
for (const auto &candidate : kFuzzyPinyinRuleKeys)
{
if (key == candidate.key)
{
entry = &candidate;
break;
}
}
if (!entry)
return false;
const auto bit = static_cast<std::uint32_t>(entry->rule);
if (!WriteConfiguredValue("input", entry->key, enabled ? "true" : "false"))
return false;
if (enabled)
g_fuzzy_pinyin_rules |= bit;
else
g_fuzzy_pinyin_rules &= ~bit;
return true;
}
const std::string &GetConfiguredQuanpinHelpcodeSchema()
{
return g_quanpin_helpcode_schema;
}
bool SetConfiguredQuanpinHelpcodeSchema(const std::string &schema)
{
if (!IsHelpcodeSchemaAvailable(schema))
return false;
if (!WriteConfiguredValue("helpcode", "quanpin_helpcode_schema", EscapeTomlBasicString(schema)))
return false;
g_quanpin_helpcode_schema = schema;
return true;
}
std::vector<CustomHelpcodeSchemaInfo> GetCustomHelpcodeSchemas()
{
std::vector<CustomHelpcodeSchemaInfo> result;
for (const auto &schema : HelpcodeUtils::list_custom_helpcode_schemas(metasequoia::data_directory()))
{
const std::string &name = schema.name.empty() ? schema.name_en : schema.name;
const std::string &name_en = schema.name_en.empty() ? schema.name : schema.name_en;
result.push_back(
{schema.schema, name.empty() ? schema.file_stem : name, name_en.empty() ? schema.file_stem : name_en});
}
return result;
}
std::string GetCustomHelpcodeDirectory()
{
return metasequoia::path_to_utf8(HelpcodeUtils::custom_helpcode_directory(metasequoia::data_directory()));
}
bool GetConfiguredShowShuangpinHelpcodeInCandidateWindow()
{
return g_show_shuangpin_helpcode_in_candidate_window;
}
bool SetConfiguredShowShuangpinHelpcodeInCandidateWindow(bool enabled)
{
if (!WriteConfiguredValue("helpcode", "show_sp_helpcode_in_candidate_window", enabled ? "true" : "false"))
{
return false;
}
g_show_shuangpin_helpcode_in_candidate_window = enabled;
return true;
}
bool GetConfiguredShowQuanpinHelpcodeInCandidateWindow()
{
return g_show_quanpin_helpcode_in_candidate_window;
}
bool SetConfiguredShowQuanpinHelpcodeInCandidateWindow(bool enabled)
{
if (!WriteConfiguredValue("helpcode", "show_qp_helpcode_in_candidate_window", enabled ? "true" : "false"))
{
return false;
}
g_show_quanpin_helpcode_in_candidate_window = enabled;
return true;
}
const std::string &GetConfiguredInputMode()
{
return g_input_mode;
}
bool SetConfiguredInputMode(const std::string &mode)
{
if (mode != "chinese" && mode != "japanese")
{
return false;
}
if (!WriteConfiguredValue("input", "mode", EscapeTomlBasicString(mode)))
{
return false;
}
g_input_mode = mode;
g_configured_input_mode_japanese.store(mode == "japanese", std::memory_order_relaxed);
// Choosing a language in settings is where the next cycle starts from;
// the cycle switch itself is independent of it.
g_active_input_mode.store(ActiveInputMode::Configured, std::memory_order_relaxed);
RefreshEffectiveTsfPreeditStyle();
NotifyImeServerInputSchemeChanged();
return true;
}
bool GetConfiguredTrilingualCycleEnabled()
{
return g_trilingual_cycle_enabled.load(std::memory_order_relaxed);
}
bool SetConfiguredTrilingualCycleEnabled(bool enabled)
{
if (!WriteConfiguredValue("keybindings", "trilingual_cycle", enabled ? "true" : "false"))
{
return false;
}
if (g_trilingual_cycle_enabled.exchange(enabled, std::memory_order_relaxed) != enabled)
g_active_input_mode.store(ActiveInputMode::Configured, std::memory_order_relaxed);
RefreshEffectiveTsfPreeditStyle();
return true;
}
const std::string &GetActiveInputMode()
{
static const std::string chinese = "chinese";
static const std::string japanese = "japanese";
switch (g_active_input_mode.load(std::memory_order_relaxed))
{
case ActiveInputMode::Chinese:
return chinese;
case ActiveInputMode::Japanese:
return japanese;
default:
// Never hand out g_input_mode itself: a config reload on another
// thread may reassign it while the key thread compares the result.
return g_configured_input_mode_japanese.load(std::memory_order_relaxed) ? japanese : chinese;
}
}
bool SetActiveInputMode(const std::string &mode)
{
if (!g_trilingual_cycle_enabled.load(std::memory_order_relaxed) || (mode != "chinese" && mode != "japanese"))
{
return false;
}
g_active_input_mode.store(mode == "japanese" ? ActiveInputMode::Japanese : ActiveInputMode::Chinese,
std::memory_order_relaxed);
RefreshEffectiveTsfPreeditStyle();
return true;
}
const std::string &GetConfiguredJapaneseSchema()
{
return g_japanese_schema;
}
bool SetConfiguredJapaneseSchema(const std::string &schema)
{
if (schema != "romaji")
{
return false;
}
if (!WriteConfiguredValue("input", "japanese_schema", EscapeTomlBasicString(schema)))
{
return false;
}
g_japanese_schema = schema;
return true;
}
const FrequencyAdjustmentConfig &GetConfiguredFrequencyAdjustment()
{
return g_frequency_adjustment;
}
bool SetConfiguredFrequencyAdjustmentString(const std::string &key, const std::string &value)
{
if (key != "mode" ||
(value != "disabled" && value != "pin" && value != "halve" && value != "linear" && value != "promote") ||
!WriteConfiguredValue("frequency_adjustment", key, EscapeTomlBasicString(value)))
return false;
g_frequency_adjustment.mode = value;
return true;
}
bool SetConfiguredFrequencyAdjustmentInt(const std::string &key, int value)
{
if ((key != "trigger_count" && key != "linear_step") || value < 1 || value > 10 ||
!WriteConfiguredValue("frequency_adjustment", key, std::to_string(value)))
return false;
if (key == "trigger_count")
g_frequency_adjustment.trigger_count = value;
else
g_frequency_adjustment.linear_step = value;
return true;
}