From cbcd3f802346c2bd493d887dcf907425aaa77d2f Mon Sep 17 00:00:00 2001 From: The412Banner Date: Thu, 24 Sep 2026 08:26:51 -0400 Subject: [PATCH] Proton launch: Quake-engine titles stay windowed at the session's size Quake III Arena, Team Arena, Return to Castle Wolfenstein and Jedi Academy set their own resolution and switch the display to it. Under gamescope with the ARM64 Proton that switch is refused ("calling CDS: failed, bad mode") and the game never gets OpenGL - device-seen on a Retroid Pocket 5 on Proton Experimental build 25406135 - and a window left at the game's own size draws small in a corner. Both wrappers (Valve's depot and an adopted GE or CachyOS) now add +set r_fullscreen 0 +set r_mode -1 +set r_customwidth/height at BL_WIDTH x BL_HEIGHT for those app ids, unless the user's launch options already set the video mode; the choice is logged. --- .../usr/local/bin/bannerlator-steam-compat | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/tools/linuxfs/overlay/usr/local/bin/bannerlator-steam-compat b/tools/linuxfs/overlay/usr/local/bin/bannerlator-steam-compat index b7c0659ad..4156846a5 100755 --- a/tools/linuxfs/overlay/usr/local/bin/bannerlator-steam-compat +++ b/tools/linuxfs/overlay/usr/local/bin/bannerlator-steam-compat @@ -87,6 +87,31 @@ bl_game_cpus() { } """ +# Quake III engine games (id Tech 3) set their own resolution and switch the display to it. Under +# gamescope with the ARM64 Proton that switch is refused ("calling CDS: failed, bad mode") and the +# game never gets OpenGL; left at its own size in a window, it draws small in a corner. So these +# titles are told to stay windowed at the session's own size - unless the user's launch options +# already say something about the video mode, which are left as they are. +BL_IDTECH3_SETUP = """ +bl_idtech3() { + BL_EXTRA_ARGS=() + case "${SteamAppId:-${STEAM_COMPAT_APP_ID:-}}" in + # Quake III Arena, Quake III: Team Arena, Return to Castle Wolfenstein, Jedi Academy. + 2200|2350|9010|6020) ;; + *) return 0 ;; + esac + case " $* " in + *r_fullscreen*|*r_mode*|*r_customwidth*|*r_customheight*) + echo "bannerlator-proton: Quake-engine title; its launch options set the video mode, left as they are" >&2 + return 0 ;; + esac + w=${BL_WIDTH:-1280} + h=${BL_HEIGHT:-720} + BL_EXTRA_ARGS=(+set r_fullscreen 0 +set r_mode -1 +set r_customwidth "$w" +set r_customheight "$h") + echo "bannerlator-proton: Quake-engine title: windowed at ${w}x${h}" >&2 +} +""" + BL_DIRECTAUDIO_SETUP = """ bl_directaudio() { base=$1 @@ -152,7 +177,8 @@ for name in %s; do if [ -d "$depot/files/bin-arm64" ] && [ -x "$depot/proton" ]; then bl_directaudio "$depot" bl_game_cpus - exec ${BL_TASKSET:-} "$depot/proton" "$verb" "$@" + bl_idtech3 "$@" + exec ${BL_TASKSET:-} "$depot/proton" "$verb" "$@" "${BL_EXTRA_ARGS[@]}" fi done echo "bannerlator-proton: no ARM64 Proton depot yet - the client downloads one as this tool's dependency; retry the launch once it has" >&2 @@ -162,7 +188,7 @@ exit 1 # and a % format would eat that into a single %, leaving a shell expansion that silently does # something else. LAUNCHER_SH = LAUNCHER_SH.replace( - "verb=$1; shift", "verb=$1; shift\n" + BL_DIRECTAUDIO_SETUP + BL_GAME_CPUS_SETUP, 1) + "verb=$1; shift", "verb=$1; shift\n" + BL_DIRECTAUDIO_SETUP + BL_GAME_CPUS_SETUP + BL_IDTECH3_SETUP, 1) # A third-party Proton dropped into compatibilitytools.d - GE-Proton, proton-cachyos, anything of # that shape - is adopted rather than used as shipped, for the same two reasons Valve's ARM64 depot @@ -196,9 +222,10 @@ if [ -f "$fake" ]; then fi bl_directaudio "$here" bl_game_cpus +bl_idtech3 "$@" # Proton takes its base directory from dirname(sys.argv[0]) without resolving it, so it has to be # started from its own tree. -exec ${BL_TASKSET:-} "$here/%s" "$verb" "$@" +exec ${BL_TASKSET:-} "$here/%s" "$verb" "$@" "${BL_EXTRA_ARGS[@]}" """ EXTRA_COMPAT_TOOL_VDF = """"compatibilitytools" { @@ -357,7 +384,7 @@ def adopt_extras(tools): # ${major%%.*} of its own, and putting it through the format would eat both. body = (EXTRA_WRAPPER_SH % entry_point).replace( 'here=$(dirname "$0")', - 'here=$(dirname "$0")\n' + BL_DIRECTAUDIO_SETUP + BL_GAME_CPUS_SETUP, 1) + 'here=$(dirname "$0")\n' + BL_DIRECTAUDIO_SETUP + BL_GAME_CPUS_SETUP + BL_IDTECH3_SETUP, 1) with open(wrapper, "w", encoding="utf-8") as f: f.write(body) os.chmod(wrapper, 0o755)