Keep Ctrl+Space out of Fcitx's toggles instead of probing for it
The terminal prefix bind read the Fcitx config and profile on every press to avoid fighting a Ctrl+Space toggle. configure --defaults now removes that key from every trigger list, so the bind only checks for a terminal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
cf9e8f3625
commit
0cd685946b
5 files changed
+39
-68
No files matched your search
@@ -1,5 +1,3 @@
|
||||
local paths = require("default.hypr.paths")
|
||||
|
||||
-- Same terminal tag test as clipboard.lua.
|
||||
local function active_window_is_terminal()
|
||||
local window = hl.get_active_window()
|
||||
@@ -16,55 +14,15 @@ local function active_window_is_terminal()
|
||||
return false
|
||||
end
|
||||
|
||||
-- Read on demand so adding an engine in a live desktop needs no reload.
|
||||
local function has_input_method()
|
||||
local file = io.open(paths.config_home .. "/fcitx5/profile", "r")
|
||||
if not file then return false end
|
||||
local in_item, found = false, false
|
||||
for line in file:lines() do
|
||||
if line:match("^%[") then
|
||||
in_item = line:match("^%[Groups/%d+/Items/%d+%]$") ~= nil
|
||||
elseif in_item then
|
||||
local name = line:match("^Name=(.+)$")
|
||||
if name and not name:match("^keyboard%-") then found = true end
|
||||
end
|
||||
end
|
||||
file:close()
|
||||
return found
|
||||
end
|
||||
|
||||
-- Keep the desktop shortcut independent of Fcitx's custom switching keys.
|
||||
-- Read the live Fcitx group so setup works without a compositor reload.
|
||||
o.bind("SUPER + I", "Switch input language", "omarchy-input-method cycle")
|
||||
o.bind("SUPER + SHIFT + I", "Switch input language back", "omarchy-input-method cycle back")
|
||||
|
||||
-- Preserve custom Ctrl + Space triggers. Without a
|
||||
-- [Hotkey/TriggerKeys] list in the config, fcitx5 uses its default, which has
|
||||
-- Ctrl + Space.
|
||||
local function fcitx5_toggles_on_ctrl_space()
|
||||
local file = io.open(paths.config_home .. "/fcitx5/config", "r")
|
||||
if not file then
|
||||
return true
|
||||
end
|
||||
|
||||
local in_triggers, has_triggers, toggles = false, false, false
|
||||
for line in file:lines() do
|
||||
if line:match("^%[") then
|
||||
in_triggers = line == "[Hotkey/TriggerKeys]"
|
||||
has_triggers = has_triggers or in_triggers
|
||||
elseif in_triggers and line:match("^%d+=Control%+space$") then
|
||||
toggles = true
|
||||
end
|
||||
end
|
||||
|
||||
file:close()
|
||||
return toggles or not has_triggers
|
||||
end
|
||||
|
||||
-- Pass the tmux/Herdr prefix through and return to direct input, so the next
|
||||
-- key reaches the multiplexer instead of the engine's composition.
|
||||
o.bind("CTRL + SPACE", "Direct input for the terminal prefix", function()
|
||||
if active_window_is_terminal() and has_input_method() and not fcitx5_toggles_on_ctrl_space() then
|
||||
if active_window_is_terminal() then
|
||||
-- Wait for the local controller reply before dispatching the next key.
|
||||
o.shell_succeeds("timeout 0.3 fcitx5-remote --check -c")
|
||||
end
|
||||
|
||||
@@ -72,6 +72,21 @@ def key_list(entries):
|
||||
return {frozenset(value.split("+")) for value in entries.values()}
|
||||
|
||||
|
||||
def without_ctrl_space(text):
|
||||
# Ctrl+Space belongs to tmux and Herdr, and the terminal binding returns to
|
||||
# direct input on it; a Fcitx toggle on the same key would undo that. Without
|
||||
# a TriggerKeys list Fcitx falls back to its default, which has Ctrl+Space.
|
||||
section = re.search(r"(?ms)^\[Hotkey/TriggerKeys\][ \t]*\n(.*?)(?=^\[|\Z)", text)
|
||||
if not section:
|
||||
return text.rstrip() + ("\n\n" if text.strip() else "") + "[Hotkey/TriggerKeys]\n0=Zenkaku_Hankaku\n1=Hangul\n"
|
||||
keys = values(section[1])
|
||||
kept = [keys[key] for key in sorted(keys, key=lambda key: int(key) if key.isdigit() else 0) if keys[key] != "Control+space"]
|
||||
if len(kept) == len(keys):
|
||||
return text
|
||||
body = "".join(f"{index}={key}\n" for index, key in enumerate(kept)) + ("\n" if section[1].endswith("\n\n") else "")
|
||||
return text[:section.start(1)] + body + text[section.end(1):]
|
||||
|
||||
|
||||
def defaults(config_home, fresh=False):
|
||||
changed = False
|
||||
files = ["config", "conf/quickphrase.conf", "conf/wayland.conf", "conf/xcb.conf", "conf/pinyin.conf"]
|
||||
@@ -80,6 +95,8 @@ def defaults(config_home, fresh=False):
|
||||
original = read(target)
|
||||
shipped = read(ROOT / "config/fcitx5" / name)
|
||||
if name == "config" or name == "conf/quickphrase.conf":
|
||||
if name == "config":
|
||||
original = without_ctrl_space(original)
|
||||
existing = sections(original)
|
||||
additions = []
|
||||
for section, entries in sections(shipped).items():
|
||||
@@ -89,7 +106,6 @@ def defaults(config_home, fresh=False):
|
||||
# Existing lists (including explicitly empty lists) are user choices.
|
||||
# A fresh profile has no working IME shortcut to preserve.
|
||||
stock_lists = {
|
||||
"Hotkey/TriggerKeys": {"0": "Control+space", "1": "Zenkaku_Hankaku", "2": "Hangul"},
|
||||
"Hotkey/ActivateKeys": {"0": "Hangul_Hanja"},
|
||||
"Hotkey/DeactivateKeys": {"0": "Hangul_Romaja"},
|
||||
"Hotkey/AltTriggerKeys": {"0": "Shift_L"},
|
||||
@@ -103,7 +119,8 @@ def defaults(config_home, fresh=False):
|
||||
if section not in existing:
|
||||
additions.append(f"[{section}]\n" + "".join(f"{key}={value}\n" for key, value in entries.items()))
|
||||
if additions:
|
||||
changed |= atomic_write(target, original.rstrip() + ("\n\n" if original.strip() else "") + "\n".join(additions))
|
||||
original = original.rstrip() + ("\n\n" if original.strip() else "") + "\n".join(additions)
|
||||
changed |= atomic_write(target, original)
|
||||
elif name == "conf/pinyin.conf":
|
||||
# Suppress the first-use cloud prompt while preserving any explicit
|
||||
# cloud-prediction preference and all other existing engine settings.
|
||||
|
||||
Reference in new issue
Block a user