Resolution gains Custom…: a width × height box (320×240 to 3840×2160, evened) with one-tap
presets for 4:3 (960×720, 1024×768, 1280×960), 16:10 (1280×800) and 16:9. A custom size replaces
the cap and the shape for that mode - the Shape row greys out and says so - and the compositor fits
it to the panel with bars where the shapes differ. Choosing any cap clears it. The device report
lists it for both modes.
The panel's shape was never allowed narrower than 16:9 (the foldable guard), so on a 4:3 panel
such as the Retroid Pocket's both choices gave 16:9 with bars. A third choice, Exactly this panel,
presents the panel's own aspect with no floor; the other two are unchanged and foldables still
default to 16:9. Offered in the mode settings and the in-session menu.
The package list opens in the front end's pane like the Settings pages. Each tier (Native ARM64,
ARM64 experimental, source builds) is a dropdown row with the rail's caret and an installed/total
pill; Native ARM64 starts open. Each package has its own Install/Remove button, and the one that is
installing shows its own step line and bar in its row (its tier opens when the install starts).
Leaving the page does not stop an install. The rail's Install emulators and Setup's Desktop & apps
highlight while the page is open.
Desktop is enabled without a runtime or the desktop package. The session's loading screen installs
whatever is missing first - the runtime, then the desktop ("downloading the desktop · 212 of 426
MB", checking, unpacking) - and starts the session when both are in. A failure ends on the loading
screen with the reason. Desktop UI and Desktop now give the same non-Adreno warning as Play before
any download.
Play and Desktop UI are enabled without a runtime. The session's loading screen downloads and
unpacks it first, reporting on its usual step line and bar ("downloading the Linux runtime · 332
of 791 MB", then checking, then unpacking), and starts the session when it is in. A failure ends
on the loading screen with the reason. Non-Adreno devices still get the warning before any
download. Setup > Linux runtime is unchanged.
- The client's sound goes back to the adaptive AAudio sink 0.1.5 shipped (bundled unchanged as
module-aaudio-classic-sink, sink name AAudioSink). Through the DirectAudio relay it stayed
choppy on an AYN Thor (Android 13, 20 ms bursts) even with a two-burst buffer.
- Steam cog > Audio: DirectAudio for games (on by default, unchanged) and Steam client audio
(Classic by default, or DirectAudio). The relay also starts when only the client wants it.
- The relay runs with --log and the daemon at info level, so audio.log carries the stream each
path was granted and, on the relay, Android's xrun count every 1000 callbacks.
- Device report shows both settings.
The unpack check compared a hand-bumped stamp only. Two sink changes shipped without a bump, so a
device that had run the earlier build kept the old module: the 40 ms ring floor and the 24 ms
device buffer never reached the AYN Thor they were for. The stamp now carries a CRC-32 of
pulseaudio.tzst. The sink's connect line names the buffer it asked for, and one line ten seconds
in reports the relay's device buffer, ring target and underruns.
The relay's default is 12 ms rounded up to whole bursts. On a device where Android grants a fast
stream (4 ms bursts) that is three bursts; on one where it grants a legacy stream (20 ms bursts,
an AYN Thor on Android 13) it is a single burst, and the Thor's client audio stuttered. 24 ms is
two bursts there and 24 ms on the fast devices. buffer_ms= overrides it.
- module-directaudio-sink keeps the relay's ring at least 40 ms deep. The relay starts at 8 ms,
which the producer inside proot missed while the client loads (two underruns on the FIT's fresh
install, steady stutter reported on another phone).
- Setup > Session logs > Share latest logs zips the newest session folder, wherever it was written
(Downloads, or app-private files when Downloads is not writable), and opens the share sheet via
a FileProvider scoped to that zip.
- Microphone defaults to on, and the app asks for RECORD_AUDIO once at first launch, together with
the storage prompt. A refusal is not asked again at start-up.
- modules/armhf held the inherited Winlator AAudio sink and two protocol modules; the app is
arm64-only and nothing loads that folder. Removed, bundle stamp bumped so installs re-unpack.
Everyone who never touched the toggle gets it; anyone who turned it off keeps it off (the setting is
only stored when changed). Off, the client's sound came from AAudio driven inside proot, which
stutters on some devices.
A second sink module: connects to the relay's socket (lazily, the relay comes up after the
daemon; a clocked null sink until then), sends the hello, maps the render ring it gets back and
keeps it topped up to the relay's target with a futex wait per burst. Every AAudio call, the
adaptive buffer and the route handling stay in the relay, outside proot. With DirectAudio on the
daemon loads this sink instead of module-aaudio-sink; the bundle carries both.
Two bursts (8 ms) of buffer with a write per burst left the client's menu sounds choppy on
device: the daemon runs under proot, where every system call is traced. The buffer now starts at
four bursts or 30 ms, whichever is more, each write hands over half of it, the IO thread asks for
nice -16 like Android's own audio threads, and the log says how big the buffer is and how many
underruns the device counted.
frontend/AddedGameArt: cover/poster/boxart/folder-name images, header, hero, logo and icon from
the game's folder (or its art subfolder); otherwise a one-time store search by folder name and
the capsule, header, hero and logo from Steam's CDN into the app's cache (a miss retried after a
week); an icon pulled from the .exe. The rail shows the portrait. The session's listing carries
the paths; bannerlator-steam-shortcuts copies them into userdata/<acct>/config/grid under the
client's names, never over a file it did not write itself, and sets the shortcut's icon. A toggle
in the cog turns the store lookup off.
Folders are a list (the single folder from before carries over); each gets /root/Games/<name>,
with a short hash of its path only when two chosen folders share a name, so adding or removing
one never moves another's games (their appids follow the guest exe path). Per-game exe choices
are keyed by the folder's path now. The cog lists each folder with its game count and a Forget,
plus an Add row.
The inherited module is gone from app/src/main/assets/pulseaudio.tzst (74 → 73 modules); CI adds
the app's own at build time and refuses a bundle that already has one. BUNDLE_STAMP bumped so an
installed app re-unpacks.
Blocking-write design: the IO thread pulls one burst at a time from the sink and hands it to a
blocking AAudio output stream, whose buffer paces the thread; latency comes from the stream's
playback timestamp; with adaptive=1 the buffer grows a burst per underrun up to a cap. Same
module arguments as before, so the daemon's config line is unchanged. build.sh compiles it with
the NDK against the app's own libpulse* and a small config.h; a workflow checks it on every push.
Four of Armada's patches apply to 3.16.29 as they are (appid-focus fallback, ARM64 night mode,
ARM64 colour-temperature slider, P3 red); two more are ported by hand: realtime-priority Vulkan
queues on GAMESCOPE_FORCE_VULKAN_REALTIME=1 without CAP_SYS_NICE, and the gamepad cursor sprite
following the X pointer. build-gamescope.yml builds Arch Linux ARM's package with them in an
Arch container, checks every NEEDED library against the runtime's own list, and publishes the
binary as a release asset for the apk to stage over /usr/local/bin/gamescope.
A Games folder chosen in the Steam cog is scanned one level deep, one subfolder per game; the
program to launch is the .exe named after the folder, else the largest, never an installer,
redistributable or crash reporter, and the settings page lets the user pick another for any
game. Each session the app writes the list, and bannerlator-steam-shortcuts puts the games into
the client's shortcuts.vdf as non-Steam games (the client's own entries kept) with the appid the
client itself derives, mapped to the ARM64 Proton through the compat registrar. They appear in
the rail under Steam and launch by steam://rungameid/ like any title.
On device the client's console log showed the calls: /usr/bin/steamos-polkit-helpers/steamos-update,
steamos-select-branch, jupiter-biosupdate and jupiter-dock-updater - the polkit-helpers path, not
/usr/bin. The missing steamos-update there was the 'Update Error' dialog. Stubs at both paths now.
Also seen on device: -steamdeck alone opts the install into the steamdeck_stable branch too, so the
branch guard is what keeps a session from reinstalling; and on the publicbeta channel Deck mode
reports installed version 0 at every start and reinstalls the same client (rc=42, the launch URL
lost with it), while on steamdeck_publicbeta the second launch comes up with no restart at all.