* fix(storage): Windows installs on a natively mounted card no longer ask for an administrator
Cards mounted directly as FAT, exFAT or NTFS were not treated as Android
storage, so wineserver reported an Anonymous owner and refused permission
changes. Its chmod on these volumes also no longer fails the request.
* fix(storage): keep NTFS owners and the chmod error
* chore(i18n): translate every missing string in all locales
Adds the 87 strings the bottom-screen Dashboard and in-game stats pages
introduced to all 32 locales, plus the 51 older settings strings Japanese
and the 50 Chinese locales were still missing. Every locale now has all
3456 translatable strings with the same placeholders as English.
Also translates 36 entries that were still English where the language
does not use the English word, and makes es-419 touch_app_id use the same
%1$s placeholder as English.
* chore(patches): drop comments from the lines downstream patches add
Removes comment lines and trailing comments from lines our patches add,
with hunk headers recounted. Lines that a later patch uses as context
and the upstream backport 0123 are left as they are.
The gamescope, FEX, proot and xodus series still apply to their pinned
sources, and the patched trees differ only in those comments.
test_hud_frames now finds the HUD writer by its first declaration.
* docs: credit Greenlight, Better xCloud, viture-v2, GameNative and DiRTTelemetryErrorFix
These were credited only in source headers and NOTICE files.
* chore: remove source comments and unused functions
Removes explanatory comments from first-party Kotlin, Java, C, C++,
Rust, TypeScript, Python, shell, Gradle, CMake and workflow files.
License and attribution headers stay, and vendored third-party trees,
generated files and license files are untouched. A shellcheck directive
is kept, and udevmon's fallthrough comments became fallthrough attributes.
Syntax trees and token streams match the previous sources apart from the
comments and Python docstrings. Module docstrings that argparse prints
are kept, and device-smoke.sh prints the same --help text from a heredoc.
Also removes 37 functions and 4 fields that nothing referenced.
* chore: remove comments from the Syncthing integration and the latest main sources
* docs: credit Syncthing and the syncthing-android fork
* chore(i18n): translate the Syncthing strings in all locales
The 107 strings the Syncthing integration added are now in all 32 locales.
sync_web_path_locked names the Syncthing tab by its label.
* docs: credit @xXJSONDeruloXx for the HUD metric readers
The CPU, GPU load, thermal and battery readers come from their
PerformanceHudView pull requests #777, #877, #991, #1406 and #1573.
The FusionHUD credit line is unchanged.
* docs: credit the HUD metric readers to @xXJSONDeruloXx's work
An older submission that returns late must not hide a newer one. Contexts
with user-generated timestamps can have gaps, so they keep the failed read
instead of waiting once per integer.
A descriptor can close without destroying its contexts and its number can
be reused. KGSL checks the owner on every wait, so one wait on the retired
timestamp keeps a read of a dead context an error.
Answer a failed read only for a context created on the same file
descriptor, so an invalid context or a closed descriptor stays an error
instead of reading as timestamp zero.
Mesa 27 polls timeline points with CMDSTREAM_READTIMESTAMP_CTXTID and
reports any failure as VK_ERROR_UNKNOWN, so gamescope aborted at start on
Quest with vk.WaitSemaphores result -13. Track each context's submitted
timestamps in the preload and answer a failed read from one millisecond
WAITTIMESTAMP calls. Log refused KGSL ioctls to session.log.
* fix: a read-only file on a USB drive no longer fails to close the first time it is read
On the RedMagic Astra 2, Android's NTFS USB drive returns EIO from close() for a file opened read-only when that first read updates its access time; the update itself succeeds and the descriptor is released. The x86 loader under FEX treats a failed close as fatal, so every Linux game newly installed or updated on such a drive failed to start once (libfreetype.so.6: cannot close file descriptor: Input/output error). The session library's close() now reports success for EIO on read-only descriptors; errors on descriptors opened for writing are still returned.
* Check the read-write close still reports EIO
* fix(desktop): video wallpapers no longer race ahead after DroidDeck returns from the background
* test: give the vrclient loader harness the clock syscall hook
* perf(fex): answer raw stat, readlink and statx syscalls through the proot fast path
FEX issues newfstatat, readlinkat and statx as raw syscall() calls, which skipped the libc wrappers the fast path hooks and fell through to ptrace. Route them through libblfastpath the same way raw openat already is, and keep the fast path's own /proc/self/exe lookup off the hooked syscall().
* test(fex): keep the raw lookup test passing on kernels without statx
A capture format or buffer change now closes the app's decoder so the
next packet reopens it with the new stride and height, and a capture
slot whose remap fails no longer keeps its old mapping.
Steam's arm64 streaming_client only decodes through a V4L2 stateful
decoder and libdrm dumb buffers, neither of which exist on Android, so
streams from a PC never showed a picture. The preload now offers it a
/dev/video-dec0 and a libdrm stand-in backed by dma-heap buffers, and
each frame is decoded by the app's MediaCodec (Qualcomm low-latency
mode) over the encoder socket, falling back to the FFmpeg in Steam's
runtime.
On a GPU without KGSL, Setup offers an experimental Mali bridge switch
(off by default; ignored on Adreno). With it on, the session starts the
bridge server, points the Vulkan loader at the bridge ICD, names Mali's
device as the stand-in render node, runs the bridge self-test into the
session log, and caps vkd3d-proton's feature level at 12_0. Flatpak
sandboxes get the bridge ICD and socket. The DRM preload falls back to
flock in a bridge session when kcmp is refused, and a clock/thermal log
is kept beside the bridge logs.
- Handlers installed with SA_RESETHAND are no longer wrapped, so a deferred one-shot signal can't fall through to the default action.
- Deferred signals are replayed with every signal blocked, so a signal landing mid-replay can't send an entry twice.
- Taking a deferral slot and holding a signal are safe when one signal interrupts another, and a held signal is released only at the nesting level that held it, so a handler's return can't block it again.
- A fifth distinct signal inside a critical section is now held pending with the thread's signal mask and released once the section ends, instead of running its handler while an object lock is held.
- A wait from a signal handler during another wait's sleep uses its own queue and, for a second device, a reference for that call only, so the outer wait keeps its queue and can't return the inner wait's result.
Wine asks for userfaultfd with the i386 syscall number on arm64, so it
never gets one and falls back to mprotect and a SIGSEGV per first write,
which under proot is a ptrace stop each time. libblsession hands it the
real userfaultfd and, where Android refuses the PAGEMAP_SCAN ioctl, answers
it from /proc/self/pagemap and UFFDIO_WRITEPROTECT.
* perf: FEX's in-root path lookups go through the fast path instead of proot
Android has no openat2, so libblsession walks RESOLVE_IN_ROOT lookups for
FEX one component at a time. Every fstatat, readlinkat and openat in that
walk was a raw syscall that stopped in proot. When libblfastpath is loaded,
the walk now calls its fstatat, readlinkat and openat, which answer without
proot and fall back to the raw syscall themselves. Without it the walk uses
the raw syscalls as before.
One lookup drops from 250-940 us to 26-86 us. A Graveyard Keeper launch
makes 37.5k proot stops instead of 54.0k and reaches its first scene
0.31 s sooner.
* test: count fstatat64 where the preload asks for it
* perf: FEX's own file opens go through the fast path instead of proot
FEX opens host files for the game with syscall(SYS_openat), so every one
stopped in proot even when the fast path could answer it. libblsession's
syscall() now sends SYS_openat to the fast path's openat as well, the same
way the in-root walk does, and makes the raw syscall when the fast path is
not loaded.
A Graveyard Keeper launch makes 28.0k proot stops instead of 37.5k; FEX's
openat stops go from 6,739 to none.
* fix: the fast path's start-up reads don't come back through syscall()
libblsession's syscall() now sends SYS_openat to the fast path. A process
named FEX starts the fast path by reading /proc/self/cmdline with
syscall(SYS_openat), which came straight back into the fast path and
waited on its own pthread_once. Its start-up reads now call libc's
syscall directly.
A process's status read for another process is served read-only, so
Chromium's ReadOnlyFile check no longer kills the browser. Chromium-based
AppImages without chrome-sandbox get --no-sandbox, and Flathub Chromium and
Electron apps skip zypak's spawn portal, which cannot work here.
* feat: run SteamOS PipeWire and WirePlumber for gamescope screen capture
* feat: hardware Remote Play encoding through MediaCodec behind Steam's V4L2 stage
* feat: deliver Remote Play touches to gamescope as touches
* feat: route game audio through PulseAudio while a Remote Play client is connected
* fix: keep the Remote Play marker when a forked child closes the encoder
* fix: allow standalone preload tests without V4L2 encoder hook
fix: let Wine write hooks into code FEX has translated
Wine 11's wineserver writes another process's memory with process_vm_writev,
which the kernel refuses on a page that is not writable. FEX's WoW64 layer
keeps the pages it has translated read-only to catch self-modifying code and
re-protects them right after a program unprotects them, so WriteProcessMemory
and NtWriteVirtualMemory into ntdll failed with a partial copy and Ubisoft
games stopped at "Error at hooking API NtProtectVirtualMemory". Wine only
falls back to /proc/<pid>/mem on ENOSYS or EPERM.
libblsession now completes a single-buffer write that wineserver was refused
with EFAULT through /proc/<pid>/mem, the way Wine wrote before Wine 11. Other
programs and every other error keep the kernel's answer. The iovecs are read
with process_vm_readv, so a bad pointer still returns EFAULT, and offsets are
64-bit in the i386 build.
Valve's FEX opens rootfs paths with openat2(RESOLVE_IN_ROOT), which the
preload emulates under proot. droiddeck-fex's rootfs links to the Steam
runtimes with absolute paths outside the rootfs, so every lookup missed
and FEX fell back to the arm64 host files: x86 bash, and with it Stardew
Valley's launcher, failed on libdl.so.2. An absolute link whose target
is missing in the root but present on the host now finishes there.
An opt-in second Steam client (Steam settings, Steam client: x86-64): Valve's
x86-64 Linux client, unchanged, runs under FEX in its own home with its own
game library and the same sign-in. Linux builds run directly, so titles such
as Team Fortress 2 and Half-Life use their Linux versions; Windows games use
DroidDeck-Proton as on arm64. The arm64 client stays the default.
- droiddeck-steam-x64: FEX and the x86-64 rootfs from DroidDeck-Components
(sha256-pinned), client install and sync from Valve's CDN, sign-in shared
with the arm64 client, its web helper on the GPU (ANGLE GL over FEX's GL
thunk, zink, Turnip), Proton wrappers for the arm64 client's tools,
DroidDeck direct for Linux builds, SD-card library (x86-64/ beside the
arm64 one), repair.
- FEX 2610 built by build-fex.yml with thunks and three patches: an openat2
fallback without openat2 (proot), newer-clang thunkgen, MAP_32BIT
exhaustion returning ENOMEM. libblsession resolves RESOLVE_IN_ROOT paths
inside the root.
- Session: x86-64 gating, shortcuts and compatibility tools per client,
logs from the client that ran.
- App: client setting with the FEX version, an x86-64 chip on the Games
header, the active client's library, art, storage, downloads, repair,
Offline Mode, Decky, desktop mode, optional VAC diagnostics; per-client
state for art, the content log, repair and leftovers.
- Strings in every locale.
- Review fixes: the library cache is saved only by the newest scan; an
abandoned session's Steam logs come from the client it recorded; distinct
VAC console names; FEXInterpreter in CPU pinning; clearer VAC hint in
pl, ru, uk, cs and bg.
install() replaces an existing cache entry by calling detach(old), which
destroys the listing when no reader holds it, and then reads old->wd to
decide whether to release the watch. When the old listing has no readers,
that read happens after the free.
Two threads that enumerate the same uncached directory at the same time
reach this path: both build a listing, the first installs an entry with
no readers, and the second replaces it.
Save the watch descriptor before detaching, as drop() already does.
* Add opt-in zRAM compression for the Steam client while a game runs
A "zRAM compression" toggle under Steam > Settings > Session, below
"Keep downloads running". Off by default, kept once set, and disabled
on devices without swap or with a kernel older than 5.4.
When on, the session watches for Steam's reaper. 20 s into a game the
client and its web helpers page their private anonymous memory out to
the device's zram with MADV_PAGEOUT from inside their own process
(libblsession zram.c, SIGURG), again every 5 minutes, and read every
swapped page back once the game exits. No root is needed: only a
process itself may do this without CAP_SYS_NICE. With the toggle off
nothing is installed or started.
RedMagic NP06J, Palworld: Steam's resident memory fell from ~1.7 GB to
~1.1 GB with ~560 MB in zram, available memory during play rose by
~0.5-0.8 GB, and ~2 MB was left in zram after the read-back.
* Turn zRAM compression on by default below 12 GB of RAM
Until the toggle is changed, it follows the device: on when the kernel
supports it and the device has less than 12 GB of RAM, read from the
advertised memory size on Android 14+ and from total memory before that.
Turning it off is kept.
---------
Co-authored-by: xXJsonDeruloXx <danielhimebauch@gmail.com>
* feat: add Cardboard mode with side-by-side lens-distorted eyes and 3DoF head tracking
* fix: derive the Cardboard field of view from the panel and recentre fully when far from upright
* feat: share the Cardboard head pose with the guest and add a SteamVR driver for it
* feat: run SteamVR's compositor on Turnip/KGSL with a display-extension layer and a driver-direct HMD
* feat: show SteamVR frames in Cardboard mode through a driver-side presenter and a fake direct-mode display
* feat: install and start SteamVR for Cardboard mode from the session drawer
* docs: describe Cardboard mode and the SteamVR path
* feat: drive SteamVR hands from the game pad and keep the Cardboard headset out of standby
* docs: record where the SteamVR dashboard stops
* fix: map Cardboard head tracking to the screen's axes and keep turns about the real vertical as yaw
* fix: give Cardboard its previous picture back when SteamVR stops
* fix: report sem_otime from emulated SysV semaphores so SteamVR's web helper can start
* fix: bridge SteamVR's shared semaphores across processes and keep Steam alive while SteamVR runs
* feat: present SteamVR straight to DroidDeck's compositor at panel resolution
* docs: update Cardboard VR notes for the dashboard, semaphore bridge and direct presenter
* fix: keep Wine and FEX signal handlers in the VR layer; add semaphore tracing
* fix: clear a stale SteamVR active marker on status
* fix: clear only an old SteamVR active marker
* fix: unblock SteamVR clients on Turnip and keep Steam's web helper alive across SteamVR runs
* fix: judge shared waits before a submit's own signals and on Vulkan 1.0 devices
* docs: record VR games, SteamVR Home and OpenXR results on the Thor
* docs: add Cardboard VR learnings from the Thor bring-up
* docs: add Cardboard VR screenshots from the Thor
* fix: keep the VR layer's display stand-in to SteamVR runs and clear a leftover marker at session start
* fix: report SteamVR start and stop failures from the helper's exit code
* Run programs, scripts and AppImages from the desktop
Windows programs open with the Proton Steam has chosen, and a Steam game's own
launch program is handed to Steam. x86-64 Linux programs and game scripts run
under FEX, and an exec the kernel refuses goes to droiddeck-open through
libblsession's binfmt.c. Programs on noexec shared storage pass KIO's checks; a
folder of Linux programs there, and sharun AppImages, are added by the app once
and run from the copy. Executable scripts run without KIO's prompt, mpv plays
on the software desktop, FEXServer's socket moves under XDG_RUNTIME_DIR, and
Proton runtimes no longer show up as games.
* Switch to Desktop from Big Picture without Deck mode
Big Picture's Switch to Desktop asks the client's SteamOS manager, which the
client only has when started as SteamOS (Deck mode). Without it the request
failed and the screen stayed on "Switching to Desktop..." for good. The live
compat helper now sees the request on its way to the client and hands it to
steamos-session-select, which opens the KDE desktop as Deck mode already did.
* Run x86 programs from shared storage where they lie
FEX asks for the guest's PROT_EXEC when it maps a program or library, which
the kernel refuses on noexec shared storage; FEX never runs those pages
itself, so libblsession maps them without it in FEX's processes. An x86
game, its libraries and its start script now run in place instead of being
copied in; only native code on noexec storage is still copied.
A Steam library's games are never copied, and a Linux game's launch program
opened from the desktop goes to Steam like a Windows one.