From 0f14a8493457294c426210f1cd18874605fe4ca2 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Wed, 7 Oct 2026 15:19:06 +0200 Subject: [PATCH] Preserve inherited input keys and later keyboard changes --- default/hypr/keyboard.lua | 4 +++- default/input-methods/configure.py | 5 +++-- test/shell.d/hyprland-keyboard-layout-test.sh | 4 +++- test/shell.d/input-method-checks.py | 13 +++++++++++++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/default/hypr/keyboard.lua b/default/hypr/keyboard.lua index 46b9a4a5..e8ec2457 100644 --- a/default/hypr/keyboard.lua +++ b/default/hypr/keyboard.lua @@ -31,7 +31,9 @@ end function keyboard.installed() local values = keyboard.vconsole() - if read_values("/etc/omarchy/input-method").XKB_LAYOUT == "kr" then + if read_values("/etc/omarchy/input-method").XKB_LAYOUT == "kr" + and (values.XKBLAYOUT or "us") == "us" + and (values.XKBVARIANT or "") == "" then values.XKBLAYOUT, values.XKBVARIANT = "kr", "" end return values diff --git a/default/input-methods/configure.py b/default/input-methods/configure.py index 0ec5751b..dc98f0ae 100644 --- a/default/input-methods/configure.py +++ b/default/input-methods/configure.py @@ -83,7 +83,8 @@ def defaults(config_home, fresh=False): existing = sections(original) additions = [] for section, entries in sections(shipped).items(): - if not fresh and section == "Hotkey/TriggerKeys": + # Missing hotkey sections also represent a choice: Fcitx's defaults. + if not fresh and section.startswith("Hotkey/"): continue # Existing lists (including explicitly empty lists) are user choices. # A fresh profile has no working IME shortcut to preserve. @@ -163,7 +164,7 @@ def selection(): preference = values(read(Path(os.environ.get("OMARCHY_INPUT_SELECTION", "/etc/omarchy/input-method")))) layout = vconsole.get("XKBLAYOUT", "us").split(",")[0] or "us" variant = vconsole.get("XKBVARIANT", "").split(",")[0] - if preference.get("XKB_LAYOUT") == "kr": + if preference.get("XKB_LAYOUT") == "kr" and vconsole.get("XKBLAYOUT", "us") == "us" and not variant: layout, variant = "kr", "" method = preference.get("INPUT_METHOD", "mozc" if layout == "jp" else "none") if method not in PRESETS: diff --git a/test/shell.d/hyprland-keyboard-layout-test.sh b/test/shell.d/hyprland-keyboard-layout-test.sh index 6bc4e4ac..10aaddb6 100644 --- a/test/shell.d/hyprland-keyboard-layout-test.sh +++ b/test/shell.d/hyprland-keyboard-layout-test.sh @@ -138,4 +138,6 @@ sddm_layouts=$(sed -n '/^local non_latin_layouts =/,+1p' "$sddm_lua" | grep -o ' fail "greeter non-latin layout list stays in sync" "$(diff <(echo "$hooks_layouts") <(echo "$sddm_layouts"))" pass "greeter non-latin layout list stays in sync with the initramfs hook" -TEST_INPUT_PREFERENCE=$'INPUT_METHOD=hangul\n XKB_LAYOUT="kr" # desktop only\n' assert_input "Korean selection overrides desktop layout and clears the US variant" "[kr] [] [$base_options]" $'XKBLAYOUT=us\nXKBVARIANT=intl\n' +TEST_INPUT_PREFERENCE=$'INPUT_METHOD=hangul\n XKB_LAYOUT="kr" # desktop only\n' assert_input "Korean selection overrides its installer US layout" "[kr] [] [$base_options]" $'XKBLAYOUT=us\n' +TEST_INPUT_PREFERENCE=$'INPUT_METHOD=hangul\nXKB_LAYOUT=kr\n' assert_input "a later US variant overrides the Korean preference" "[us] [intl] [$base_options]" $'XKBLAYOUT=us\nXKBVARIANT=intl\n' +TEST_INPUT_PREFERENCE=$'INPUT_METHOD=hangul\nXKB_LAYOUT=kr\n' assert_input "a later layout overrides the Korean preference" "[de] [nodeadkeys] [$base_options]" $'XKBLAYOUT=de\nXKBVARIANT=nodeadkeys\n' diff --git a/test/shell.d/input-method-checks.py b/test/shell.d/input-method-checks.py index 0b595ea6..98b0fb14 100644 --- a/test/shell.d/input-method-checks.py +++ b/test/shell.d/input-method-checks.py @@ -98,6 +98,19 @@ class InputMethodTest(unittest.TestCase): self.assertEqual(config["Hotkey/EnumerateGroupForwardKeys"], {}) self.assertNotIn("Control+space", config["Hotkey/TriggerKeys"].values()) + def test_existing_engine_inherited_hotkeys_are_preserved(self): + path = self.config / "fcitx5/config" + setup.atomic_write(path, "[Behavior]\nActiveByDefault=True\n") + setup.defaults(self.config, fresh=False) + self.assertEqual(path.read_text(), "[Behavior]\nActiveByDefault=True\n") + + def test_korean_preference_yields_to_a_later_keyboard_change(self): + self.preference.write_text("INPUT_METHOD=hangul\nXKB_LAYOUT=kr\n") + self.assertEqual(setup.selection(), ("hangul", "kr")) + for layout, variant, expected in [("de", "nodeadkeys", "de-nodeadkeys"), ("us", "intl", "us-intl")]: + self.vconsole.write_text(f"XKBLAYOUT={layout}\nXKBVARIANT={variant}\n") + self.assertEqual(setup.selection(), ("hangul", expected)) + def test_live_append_keeps_every_method_and_layout(self): info = json.dumps({"data": ["de-nodeadkeys", [["keyboard-de-nodeadkeys", ""], ["mozc", "jp"]]]}) updated = json.dumps({"data": ["de-nodeadkeys", [["keyboard-de-nodeadkeys", ""], ["mozc", "jp"], ["hangul", ""]]]})