#!/usr/bin/env bash # Fix 5 (Wayland only): let the game window receive keyboard focus. # # On KDE Plasma Wayland (via Xwayland) the game's top window advertises the ICCCM # "Globally Active" input model (WM_HINTS input=False + WM_TAKE_FOCUS), so the # compositor never hands it Wayland keyboard focus; mouse works (position-based) # but the keyboard does not. Setting Wine's X11 driver to not use take-focus makes # the window advertise the passive model (input=True) and the compositor grants # keyboard focus. # # HKCU\Software\Wine\X11 Driver -> "UseTakeFocus"="N" # # Harmless on native X11 sessions. Must be applied with the prefix's wineserver # stopped so it sticks. This edits the Proton prefix's user.reg directly (no wine # tooling needed). Backup written as user.reg.bak-. set -euo pipefail GAME_ROOT="${1:?usage: 05-wayland-keyboard.sh }" APPID=2326780 PFX="$GAME_ROOT/../../compatdata/$APPID/pfx" REG="$PFX/user.reg" [ -f "$REG" ] || { echo "!! prefix not found: $REG (launch the game once so Proton creates it)"; exit 1; } if grep -qa 'UseTakeFocus"="N"' "$REG"; then echo "== Fix 5: already applied (UseTakeFocus=N)"; exit 0 fi # stop only this prefix's wineserver, matched via WINEPREFIX in its # environment, so the value sticks; other prefixes are left alone kill_prefix_wineserver() { # local pfx pid env_pfx pfx="$(cd "$1" 2>/dev/null && pwd)" || return 0 for pid in $(pgrep -x wineserver 2>/dev/null || true); do env_pfx="$(tr '\0' '\n' < "/proc/$pid/environ" 2>/dev/null \ | sed -n 's/^WINEPREFIX=//p' | head -1 || true)" if [ -n "$env_pfx" ] && [ "${env_pfx%/}" = "$pfx" ]; then kill -9 "$pid" 2>/dev/null || true fi done } kill_prefix_wineserver "$PFX" cp "$REG" "$REG.bak-$(date +%s)" python3 - "$REG" <<'PY' import sys, time path = sys.argv[1] sec = r'[Software\\Wine\\X11 Driver]' # literal text in user.reg (backslashes doubled) key = '"UseTakeFocus"="N"\n' def is_global_x11_header(line): # the global section only, not AppDefaults\...\X11 Driver; Wine may append # a modification time after the bracket s = line.strip() return s == sec or s.startswith(sec + ' ') with open(path, 'r', encoding='utf-8', errors='surrogateescape') as f: lines = f.readlines() out, i, done = [], 0, False while i < len(lines): line = lines[i] if not done and is_global_x11_header(line): out.append(line); i += 1 if i < len(lines) and lines[i].startswith('#time='): # keep the timestamp line out.append(lines[i]); i += 1 out.append(key) while i < len(lines) and not lines[i].lstrip().startswith('['): if not lines[i].strip().startswith('"UseTakeFocus"'): # drop any stale copy out.append(lines[i]) i += 1 done = True continue out.append(line); i += 1 if not done: # section absent: append it now = int(time.time()) filetime = (now + 11644473600) * 10000000 # Wine #time= is a hex FILETIME out.append('\n' + sec + ' %d\n' % now) out.append('#time=%x\n' % filetime) out.append(key) with open(path, 'w', encoding='utf-8', errors='surrogateescape') as f: f.writelines(out) print("wrote UseTakeFocus=N") PY echo "== Fix 5: applied (Wayland keyboard focus). Relaunch the game."