Apply the system keyboard layout to the SDDM greeter (#6896)

* Apply the system keyboard layout to the SDDM greeter

* Extract shared assert_resolved_input helper

* Make the missing vconsole.conf tests actually open nothing

The stub only returned nil for an unset OMARCHY_VCONSOLE, but bash passes it
through as "", so both fallback cases were reading an empty temp file instead
of exercising the failed open.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: David Heinemeier Hansson <david@hey.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-10-03 20:23:00 -04:00
1 parent 18328559b9
commit 75b327bc28
2 files changed
+104 -10

No files matched your search

+49
View File
@@ -1,6 +1,55 @@
-- Minimal Hyprland config for the SDDM Wayland greeter.
-- SDDM starts the greeter itself after the compositor is ready.
-- The layout resolution below is deliberately duplicated from
-- default/hypr/input.lua. The greeter runs as the sddm user and starts from
-- this file directly, so it never goes through default/hypr/bootstrap.lua --
-- which builds package.path from $HOME and therefore can't set up require()
-- here. Keep the two in sync.
local function read_vconsole()
local values = {}
local file = io.open("/etc/vconsole.conf", "r")
if not file then
return values
end
for line in file:lines() do
local key, value = line:match("^%s*([%w_]+)%s*=%s*(.-)%s*$")
if key and value then
value = value:gsub("%s+#.*$", "")
value = value:gsub('^"(.*)"$', "%1")
value = value:gsub("^'(.*)'$", "%1")
values[key] = value
end
end
file:close()
return values
end
-- Layouts that can't type Latin letters. Keep in sync with the list in
-- etc/mkinitcpio.conf.d/omarchy_hooks.conf and default/hypr/input.lua.
local non_latin_layouts =
" af am ara bd bg by et ge gr il in iq ir kg kh kz la lk mk mm mn mv np rs ru sy th tj ua "
local vconsole = read_vconsole()
local kb_layout = vconsole.XKBLAYOUT or "us"
local kb_variant = vconsole.XKBVARIANT or ""
local kb_options = ""
-- A greeter that can't type Latin letters can't take a password either, so a
-- non-Latin layout gets us in front, reachable back with Left Alt + Right Alt.
if non_latin_layouts:find(" " .. kb_layout:match("^[^,]*") .. " ", 1, true) then
kb_layout = "us," .. kb_layout
kb_variant = "," .. kb_variant
kb_options = "grp:alts_toggle"
end
hl.config({
input = {
kb_layout = kb_layout,
kb_variant = kb_variant,
kb_options = kb_options,
},
misc = {
disable_hyprland_logo = true,
disable_splash_rendering = true,
+55 -10
View File
@@ -4,8 +4,9 @@ source "$(dirname "${BASH_SOURCE[0]}")/base-test.sh"
require_command lua
resolved_input() {
OMARCHY_PATH="$ROOT" OMARCHY_VCONSOLE="${1-}" lua <<'LUA'
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")
@@ -16,7 +17,9 @@ io.open = function(path, mode)
return real_open(path, mode)
end
if not vconsole then
-- 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
@@ -35,19 +38,29 @@ hl = {
o = { window = function() end }
require("default.hypr.input")
require(os.getenv("OMARCHY_MODULE"))
LUA
}
assert_input() {
local description="$1"
local expected="$2"
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 (( $# > 2 )); then
actual=$(resolved_input "$3")
if (( $# > 3 )); then
actual=$("$resolver" "$4")
else
actual=$(resolved_input)
actual=$("$resolver")
fi
[[ $actual == "$expected" ]] ||
@@ -55,24 +68,48 @@ assert_input() {
pass "$description"
}
assert_input() {
assert_resolved_input resolved_input "$@"
}
assert_greeter_input() {
assert_resolved_input resolved_greeter_input "$@"
}
base_options="compose:caps,shift:both_capslock_cancel"
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
'
# 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"
@@ -80,6 +117,14 @@ hooks_layouts=$(awk -F')' '/\) ;;$/ { gsub(/[[:space:]|]+/, "\n", $1); print $1
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"