A frame limit only paced what reached the screen: a game without vsync
kept drawing hundreds of frames a second that were thrown away (AIO at
a 60 fps limit drew 155 to 2,446 fps), so the limit saved no power.
With Performance mode on and a frame limit set, the session now sets
gamescope's per-game limit over gamescope_control (gamescopectl
debug_set_fps_limit). gamescope's limiter then has its WSI layer hold
Vulkan games, DXVK and VKD3D included, to the limit. gamescope's own
--framerate-limit option is reset on its first frame in 3.16.29, and
Steam resets the per-game limit while it starts, so the session watches
gamescope's limiter state file and sets the limit again whenever it is
off (a once-a-second check with shell builtins, no process started).
With Performance mode off, a limit paces the screen only, as before.
The session also gives gamescope a fixed limiter state file path, so
gamescope stops leaving a new one behind on every start, and removes
the old ones.
Device-proven on the AYANEO Pocket FIT (Turnip, AIO D3D11, 45 s runs on
battery, three runs each): at a 60 fps limit the game held exactly
60.0 fps and the device drew 7.08 W instead of 9.45 W, with 1.3 instead
of 2.6 busy cores and 5 instead of 11 % GPU load. No limit: unchanged.
Once Blizzard's Agent has removed a game it answers GET /game/<uid> with
an empty object, not installed false, so a finished uninstall read as a
failure and opened Battle.net.
A cancelled install now waits for its Agent job to end before the
uninstall runs, and removes the folder only once the Agent no longer
lists the game or the folder is empty. Uninstall gives up when Battle.net
or a game keeps the prefix busy for a minute, so the page falls back to
opening Battle.net. An uninstall only counts when the Agent reports the
game as not installed.
Battle.net games now install like Epic, Xbox and itch games: Install,
pick a location, and the download runs in the Downloads tab while the
Stores page stays open. droiddeck-battlenet starts Blizzard's own Agent
on its own under a headless gamescope and drives its local API, so no
window opens and nothing waits on the Battle.net client.
- Progress, speed and time left come from the Agent, with pause, resume
(the Agent keeps what it downloaded) and cancel, which uninstalls the
partial game and removes its folder.
- A job only finishes once the Agent has really run it and the game is
playable; the first status after a request can still describe the
previous operation.
- Installed games check Blizzard's version service and show Update when
the build on disk is behind; the update repairs missing files too.
- Battle.net games can be uninstalled from their page through the Agent.
- Games land in the chosen location: internal storage, other storage or
a Games folder.
- The hero keeps its action row clear of the playtime column.
Steam games and Battle.net started from the KDE desktop run on the GPU in gamescope, opened as a KDE window, and Plasma no longer leaves programs with a zero realtime CPU limit that got games killed.
The flow solver starts at flowFinest_, but its images, uniform buffers and descriptor sets were allocated for every pyramid level. Keep the seven-level indices and allocate flow resources only where the solver reads and writes them.
The default preset avoids three unused RGBA16F images, saving 38,707,200 bytes of pixel payload at 2560x1440 without changing the shaders or recorded GPU work. Add native allocation/dispatch regressions and document the resource budgets.
* fix: Reuse the prefix the client made for a shortcut id
droiddeck-proton-run builds the prefix path as steamapps/compatdata/<appid>,
but the client names that folder with the signed 32-bit form of the shortcut
id. A shortcut whose id has the top bit set therefore got a second, empty
prefix, and the first launch after that ran wineboot from scratch: the
session sat on a black screen for the length of a full prefix init, and the
old prefix was never reused.
Match the folder by its unsigned value instead, the way the client does:
int(name) % 2 ** 32 == appid % 2 ** 32. prefix_of() also accepts a negative
folder name now, and reports its unsigned id so the Steam environment it sets
up stays valid.
* fix: Reject folder names int() cannot read, and skip files
lstrip("-").isdigit() accepts "--1" and superscript digits, which int() then
raises on, and one such entry in compatdata would stop shortcut resolution.
Match r"-?[0-9]+" instead. A matching name is also only used when it is a
directory, so a stray file cannot take STEAM_COMPAT_DATA_PATH.
* fix: reuse signed shortcut compatdata prefix
* fix: prefer initialized Proton shortcut prefixes
---------
Co-authored-by: xXJsonDeruloXx <danielhimebauch@gmail.com>
Co-authored-by: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com>
* feat: Show the drawer's game settings when a game starts without Steam
A session started without the client has no Steam overview to read the
current title from, so the drawer's game profile group stayed empty and the
per-game settings, FEX preset, environment and unfolded controls were hidden.
The launch already knows which game it starts, so carry its id into the
session and fall back to it when Steam has nothing to report.
* fix: Pass the game id into the service start, and keep the unfolded toggle to Steam
SessionActivity.surfaceCreated() hands the mode, program and args to
SessionService.start(), which builds the service intent. EXTRA_GAME_ID was
only put on the activity intent, so a new no-Steam session never received it
and the drawer fallback stayed empty. Pass it through.
The unfolded-controls toggle also went out for direct sessions, but
applyUnfoldedControls() still requires Steam mode and a Deck pad, neither of
which a direct session sets up. Keep the toggle to Steam.
* fix: hide Steam controls in direct session drawer
---------
Co-authored-by: xXJsonDeruloXx <danielhimebauch@gmail.com>
Co-authored-by: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com>
Every grid-<slot> record held only a file signature, not which grid
folder it came from. After switching accounts or reinstalling the
runtime, the new, empty grid folder looked like the user removed every
image in Steam, and all chosen art was deleted. The grid folder now
carries a random id and records store it; a record from another folder
counts as a first look, so the choice is placed again.
fix: never accept a record made with the fallback grid id
CodeRabbit: when the grid id could not be read or written, "?" was
stored in records and a later "?" matched it, so an empty grid could
still delete local art. Records now never match the fallback id, and a
blank .droiddeck-grid file is replaced instead of blocking the rename.
Co-authored-by: ovurrsl <ovurrsl@users.noreply.github.com>
Co-authored-by: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com>
* fix: the update status receiver only acts on this app's own install session
Before Android 13 the receiver was exported, so any app could send a
fake PENDING_USER_ACTION broadcast and have DroidDeck launch an arbitrary
intent. The receiver now ignores broadcasts whose session id does not
match the running install, launches only a system installer activity,
and strips URI grant flags.
* fix: record the install session id before committing it
CodeRabbit: the status broadcast could arrive between commit() and
install() returning, while session was still -1, and be dropped. The id
is now reported through a callback before commit, and reset to -1 at
the start of each attempt so a late broadcast from an earlier session
is ignored.
* fix: declare package installer confirmation query
* Query current PackageInstaller confirmation action
---------
Co-authored-by: ovurrsl <ovurrsl@users.noreply.github.com>
Co-authored-by: xXJsonDeruloXx <danielhimebauch@gmail.com>
Co-authored-by: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com>