A German keyboard typed US characters — nothing carried the box's layout into the session #257

Merged
enricobuehler merged 1 commits from worktree-ipad-keyboard-layout into main 2026-08-15 21:58:29 +00:00
1 Commits
Author SHA1 Message Date
enricobuehler 94be547da0 fix(input): a German keyboard typed US characters — nothing carried the box's layout into the session
ci / bun-nix (pull_request) Successful in 21s
ci / web (pull_request) Successful in 1m5s
ci / docs-site (pull_request) Successful in 9m7s
ci / rust-arm64 (pull_request) Successful in 9m47s
android / android (pull_request) Successful in 15m41s
ci / rust (pull_request) Successful in 16m49s
Reported from an iPad on a Bazzite host in Game Mode: `#` arrived as `\`, `-` as `/`, `ä` as `'`.
That set is not random — they are the US ANSI keys that sit where the German ISO ones do, which
says the client was right and the host session was resolving positions with a US keymap.

The key wire is US-POSITIONAL by design: a client sends the physical key, `vk_to_evdev` turns it
into an evdev code, and the session's keymap picks the character. So the contract is "host layout
== the layout on the client's keyboard", and nothing was upholding it:

- Nothing exports `XKB_DEFAULT_*`. `localectl set-x11-keymap de` writes
  /etc/X11/xorg.conf.d/00-keyboard.conf — a file only Xorg reads — and libxkbcommon's fallback
  chain stops at the env vars, which no session manager sets. Compiling from empty names on a
  properly-configured German box therefore yielded evdev/pc105/us, silently.
- gamescope reads those env vars and then publishes the keymap to nobody. It builds the keymap
  onto `keyboard_group`, but the seat carries `virtual_keyboard_device` — a stub whose own comment
  says it exists "only to set the keymap" and which never gets one. `wlserver_keyboardfocus()`
  rebinds that stub on every focus change, and the real group only reaches the seat from a libinput
  key event, which a `--backend headless` session never has. Verified on the box: both Xwayland
  servers map evdev 53/40/43 to slash/apostrophe/backslash on a de/nodeadkeys machine.

`pf_host_config::layout` resolves what the box actually recorded (`XKB_DEFAULT_*`, then
xorg.conf.d, then vconsole's XKBLAYOUT — never vconsole's KEYMAP, whose names are console names and
do not map onto xkb's). From there:

- the wlroots injector compiles its uploaded keymap from it instead of from empty names, which
  fixes Sway/Hyprland hosts outright;
- all four gamescope launch paths hand the session `XKB_DEFAULT_*`, gated behind a `+pfhdr8` probe
  that warns when the binary predates the fix rather than leaving it unexplained;
- `sync_session_keyboard_layout()` covers the case none of that reaches — the autologin session
  punktfunk ATTACHES to, where no launch-time decision applies — by pointing each gamescope
  Xwayland at the box's layout on adoption. Everything in Game Mode is an X11 client of those
  servers, so this is the leg that fixes the report. `PUNKTFUNK_SESSION_LAYOUT=0` turns it off.

gamescope patch 0010 sets the keymap on the stub as well, which is what makes the env legs mean
anything for Wayland-native clients. It is NOT build-verified (no gamescope build environment
here); the series does apply `git am`-clean at the pinned 5fb8dce4.

Verified: 10 new unit tests over the resolver; pf-inject + pf-host-config clippy `-D warnings` and
those tests on real linux-gnu in the CI image; pf-vdisplay checks and clippies clean on linux-gnu
via scripts/xcheck.sh, confirmed non-vacuous with a planted error. The character mapping itself was
confirmed on the box with `xmodmap -pke` before and after applying the layout.
2026-08-15 20:25:46 +02:00