Kurt HimebauchandK11MCH1 906e60a042 feat(desktop): show programs installed by Windows installers (#2146)
* Desktop: programs a Windows installer adds get a menu entry and desktop icon

Opening a Windows installer on the desktop runs it with Proton in a prefix
of its own (droiddeck-proton-run's "own" prefix). Proton's menu builder
turns the installer's Start Menu links into .desktop files, but keeps them
in the prefix's drive_c/proton_shortcuts with a Windows .lnk path as Exec,
and nothing outside the prefix pointed at the installed program: Nexon
Launcher installed fine on an AYANEO Pocket FIT Elite and could only be
found by browsing the hidden compatdata folder.

droiddeck-desktop-programs reads proton_shortcuts in every prefix that is
neither a Steam game's nor a current Steam shortcut's, resolves each link to
its program inside the prefix, and writes a menu entry that opens it with
droiddeck-open, so it runs in that same prefix, with the icon Proton
extracted. A program whose installer also put a link on the Windows desktop
gets a desktop icon once; one the user deletes is not put back. Entries go
away with their program; uninstallers get none. It runs when the desktop
starts and after droiddeck-open finishes a Windows program.

* Desktop programs: only programs that run in their prefix, Windows argument rules, one refresh at a time

A link resolved through Proton's Z: mapping could name a program outside
the scanned prefix, and droiddeck-open would then run it in another one.
A program is now listed only when droiddeck-proton-run's prefix_of puts it
in the prefix it was found in.

Shortcut arguments were split with shlex, so a Windows argument with an
apostrophe raised and stopped the whole refresh; they are now split by
the Windows command-line rules.

The desktop start and the end of a Windows program can refresh at the
same time and race on the same temporary files; refreshes now take turns
on a lock beside the placement record.

* Desktop programs: extract the script at session start

SessionFiles lists the overlay scripts the app copies into the runtime;
droiddeck-desktop-programs was missing, so a real build would never have
installed it and check_session_assets failed the build.

* Desktop programs: a compatdata folder it cannot read changes nothing

Any error listing steamapps/compatdata made the refresh see no prefixes,
so it removed every program entry and forgot which desktop icons it had
placed; the next good run then put back icons the user had deleted. Only a
missing compatdata now means there is nothing installed; any other read
error leaves the menu, the desktop and the placement record as they were.

* Desktop programs: an unreadable shortcut or desktop folder changes nothing

os.path.isdir and glob read an unreadable proton_shortcuts or Windows
desktop folder as empty, so the refresh dropped that prefix's entries and
placement records and a later run put back icons the user had deleted.
Those folders are now listed directly: a missing one is still empty, and
any other error leaves the menu, the desktop and the record as they were.

* Desktop programs: only a missing file reads as absent

read(), link_target() and host_path()'s letter-case lookup turned any
error into "absent", so an unreadable placement record read as empty and
brought back icons the user had deleted, and an unreadable shortcut or
link dropped its program's entry. A missing file, a wrong file type and a
malformed link still read as absent; any other error now reaches main(),
which leaves the menu, the desktop and the record as they were.

* Desktop programs: keep the placement record true when a run fails part way

The placement record was read after the menu was already rewritten, so an
unreadable record changed the menu although the run reported it had not.
It is now read first.

When writing desktop icons failed part way, the icons already written were
not recorded, and the next run recreated any of them the user had deleted.
The record is now written from the icons that exist after the attempt,
icons are written in a fixed order, and a failed write leaves no temporary
file on the desktop.

os.path.exists and isfile also read a program whose path cannot be reached
as missing; a stat that fails only because the file is absent now counts
as missing, and any other error leaves everything as it was.

* Desktop programs: leave the menu alone when Steam's shortcuts cannot be read

The list of Steam shortcuts comes from a reader that treats an unreadable
or damaged shortcuts.vdf as having no shortcuts, so a Steam shortcut's
prefix could get a menu entry and a desktop icon of its own. The file is
now checked first: a missing one still means no shortcuts, and any other
read or parse failure leaves the menu and the desktop as they were.

* Desktop programs: an unreadable Steam account or empty shortcuts file also stops the refresh

account_dir takes an unreadable loginusers.vdf or userdata folder as
missing, so the shortcuts check could be skipped and a Steam shortcut's
prefix still get its own entry. Both are now read first, and an access
error leaves the menu as it was.

An empty shortcuts.vdf is now parsed like any other, so it stops the
refresh too, the same way droiddeck-steam-shortcuts refuses to rewrite it.

---------

Co-authored-by: K11MCH1 <81425424+K11MCH1@users.noreply.github.com>
2026-11-05 18:33:15 -05:00
2026-10-13 21:27:42 -04:00
2026-11-01 12:58:09 -05:00
2026-10-13 21:27:42 -04:00
2026-10-13 21:27:42 -04:00
2026-10-13 21:27:42 -04:00

DroidDeck

DroidDeck brings the SteamOS experience to Android: Steam Gaming Mode, full Linux desktop, integration with GOG, Epic Games, Xbox, and other storefronts, Steam VR, and more!

100% open source. No ads. No telemetry. No source withheld.

Download DroidDeck 0.6.9 Join the DroidDeck Discord

Star History Rank

Droid-Deck%2FDroidDeck | Trendshift Droid-Deck%2FDroidDeck | Trendshift

On a Retroid Pocket Duo: Play Steam from the DroidDeck launcher, open Pac-Man in Steam Big Picture and play it, exit the game, then Switch to Desktop from the Power menu

DroidDeck has no website. Only download it from this repository's Releases.

The Library switching source chips, changing the card size and opening the Filter menu A Windows game running through Proton with the on-screen controls Two controllers playing split screen on one handheld The Kubuntu desktop with Discover open

Install

  1. Android 9 or newer on Adreno 730 or newer, or any 8xx. Adreno 6xx is experimental. Mali, Xclipse, PowerVR and Adreno 710 are unsupported. No root needed.
  2. About 3 GB free for the runtime, plus 1.1 GB for the desktop and emulators.
  3. Install the APK, install the Linux runtime, press Play and sign in to Steam.
  4. On Android 14 and newer, turn on Disable child process restrictions in Developer options. On Android 12 and 13, Steam's first launch shows a Fix it for me button that walks you through it.

Smart glasses setup is in the support matrix. Release notes are in docs/releases.

Help, Preview builds and device reports are on Discord. For bugs, post in #bug-reports with the zip from Share logs.

Build

Needs Docker, Java 17, the Android SDK and NDK, Rust via rustup, GitHub CLI, Python 3 and zstd.

tools/build_local.sh

The APK lands in app/build/outputs/apk/release/. Install it with tools/deploy_local.sh. After the first run, ./gradlew :app:assembleRelease reuses the prepared inputs. Unit tests run with ./gradlew -PskipRust=true :app:testDebugUnitTest, and the helper and preload suite with bash tools/test_local.sh.

On Windows, see tools/run_windows.ps1 and tools/prepare_windows_runtime.py.

Limits

Compatibility and performance vary by device. Desktop compositing is software rendered, and Firefox sandboxing is reduced under proot.

Credits and licence

DroidDeck is GPL-3.0. Steam and Proton belong to Valve Corporation. VITURE, RayNeo, XREAL, Rokid, REDMAGIC, Nintendo Switch and Joy-Con are trademarks of their owners. This project is not affiliated with any of them.

Runtime, Wine and Proton
  • Runtime, shim, input and controller work build on WinNative and Bannerlator by maxjivi05.
  • mmap ring buffer controller input by ribbit384 (WinNative #345).
  • FEX (MIT) runs x86 code. DroidDeck's build puts the platform name under AT_PLATFORM so sharun AppImages by VHSgunzo (MIT) start through its userland-execve loader (MIT). FEX's code disk cache was checked in its source.
  • x86 AppImages, and arm64 ones whose runtime can't unpack on Android, are unpacked with uruntime by VHSgunzo (MIT), shipped unmodified with its licence.
  • x86 programs linking OpenSSL 3 use the copy in the Steam Linux Runtime 4.0. x86 Linux games get the /etc/asound.conf that pressure-vessel writes.
  • WriteProcessMemory hooks fall back to /proc/<pid>/mem the way Wine's server/ptrace.c did before Wine 11.
  • Wine write watches use userfaultfd support in Proton's dlls/ntdll/unix/virtual.c (LGPL-2.1-or-later). libblsession supplies the arm64 syscall number and answers PAGEMAP_SCAN from /proc/self/pagemap.
  • libblsession runs Wine's arm64 SIGUSR2 context handler (dlls/ntdll/unix/signal_arm64.c, LGPL-2.1-or-later) in place on a frame laid out like Linux's arch/arm64/kernel/signal.c, and serves fill_vm_counters in dlls/ntdll/unix/process.c the kernel's own /proc/<pid>/status.
  • droiddeck-ntsync follows the Linux ntsync semantics in Documentation/userspace-api/ntsync.rst (GPL-2.0).
  • The proot fast path orders bindings like PRoot's path/binding.c (GPL-2.0-or-later).
  • DiRT 3 and DiRT Showdown keep their telemetry socket the way RoccoC's DiRTTelemetryErrorFix (MIT) does, based on the fix by Carsten Gottschling.
  • Games use Mesa Turnip's per render pass memory choice. The Steam client keeps TU_DEBUG=sysmem.
  • Black Ops III and Dark Souls Remastered start without umu-protonfixes (BSD-2-Clause), whose 311210.py and 570940.py fail under ARM64 Proton.
  • ReSkate (GPL-3.0) by Dingo Shenanigans uses the launch option from its README and D3D12 feature level 12_1.
  • The arm64 client's sign in was checked by reading Steam's saved token with the ConnectCache format from deadlock-api-ingest.
Stores
  • Epic Games and GOG build on Phobos's adaptation of Legendary and gogdl. GOG install script registry entries are read the way Heroic Games Launcher (GPL-3.0) reads them.
  • Epic's official Linux launcher preview runs through FEX. That setup is outside Epic's listed Linux support.
  • Battle.net is by @kayakyakr. It runs Blizzard's own client in Proton (downloaded from Blizzard, not included) and shows key art from the Battle.net Shop. Without Steam, its games see the pad as an Xbox 360 controller via PROTON_SPOOF_STEAMINPUT_VIDPID in Proton's dlls/winebus.sys.
  • Battle.net installs drive Blizzard's Agent the way @barncastle's Battle.Net-Installer does, with requests from wowdev.wiki. Read without taking code: @mubaraknumann's unifideck, @djmittens's neo-mittens bnetswitch, @BCD1210's soju and Lutris's product.db reader.
  • World of Warcraft addon imports replace an addon's folder from a temporary copy, as WowUp does, and read flavour folders the way WoWDeck does. No code from either is used.
  • Xbox games load @domsvoboda's xodus-nexus modules (LGPL-2.1-or-later), including wintypes.dll. Minecraft's GameInput setup was checked against @Wyze3306's BedrockOnLinux (MIT).
  • The Xbox Cloud Gaming player uses the WebRTC client of @unknownskl's Greenlight (MIT), and its offer tweaks and sharpening follow @redphx's Better xCloud (MIT). See tools/xcloud-player/NOTICE.md.
  • The itch.io butler pack ships butler's 7z.so, itch.io's build of 7-Zip by Igor Pavlov (LGPL-2.1, unRAR restriction on RAR code), unmodified. Other archives use Apache Commons Compress and the runtime's bsdtar (libarchive).
  • The Store uses Flatpak (LGPL-2.1) through DroidDeck's own bubblewrap shim. Chromium and Electron apps start directly, since zypak by @refi64 (BSD-3-Clause) always uses Flatpak's spawn portal.
  • Folder sync runs Syncthing (MPL-2.0), built from @researchxxl's syncthing-android fork (MPL-2.0).
Audio, video and HUD
  • Steam audio goes through @The412Banner's DirectAudio PulseAudio sink (LGPL-2.1-or-later), built from tools/directaudio/sink.
  • The DroidDeck HUD is @The412Banner's FusionHUD (GPL-3.0 with an attribution term). Its readings match Valve's MangoHud build (MIT), with frame times from gamescope patch 0125.
  • The HUD's CPU, GPU load, thermal and battery readers come from @xXJSONDeruloXx's work.
  • LSFG frame generation comes from Camille LaVey and the Eden project, following lsfg-vk, ported by @maxjivi05. It needs your own copy of Lossless Scaling and ships none of its shaders.
  • Remote Play decodes through Android's MediaCodec or the FFmpeg (LGPL-2.1-or-later) in Steam's runtime. Qualcomm low latency settings follow Moonlight (GPL-3.0).
  • PipeWire (MIT) sockets use the settings in its module-access.c, and WirePlumber (MIT) keeps those clients at full permissions.
Desktop
  • The KDE pacman repository snapshots Arch Linux ARM and installs with pacman (GPL-2.0-or-later).
  • KWin (GPL-2.0-or-later) is patched to pass on the scroll data its wayland_backend.cpp leaves as a TODO.
  • Read without taking code: Qt's qwaylandinputdevice.cpp and qguiapplication.cpp, Konsole's TerminalDisplay.cpp, and Mesa's u_threaded_context.c and zink_resource.c.
  • The Steam Keyboard window rule follows Valve's steamdeck-kde-presets with Bazzite's 0001-steam-virtual-keyboard.patch.
  • Steam's Browse buttons open a GTK 3 file chooser via PyGObject (LGPL-2.1-or-later), shown over Steam by gamescope (BSD-2-Clause) through GetAppIdFromReaper.
  • The AYN Odin 2 phantom second display was confirmed by @SapphireRhodonite's WatermelonDS 0.0.5 notes and iiSU #363, without taking their code.
Smart glasses
VR, Joy-Con and hand tracking
  • Joy-Con VR ports the arm model from Google's GVR SDK for Unity (Apache-2.0) and adapts the hand skeleton from Valve's OpenVR handskeletonsimulation sample (BSD-3-Clause), which follows Moshi Turner's work in Monado. Index offsets come from Monado's vive_poses.c (BSL-1.0).
  • Joy-Con research: Linux's hid-nintendo by Daniel J. Ogorchock, @dekuNukem's Nintendo_Switch_Reverse_Engineering, SDL and AOSP. Checked against ALVR, OpenComposite, PSMoveSteamVRBridge, @F8LEFT's JoyConVr, @krnelpan1c's Daydream-Controller-SteamVR, @yoyobuae's joyconvr, Moonlight and Dolphin. See docs/third-party/joycon.
  • Camera hand tracking runs Google's MediaPipe Pose Landmarker (Lite), BlazePose GHUM 3D and Hand Landmarker (Apache-2.0) on CameraX frames (Apache-2.0), with usage reports off. Checked against JoyConVr's camera app (MIT).
  • Tracking math: the GVR arm model's accelerometer mode, Eric Foxlin's zero velocity updates, Frosio, Pedersini and Borghese's accelerometer autocalibration, a Kalman filter (Kalman, 1960), Horn's quaternion method (1987) and the 1€ filter (Casiez, Roussel and Vogel, 2012).
  • Gestures follow ALVR's hand_gestures.rs (MIT) and Meta Quest's menu pinch, tuned on HaGRID photos (CC BY-SA 4.0, not included).
  • The glove model follows OpenVR's render model reference, is meshed with surface nets (Gibson, 1998; Mikola Lysenko), and uses Inigo Quilez's smooth minimum, distance field ambient occlusion and polygon distance (MIT). See docs/third-party/handtracking.

Star history

Star history chart
S
Description
No description provided
Readme GPL-3.0
840 MiB
0 Stars 1 Watchers 0 Forks
Languages
Kotlin 50.6%
Python 16.7%
C 14%
C++ 8.5%
Rust 3.6%
Other 6.4%