Files
DroidDeck/README.md
MaxsTechReview a105e4e49c Steam x86-64 client: menus come up, sign-in stays put, games listed once (#1096)
* Steam x86-64 client: menus come up, sign-in stays put, games listed once

Switching the Steam client from arm64 to x86-64 left the client on a blank
screen behind "Downloading the Steam client update 100%", and each switch
signed one of the clients out.

- The x86-64 client is Valve's steamrt64 platform, but it was installed and
  synced from the ubuntu12 manifest, which lags it (1792108788 against
  1792192922). The client was therefore never the arm64 client's build, the
  arm64 web helper was never used, and the client then updated itself in
  place after the helper had been chosen. Install, sync and the build check
  now read the steamrt64 manifest.
- The x86 web helper's GPU process loads libpci, which exits when
  /sys/bus/pci/devices lists a device whose files the app may not read (a
  RedMagic's PCIe root ports). Every GPU process exited with status 1, even
  in software, until Chromium gave up and the menus stayed blank. An empty
  folder is bound over /sys/bus/pci/devices on such devices.
- Steam refuses one client's login token from the other ("Invalid
  Password" for a token the arm64 client signed in with minutes before), so
  copying the newer token at every start signed the other client out. Each
  client now keeps its own token; a client with no accounts only gets the
  other's account list.
- Games could be listed twice: an added game whose folder a Steam import
  already covers is now shown as that Steam game only, a client switch
  drops the old client's imported entries, an import stays one when its
  ownership cannot be confirmed instead of gaining a shortcut beside it,
  and only the client's own library folders count as an install.

* Steam x86-64 client: share the arm64 sign-in again

The x86-64 client refused the arm64 client's login token because it did
not know the account's SteamID: it takes it from Accounts in
config/config.vdf, which this script creates without one. It logged on as
[U:1:0] with a valid token, Steam answered "Invalid Password", and the
client dropped the token and AutoLoginUser, so every switch asked for the
password.

The newer sign-in is copied between the clients again, each start adds
the accounts either client knows to the other's config.vdf, and an
AutoLoginUser or RememberPassword one client lacks is filled in. Device:
the x86 client signs in with the arm64 token on a fresh install and on an
install left signed out, and the arm64 client stays signed in.
2026-10-18 20:38:48 -04:00

13 KiB
Raw Permalink Blame History

DroidDeck

DroidDeck brings the SteamOS experience to Android: Valve's Steam client in Big Picture on your Adreno handheld, with Windows games through Valve's ARM64 Proton.

Join the DroidDeck Discord

Tapping DroidDeck on the Android home screen and landing in Steam Big Picture

Note: DroidDeck does not have a stand-alone website. Do not click on any download links from websites claiming to be the DroidDeck team.

Requirements and install

Use Android 9 or newer on a supported Adreno device (730 or newer, or 8xx). Adreno 6xx is experimental: DirectX 11 uses DXVK 2 and may run, and DirectX 12 games can still crash. Mali, Xclipse, PowerVR, and Adreno 710 are unsupported. No root is required. Allow about 3 GB for the runtime and 1.1 GB more for the desktop and emulators. Install the APK from Releases, install the Linux runtime, then press Play and sign in. Steam downloads on first launch. Install Desktop & apps to use the desktop and emulators. The Store installs Linux apps and games from Flathub (ARM64 builds) with Flatpak; with additional options to install Appimages and set up scripts. Stores (Setup › Stores › "Show Stores in the rail") signs in to GOG, Epic Games and Amazon Games and downloads games into the Games storage. Epic keeps its direct API install path. Opening Epic Stores prepares Epic’s official Linux launcher preview. When installed, Epic games launched from DroidDeck use that client, and games installed in the client are imported into DroidDeck’s library and shortcuts. The x86-64 client runs through FEX on ARM and DroidDeck retains its Proton hooks; this preview and ARM setup are outside Epic’s listed Linux support. The cog on a store’s chip row sets its opening tab and download speed tier.

On Android 14 and newer, before Steam launches, you must turn on Disable child process restrictions in Developer options. If this option is not available in developer settings (Android 12 and 13 devices), first launch of Steam will present a "Fix it for me" button, which will help automate the setup process.

Smart glasses

Use Setup → Smart glasses for app-wide virtual-screen settings across DroidDeck menus, launchers, desktop sessions, and games. Supported VITURE, RayNeo, and XREAL connections provide 3DoF; Luma Ultra adds optional experimental 6DoF and stereo-assisted hand/Joy-Con tracking. Model support and SDK requirements differ: see the smart-glasses support matrix for supported devices, controls, setup, and limitations.

Community

Join the DroidDeck Discord for help, Preview builds, and device reports. Bug reports go in its #bug-reports forum; attach the zip from Share logs (Setup, or the session drawer) so the logs come with it.

Build

Run tools/build_local.sh with Docker, Java 17, the Android SDK/NDK, Rust through rustup, GitHub CLI, Python 3, and zstd installed. It prepares the same native libraries, pinned components and audio sinks as CI, then checks the APK's assets, JNI exports and dependencies. The APK is at app/build/outputs/apk/release/app-release.apk. Set DROIDDECK_PA13_SOURCE_DIR to an existing PulseAudio 13.0 source directory to skip downloading it. Set DROIDDECK_SIGNING_ENV to a signing environment file to install over a release build; relative keystore paths resolve beside that file. To install on an attached device, run tools/deploy_local.sh.

After preparation, ordinary ./gradlew :app:assembleRelease builds can reuse the verified inputs. If native sources, component pins or staged files change, packaging stops with instructions to rerun the helper. Generated audio stays under app/build/; the tracked base bundle is never modified. ./gradlew -PskipRust=true :app:testDebugUnitTest needs no native preparation. For an APK without store engines, use DROIDDECK_SKIP_RUST=1 tools/build_local.sh; it omits cached engine libraries too.

For the prepared Windows checkout, run ./tools/run_windows.ps1 (optionally with -Serial <adb-serial>), or select DroidDeck XR in Android Studio and run the app module on the connected phone. Machine-specific options live in the ignored local.properties: droiddeck.python selects a Python executable, droiddeck.skipRust=true omits the optional store download engines, and droiddeck.debugApplicationIdSuffix=.xr installs DroidDeck XR Dev alongside the signed release. The development app has its own runtime and Steam login. The local Java selection is in .gradle/config.properties.

python tools/prepare_windows_runtime.py restores this checkout's audited runtime inputs from the pinned CI APK, three locally rebuilt preload libraries, and the checksum-pinned Gamescope archive in build/windows-setup/. Start Docker Desktop's Linux engine first: preparation now rebuilds DroidDeck's ARM64 VR driver and Vulkan layer from current source and runs their native regression tests. The compiler image is built automatically on first use. Vulkan headers come from the NDK beside --llvm-strip, or from an explicit --vk-include <NDK-sysroot-include> directory.

To reuse that build, pass --rebuilt-vr build/windows-setup/vr. Preparation verifies its vr-build.json against the current Git revision, source files, Vulkan headers, output checksums, and ARM64 architecture; missing or stale builds fail instead of falling back to the donor's VR libraries. The script checks the actual donor CI merge tree, still rejects unrelated unaudited runtime changes, and records component provenance in app/build/windows-runtime-provenance.json. Preserve these ignored preparation files when rebuilding locally; clean removes the prepared assets and their verification manifest.

Run the helper and preload regression suite with bash tools/test_local.sh. On macOS it runs in Linux through Docker, matching the helpers' runtime and CI instead of compiling Linux preloads against macOS headers.

Limits

Compatibility and performance vary by device; hardware validation is limited. Desktop compositing uses software rendering. Firefox sandboxing is reduced under proot. Logs are kept app-private (files/logs/, the last 30 sessions); Share logs packs the newest session, with the stores and tool logs, into one scrubbed zip.

Credits and licence

GPL-3.0. Runtime, shim, input, and controller work build on WinNative and Bannerlator (maxjivi05). Epic Games and GOG support build on Phobos’s adaptation of Legendary and gogdl, respectively. LSFG frame generation is from the work of Camille LaVey and the Eden emulator project, following lsfg-vk, ported to WinNative and DroidDeck by @maxjivi05; it needs your own copy of Lossless Scaling and ships none of its shaders. x86 AppImages, and any whose own runtime cannot unpack them, are unpacked with uruntime by VHSgunzo (MIT), shipped unmodified with its licence. The Steam client's audio goes through @The412Banner's DirectAudio PulseAudio sink (LGPL-2.1-or-later), built from tools/directaudio/sink with its request size kept to its ring so clients are not asked to queue 100 ms ahead. mmap ring-buffer controller input was developed by ribbit384 (WinNative #345). Why the x86-64 Steam client refused the arm64 client's sign-in was checked by reading Steam's saved token with the ConnectCache format from deadlock-api-ingest. See LICENSE. Steam and Proton belong to Valve Corporation; this project is not affiliated with Valve.

XR glasses support builds on WinNative's VITURE glasses work. VITURE protocol details were checked against XRLinuxDriver by @wheaney, including the VITURE protocol header it ships, ar-drivers-rs by @badicsalex and the go-macos/iokit VITURE package; the optional VITURE SDK belongs to VITURE and is not included. RayNeo support adapts @informalTechCode's RayNeo-Air-Series-3DoF-Scaffold (Apache-2.0) and references @verncat's RayNeo-Air-3S-Pro-OpenVR (MIT) and Monado (BSL-1.0); see docs/third-party/rayneo. XREAL 1S support references XRLinuxDriver and xrealOneDeviceKit by @wheaney, xreal_one_driver by @rohitsangwan01 (MIT) and xr-tools by @0xcaff. The REDMAGIC glasses-display findings draw on @mekhontsev's MagicDesk investigation and @Coding-BR's decompiled Nubia kernel. VITURE, RayNeo, XREAL and REDMAGIC are trademarks of their owners; this project is not affiliated with them.

Joy-Con VR controllers port the arm model of Google's Google VR SDK for Unity (Apache-2.0), adapt the hand skeleton of Valve's OpenVR handskeletonsimulation sample (BSD-3-Clause), which follows Moshi Turner's hand simulation in Monado, and use the Index controller offsets from Monado's vive_poses.c (BSL-1.0). Joy-Con behaviour was researched in Linux's hid-nintendo driver by Daniel J. Ogorchock, @dekuNukem's Nintendo_Switch_Reverse_Engineering, SDL and the Android Open Source Project, and checked against ALVR, OpenComposite, PSMoveSteamVRBridge, @F8LEFT's JoyConVr, @krnelpan1c's Daydream-Controller-SteamVR, @yoyobuae's joyconvr, Moonlight and Dolphin; see docs/third-party/joycon. Nintendo Switch and Joy-Con are trademarks of Nintendo; this project is not affiliated with Nintendo.

Camera hand tracking runs Google's MediaPipe Tasks Vision library with its Pose Landmarker (Lite) model, BlazePose GHUM 3D, and its Hand Landmarker model (all Apache-2.0), on frames from AndroidX CameraX (Apache-2.0); MediaPipe's usage reports are switched off. The design was checked against @F8LEFT's JoyConVr camera app (MIT), and the short Joy-Con movements follow the accelerometer mode of the Google VR SDK for Unity's arm model, Eric Foxlin's zero-velocity updates and Frosio, Pedersini and Borghese's accelerometer autocalibration, and the camera and Joy-Con are joined by a Kalman filter (R. E. Kalman, 1960). Bare camera hands take their gestures after ALVR's hand_gestures.rs (MIT) and Meta Quest's menu pinch, with distances checked on photos from the HaGRID gesture set (CC BY-SA 4.0, not included), fit the palm with Horn's quaternion method (1987) and smooth it with the 1€ filter (Casiez, Roussel and Vogel, 2012); the white glove model follows Valve's OpenVR render model reference and Index controller poses, is meshed with surface nets (Gibson, 1998; Mikola Lysenko) and blends and shades it with Inigo Quilez's smooth minimum and distance-field ambient occlusion, porting his polygon distance (MIT); see docs/third-party/handtracking.