05-wayland-keyboard.sh 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. #!/usr/bin/env bash
  2. # Fix 5 (Wayland only): let the game window receive keyboard focus.
  3. #
  4. # On KDE Plasma Wayland (via Xwayland) the game's top window advertises the ICCCM
  5. # "Globally Active" input model (WM_HINTS input=False + WM_TAKE_FOCUS), so the
  6. # compositor never hands it Wayland keyboard focus; mouse works (position-based)
  7. # but the keyboard does not. Setting Wine's X11 driver to not use take-focus makes
  8. # the window advertise the passive model (input=True) and the compositor grants
  9. # keyboard focus.
  10. #
  11. # HKCU\Software\Wine\X11 Driver -> "UseTakeFocus"="N"
  12. #
  13. # Harmless on native X11 sessions. Must be applied with the prefix's wineserver
  14. # stopped so it sticks. This edits the Proton prefix's user.reg directly (no wine
  15. # tooling needed). Backup written as user.reg.bak-<timestamp>.
  16. set -euo pipefail
  17. GAME_ROOT="${1:?usage: 05-wayland-keyboard.sh <game-root-dir>}"
  18. APPID=2326780
  19. PFX="$GAME_ROOT/../../compatdata/$APPID/pfx"
  20. REG="$PFX/user.reg"
  21. [ -f "$REG" ] || { echo "!! prefix not found: $REG (launch the game once so Proton creates it)"; exit 1; }
  22. if grep -qa 'UseTakeFocus"="N"' "$REG"; then
  23. echo "== Fix 5: already applied (UseTakeFocus=N)"; exit 0
  24. fi
  25. # stop only this prefix's wineserver, matched via WINEPREFIX in its
  26. # environment, so the value sticks; other prefixes are left alone
  27. kill_prefix_wineserver() { # <pfx-dir>
  28. local pfx pid env_pfx
  29. pfx="$(cd "$1" 2>/dev/null && pwd)" || return 0
  30. for pid in $(pgrep -x wineserver 2>/dev/null || true); do
  31. env_pfx="$(tr '\0' '\n' < "/proc/$pid/environ" 2>/dev/null \
  32. | sed -n 's/^WINEPREFIX=//p' | head -1 || true)"
  33. if [ -n "$env_pfx" ] && [ "${env_pfx%/}" = "$pfx" ]; then
  34. kill -9 "$pid" 2>/dev/null || true
  35. fi
  36. done
  37. }
  38. kill_prefix_wineserver "$PFX"
  39. cp "$REG" "$REG.bak-$(date +%s)"
  40. python3 - "$REG" <<'PY'
  41. import sys, time
  42. path = sys.argv[1]
  43. sec = r'[Software\\Wine\\X11 Driver]' # literal text in user.reg (backslashes doubled)
  44. key = '"UseTakeFocus"="N"\n'
  45. def is_global_x11_header(line):
  46. # the global section only, not AppDefaults\...\X11 Driver; Wine may append
  47. # a modification time after the bracket
  48. s = line.strip()
  49. return s == sec or s.startswith(sec + ' ')
  50. with open(path, 'r', encoding='utf-8', errors='surrogateescape') as f:
  51. lines = f.readlines()
  52. out, i, done = [], 0, False
  53. while i < len(lines):
  54. line = lines[i]
  55. if not done and is_global_x11_header(line):
  56. out.append(line); i += 1
  57. if i < len(lines) and lines[i].startswith('#time='): # keep the timestamp line
  58. out.append(lines[i]); i += 1
  59. out.append(key)
  60. while i < len(lines) and not lines[i].lstrip().startswith('['):
  61. if not lines[i].strip().startswith('"UseTakeFocus"'): # drop any stale copy
  62. out.append(lines[i])
  63. i += 1
  64. done = True
  65. continue
  66. out.append(line); i += 1
  67. if not done: # section absent: append it
  68. now = int(time.time())
  69. filetime = (now + 11644473600) * 10000000 # Wine #time= is a hex FILETIME
  70. out.append('\n' + sec + ' %d\n' % now)
  71. out.append('#time=%x\n' % filetime)
  72. out.append(key)
  73. with open(path, 'w', encoding='utf-8', errors='surrogateescape') as f:
  74. f.writelines(out)
  75. print("wrote UseTakeFocus=N")
  76. PY
  77. echo "== Fix 5: applied (Wayland keyboard focus). Relaunch the game."