* Return Caps Lock to normal and make Shift + Caps Lock the compose key Caps Lock as compose, with both Shifts as Caps Lock, confused too many people. Stock XKB has no option for compose on the shifted Caps key, so Omarchy defines one in /etc/xkb, which libxkbcommon reads before the system rules. The explicit actions keep the Shift level from toggling Caps Lock. Lone Shift is no longer part of Caps Lock, which leaves it free for input methods. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Require xkbcli so the keymap check cannot silently skip Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
166 lines
6.2 KiB
Bash
166 lines
6.2 KiB
Bash
#!/bin/bash
|
|
|
|
source "$(dirname "${BASH_SOURCE[0]}")/base-test.sh"
|
|
|
|
require_command lua
|
|
require_command xkbcli
|
|
|
|
resolved_input_for() {
|
|
local module="$1"
|
|
OMARCHY_PATH="$ROOT" OMARCHY_MODULE="$module" OMARCHY_VCONSOLE="${2-}" lua <<'LUA'
|
|
package.path = os.getenv("OMARCHY_PATH") .. "/?.lua;" .. package.path
|
|
|
|
local vconsole = os.getenv("OMARCHY_VCONSOLE")
|
|
local real_open = io.open
|
|
|
|
io.open = function(path, mode)
|
|
if path:match("/omarchy/keyboard%-layouts$") then
|
|
local selected = os.getenv("TEST_KEYBOARD_SELECTION")
|
|
if not selected then return nil end
|
|
local file = io.tmpfile()
|
|
file:write(selected)
|
|
file:seek("set")
|
|
return file
|
|
end
|
|
|
|
if path == "/etc/omarchy/input-method" then
|
|
local preference = os.getenv("TEST_INPUT_PREFERENCE")
|
|
if not preference then return nil end
|
|
local file = io.tmpfile()
|
|
file:write(preference)
|
|
file:seek("set")
|
|
return file
|
|
end
|
|
|
|
if path ~= "/etc/vconsole.conf" then
|
|
return real_open(path, mode)
|
|
end
|
|
|
|
-- An unset OMARCHY_VCONSOLE reaches Lua as "", never nil, so the cases that
|
|
-- ask for a missing /etc/vconsole.conf need the empty string to fail the open.
|
|
if not vconsole or vconsole == "" then
|
|
return nil
|
|
end
|
|
|
|
local file = io.tmpfile()
|
|
file:write(vconsole)
|
|
file:seek("set")
|
|
return file
|
|
end
|
|
|
|
hl = {
|
|
config = function(config)
|
|
local input = config.input
|
|
print(("[%s] [%s] [%s]"):format(input.kb_layout, input.kb_variant, input.kb_options))
|
|
end,
|
|
}
|
|
|
|
o = { window = function() end }
|
|
|
|
require(os.getenv("OMARCHY_MODULE"))
|
|
LUA
|
|
}
|
|
|
|
resolved_input() {
|
|
resolved_input_for "default.hypr.input" "${1-}"
|
|
}
|
|
|
|
# The greeter compositor resolves the layout from its own entrypoint.
|
|
resolved_greeter_input() {
|
|
resolved_input_for "default.sddm.hyprland" "${1-}"
|
|
}
|
|
|
|
assert_resolved_input() {
|
|
local resolver="$1"
|
|
local description="$2"
|
|
local expected="$3"
|
|
local actual
|
|
|
|
if (( $# > 3 )); then
|
|
actual=$("$resolver" "$4")
|
|
else
|
|
actual=$("$resolver")
|
|
fi
|
|
|
|
[[ $actual == "$expected" ]] ||
|
|
fail "$description" "expected: $expected"$'\n'"actual: $actual"
|
|
pass "$description"
|
|
}
|
|
|
|
assert_input() {
|
|
assert_resolved_input resolved_input "$@"
|
|
}
|
|
|
|
assert_greeter_input() {
|
|
assert_resolved_input resolved_greeter_input "$@"
|
|
}
|
|
|
|
base_options="omarchy:shift_caps_compose"
|
|
toggle_options="$base_options,grp:alts_toggle"
|
|
|
|
assert_input "missing vconsole.conf falls back to us" "[us] [] [$base_options]"
|
|
|
|
assert_input "us layout passes through" "[us] [intl] [$base_options]" 'XKBLAYOUT=us
|
|
XKBVARIANT=intl
|
|
'
|
|
|
|
assert_input "latin layouts are left alone" "[de] [nodeadkeys] [$base_options]" 'XKBLAYOUT=de
|
|
XKBVARIANT=nodeadkeys
|
|
'
|
|
|
|
assert_input "non-latin layout gains us in front" "[us,ara] [,] [$toggle_options]" 'XKBLAYOUT=ara
|
|
'
|
|
|
|
assert_input "prepended us keeps variants aligned" "[us,ru] [,phonetic] [$toggle_options]" 'XKBLAYOUT=ru
|
|
XKBVARIANT=phonetic
|
|
'
|
|
|
|
assert_input "non-latin layout in front gains us even when us trails" "[us,il,us] [,] [$toggle_options]" 'XKBLAYOUT=il,us
|
|
'
|
|
|
|
caps=$(xkbcli compile-keymap --include "$ROOT/etc/xkb" --include-defaults --layout us,ru --options omarchy:shift_caps_compose | grep -A5 'key <CAPS>')
|
|
[[ $caps == *"Caps_Lock,"*"Multi_key ]"* && $caps == *"LockMods(modifiers=Lock),"*"NoAction() ]"* ]] ||
|
|
fail "Shift + Caps Lock composes without toggling Caps Lock" "$caps"
|
|
pass "Shift + Caps Lock composes without toggling Caps Lock"
|
|
|
|
# The greeter takes no compose or capslock options: they're session comfort
|
|
# settings with nothing to do with typing a password.
|
|
assert_greeter_input "greeter falls back to us without vconsole.conf" "[us] [] []"
|
|
|
|
assert_greeter_input "greeter follows the system layout" "[fr] [] []" 'XKBLAYOUT=fr
|
|
'
|
|
|
|
assert_greeter_input "greeter keeps a latin layout in front" "[us,ru] [,phonetic] [grp:alts_toggle]" 'XKBLAYOUT=ru
|
|
XKBVARIANT=phonetic
|
|
'
|
|
|
|
hooks_conf="$ROOT/etc/mkinitcpio.conf.d/omarchy_hooks.conf"
|
|
input_lua="$ROOT/default/hypr/input.lua"
|
|
|
|
hooks_layouts=$(awk -F')' '/\) ;;$/ { gsub(/[[:space:]|]+/, "\n", $1); print $1 }' "$hooks_conf" | grep '^[a-z]\+$' | sort)
|
|
lua_layouts=$(sed -n '/^local non_latin_layouts =/,+1p' "$input_lua" | grep -o '"[^"]*"' | tr -d '"' | tr ' ' '\n' | grep '^[a-z]\+$' | sort)
|
|
|
|
[[ -n $hooks_layouts ]] || fail "non-latin layout list is readable from omarchy_hooks.conf"
|
|
|
|
[[ $hooks_layouts == "$lua_layouts" ]] ||
|
|
fail "non-latin layout lists stay in sync" "$(diff <(echo "$hooks_layouts") <(echo "$lua_layouts"))"
|
|
pass "non-latin layout lists stay in sync with the initramfs hook"
|
|
|
|
sddm_lua="$ROOT/default/sddm/hyprland.lua"
|
|
sddm_layouts=$(sed -n '/^local non_latin_layouts =/,+1p' "$sddm_lua" | grep -o '"[^"]*"' | tr -d '"' | tr ' ' '\n' | grep '^[a-z]\+$' | sort)
|
|
|
|
[[ $hooks_layouts == "$sddm_layouts" ]] ||
|
|
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 its installer US layout" "[kr] [] [$base_options]" $'XKBLAYOUT=us\n'
|
|
TEST_INPUT_PREFERENCE=$'INPUT_METHOD=none\nXKB_LAYOUT=la\n' assert_input "Lao selection gets Lao behind US on the desktop" "[us,la] [,] [$toggle_options]" $'XKBLAYOUT=us\n'
|
|
TEST_INPUT_PREFERENCE=$'INPUT_METHOD=none\nXKB_LAYOUT=xx\n' assert_input "an unknown desktop override is ignored" "[us] [] [$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'
|
|
|
|
TEST_KEYBOARD_SELECTION=$'XKBLAYOUT=us,fr\nXKBVARIANT=intl,\n' assert_input "menu selections replace installer desktop defaults" "[us,fr] [intl,] [$base_options]" $'XKBLAYOUT=jp\n'
|
|
TEST_KEYBOARD_SELECTION=$'XKBLAYOUT=us,fr\nXKBVARIANT=intl,\n' assert_greeter_input "menu selections leave the greeter keyboard unchanged" "[jp] [] []" $'XKBLAYOUT=jp\n'
|
|
|
|
TEST_KEYBOARD_SELECTION=$'XKBLAYOUT=us,ru\nXKBVARIANT=,phonetic\n' assert_input "saved non-Latin selections retain the layout shortcut" "[us,ru] [,phonetic] [$toggle_options]" $'XKBLAYOUT=us\n'
|