The wall waited about a second and a half for the game list on every start: Steam's own
manifests are quick, but the added games' folder walk and art lookups are not. The last list
is now kept in the app's files (LibraryCache) and shown before the first frame, and the fresh
one replaces it in a single update. The wall's order is fixed (last played, then game ID), so
the same games always land in the same capsules and the refresh moves nothing. The added-games
scan on every resume also leaves the main thread, which it stalled for most of a second.
Backing out of the loading screen, closing the ended screen and the session ending on its own
used the slide-down card exit. Now, while the session is on screen, the blue spreads out of
the throbber's ball (the middle of the screen once a game is up) until it covers the screen,
and the front end opens on it and draws it back into the button the session came from, as it
already did after the drawer's Stop. A retry, a session closing off screen and animations off
still close at once; a timeout closes it should the flood never run.
All string resources get a Spanish translation except brand names and strings that are only a
format. The app declares English and Spanish in a locale config, so it is offered in Android's
per-app language setting and follows the system language otherwise. Short rail labels keep
to the rail's width (Configuración, Actualizar).
It was drawn with the current selection, so leaving it for another rail item turned the
outgoing page into a copy of the new one for a few frames, which then faded out before the
real page rose in: a flash and a dip moving from Games to Steam.
Opened with a pad, the editor and its variable dialog put focus on their first control; as
windows of their own they otherwise gave the pad nothing to move from. On/off variables pick
from their two values in the same dropdown as other choices instead of a switch. App dialogs
keep the page's focus ring out of sight behind them. The footer's Add variable button only
appears once the settings are loaded: drawn disabled first, it came up without its box.
The settings row gets a one-line hint and the same secondary button as every other row. The
editor moves from a stock Material alert, with a paragraph of explanation and a column of text
buttons, into the app's dialog: a profile chip with its menu, the variables in a panel with
each one's value as a chip whose menu changes, removes or restores it, and a footer to add a
variable, reset the profile or finish. Editing a variable uses the same dialog, its name and
value as chips or the app's text fields, and the variable's note in the footer.
LB, RB or a tab now start focus at the top of the new page instead of wherever it was last
left on that page; closing a menu or reopening the drawer still returns to where it was. On
Settings the top option is Android apps when DroidDeck is the Home app.
The ring fills in as it pinches to a dot, crosses as one round dot on a timed ease with no
stretch, and opens into the control as it gets there instead of settling as a circle first.
- A held direction runs the ring along as one piece and keeps its speed between steps;
restarting each edge from rest (and the trailing edge's delay) made it stutter.
- A jump of more than 360dp pinches the ring to a drop, carries it over and opens it onto
the new control instead of stretching a box across the screen.
- The ring keeps out of launch, stop and return floods (RingVeil) and appears on the
focused control once they are done.
- Leaving a page by moving along the rail no longer pulls focus into the new pane; only
backing out of a page hands focus to the control it was opened from.
- Selection is a radio dot or a tick, never a ring: the Updates channels and test builds,
the GPU modes and driver pairs, the Games list and suggested app icons lose their
persistent rings, and the GPU cards use the sliding ring for focus like everything else.
The cog's tile stretches over the pane on the same loose springs as Play, far edges first and
blobby in flight, while the page behind sinks; the settings rise in once it is covered. Back
draws the tile down into the cog on critically damped springs. The focus ring stays out of
it and appears on the focused control once the motion is done, replacing the ring riding the
edge of a stretching box.
The bloom is now the opening control's own rounded box stretching out to the pane, not a
circle. With a controller the focus ring rides its edge out to the pane's frame and springs
in onto the page's first control; on Back it rides the box back into the control, which
takes focus again. By touch, with no ring up, the bloom traces the same edge itself.
Closing a page used to leave focus on the page while it animated out, so nothing took it
back afterwards.
The page grows from the cog as a filled disc with an accent rim and folds back into it on
Back, while the pane under it sinks and returns. Pages also keep drawing while they leave
instead of falling back to the missing-page note for a frame.
Opening the settings no longer scans the added-games folders, reads the driver files and the
storage volumes on the main thread first: the page opens at once and those land while it
animates in.
Compose already scales its animation clock by the system animator duration
scale, so Motion.tw no longer multiplies durations a second time; Motion.ms
stays for coroutine delays. The drawer tabs, launch flood and the default-spec
expand/fade transitions now go through Motion, keeping their current speeds.
DROIDDECK_CI_TOKEN never existed, so PR Release Publish skipped its
upload, and a personal token would expire and belong to one person. Both
workflows now mint a token of the org's DroidDeck CI Publisher app,
installed on DroidDeck-CI only with Contents: write (its ID in the
DROIDDECK_CI_APP_ID variable, its key in DROIDDECK_CI_APP_KEY). No
fallback: a main build without them fails its announce job.
Run artifacts need a GitHub sign-in to download. Each signed main build
is now also a prerelease main-<sha> of the public DroidDeck-CI (all
variants and their SHA-256s; the last 10 kept, PR releases untouched),
and the Discord post links its standard apk and the release. Without
DROIDDECK_CI_TOKEN the post links the run's artifacts as before.
A job after the signing of a main build posts to the Discord's builds
channel: the merged PR (or the commit), and a link to the run's signed
apks. The channel's webhook URL is the DISCORD_BUILDS_WEBHOOK secret;
without it the job posts nothing and passes.
A local build was signed with the testkey alone, which a release refuses
as an update ("signatures do not match"): testing on a device with a CI
build meant uninstalling it and losing its data first.
With an untracked .signing.env (RELEASE_KEYSTORE, RELEASE_STORE_PASSWORD,
RELEASE_KEY_ALIAS) in the checkout, or in the main checkout for a
worktree, build_local.sh finishes with tools/release/sign-apk.sh as CI's
main builds do, and the apk installs over a release. Without the file
nothing changes. DROIDDECK_SIGNING_ENV points at the file explicitly.
The desktop forced a software cursor (WLR_NO_HARDWARE_CURSORS=1) because
labwc's cursor never showed when handed to the app's compositor. With a
software cursor wlroots never scans out: every frame of a full-screen game
was composited by labwc first ("Direct scan-out disabled by software
cursor", 2284 times in one Adreno 825 report).
Why the cursor never showed: wlroots' Wayland backend attaches and commits
its cursor surface before wl_pointer.set_cursor gives it the cursor role,
and sets it again with nothing new committed each time the pointer enters.
The compositor released a role-less surface's buffer on commit, so the
cursor surface had no image by the time it became the cursor.
- A role-less surface's last buffer (cursor-sized only) is held until the
next commit; set_cursor publishes it, and a repeat set_cursor of the
surface already shown unhides its last image.
- A dma-buf cursor is read back after the client's implicit render fence,
as the zero-copy path waits for it.
- The desktop no longer forces a software cursor.
Device-tested on the AYN Thor (Adreno 740, 1920x1080): labwc's own cursor
is drawn, with its shapes (I-beam over a terminal); wlroots logs "Direct
scan-out enabled" for a full-screen glxgears. At the same 60 fps on screen
the GPU is 3.0 % busy against 4.6 % with the software cursor, both at
550 MHz.
main's build signed only the standard package, so the pubg, antutu and
ludashi variants existed only after a manual Release APKs run. The build job
now reads the variant list from tools/release/variants.txt, as release.yml
does, and main's signing job signs all of them.
KGSL's files and thermal zone names differ between Snapdragon generations.
Each value now takes the first source that exists, in the order GameNative
and the Winlator forks read them: GPU load from gpu_busy_percentage or
devfreq/gpu_load, GPU clock from devfreq/cur_freq or gpuclk, GPU temperature
from KGSL's temp, devfreq/temp or a zone whose sensor name has "gpu". CPU
zones are any whose name has "cpu" and not "gpu" (cpuss-0, cpu0-silver-usr,
apc1-cpu0-usr, ...), matched without regard to case.
mangoapp's VRAM figure on a Qualcomm driver comes only from the gpu_mem_total
trace event. The tracefs stand-in's event list now declares that event as
Android kernels do, and reading the stand-in's events answers with one
record carrying the total KGSL reports in /sys/class/kgsl/kgsl/page_alloc,
which every Adreno under Android has and the app can read.
Valve's mangoapp reads an Adreno GPU's load, clock and temperatures from the
paths of its own hardware, and the CPU temperature from thermal zones named as
mainline kernels name them. Every Adreno under Android keeps the GPU values in
KGSL's sysfs, readable by the app, and Android kernels name the CPU sensors
cpuss-N / cpu-N-M; the session binds each where mangoapp looks. Thermal zones
are found by sensor name, since their numbering differs per device, and
anything that cannot be found is left alone.
Three things kept Valve's mangoapp from drawing the overlay in a session:
- GLFW 3.4 takes XDG_SESSION_TYPE=wayland with WAYLAND_DISPLAY set as
Wayland-only, and mangoapp needs gamescope's X server; the wrapper sets
XDG_SESSION_TYPE=x11.
- On a Qualcomm display driver it opens a tracefs instance for the GPU memory
total and aborts when that fails, which it always does for an Android app.
The session preload now places the instance in an empty directory of its own
when the real one cannot be made.
- gamescope sends it each frame's timings through a System V message queue,
and Android kernels have no System V IPC. sysv.c now emulates the queue
operations gamescope and mangoapp use, beside its semaphores and shared
memory.
The Quick Access menu's performance overlay toggle and level slider stayed
greyed out: the client exposes SteamClient.System.Perf only with -steamos3,
which SteamOS and Armada pass alongside -steamdeck. Deck mode now launches the
client with both. The session already puts package/beta back to the forced
branch before every start, which keeps -steamos3's own branch switch from
turning into an update loop.
The Deck mode switch moves from the Performance page to the Steam page's
Client settings, next to the client branch it selects by default.