Pre-defined Steam launch options: picker menu — #630 port staging - #6
Closed
Leb-Sun wants to merge 2 commits into
Closed
Pre-defined Steam launch options: picker menu — #630 port staging#6Leb-Sun wants to merge 2 commits into
Leb-Sun wants to merge 2 commits into
Conversation
Leb-Sun
force-pushed
the
steam-launch-options-630-port
branch
from
July 21, 2026 01:58
3058f44 to
5bd6dd4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Games with multiple Steam launch entries (Play DX11 / DX12, safe mode, mod
launcher…) always booted the default one. This adds a "Launch Options" item to
the STEAM dropdown on both game screens — only shown when the game actually has
2+ entries — opening a Workshop-style picker (WsBg scheme, pane-nav wired).
Tapping a row saves it as the game's default.
The selection is explicit per-shortcut state (
launch_option_exe/launch_option_argsextras, stored verbatim): untouched shortcuts show thedefault entry as active, a manual exe change in shortcut settings turns options
off entirely (a custom exe is never adopted as a launch option), and a read-only
hint under Exec args shows the args the active option appends (new string in all
locales). LaunchInfo gains
argumentsandlaunchIdfields parsed from appinfoconfig.launch; stale cached rows heal via a one-shot PICS re-fetch when the
picker opens.
At launch the selection routes through Steam itself: the Android side resolves
the picked entry's config.launch key (list position as fallback for pre-launchId
caches) and hands it to the in-Wine launcher, which drives steamclient's own
IClientAppManager::LaunchAppwith that index — Steam registers and starts thepicked entry, and the session survives relaunches (forcing the exe directly lost
the Steam session on the second launch). Direct-exe mode remains only for a
user-overridden exe that isn't in appinfo at all, and the launcher still falls
back to CreateProcess if LaunchApp can't spawn the game.
Custom launch options from WinNative-Emu#520's
%command%pipeline still compose on top.LaunchApp alone launches the entry's stock command line, so with custom args
present the launcher uses LaunchApp only to register the game, reaps that spawn,
and relaunches the exe via CreateProcess with the full combined argv (entry args
warning instead of mid-token truncation, no raw command lines in logs since
execArgs can carry credentials). The Rust client's
setLaunchCommandLinegetsthe custom-only args so the entry's own args aren't applied twice, the steam-env
stamp now includes the effective line so switching options re-runs that setup,
and warm launches re-push the command line and family-shared flag (both
per-process native state).
The entry's own args are resolved from appinfo at launch time (falling back to
the picker's recorded selection, then the persisted override, if appinfo can't
name the entry), so every launch path ships the same command line. Steam applies
an entry's args only on the spawn it makes itself; the moment we take over —
reaping for custom args, or falling back — the line has to be rebuilt from our
own state. Resolving it from appinfo means a default-entry pick (which stores no
override) and an untouched shortcut both still carry the entry's args, e.g.
Subnautica's default
-silent-crashes -vrmode none. Which path handles a launchis invisible in the game's arguments.
localconfig's
LaunchOptionsis only ever cleared, never written with a value:in-Wine Steam runs a non-empty value through ShellExecute, which pops a modal
"File not found." box that wedges steamclient's launch thread. It was never a
delivery path anyway — args reach the game via argv — so this channel does
hygiene only, and skips rewriting the file when nothing would change.
Review + testing hardening: the direct-exe override compares the full relative
path (entries differing only by directory were silently ignored) and
re-selecting Steam's default entry clears the args override so launches return
to LaunchApp. One shared exe-path normalizer is used on both the persist and
launch sides. The picker re-reads state on every open, both game screens share
one state holder, and saving a selection no longer loads every shortcut's icon.
The on-disk option filter matches case-insensitively, like the launch path
resolves those same paths. The picker and the game-screen launch resolve the
game's shortcut through one shared resolver over the loaded container list, so
a pick can't land in an orphaned or corrupt container dir the launch never
reads (and launching stops icon-decoding every shortcut just to find one).
Verified on device across: Halo MCC (non-default
-no-eacentry) with customargs added, changed, and removed, and after re-creating the game's container and
opening it in multiple containers; Subnautica (default entry and a no-arg picked
entry) with and without custom args, including full app close/reopen. Every case
delivered the resolved args exactly once with no doubling and the picked shortcut
resolving to the container that launched.
Limitations / needed follow-up (each planned as its own small PR):
cached yet,
LaunchAppreturns MissingConfig on every attempt, so the launcherspends ~20s retrying and then starts the game via CreateProcess. The resolved
args are still delivered, but the game has to self-register through
SteamAPI_Init against a session that may not be ready — usually it succeeds, but
a strict title (Halo MCC observed) will quit itself if it comes up unregistered.
It self-corrects once appinfo is cached (once per install, online). Independent
of the launch method and of the args. Follow-up: pre-warm appinfo before launch,
or (see below) launch the entry directly and skip the LaunchApp round-trip.
shortcut (first loaded container's); a home-screen shortcut pinned from
another container still launches with that shortcut's own per-shortcut
settings. A container-aware picker is a possible follow-up if per-container
selections are ever needed.
LaunchApp index; a game with keyed/non-sequential config.launch entries could
start the wrong entry until the cache heals (the picker's PICS re-fetch, plus
the CreateProcess fallback, bound the damage).
service is tearing down (persist no-ops), needing a picker reopen to show
fresh entries.
arguments, so a game listing two entries that agree on both (common for
no-argument entries) resolves to the first of them. They launch identically,
but a per-entry detail such as a beta key would come from the wrong one.