Commit Graph
572 Commits
Author SHA1 Message Date
Juan MartíandxXJsonDeruloXx 206c8a8846 Add Steam background download mode (#335)
* Add Steam background download mode

* Keep active Steam downloads running

* Make background downloads a toggle

* fix: keep background downloads alive between Steam's rate log lines

* Track all Steam content update types

---------

Co-authored-by: xXJsonDeruloXx <[email protected]>
2026-10-07 10:36:49 +00:00
MaxsTechReview ef38c975b9 Draw Steam's CJK and Thai text with the device's fonts (#374)
Steam ships no CJK font and the rootfs only has DejaVu, so a phone set to
Chinese, Japanese or Korean got Steam in that language with every character
drawn as a box. Bind Android's /system/fonts into the guest, index it again
whenever it changes, and prefer the matching Noto CJK face for each language.
2026-10-07 02:23:27 -04:00
The412Banner 1f13285ab8 File manager: up/back work from any start folder, and a ".." row to go up
Opening the manager at a folder (a game's files, its Proton prefix, the
saves folder, or the picker's remembered folder) ran openDrive() on the
start dir, which pinned the up/back floor to that folder: the toolbar
arrow was greyed out and Back closed the screen. The start dir now opens
with loadDirectory(), keeping the volume-root floor computed for it, and
folders in the app's private storage (internal Steam library, prefixes)
floor at the app's files dir instead of themselves.

Adds a ".." (Up one level) row at the top of the list and grid whenever
the current folder is above its floor, sharing one goUp() with the
toolbar arrow. Covers both screens built on FileManagerScreen: the File
Manager and the in-app file/folder picker. Strings for all 9 locales.
2026-10-06 23:52:59 -04:00
MaxsTechReview 1bc23ac169 Language setting and translations: Spanish, Japanese, Korean, Chinese; Steam follows it (#187) (#284)
* l10n: move hard-coded UI text into string resources (#212)

About 650 user-facing strings were written in Kotlin and Java, so no
translation could reach them and the UI stayed half English in every
language. They now live in values/strings.xml.

- Dialogs, menus, status lines, toasts and errors shown to the user go
  through getString/stringResource.
- Code that matched on English text now uses IDs: installer and loading
  stages, Decky stages, storage labels and the Back button's order labels.
- Choice lists (upscalers, FPS limits, textures, themes, presets) carry
  @StringRes labels instead of English.
- Robolectric tests read the app's resources (includeAndroidResources).
- Six strings nothing used any more are dropped.

Existing translations are untouched; the new strings show in English
until they are translated.

* Layouts: fit longer text in other languages, and small phones

Found by screenshotting every page, tab and menu in each language on a
phone, a tablet, a 640dp phone and a foldable.

- Rail labels shrink to fit instead of being cut off (FitText).
- Tab rows tighten when they overflow and scroll the chosen tab into view.
- Menus fade over a down arrow when more entries are below, and open
  scrolled to the current choice.
- On a small phone the desktop card's button moves under its title,
  emulator tiles go two across, the GPU support verdict wraps and the
  Updates page stacks the channels under the status.

* l10n: translate every string into Spanish, Japanese, Korean and Chinese (#187)

Simplified Chinese, Traditional Chinese (Taiwan) and Traditional Chinese
(Hong Kong, reviewed for Hong Kong wording) are complete, as are Spanish,
Japanese and Korean: no missing keys and no placeholder mismatches.

* Language setting: follow the system, or a language picked in Setup; Steam uses it too (#187)

- Setup -> Launcher starts with a Language row: "System default (...)"
  and each language by its own name. With nothing picked the app follows
  the system language; a pick applies at once and stays until System
  default is picked again.
- Steam keeps its language in ~/.steam/registry.vdf under both
  Valve/Steam/language and Valve/Steamsteamglobal/language. Before each
  start of the client the guest helper droiddeck-steam-language writes
  both (and exportedsettings.json), and the client gets -language.
  SteamLanguage maps the app's language to Steam's names: english,
  spanish/latam, japanese, koreana, schinese, tchinese.

* l10n: translate the strings main added since, and Chinese review fixes

- Translate the 32 strings added on main since (Windows components,
  device details, Force SSBS, native Steam sleep) into all six languages.
- zh-CN: busy labels say what is in progress again (正在取消, 正在导出
  ...), as the other languages do.
- zh-CN: "set up" is 配置, as Setup is; 设置 stays for Settings.
- zh-TW: proton_compatible_note says 前綴 rather than "prefix".
2026-10-06 22:00:21 -04:00
MaxsTechReview 50f50b16e0 l10n: move hard-coded UI text into string resources (#212) (#282)
About 650 user-facing strings were written in Kotlin and Java, so no
translation could reach them and the UI stayed half English in every
language. They now live in values/strings.xml.

- Dialogs, menus, status lines, toasts and errors shown to the user go
  through getString/stringResource.
- Code that matched on English text now uses IDs: installer and loading
  stages, Decky stages, storage labels and the Back button's order labels.
- Choice lists (upscalers, FPS limits, textures, themes, presets) carry
  @StringRes labels instead of English.
- Robolectric tests read the app's resources (includeAndroidResources).
- Six strings nothing used any more are dropped.

Existing translations are untouched; the new strings show in English
until they are translated.
2026-10-06 21:59:31 -04:00
MaxsTechReview adc86fc7df Fix Audio Crackling on Pulse & DirectAudio (#338)
* Build the DirectAudio relay from source

The relay helper shipped as a prebuilt libdirectaudiorelay.so with no source in the
repo. Its source (The412Banner/directaudio @ 3548a4a, the commit the bundled driver
names in version.txt) now lives in tools/directaudio-relay, unmodified, and CI and
build_local.sh compile it into jniLibs the way proot is built. It shares
da_relay_proto.h with the PulseAudio sink, which is byte-identical to its own copy.

* DirectAudio relay: prime before playing, one consumer, safe buffer floors

- The output stream started before the client had its ring, so every connection
  opened with a run of short callbacks: six clicks per stream on a RedMagic, even idle.
  The callback now plays silence until the ring holds its target, and only a real
  underrun afterwards counts.
- An underrun ends the block with a short fade and waits for the ring to refill to
  target before resuming with a fade in, instead of zero-padding every short callback.
  Each callback spent starved grows the target, so one stall sizes it for the next.
  With a producer freezing 60 ms every 2 s: 14 underruns before, 1 after.
- During a reopen the outgoing and incoming streams could both drain the ring. Only
  the promoted stream reads it now; any other plays silence.
- The ring target starts at 40 ms instead of two bursts (8 ms) and keeps what it
  learned across reopens.
- The device buffer is at least two bursts; 12 ms was a single burst on devices that
  grant a 20 ms legacy stream.

* PulseAudio: the classic path plays through the native-rate AAudio sink

The classic path loaded the prebuilt 0.1.5 module, which opened AAudio at the daemon's
default 44.1 kHz: Android gave it a legacy stream (20 ms bursts) and resampled, after
PulseAudio had already resampled the 48 kHz clients down. Its data callback also waited
on the daemon's IO thread, and it started at two bursts and grew only after an audible
underrun. The app's own module-aaudio-sink, built by CI into the same bundle, now
carries that path:

- The stream opens at the device's rate (48 kHz) with the buffer at two bursts or
  30 ms, whichever is more, still growing a burst per underrun.
- A write that times out part way keeps the rest for the next write instead of
  dropping it.
- A stream reopened after a route change asks for the rate the first one got, so the
  sink keeps its format instead of unloading.

* Audio: DirectAudio sink at audio priority, deeper client buffers, no decay probing

- module-directaudio-sink's IO thread asks for nice -16 like module-aaudio-sink's, so
  the client's producer keeps up under game load.
- PULSE_LATENCY_MSEC=60 for the session: libpulse clients under FEX (the Steam
  client, winepulse when DirectAudio is off) keep enough queued to ride out a late
  wakeup.
- BANNER_AUDIO_DIRECT_DECAY=0: the driver's decay shrinks the device buffer every
  10 s of quiet until it underruns again, a click by design.

Both sit ahead of Downloads/droiddeck-env and per-game environment, which still win.

* Audio: drop the session-wide PULSE_LATENCY_MSEC

On an AYN Thor it took the Steam client's stream to 110 ms (60 ms tlength on top of the
sink's 40 ms) with no underruns to show for it, and the delay was audible. Clients pick
their own buffers again; the per-game option still sets it.

* PulseAudio sinks: wait in the poll loop, never in a blocking call

Clients' audio reaches a sink as messages its IO thread handles in pa_rtpoll_run. The AAudio
sink spent each iteration inside a blocking AAudioStream_write for up to a burst, and the
DirectAudio sink inside a futex wait for up to a burst, so a client's data sat unread and its
stream ran dry: a Chromium-shaped client (44.1 kHz float, 30 ms tlength, 10 ms minreq) had
304 underflows in 30 s on the AAudio sink, heard as crackling in the Steam client. Bursts of
20 ms (a legacy stream, an AYN Thor) made it worst.

- module-aaudio-sink writes without blocking, renders only what the device has room for, and
  comes back after half a burst. 0 underflows in 30 s, with and without 8 busy cores.
- module-directaudio-sink tops the ring up on the same half-burst timer instead of sleeping
  on the relay's futex.

* DirectAudio relay: vendor the source the shipped binary was built from

The import took 3548a4a, the commit the bundled driver names in version.txt, but the relay
binary that shipped came from e4727e9 on the same branch (feat/linux-relay-mic): its strings
match a build of that source exactly. 3548a4a has no --mic-fifo, so with the microphone on the
relay printed its usage and exited, and DirectAudio played nothing. This is e4727e9 as
published; the fixes made on top of 3548a4a follow in the next commit.

* DirectAudio relay: prime before playing, one consumer, safe buffer floors (on e4727e9)

The same changes as ca5d39b, carried onto the e4727e9 source: playback waits until the ring
holds its target, an underrun fades out and re-primes, starved callbacks grow the target, only
the promoted stream drains the ring, the ring target starts at 40 ms and survives reopens, and
the device buffer is at least two bursts.

* Steam client audio: DirectAudio by default

A fresh install plays the Steam client through the DirectAudio relay. Choosing Classic in the
Steam cog is stored and kept; only an install that never chose falls back to DirectAudio.
2026-10-06 17:10:00 -04:00
Juan Martí f3fd3a2307 feat: recognize Adreno 650 as supported (#326) 2026-10-06 16:02:54 -04:00
Kurt Himebauch 1e877fa602 fix: give each new session the current screen's output size (#357) 2026-10-06 19:17:52 +00:00
Kurt Himebauch 906009a33a fix: share session logs in about a second instead of re-scrubbing them (#352) 2026-10-06 14:51:43 -04:00
The412BannerandThe412Banner 08559a02f8 Windows components: install .msi packages (XNA, PhysX, GFWL, MSXML...) without running them (#347)
* Windows components: install .msi packages (XNA, PhysX, GFWL, MSXML...) without running them

droiddeck-msi-install reads a Windows Installer package's tables with msiinfo and unpacks its
cabinets with cabextract, then lays out what Wine's msiexec would have installed: files at their
64-bit Windows paths (Program Files (x86), syswow64...), .NET assemblies in the GAC, side-by-side
assemblies in winsxs, and the registry values in the view the component writes through. Conditions,
features and the folder-setting custom actions are evaluated as on Windows 10 64-bit; what needs
the package's own code (DXSETUP, self-registration) is listed in the component's notes.

droiddeck-wincomponents puts a component's drive_c tree into the prefix (keeping the prefix's own
spelling of each folder) and writes its registry values into system.reg/user.reg before launch,
following Wine's link keys (Wow6432Node\Classes, CurrentControlSet...). Turning it off puts every
value back and takes an added PATH entry out again.

The app installs these from the Windows components menu like the DLL ones: it downloads the .msi
into the runtime and runs the installer there (GuestCommand). msitools 0.106 and cabextract 1.11
come from Arch Linux ARM, pinned in tools/msitools/packages.txt and hosted on the msitools-0.106-r1
release. wine-gecko stays hidden (Proton ships it) and wine-mono keeps waiting (uninstall step).

* Windows components: open with the top switch focused, by touch or by pad

The dialog's window starts in touch mode, where a switch cannot take focus, and the first switch
was asked once, two frames after the list loaded, only when a pad opened it - racing the dialog's
own Done focus. Now it always puts the window in pad mode, holds Done while the list loads and
then asks the top switch every frame until it has focus.

* Windows components: give msitools its own libraries; show a failed install on its row

msiinfo and cabextract sit with their libraries in /usr/local/lib/droiddeck-msitools, which the
loader does not search: every .msi install stopped at "libmsi-1.0.so.0: cannot open shared
object file". droiddeck-msi-install now runs them with that folder on LD_LIBRARY_PATH.

A failed install also says so under the component itself ("Could not install: ..."), not
only at the foot of the dialog, past the list.

---------

Co-authored-by: The412Banner <[email protected]>
2026-10-06 14:14:28 -04:00
Kurt Himebauch 1d93626453 feat: remove Force game windows fullscreen now that games run on their own Xwayland (#349) 2026-10-06 18:04:22 +00:00
The412BannerandThe412Banner 3278afaa1a Fast path: hold every bind, so added games start in their own folder (#343)
* Fast path: hold every bind, so added games start in their own folder

libblfastpath kept the first 64 binds and dropped the rest without a word. A session has 87 (two
Games folders are the last two), so /root/Games/<folder>/... was mapped into the rootfs - the
empty mount point - instead of the bound storage. Wine resolves a new process's working directory
by those paths, found no drive for it and started every added game in C:\windows ("could not
find DOS drive for current working directory"): DiRT 3 did not mind, FEZ looks for
Content\Essentials.pak there and quits.

The library now holds 256, and turns itself off for the process when a bind does not fit or the
list runs past that, leaving the call to proot, which never maps it wrong. The app keeps the fast
path off for a session with more binds than that.

Verified on an AYANEO Pocket FIT: FEZ (added game) reaches its title screen; the warning is gone.

* Fast path: say in the session log when it stays off

---------

Co-authored-by: The412Banner <[email protected]>
2026-10-06 13:48:01 -04:00
Kurt Himebauch bf221ffbbd fix: resolution presets keep the panel shape at 720p, 900p and 1080p (#337) 2026-10-06 13:19:46 -04:00
Kurt Himebauch c636eea355 fix: hold flood edges at the page edge once reached (#330) 2026-10-06 12:07:24 -04:00
The412BannerandThe412Banner d61af961c1 Windows components for Steam and added games; no more redist installer prompts (#287)
* Put a game's Windows components into its Proton prefix at launch

droiddeck-wincomponents copies the DLLs of the components picked for a game
into compatdata/<appid>/pfx before Proton starts and hands their overrides to
WINEDLLOVERRIDES, so the registry is never touched. It re-checks every launch
(an older Proton deletes the files it once placed), takes a component's files
out again when it is turned off, and forgets Proton's config_info then so
Proton puts its own builtins back.

* Windows components for added games: picker, packages and their catalog

Settings > Games lists a Windows components row under each added game. Its
dialog switches components on per game; the first switch-on for any game
downloads the package (a catalog of its own beside desktop.json, installed
with the desktop package installer to /opt/droiddeck/wincomponents/<id>).
The picks are kept in the app and published to the runtime at every session
start for droiddeck-wincomponents to apply.

tools/wincomponents/package.py builds the packages: Bannerlator's eight
wincomponents archives re-rooted, and OpenAL from OpenAL Soft's release
(soft_oal.dll placed as OpenAL32.dll, 32- and 64-bit).

* Windows components: list the installed ones when the catalog cannot be reached

Offline the dialog only looked up ids it already knew - the catalog's and
the game's picks - so a component downloaded earlier never showed.

* Windows components: Bannerlator's list with Recommended, on the game's launch settings

The picker moves from Settings to a Windows components card in an added
game's launch settings. It offers Bannerlator's whole catalog
(components.json on winlator-contents): what the game's folder points at
first (Bannerlator's DependencyDetector, mapped to the DLL-copy twin where
there is one, oalinst -> oalinst_dll), then every component that installs
here, then the ones that need a Windows installer, greyed until DroidDeck can
run one.

A component installs by running its catalog steps into the runtime once:
archives downloaded (one download per archive for a batch), DLLs found by
name as Bannerlator does and copied to /opt/droiddeck/wincomponents/<name>,
overrides and bundled components written to component.json. The launch
script now takes catalog names (xaudio2.7), expands bundles (directmusic),
places files one level down (drivers/gm.dls) and keeps an override's own
mode (dsound=n). Drops the hand-made packages and their script.

* Windows components: Steam games too, and a pad that can drive the list

The card shows on a Steam title's launch settings as well, keyed by its
appid - the compatdata folder droiddeck-game-env reads - and Recommended
scans the game's install folder.

With a pad: Done takes focus while the catalog loads and the first
component once it is there (the list was not drawn yet when the dialog
asked for focus, so nothing had it); switches stay enabled during a
download and a press then is ignored (a disabled switch dropped the
pad's focus); LB/RB jump between Recommended, All, the installers and
Done.

* Redist markers from games' own install scripts; VC++ runtime as installed

The client still started a game's own _CommonRedist installer - its
script sits in the game's folder, often with another version in the
value name, and only Steamworks Shared's were read. On ARM64 Proton the
x64 VC++ 2015-2022 bundle then stops with "Microsoft Visual C++
2015-2022 Redistributable (arm64) is required" (reported on an AYN Odin 2
Portal Max).

- Read installscript*.vdf from every installed game in each library as
  well; the list is cached until an appmanifest changes, since this runs
  every quarter minute.
- Record the VC++ 2015-2022 runtime as installed where its installers
  look (VisualStudio\14.0\VC\Runtimes\{x86,x64,arm64}, both registry
  views), so any bundle - the client's or a game's own - finds it and
  exits; Proton's msvcp140/vcruntime140 serve the game.
- Join the Wow6432Node path instead of sub(): mawk and gawk read the
  replacement's backslashes differently (gawk, the runtime's, was right).

* Mark a game's install-script processes as run just before the client evaluates them

An Odin 3 bundle (0.3.1, Proton Experimental ARM64) shows the client
running its install-script evaluator on the first launch of three new
titles (409710, 2161700, 3404260) while their Steamworks Shared
redistributables (VC++ 2010/2012/2019/2022, DirectX) were still
downloading - 2161700 at 22:03:22, the shared app finished at 22:03:29 -
so the periodic seeder had nothing to mark yet and the installers ran.

The client writes the exact steps to legacycompat/evaluatorscript_<id>.vdf
and starts iscriptevaluator.exe through our launcher. droiddeck-game-env
now hands that script to droiddeck-seed-redists first, which marks every
process in it in the template and the prefixes before the evaluator
reads them.

The seeder's reader no longer assumes a depth: a block with a HasRunKey
is a process named by the key it opened under, wherever it sits.

* Windows components: keep a file that was not Proton's, put it back when turned off

A component's DLL replaced whatever had that name in the prefix, and
turning it off deleted ours, so a file a real installer (or the user) had
put there was gone for good. Before replacing a file that is neither
Proton's own (a symlink into Proton, or a Wine builtin/placeholder DLL)
nor an earlier copy of ours, keep it under
compatdata/<id>/.droiddeck-wincomponents-backup and move it back when the
component is turned off. The launch note says how many went back.

* Windows components: recommend what Steam installs, say what the game already has

- Steam titles: the appmanifest's SharedDepots (Steamworks Shared, app
  228980) list the VC++ 2005-2022 and DirectX redists Steam installs with
  the game; they live outside the game folder, so the folder scan never
  saw them. They join Recommended as "Steam installs VC++ 2010 with this
  game" (SteamRedists).
- Bannerlator's PrefixInstalledDetector, on compatdata/<id>/pfx:
  OpenAL32, Mono, PhysX and Games for Windows Live that are already there
  from elsewhere show "Already in this game". Files droiddeck-wincomponents
  placed itself do not count.
- The game page passes the prefix and, for Steam titles, the appid.

* Windows components: friendly names

The list showed catalog keys (oalinst_dll, vcredist2010_dll, dotnet452,
d3dcompiler_47). Bannerlator's catalog has no display name and some of
its descriptions do not fit the entry, so WinComponentNames gives one:
families by pattern (Visual C++ <year>, .NET Framework x.y[.z], XAudio,
D3DCompiler, MSXML, Wine Mono, DirectMusic parts), the rest from a
table, the key itself for anything new. The key stays in the row's
detail for logs and bug reports; both lists sort by the name, and the
game page's card names the picks the same way.

---------

Co-authored-by: The412Banner <[email protected]>
2026-10-06 10:46:27 -04:00
The412BannerandThe412Banner bc01b77752 Force SSBS for Proton games (libssbs.so), switch under FEX preset (#328)
* Force SSBS for a game's Wine: libssbs.so, opt in per game

Wine's arm64 signal code resumes threads from a Windows CONTEXT whose
Cpsr never carries PSTATE.SSBS, so every signal return leaves the
thread running with speculative store bypass disabled - slower on cores
such as Oryon. Bannerlator fixes it in its own Wine builds
(force_ssbs.diff, Sonicadvance1) and with an LD_PRELOAD adapter for
layers it cannot rebuild. DroidDeck's Protons (Valve, GE, CachyOS ARM64)
come prebuilt from Steam but run the same aarch64 Wine signal path, so
the adapter applies here: tools/linuxfs/ssbs/ssbs_adapter.c, the same
source built for glibc (+ sysv_signal), staged to
/usr/local/lib/libssbs.so.

droiddeck-game-env preloads it when the game's (or the shared)
environment sets DROIDDECK_FORCE_SSBS=1, and only into an ARM64 Proton.
Off unless asked for: the gain is unproven on Snapdragon 8 Gen 3.

For testing: SSBS_ADAPTER_OBSERVE=1 counts without changing anything
(the baseline) and SSBS_ADAPTER_DEBUG=1 logs the fixes; both also count
signals that ARRIVE while a thread runs without SSBS, which shows
whether anything besides signal returns clears it. Checked on an ARM64
Linux kernel: a handler that clears SSBS left 4 of 5 later signals
arriving with it clear; with the adapter, 0 of 5.

* Force SSBS: a switch under the FEX preset in Steam settings

Steam settings > Games gets "Force SSBS" right under the FEX preset, off
unless turned on. Like the preset it goes into the game environment's
defaults (DROIDDECK_FORCE_SSBS=1), published at once and read by
droiddeck-game-env on the next game launch, so a game's own environment
can still set it to 0.

Measured on the device with the adapter's counters (AYANEO Pocket FIT,
DiRT 3 on GE-Proton11-7-aarch64): observing only, 8192 of 8277 signals
found the thread running without SSBS; with the adapter, none did - it
keeps the bit set everywhere Wine clears it, as a rebuilt ntdll would.
The speed-up itself is reported on Oryon cores and was not measurable on
the FIT's 8 Gen 3, hence off by default.

---------

Co-authored-by: The412Banner <[email protected]>
2026-10-06 09:27:15 -04:00
UnbelievableFlavour a1b01a04a8 Added support for Adreno 6xx devices (#320) 2026-10-06 09:08:04 -04:00
Kurt Himebauch b44235c495 Add Native Steam background sleep and automatic wake (#291) 2026-10-06 00:15:39 -04:00
xXJsonDeruloXx 369e218afb Return frontend-launched sessions to Android Home 2026-10-05 21:34:36 -04:00
The412BannerandThe412Banner 2a905510d5 Device details on the GPU card; Adreno model and SoC name when the kernel leaves them out (#295)
* Device details on the GPU card, and the Adreno model when the kernel leaves it out

The Components page called an AYANEO Pocket FIT (Adreno 750) "Adreno"
and warned it was "Below tested hardware (Adreno 725 and newer)": its
kernel names the GPU "Adreno33v2", GpuInfo wanted three digits, and the
SoC model is blank. When KGSL's name has no model, take it from the
phone's Vulkan driver (once this process has asked it) or from the
platform code name (pineapple -> 750, kalama -> 740, sun -> 830, ...).

The GPU card now opens with a tap or A onto the device's details,
gathered once off the main thread (DeviceInfo): SoC (maker, model,
platform, soc0 id), GPU (model and where it came from, the kernel's
name, clock, load, temperature, support), CPU clusters, memory, system,
the phone's own Vulkan driver - device, API, driver version and build,
conformance, limits, the extensions DXVK and VKD3D lean on, Android's
certified version and level - and the drivers DroidDeck uses. A button
copies it all for a bug report; each section takes pad focus so the
d-pad walks the list.

Vulkan is asked through a new small native library (deviceinfo,
vkinfo.c) that opens the system loader with dlopen and keeps nothing
open.

* Device details: the chip's model and name, CPU clusters, GPU clock range

Ideas from ClusterTune (GPL-2.0, its approach, not its code), which
tunes clocks on AYN, Retroid and other handhelds:
- The SoC model is read where vendors put it - ro.soc.model,
  ro.vendor.qti.soc_model, Android's SOC_MODEL, ro.fota.platform (unless
  it is a branch name like "MSM_13.0") - since handhelds often leave
  Android's blank (Pocket FIT: SG8350P).
- SocNames names it: model codes we are sure of (QCS8550 -> Snapdragon
  8 Gen 2, SM8750 -> Snapdragon 8 Elite, ...), else the platform as a
  family (pineapple -> "Snapdragon 8 Gen 3 family"). The GPU card's
  subtitle shows it: "Snapdragon 8 Gen 3 family (SG8350P)".
- CPU clusters come from the cpufreq policies (the cores each covers,
  min-max speed) rather than grouping cores by top speed.
- GPU clock range from KGSL's step list, or a devfreq GPU node on
  non-Adreno chips (Mali), not only the top clock.

* Device details: drop Qualcomm's own "Driver Build:" label from the driver build line

---------

Co-authored-by: The412Banner <[email protected]>
2026-10-05 20:32:51 -04:00
Kurt Himebauch da29919b5a Fix legacy Steam sleep and missed wake handshakes (#289)
Fix legacy Steam sleep and recover missed wake handshakes
2026-10-05 16:49:55 -04:00
輕鈴andKurt Himebauch 05608ac4d4 Refine Simplified Chinese localization (#232)
Co-authored-by: Kurt Himebauch <[email protected]>
2026-10-05 15:58:59 -04:00
starwishHiandKurt Himebauch 135b84a501 Add French and Russian translate, add fr,ru,jp,ko in locales_config.xml And overlap #234 and #259 (#286)
* Create values-zh-rCN

values language of Simplified Chinese

* Delete app/src/main/res/values-zh-rCN

* Add Korean,Japanese and Tranitional Chinese (zh-TW,zh-HK,zh-MO) translations

* Delete app/src/main/res/values/values-ko/strings.xml

* Create strings.xml

Add korean translate

* Add French translate

* Add Russian translate

* Fix French translate error

* Add the fr,ko,ja,ru in locales_config.xml
Fix French bulid error

* Overlap #234 and #259

---------

Co-authored-by: Kurt Himebauch <[email protected]>
2026-10-05 15:52:39 -04:00
Kurt Himebauch d9bb5e15b0 Use plain-ID .steam files for frontend exports (#277) 2026-10-05 13:48:18 -04:00
The412BannerandThe412Banner 46cdc32cf1 Firefox: skip shared-memory seals (tabs crash on Android 16) (#280)
On Android 16 phones (Galaxy S25 SM-S931B, SM-S948B, RedMagic NP05J)
every Firefox and LibreWolf tab dies at once: Firefox seals the size of
the shared memory it hands a tab, adding the seal fails there, Firefox
only warns, and the tab refuses the unsealed memory ("IPDL protocol
error: Shared memory PlatformHandle is not safe to map") and exits on
signal 11. The window opens; no page ever loads.

MOZ_SHM_NO_SEALS=1 is Firefox's own switch to skip the seals
(ipc/glue/SharedMemoryPlatform_posix.cpp). Set it beside the sandbox
switches in all three places a browser starts from: the desktop session,
AppImages, and Flatpak sandboxes.

Co-authored-by: The412Banner <[email protected]>
2026-10-05 13:37:08 -04:00
Lobanokivan11 387572e733 merge bridge to main (second time) (#279) 2026-10-05 17:09:24 +00:00
The412BannerandThe412Banner 9f46c7a50b Force game windows fullscreen: off by default (#276)
A game whose resolution differs from the screen's (DiRT 3 at 1280x720)
fights --force-windows-fullscreen: every forced resize makes it rebuild
its swapchain, and the picture flickers between its own size and the
screen's - often from the first menu, before the player can reach the
game's resolution setting. It also drove the Xwayland dma-buf leak fixed
separately.

The switch stays in Settings > Games and the session drawer, now off
unless turned on; its hint says when to turn it on (a game coming back
small or in a corner after switching apps, e.g. FlatOut) and when to
leave it off (flicker). Users who already set it keep their choice. The
session script's fallback when the app passes nothing is off as well.

Co-authored-by: The412Banner <[email protected]>
2026-10-05 12:44:45 -04:00
Kurt Himebauch b89d87cbde Add app UI scale presets to Setup (#272) 2026-10-05 11:25:51 -04:00
Kurt Himebauch 06e06b9673 Fix MangoHud double-counting battery power and remaining life (#270) 2026-10-05 15:07:40 +00:00
Kurt Himebauch b896e7a3b9 Compact Steam, Desktop, and Setup settings headers (#268) 2026-10-05 10:34:31 -04:00
Kurt Himebauch c91daa9147 Keep the game undimmed while the session menu is open (#267)
Remove background dimming from the session menu
2026-10-05 09:59:35 -04:00
Kurt Himebauch 31ea194f3d Prepare 0.3.1 release (#263)
* docs: draft 0.3.1 release notes

* chore: bump version to 0.3.1 (11)
2026-10-05 07:46:36 -04:00
MaxsTechReview 472bb28c88 Fix controller rings on linked data dirs, route rumble to the controller, capture the mouse for pointer locks (#242)
- #166: export the fake-input ring paths with the spelling the session tree is bound under
  (Context.getFilesDir(), /data/user/0/...) instead of the canonical path. Where /data/user/0
  resolves to /data/data (ColorOS 16) the guest has nothing bound at the canonical prefix, so
  libfakeinput could not open the rings and every pad, physical and on-screen, was dead. The ring
  path logic moves to FakeInputRingPaths so it can be unit-tested without the native library.
- #219: play a game's rumble on the physical controller that last drove the pad, when Android
  exposes its motors (strong and weak separately on Android 12+), and fall back to the device's
  vibrator for the on-screen pad or a controller without rumble. PadBridge records the active
  controller; the on-screen pad taking over moves rumble back to the phone.
- #153: when a program takes a pointer lock (a mouse-look game through gamescope), capture a
  physical mouse and send its raw deltas as relative motion, so the camera no longer stops where
  Android's pointer meets the screen edge. Capture is released for the drawer, the PC keyboard,
  picture in picture, focus loss and pause, and when the lock ends.
2026-10-05 07:33:33 -04:00
Kurt Himebauch eaa4a7a047 Clarify display settings and organize in-session controls (#247)
* Clarify display sizing, game fit and scaling controls

* Replace aspect and resolution controls with one dimension menu

* Organize Steam session settings into tabs

* Keep session resolution out of the live controls panel

* Group live session controls and add a conditional second-screen tab

* Refine session sliders, tab icons and contextual controls

* Simplify screen effects with presets and collapsed advanced controls

* Give in-session tab icons more spacing

* Center the loading cancel button glyph
2026-10-04 23:32:30 -04:00
MaxsTechReview 4fa467c800 Fetch the Steam client again when it never shows up (#225)
A client stopped part way through updating itself can be left unable to
draw anything. Its files and our install stamp are still on disk, so the
installer never looked at it again and every session sat on "Waiting for
Steam" with nothing retried.

The session now adds a line to package/droiddeck-unconfirmed each time it
starts the client, and the app removes the file on the client's first
frame. After two starts in a row with nothing shown, the installer:
- drops Steam's update manifests, the stamp, .crash and steam.pid
- deletes package zips whose sha1 no longer matches their name
- downloads the client again over the old one

steamapps, config and userdata are left alone. An automatic repair runs
once per failure, until a start is seen again.

The Steam page's Client settings get a "Repair Steam client" item that
queues the same repair for the next start. The request is only cleared
once the download completes, so an interrupted repair is retried.
2026-10-04 21:33:16 -04:00
Kurt Himebauch 26cb9e2f03 Add Decky plugin ZIP import (#79)
* Add Decky plugin ZIP import

* Match Decky plugin identity and loader settings on ZIP import

* Use the shared focus ring for the Decky toggle
2026-10-04 18:09:46 +00:00
MaxsTechReview 3858a69867 Start fresh installs on droiddeck-esync (#223)
In the Valheim and PEAK runs esync used the least CPU per frame. fsync
stays available, and esync still hands a Proton build without a pack to
droiddeck-fsync until its pack is published. A sync someone already picked
is kept.
2026-10-04 12:55:32 -04:00
MaxsTechReview 49a5f4aa94 ESync, FSync, NTSync + OpenNow + FEX for Desktop Apps (#197)
* Make the Compatible Proton ARM64 the default and label compatible builds

The CompatToolMapping "0" default always points at DroidDeck's Proton
ARM64 tool, and per-game picks of adopted GE/CachyOS builds are kept,
including when the shortcuts writer remaps.

The tool is named after the Proton build it runs, Proton Experimental
ARM64 (Compatible), or Proton 11.0 ARM64 (Compatible) once that newer build
is installed. Adopted builds carry the same Compatible mark, read from a
string resource the app stages into the runtime, and the Proton page
explains which builds have the prefix fixes.

The registrar is renamed steam-compatibility and the tool
droiddeck-proton-arm64. Existing installs migrate on the next session: the
old tool directory is removed, every mapping that named it moves to the new
tool, and the retired bannerlator-steam-compat script is removed from the
runtime. CI and local packaging include the renamed script.

* Drop the duplicate R import left by merging main

* Add userspace ntsync for Proton on Android

Android kernels have no /dev/ntsync and its app policy refuses futex_waitv,
so every Proton 11 build here fell back to wineserver for every wait.
libblsession now serves /dev/ntsync itself: wineserver opens a memfd region,
each sync object is a timerfd that carries its slot, and the kernel ntsync
ioctls are answered in shared memory with futexes, following the kernel
driver's wake order. Valve's Proton Experimental ARM64, GE-Proton and
proton-cachyos use it unchanged.

Locks record their owner so a killed process cannot wedge the others,
signals are held off inside each critical section, dead processes and
exiting threads are swept, and object lifetimes follow close, dup2, dup3,
close_range and received descriptors.

Fast sync is on by default under Performance > Session fixes, and
PROTON_NO_NTSYNC=1 turns it off per game.

* Add droiddeck-esync as the default sync for Proton on Android

Without a kernel ntsync driver, Proton 11 builds here fell back to
wineserver for every wait. droiddeck-esync brings eventfd and shared-memory
sync to the exact Proton build a game runs on: a pack holds that build's
ntdll.so and wineserver with the patch series in tools/droiddeck-esync
applied, and the guest runs Proton from a shadow tree that links the stock
tool and swaps in those two files. A wineserver from a pack still prefers
/dev/ntsync when a kernel driver or droiddeck-ntsync is there.

Packs are built in Valve's SDK image from each release's own wine source
(Proton Experimental ARM64 tags, GE-Proton and proton-cachyos releases); the
control build must reproduce the release binaries byte for byte, and a
signed index lists the packs. The APK bundles the packs pinned in
release.env, the app fetches newer ones, and Steam's compatibility tool
picks the pack for each Proton build, so new Proton releases get packs
without an app update.

The ntdll side reads, writes and polls its eventfds with raw system calls,
so the guest's LD_PRELOAD wrappers stay out of the sync path. The server
abandons a mutex once a thread killed by TerminateThread has really exited
and when its process dies, and a mutex's owner and recursion count change
in one atomic step, so a killed owner cannot touch the next owner's count.

droiddeck-esync is on by default and PROTON_NO_ESYNC=1 turns it off per
game; droiddeck-ntsync, the userspace ntsync, is now an opt-in toggle.

* Watch Valve's Proton releases and build droiddeck-esync packs for them

A scheduled workflow reads the Proton ARM64 builds Steam ships (anonymous
steamcmd app info) and the upstream source tags, compares them with the
previous watch state and starts the pack build for any build that has no
pack yet. The pack build applies the patch series with --fuzz=0 and runs a
smoke test of each new pack against the esstress suite before it is
published.

* Add droiddeck-fsync, keep Steam and the app on one Proton, fix DirectAudio

droiddeck-fsync: libblsession answers futex_waitv(2), which the app
sandbox refuses, in userspace, so Proton's own fsync runs on the stock
Proton files with no pack. Waiters register the words they wait on in a
table beside the fsync shared memory and sleep on a futex of their own;
FUTEX_WAKE on the fsync memory wakes the registered waiters. Slots carry
one owner word that the sweeper and the owner claim with a CAS, and the
mapping table is rebuilt under a lock held across the real munmap, so a
reused address or a recycled slot cannot lose a waiter. It stands in for
a Proton build without a pack (droiddeck-ntsync where fsync is off), takes
over for games with esync off, and the "droiddeck-fsync first" toggle
(BL_FSYNC_FIRST, also per game) puts it ahead of the packs. The games
Proton keeps off fsync and esync never get it. A launch into a prefix
whose wineserver is still running keeps that server's sync choice.

One Proton for Steam and the app: the Components page's Proton is the
default Steam uses, applied live through the client while Steam runs
(droiddeck-steam-compat) and at the next start otherwise; a choice made in
Steam's menu comes back to the app. Requests are ordered by a sequence that
never goes backwards, and the page no longer jumps back to the default
after a swap or restore.

DirectAudio: the wrapper wrote the driver key with its backslashes
stripped, so no game ever used DirectAudio. Wine 11 also loads a builtin
only from the prefix, so the driver is now linked into system32 and
syswow64 before "directaudio,pulse" is selected; mmdevapi falls back to
winepulse whenever DirectAudio cannot start.

* Pick esync, ntsync or fsync from the Steam settings' Games group

A three-way tab row above the FEX preset sets the sync Proton games use. It
writes the existing switches together (droiddeck-ntsync, droiddeck-fsync
first, droiddeck-esync), so the Performance toggles and the tabs always agree,
starts on esync and keeps the choice until it is changed again.

* Make droiddeck-fsync the default sync for Proton games

A fresh install, or one that never picked a sync, starts on fsync; a choice made
in the Steam settings' Games group still sticks. Games Proton keeps off fsync
still fall back to droiddeck-esync.

* Add wineserver to the Wine sync tabs and order them ntsync, fsync, esync, wineserver

wineserver turns every in-process sync off so games wait through wineserver;
picking any other tab turns sync back on. The tabs shrink to fit a narrow pane.

* Shorten the Wine sync description

* Answer openat2 and execveat for FEX in the session preload

FEX opens every file of its x86 rootfs with openat2(RESOLVE_IN_ROOT) and
only falls back to the host path on EXDEV. Under proot on Android openat2
answers ENOSYS, so each lookup of an x86 library silently went to the host
and missed. The exported syscall() now turns openat2 into openat once the
real call reports ENOSYS: RESOLVE_IN_ROOT paths are taken relative to the
root fd (FEX has already resolved the links inside the rootfs), and an
absolute path under RESOLVE_BENEATH fails with EXDEV as the kernel would.

execveat with an absolute path, or relative to AT_FDCWD, and without
AT_EMPTY_PATH or AT_SYMLINK_NOFOLLOW, is plain execve; it is sent as such
because proot refuses execveat with a directory fd, which FEX uses when it
re-executes itself for a child x86 program.

* Run x86 Linux games and programs through FEX

Steam: Linux-only titles that have no ARM64 build (from the client's
appinfo cache: type game, demo, application or beta) are mapped to a new
compatibility tool, droiddeck-fex ("Linux x86 (FEX)", linux to linux,
no required runtime). Valve's own "fex" tool needs pressure-vessel, which
cannot run under proot, so a mapping to it or to a Steam Linux Runtime is
replaced; any other pick the user made stays. Titles that also ship for
Windows stay on Proton. When such a title is installed and FEX or the
sniper runtime is missing, the client is asked to install them.

droiddeck-fex runs the program under the FEX that Steam installs (app
3127680), with Steam Linux Runtime 3.0 (sniper, app 1628350) as the x86
rootfs:
- the sniper platform is linked in place from its usr-mtree (relative
  links only, files/usr -> .), once per runtime version;
- the x86_64 and i386 builds of libblsession and libfakeinput go into the
  runtime's lib/<triplet>/droiddeck and its etc/ld.so.preload, mirroring
  the session's own list, so x86 programs get the pads and the session
  fixes (FEX passes their system calls straight to the kernel, so the
  ARM64 preloads cannot reach them);
- Config.json sets RootFS and the GL and Vulkan thunks, so x86 games draw
  with the device's Turnip and Mesa;
- the FEX preset and the game's own environment from the Steam settings
  apply, and ARM64 entries are dropped from LD_PRELOAD/LD_LIBRARY_PATH.
It also links ~/.steam/sdk64 and sdk32 to the client's x86 steamclient
when the client has one and nothing is there yet.

Desktop: x86_64 and i386 AppImages can be imported now; they unpack
through FEX (or unsquashfs) and record their architecture. Scripts,
AppImages and programs started in a run session use FEX automatically
when they are x86, and each added app has a "Use FEX" Auto/On/Off choice
in its edit dialog. The launchers' "still running" checks also look at
FEX processes, whose exe is FEX rather than the program. A GitHub release
without an ARM64 AppImage falls back to its x86_64 one. Decky's x86
loader starts through droiddeck-fex instead of the fex-mesa rootfs path,
which no runtime ships.

The x86 preloads are built against glibc 2.31 (Debian 11) so they load
in sniper: tools/linuxfs/build-x86-preloads.sh, run in CI and by
build_local.sh. fakeinput's stat hooks fall back to __xstat on a glibc
without a stat export, and mmap64 takes off64_t for the i386 build.

* Keep a FEX unixlib from outliving its DLL

A FEX package or original bundle without the aarch64-unix helpers left
the previous version's libarm64ecfex.so / libwow64fex.so in the Proton,
so the new DLLs loaded a unixlib built for another FEX. A swap now removes
the helpers the package does not carry; FEX then runs DLL-only, which it
already falls back to when Wine cannot load the unixlib.

* Fix the Wine sync choices an audit found

- wineserver tab: it passed Proton's environment through, so a build with
  esync built in (GE, cachyos) still ran esync and a kernel ntsync would
  still be used. It now sets PROTON_NO_ESYNC, PROTON_NO_FSYNC and
  PROTON_NO_NTSYNC (unless the game's own environment sets them).
- ntsync tab: a Proton whose wineserver has no /dev/ntsync client got
  wineserver-only sync although fsync would work. The hash cache records
  whether the wineserver has one; without it the launch goes to
  droiddeck-fsync, and no stand-in or pack turns droiddeck-ntsync on.
- A build with esync built in returned before the per-game fallbacks, so
  a game that turns esync off ran with no in-process sync. It now gets
  droiddeck-fsync, or droiddeck-ntsync where fsync is off too.
- When a pack's proton cannot start and the stock one is used, or the
  selection fails, the prefix's record now says stock, so a second
  launch into the same running prefix no longer picks the pack's ntdll.
- droiddeck-fsync: its mutex is held across fork and re-made in the
  child, a closed or replaced shm fd is forgotten (close, dup2, dup3,
  close_range, closefrom), and a waiter woken while it was leaving on a
  timeout or signal reports the wake instead of losing it.
- BL_SYNC_MIN_NOFILE applies per game to every verb, debug logs carry
  prefixes.json, the device report names the chosen sync instead of the
  three raw switches, and PATCHES.md and the proot notes describe the
  selector, the fsync default and droiddeck-fsync's futex_waitv.

* Name the runtime's scripts and files droiddeck, migrating old installs

The guest scripts are droiddeck-* now (droiddeck-session and the other 19),
as are the files the app and scripts generate (etc/droiddeck, the network
state files, the components dir, the Steam install stamp, the redist marker,
the extra-Proton wrapper and backups) and the compositor's own names
(libdroiddeckwayland, droiddeck_* sources, DROIDDECK_* switches, the
wl_output and seat names).

Installed runtimes carry on: LegacyNames moves etc/bannerlator and
share/bannerlator over and leaves relative links at the old paths before
every proot command, turns any old script into a link to its new name,
moves the generated files and points menu entries at the new scripts.
The session scripts and steam-compatibility move their markers on first
use, the components dir's desired.tsv is rewritten with it, and the
runtime counts as installed under either session script.

Kept: the banner_ahb_v1/banner_desktop_v1 wire names and BANNER_AUDIO_DIRECT
(read by prebuilt Turnip and Wine builds), Banners-Turnip and other
third-party names, BL_* switches, libblsession, and credits to Bannerlator.

* Unpack x86 AppImages without FEX, then ask Steam for FEX

Importing an x86 AppImage failed when FEX was not installed yet ("needs FEX
to unpack and no unsquashfs is installed"). AppImages now unpack with
uruntime (VHSgunzo, MIT, v0.8.1 aarch64, pinned by sha256 and shipped
unmodified with its licence), which reads SquashFS and DwarFS images of any
architecture natively. FEX's own --appimage-extract and unsquashfs stay as
fallbacks, and an ARM64 image whose own runtime cannot unpack it goes the
same way.

An x86 import with FEX missing records that FEX is wanted, so the next
Steam session requests FEX and the Steam Linux Runtime, and the program's
page says FEX is not set up yet and how to get it.

* Run Vulkan Qt Quick AppImages on Wayland

OpenNOW (Qt 6.8, Qt Quick on Vulkan) closed within two seconds in a run
mode. The session sends toolkits to gamescope's Xwayland, and Qt's xcb
backend only presents with Vulkan when Mesa's X11 WSI does, which needs
DRI3; Xwayland on KGSL has none, so Qt found no queue that can present and
aborted ("Failed to create RHI").

An AppImage that bundles Qt Quick, the Vulkan loader and Qt's Wayland
plugin now runs on gamescope's Wayland socket (the desktop's, outside
gamescope), where Turnip presents as it does for games, with Qt's xcb
backend as the fallback. SDL_VIDEODRIVER and XDG_SESSION_TYPE follow, so
helpers that pick their window system from them (OpenNOW's streamer) agree
with Qt. Everything else stays on X11; a qt file in the app's directory
(wayland or xcb) overrides the choice.

* Keep gamescope's WSI layer away from Qt apps on its Wayland socket

OpenNOW still closed at once: its FFmpeg (the Raspberry Pi fork, libavutil
61) creates its Vulkan queues internally synchronized whenever the driver
offers VK_KHR_internally_synchronized_queues, which DD-Turnip does. The
gamescope WSI layer registers every queue of a new device through
vkGetDeviceQueue, which only returns queues created without flags, so it
got VK_NULL_HANDLE and vkroots aborted on assert(obj) before Qt opened a
window.

Programs that draw on gamescope's Wayland socket do not need the layer,
which only carries X11 windows past Xwayland, so it is turned off for
them. OpenNOW bundles no Vulkan loader (it reaches Vulkan through Qt and
dlopen), so a binary that uses QVulkanInstance counts as a Vulkan app too,
and a Qt Quick app left on X11 says why in the session log.

* Keep the end of the session log, and say when FEX is missing

The session sends its output through head -c into session.log. head
writes through stdio, so it held up to 4 KB that proot's --kill-on-exit
threw away when the guest ended: every log stopped mid-line at a 4096-byte
boundary, losing exactly the lines that say why a program quit (an x86
AppImage's "x86 Linux programs need FEX" among them). dd writes each read
as it comes and keeps the same cap.

A run mode always ends with status 0 (gamescope does not pass its child's
on), so a program that could not start just closed. When the log says FEX
is missing, the ended screen now says so and how to get it.

The app reads FEX's readiness from status.json, which only a launch or an
import wrote, so it still said "not set up" after Steam installed FEX.
droiddeck-fex status rewrites it, and a Steam session ends with it while
FEX is wanted. FEX is also looked for at the tool's root and under files/,
and a tool folder without it lists what it holds.

* Let FEX and its runtime install runnable on an SD card library

A Steam library on shared storage loses the executable bits, so native
tools are staged in private directories bound over it; only Proton and
SteamLinuxRuntime folders were. FEX (3127680) installed there could never
run, droiddeck-fex saw no FEX and kept asking Steam for it. FEX and the
sniper runtime (1628350) now get private directories from their first
download, whatever their install folder is called.

* Keep fsync off in the pack smoke test

A runner whose kernel allows futex_waitv started stock Proton with fsync,
so the server-side baseline never said what the test waits for and every
pack was waved through as untestable; the esync run would have measured
fsync too.

* Judge a pack against stock's own esstress failures

Stock Proton fails esstress's dup wait all under server-side sync as well
(a Wine bug, seen on the phone too), and any stock failure made the test
call the runner unusable. A pack now fails only when it crashes, hangs or
fails a check stock passes.

* Ask Steam for FEX and its runtime one at a time

Both steam://install requests were started together, and the running client
took only the first: FEX installed, the sniper runtime FEX needs never did.
Each request now waits for the client to take it before the next.

* Bundle the first published droiddeck-esync packs in the apk

* Log how an x86 program under FEX ends

FEX keeps its own errors quiet unless asked, so a program it could not run
left nothing in the session log. Its log now goes to stderr, and the AppImage
launcher records the program's exit status or the signal that ended it.

* Show the log a failed AppImage program wrote

Programs like Eden stop themselves on a failed check and say why only in
their own log under the home folder. When the program ends badly, the end of
any log it wrote during the run is copied into the session log.

* Report where an x86 program under FEX hit an illegal instruction

A tiny x86_64 preload, built without libc, catches SIGILL and writes the
address, the instruction bytes and the mapping they belong to before the
program dies as it would have. droiddeck-fex preloads it into x86_64 programs.

* Load the fault reporter from the x86 runtime

Programs under FEX look libraries up in the x86 runtime, so the reporter is
copied there with the other preloads and named by its path inside it.

* Hand FEX its x86 runtime and thunks through the environment

Valve's FEX runs portable and reads its config beside itself, so the
Config.json droiddeck-fex writes was not seen: programs ran without the
runtime. The runtime, thunk libraries and thunk list now also go in FEX's
own environment variables, and each launch says what it was given.

* Use the sync pack built for the installed Proton release

Some Proton releases ship byte-identical ntdll.so and wineserver (Valve
11.0-2b/2c, CachyOS 0702/0703), so a pack built for a newer release
matched an older install exactly and the older install never fetched its
own pack. A pack from a newer release no longer counts as an exact match;
among matches the one built for the installed version wins, then rev.

The app ranks the pack a tool already uses the same way and only fetches
a pack that ranks higher, reads the release epoch from tools.tsv and
refetches when tools.tsv changes. A prefix whose server is still running
re-checks its recorded pack on waitforexitandrun and starts fresh when a
better one now applies.

* Keep published sync packs and revocations consistent

The publish step never replaces a pack that has an entry beside it; a
pack left without its entry by an interrupted run is replaced so the
build stops repeating. Discovery counts revocations the signed index does
not show yet, including superseded packs only the flavor releases list,
so the index is signed again. The release watch always builds every
flavor.

* Fetch the droiddeck-esync packs from DroidDeck-Components

The packs, the signed index and the apk bundle now live in the releases
of Droid-Deck/DroidDeck-Components, together with the pipeline that
builds them, so this repository keeps only the app and the guest code
that consumes them. The app's index and asset URLs point there,
release.env names the repository its bundle comes from, and the apk
builds drop bundled packs that the published index revokes.

* Stop the apk build when the published sync index cannot be read

* Ask Steam for one FEX dependency at a time

The client shows a confirmation dialog for each steam://install, and a
second request replaces the first dialog, so FEX was never installed
when the runtime was requested right after it. The next dependency is
now requested once the previous one has an app manifest.

* Use generic names in the AppImage test fixtures

* Give x86-64 apps under FEX the Vulkan thunk

Apps that bring their own glibc and Vulkan loader skipped FEX's thunk
overlay and loaded their bundled desktop Mesa drivers, which crash on the
phone's GPU during Vulkan setup. droiddeck-fex now adds an x86-64 audit
module when a program's bundled glibc and libstdc++ meet the thunk's
version needs. The module sends libvulkan.so.1 lookups to a nodelete stub
that needs FEX's thunk path. That keeps the thunk mapped when an app
unloads Vulkan and loads it again, since FEX keeps its first trampoline
links and the old addresses get reused.

The fault reporter now restores the default action before re-raising,
because FEX ignores SA_RESETHAND and the handler looped.
2026-10-04 12:43:57 -04:00
starwishHi 4cc9f2a3f3 Add Korean,Japanese and Traditional Chinese(Taiwan,Macau and Hongkong)translate (#216) 2026-10-04 12:13:13 -04:00
Kurt Himebauch b0d7fc01d8 Add opt-in Steam storage diagnostics (#158) 2026-10-04 08:19:48 -04:00
Magarto 9cc40246a3 Start .NET and Godot games under FEX without per-game setup (#168) 2026-10-04 08:19:11 -04:00
The412Banner 423cf11919 Effects page in the session menu: scaling modes, screen effects and texture filtering (#198) 2026-10-04 08:17:00 -04:00
Dantalian 33f7213599 l10n: add Simplified Chinese (#202) 2026-10-04 11:21:24 +00:00
Kurt Himebauch 6f1fb462ed Add controller and desktop quality-of-life options (#194) 2026-10-04 00:56:09 -04:00
Kurt Himebauch 336b8851f7 Add Touch Off and compact session menu shortcuts (#196)
Add touch off mode and compact session menu shortcuts
2026-10-04 04:37:56 +00:00
Kurt Himebauch 5da3cf673c Fix automatic Picture in Picture after session handoffs (#195)
Preserve session readiness for automatic picture in picture
2026-10-04 04:24:25 +00:00
Kurt Himebauch 9e54e094fb Restore 500 ms Back double-press window (#193) 2026-10-04 03:40:10 +00:00
Kurt Himebauch eb9ca4ab6b Remove redundant in-session Desktop button (#188) 2026-10-03 23:00:02 -04:00
Kurt Himebauch c535f6ef6e Empty library intro (#182) 2026-10-03 22:27:24 -04:00
Kurt Himebauch db2e1d7da2 Fix runtime removal and first-run Steam setup (#185)
* Fix runtime removal races and show removal progress

* Bootstrap Steam from the selected client channel

* Show runtime progress below its Setup row

* Always stage current session helpers and verify APK assets

* Initialize Steam UI scale once using the handheld default
2026-10-03 22:03:26 -04:00