The session sets TU_DEBUG=sysmem for the Steam client's interface and hands
games their own value in DROIDDECK_GAME_TU_DEBUG, which droiddeck-game-env
applies to Steam launches (#1188). droiddeck-appimage-run never did, so
emulators and other AppImage programs rendered every pass through system
memory instead of Turnip's per render pass choice.
Apply the game value in droiddeck-appimage-run the same way: an empty
value unsets TU_DEBUG, any other value replaces it. Chromium-based apps,
like the Steam client's own interface, keep the session's setting.
fix(proton): a Valve Proton picked for a game in Steam stays on the ARM64 build made from it
Steam lists Valve's own Proton Experimental and Proton 11.0 next to the
DroidDeck tools. They are not runnable here, so a per-game pick of one was
replaced with the default. That used to be Proton Experimental ARM64, so the
pick looked kept; since an installed DroidDeck-Proton became the default, the
game is moved to DroidDeck-Proton instead.
When Steam records one of those tools for a title the sync already knows
(a title it mapped or one with a recorded pick), the title now gets the
DroidDeck tool built from the same Valve depot, stops following the default
and the choice is mirrored into the app's per-game setting. The same applies
to Steam's default. Titles nobody picked anything for still follow the
default, and Valve tools with no ARM64 build still fall back to it.
Mesa 26.0.8, which the Ubuntu runtime builds, carries the backport of
"zink: create views for samplers lazily", which dropped the depth/stencil
aspect check in init_sampler_view(). On drivers with
needs_zs_shader_swizzle (Imagination) every colour view with a constant-1
alpha, such as RGBX/BGRX textures, then got the red-only depth swizzle
view, so Steam's interface and games showed red as R=G=B.
Carry Icenowy Zheng's upstream fix d55418dae7 (Mesa MR 41923), which
restores the aspect check. It applies on top of patch 0004.
* ci: run approved fork PR compile jobs on the disposable fork runner
* ci: ignore fork runner heartbeats from the future
* ci: target the fork runner by its own label
* Read the fork runner heartbeat from a public gist
* Fall back to an anonymous read of the fork runner heartbeat
* docs: DroidDeck 0.7.0 release notes
* docs: add 0.7.0 release note media
* docs: drop the 30 second summary from the 0.7.0 notes
* docs: add the dual-screen Dashboard shots to the 0.7.0 notes
* docs: show the Dashboard as one stacked dual-screen image
* docs: add the swap screens GIF to the 0.7.0 notes
* docs: swap in the new Syncthing screenshot
* docs: add the HUD dock GIF to the 0.7.0 notes
* docs: add the Game Jolt screenshot to the 0.7.0 notes
* docs: show the full screen in the HUD dock GIF
* docs: merge the GPU bullets, add the no proprietary components line, put images above their bullets
* docs: add the 0.7.0 APK download button to the release notes
* docs: add the first-time contributors to the 0.7.0 notes
* docs: add KeegantheVeganat0r as a first-time contributor
* docs: add the shared Steam libraries screenshot to the 0.7.0 notes
* docs: rewrite the shared Steam libraries bullet and move it up
* docs: add the Mali support photo to the 0.7.0 notes
* docs: center 0.7.0 release media
* docs: credit The412Banner for Mali support in 0.7.0 notes
* docs: add the KDE desktop GIF to the 0.7.0 notes
* docs: add the desktop and swapped game clips to the 0.7.0 notes
* docs: use the Duo photo for the swapped screens
* docs: replace the swap screens GIF with the Duo recording
* docs: drop the swap screens GIF
* docs: keep a blank line before each release image
* docs: tweak 0.7.0 release notes
* docs: move smaller 0.7.0 items to improvements
* docs: fold the bottom-screen HUD into the Strip HUD bullet
* docs: pair the swap screens and KDE desktop media
* docs: move Game Jolt and Syncthing up in 0.7.0 features
* docs: put the swap screens and KDE desktop cluster after Mali in 0.7.0 features
* docs: reword shared Steam libraries and reorder KDE desktop bullet
* docs: add recent merged PRs to the 0.7.0 notes
* docs: auto component updates follow DroidDeck-Proton only
* docs: add input profiles, low-RAM memory, driver Auto and other recent changes to the 0.7.0 notes
* docs: add the second-screen layout editor to the 0.7.0 controller profiles notes
* docs: refresh the 0.7.0 pull request and contributor counts, mention controller profiles in the summary
* docs: 0.7.0 notes add the latest fixes and the DroidDeck-Proton, DD-Turnip and component changes
Adds the user-visible pull requests merged after the last refresh (Library
sort, runtime pack update, desktop app launches, GitHub AppImage variant,
DroidDeck-Proton default, component auto-update cleanup, UBWC and FEX preset
settings, controller navigation) and a section for what shipped in
DroidDeck-Proton 11-11.10 to 11-12.3, DD-Turnip 0.4.0 and DroidDeck-Components
since 0.6.9.
---------
Co-authored-by: maxjivi05 <[email protected]>
When a Build APK run finishes, Build validation finds its pull request with
commits/{sha}/pulls, which GitHub answers with an empty list for a commit that
only exists on a fork. The validator then took the build as superseded, so the
"Build APK" status of every fork pull request stayed at "awaiting APK
compilation" after a successful build. It now falls back to the open pull
request for the run's head owner and branch, still requiring the head commit
to match.
* ci: cache the pinned Turnip archive and retry its download
"Prepare graphics driver variants" fetched the same checksum-pinned Banners-Turnip
zip from GitHub on every run with a single 120 s attempt, so a 502 or a read
timeout failed whole PR builds three times today. The archive is now cached
between runs (it is still checked against its SHA-256 after a restore), and a
cold download is retried on timeouts, 429 and 5xx with a growing pause.
* ci: keep PowerVR's verified downloads between runs
tools/powervr/fetch.py already keeps checksum-verified downloads in
~/.cache/droiddeck-build, but CI never saved that folder, so every run fetched
the PowerVR components again and a read timeout failed "Build the add-on
packs". The folder joins the existing pack-inputs cache, keyed on
release.json too.
* fix(session): give the runtime a machine ID so D-Bus programs start
The Ubuntu runtime ships an empty /etc/machine-id for systemd to fill on first
boot, and nothing fills it in a DroidDeck session. libdbus aborts when it needs
the ID ("D-Bus library appears to be incorrectly set up"), so melonDS closed at
once from the desktop and GTK Flatpaks started outside the session bus failed.
The session now writes a fresh ID once when the file is empty or malformed and
never changes a valid one.
* fix(session): check the whole machine ID file and reject the all-zero ID
fix(desktop): catalog AppImages start from the desktop menu through the shipped runtime
RPCS3 from the desktop menu exited at once: its own AppImage runtime runs
dwarfsextract from a memfd, which PRoot cannot execute (EACCES), and it has no
fallback. Emulator rail launches already avoid this by starting arm64 images
through the shipped uruntime (#1191); the menu entries the catalog writes did not.
The rule now lives in one launcher, droiddeck-appimage-exec, used by the session
and by the catalog's menu entries. Entries written by older versions are moved
to it with the existing menu migration.
* fix(desktop): a GitHub link to one AppImage installs and updates that variant
* fix(desktop): latest-release links, bad escapes and 32/64-bit variants
A /releases/latest/download/<file>.AppImage link now names its asset too, a
malformed %-escape in the field no longer throws while the dialog composes,
and x86-32 and x86_64 assets no longer collapse to the same variant once
version numbers are dropped.
* fix(runtime): an update that brings a new PRoot pack no longer asks to install the runtime again
* A pack-only install recovers an interrupted swap first and falls back to the full install
* fix(proton): an installed DroidDeck-Proton replaces a Proton Experimental default nobody picked, and added shortcuts follow the default
* test: an owned shortcut left on the previous default follows a default change
* fix(input): taps outside the PC keyboard reach the game while it is open
* fix(input): the game's cursor stays visible while only the PC keyboard is open
* fix(input): a touch cancelled mid-stream stays closed until the finger lifts
* A resize also ends a pending two-finger touch
* A new touch clears a dismissal left by a stream that ended elsewhere
* A new touch is not held back by a three-finger tap whose end was consumed
* fix(library): added and Battle.net games sort by when they were played and installed
Recently played and Recently installed put every added game, Battle.net
games included, at the bottom: nothing recorded a launch or an install time
for them, and the Steam play-time lookup was never reached once an entry had
a game. Launching an added game now records its play time, added games carry
their folder's install time (kept in the library cache), Battle.net installs
report theirs, and a zero time falls through to the next source.
* fix(library): an added game's play time is recorded only after it launches and kept when it is rebuilt
* fix(library): an added game's play time is recorded only when its launch is accepted
* fix(library): an added game's play time is recorded when its session actually starts
* fix(library): an added game's play time is recorded when its session service starts
* fix(drivers): leaving a driver bundle's display half also drops its runtime half
Picking either half of an Android + Linux bundle selects both, but picking
another display driver afterwards changed only the display. The session
kept loading the bundle's Linux driver, so switching back to DroidDeck's
drivers could still leave the session failing to start. The runtime driver
now goes back to the default when the display leaves the active bundle; a
runtime driver chosen on its own is kept.
* fix(drivers): picking the Auto display hands both halves to Auto's latest DD-Turnip pair
* fix(drivers): the Auto display pick puts the display on Auto even when no pair fits the GPU
* gamescope: a confine is relative only while it holds the X pointer
Patch 0128 made every hidden-cursor confine relative. Games that hide the
system cursor, draw their own and confine the pointer to their window then
took the host lock, and a direct touch drag became camera motion with no
button held, so cards and items could not be dragged.
A confine now goes relative once the X pointer stays away from the seat
position while input moves, as when a game recentres it or clips it to a
small rectangle, and returns to absolute when the pointer follows again or
the confine ends. Locks and the warp follow rule are unchanged.
* gamescope: a constraint without a hint is relative only while the game holds the X pointer
Xwayland turns a hidden-cursor grab into a lock with no cursor position hint, so the confine-only rule never applied to games that draw their own cursor (Stardew Valley, Balatro). A hinted constraint is relative at once, as upstream. An unhinted lock or confine goes relative once the X pointer stops following the seat's motion, which a recentring or clipping game does and a drawn-cursor game does not.
* gamescope: a pointer constraint is relative only while the game holds the X pointer
Stardew Valley's grab with a hidden cursor becomes an Xwayland lock with a cursor position hint, which upstream made relative at once. Without relative mode that lock still follows gamescope's relative motion and touches still place the pointer, so the hold rule now decides for every constraint, hinted or not.
* gamescope: a hinted lock starts relative and is released once the X pointer follows
Releasing a hinted lock only through the hold rule let a recentring game's first touch swipe press the button. A hinted lock now starts relative as upstream and is released after 30 moving frames on which the X pointer follows the motion gamescope sends, counted from the relative motion while the seat is locked.
* gamescope: count away frames within one half-second window
* gamescope: a hint that arrives with the game's first warp starts the lock relative
* fix(input): end the touch press when a program locks the pointer mid-swipe
A game that only shows its pointer lock after its first warp took the first
swipe as an absolute drag, with the button held until the finger lifted.
The first locked move now ends the forwarded touch before sending deltas.
* gamescope: a new focus window or constraint starts its own pointer hold
A held state from one constrained window carried over to the next one
when focus moved before a reset frame. Track the input focus window and
the constraint, and restart the hold when either changes.
* gamescope: read the pointer constraint under the wlserver lock
GetCursorConstraint asserts the lock is held, so the hold tracker
aborted as soon as a program constrained the pointer.
* fix(input): a mid-swipe pointer lock ends the touch but keeps a held button
The first locked move cancelled the whole direct-touch hold, releasing a
right button held by a long press. It now cancels only the forwarded touch;
the held button is released when the finger lifts, as before.
* fix(input): extra fingers during a locked swipe stay out of the cancelled touch
Once a locked swipe has ended the forwarded touch, a second finger restarted
it with a new touch-down while the swipe carried on as relative motion. Extra
fingers are now ignored until the swipe ends; the final lift still releases a
held button.
* fix(input): a swipe that ended its touch stays closed until the finger lifts
* fix(session): close the bottom panel from the docked menu's Close again
* fix(input): keep imported screen keys on screen when the gamepad is hidden
Screen keys used to be shown on their own, whatever the gamepad did. Since
they were merged into the gamepad layout they disappeared with a controller
connected, in desktop sessions, with on-screen controls set to Never, in
Steam + QAM mode and in Steam's touch mode.
Elements now carry an always-shown flag. The screen key migration sets it on
imported keys (and once on keys merged by earlier builds), and the on-screen
controls keep flagged elements drawn while the rest of the pad is hidden,
in buttons-only mode and with the quick toggle, without duplicating them in
the unfolded strip.
* fix(dual-screen): give the profile controls panel the bottom bar with swap
* fix(dual-screen): keep the layout editor on the bottom screen
The bottom panel was recreated above an open input editor or input
settings window whenever it closed and reopened (session stop/start, or
the bottom screen briefly reported as taken), hiding the editor. The panel
now waits while one of our input windows holds its display and reopens
when the last one closes.
An app arrival recorded before one of our windows covered the bottom
screen no longer marks the screen taken, the editor picks its display only
when a display is added or removed, and it skips a bottom screen another
app holds.
* fix(input): label keyed D-pads and sticks with their keys
Screen key D-pads and sticks used to show each direction's key (W, A, S, D)
when labels were on. The unified renderer drew no direction labels and only
offered Show label for buttons and gamepad sticks, so imported labels did
nothing.
D-pads and sticks bound to directional keys now draw each bound key's name
at its direction through the shared key-label helper, and Show label is
offered for every D-pad and stick.
* fix(input): remember the layout editor's snap choice
* refactor(input): drop controller pref setters and drawer field nothing uses
* fix(input): restore the Deck tabs and right trackpad in the unfolded strip
With Deck controls, the unfolded strip used to offer the Gamepad, Grips,
Left, Right and Both tabs and put a trackpad in the right stick slot. The
unified strip only showed the gamepad with two sticks.
For Deck pads the strip bar now carries the shared Deck panel tabs and
title, the other tabs show the same stock Deck panel layouts as the second
screen, and the docked gamepad takes its stick or trackpad per side from the
Deck gamepad layout. Other pads keep the plain two-stick strip.
* fix(input): carry the Deck gamepad stick or trackpad choice into its layout
The Deck controls Gamepad tab used to let each side switch between a stick
and a trackpad. That choice is now part of the editable Deck gamepad
layout, but the saved choice was never moved over and its setter was left
without callers.
The library upgrade now applies a saved non-default choice to the Deck
gamepad layout once, only while that layout is still the stock one, and the
unused setter is removed.
* chore(i18n): drop the Deck surface switch string nothing shows
* fix(input): offer Show label only where a label can be drawn
* fix(input): Show label follows the unsaved action and diagonal-only sticks
The element properties now get the editor's draft action, so changing a
stick or D-pad to keys offers Show label before saving. A stick bound only
on its diagonals counts as labelled too.
* fix(input): only sticks count their diagonal keys for Show label
Diagonal key names are drawn on sticks only, so a D-pad bound just on its
diagonals no longer offers a label it can't show.
fix(storage): check a raw /mnt/media_rw library against its storage volume
A Second library picked as /mnt/media_rw/<volume>/... was always treated as
unplugged, because Android only reports a volume's state for its /storage
path. Sessions then ran with internal storage only and the library's games
were missing. Map the path to its /storage form before the lookup, reusing
UsbLink.storagePath, and drop the copy in SteamLibraries.
* fix(input): restore on-screen touch behaviour: slide between buttons, mouse pass-through, keyboard on release, overlay haptics, stick reach and hit areas
* fix(dual-screen): restore second-screen bars, swapped Terminal and Deck panel tabs
* fix(input): fit on-screen layouts to the real view size, cutout, Steam + QAM mode and unfolded strip
* fix(input): unfolded strip keeps press haptics
* fix(input): restore layout import/export, element editor rows and layout tools
Bring back .icp layout import and export on the saved layouts page, the
per-element image, opacity and label controls, a uniform size slider and
reset positions in the layout editor, and non-destructive Steam, QAM and
keyboard button toggles stored on the layout style. Remove strings left
unused by the input profile unification.
* fix(input): keep hit slack to gamepad-shaped buttons and fit touch tests to saved layout geometry
* fix(input): button size regenerates the gamepad and scales spacing
A generated gamepad now stores its button size and is regenerated at that
size, so spacing follows the radius and the layout keeps regenerating on
rotation. Edited layouts scale positions from their anchor edge or centre
so gaps grow and shrink with the controls.
* fix(input): button size rescales moved gamepads around the generator
Gamepad layouts that no longer match the generated positions exactly were
scaled element by element from screen thirds, which split the face buttons
and D-pad across anchors. Gamepad controls now take the generator's
positions at the new size plus each control's own offset from the
generated position, so groups keep their shape and spacing.
* perf: answer Steam's missing-file checks on FUSE libraries from the directory cache
Before creating and committing each file, the Steam client checks whether it
already exists under its exact, lower-cased and folded spellings. On an SD or
USB library reached through Android's FUSE layer every one of those misses is a
round trip to MediaProvider (Brawlhalla: about 65,000 misses, 92 of the 161 s
of preallocation and 60 of the 93 s of commit).
The client already caches the listings of its steamapps directories on FUSE and
keeps them exact through its own changes, inotify and a short TTL. access() now
answers a name that is absent from a live listing of its directory without
asking the filesystem. Directory paths match case-insensitively only when the
library's filesystem folds case, and names compare case-insensitively, so an
existing file is never reported missing.
* fix: reuse a listing for a differently cased folder only when the whole path folds case
Checking that steamapps alone folds case let a listing of one folder answer
for a different folder below it whose name differs only in case. The fill
now compares the listed folder with its fully case-swapped path below
steamapps, so a listing is reused across case only when the filesystem
folds every level.
* fix(dircache): match a folded listing only below the same prefix and skip it during a tracked change
* fix(linuxfs): a folder renamed or removed above a cached listing drops that listing
* fix(linuxfs): watch the folders above a cached listing so a rename or removal by any process drops it
* fix(linuxfs): also watch the folders above steamapps, and stop a stuck probe from hanging the tests
* fix(linuxfs): a listing under a symlink or an unwatchable folder no longer answers absent lookups
* fix(linuxfs): a symlink check that cannot be watched also stops absent answers
* fix(linuxfs): a differently cased lookup reuses a listing only for the same folder
fix(input): stop L2/R2 keys from overriding analog trigger axes
Pads such as the GameSir X2 Pro send KEYCODE_BUTTON_L2/R2 alongside
their analog trigger axes. Each key-down and every key repeat set the
trigger to full, and the next motion event set it back to the axis
value, so a held trigger jumped to full. Android repeats only the most
recently pressed key, so with both triggers held one side kept being
forced to full while the other tracked its axis.
When a device reports an analog axis for a trigger side (LTRIGGER or
BRAKE, RTRIGGER or GAS, or the wired Xbox Z/RZ layout), its key no
longer drives that trigger. Key repeats never change a trigger value.
Pads with digital-only triggers keep the key behaviour unchanged.
* fix(dual-screen): docked handhelds switch to single-screen mode
With Dual screen on Auto, a dual-screen handheld connected to a wired
external display (dock, HDMI or USB-C monitor) no longer puts the menu,
tabs or session controls on the built-in bottom screen, which nobody can
see while it sits in a dock. The launcher and sessions use the single-screen
layout on the main screen, which the external display mirrors. A bottom
panel opened automatically closes once when the dock is connected and comes
back after undocking.
XR glasses, virtual and overlay displays and switched-off displays do not
count as a dock, and Dual screen set to On keeps the bottom screen.
* fix(dual-screen): only an automatic bottom panel closes on docking, and glasses are told apart from the dock display
* fix(dual-screen): keep the automatic-panel state across activity recreation and reconcile docking before settling the panel
* fix(components): DXVK Auto on Adreno 6xx keeps DroidDeck-Proton on DXVK-gplasync 2.7.1
* fix(components): DXVK Auto says so when the pinned DXVK could not be installed
* fix(components): a DXVK the player picked is shown as their choice, not a failed pinned install
* fix(drivers): a driver repo that fails to answer no longer changes the Auto pick
The release check reads every driver repo and saved the list even when one
of them failed, so that repo's drivers vanished until the next good check.
When GitHub rate-limited or dropped the DroidDeck driver repo, Auto on an
Adreno 6xx saw no DD-Turnip release, switched to Banner's Turnip, and then
switched back after the next clean check.
A repo that fails now keeps its releases from the last good check, while
still being recorded as failed. A repo that answers and no longer lists a
driver still drops it.
* test(drivers): carry-over is checked through refresh with one repo failing
* fix(drivers): drop a repo's partial results when its answer fails and keep the refresh test offline
perf(hud): rasterize Strip graph and logo separately
Replace PR2194's whole-View software layer with a hardware-accelerated
Strip that rasterizes its graph and logo into small reusable bitmaps.
Keep the 66 ms graph invalidation cap, clear stale graph pixels, and
refresh cached logo colours when the theme changes.
Include the comparison method and six sanitized benchmark samples.
With matched Steam navigation, the hybrid averaged 12.2% less UI-thread
CPU and 34.0% lower draw p95 than the PR2194 software path. App-process
CPU averaged 5.9% lower in this run; whole-device and battery savings
are not established. Current-build physical flicker confirmation is
recorded separately from timing validation.
Validation: all 12 DroidDeckHudPanelTest checks passed; comparison APK
built and six alternating device samples completed with matched input
counts, no focus loss, thermal status 0, and no frame metric report drops.
* feat: move UBWC compatibility from Components to Settings
* fix: update driver menu tests for the moved UBWC restart check
* fix: load saved UBWC mode at startup and block Settings restart during runtime operations
* ci: run compile jobs on the org workstation runner when it is idle
* ci: pick the compile runner right before each heavy job
* ci: cover the runner picks in the job dependency test
Keyboard + trackpad and Deck grips + trackpads are second-screen panels again, rendering the stock layouts without changing the input profile. The last panel is remembered separately for desktop and game sessions; desktop sessions start on the trackpad. A Deck panel without the Deck pad falls back to the default. Stock panels left Steam blue return to the neutral look once.
* fix(fex): show which preset Use Default resolves to and rename FEX defaults to Stock FEX
* test(components): the FEX preset chip names its resolved preset instead of Use Default
* fix(fex): the game settings menu keeps Use Default beside the inherited preset
* fix(fex): inherited preset chip follows the current Default preset
* fix(input): bring back the classic on-screen gamepad look, quick hide toggle and adaptive sticks switch
The unified input surfaces (#1439) drew the built-in gamepad as large
rounded boxes labelled with control names, swapped the compact keyboard
button for a wide pill, dropped the bottom-left show/hide toggle and left
the Adaptive sticks setting with no effect once a layout was migrated.
Gamepad elements are round again with shoulder pills, D-pad arrows, the
old glyph labels and the tint-derived fill and outline. The keyboard is a
small round button beside a restored quick show/hide toggle. Stick touch
areas reach 25% further outward while keeping their inner edge, and the
session menu gets an Adaptive sticks switch that writes the active
layout's sticks. Libraries saved by the first unified build are restyled
once without touching user edits or other layouts.
* fix(input): overlay keyboard opens on the main screen and the D-pad matches the face buttons
A keyboard command now carries the role of the surface that sent it, so the
overlay's keyboard button opens the main screen keyboard as before the
unified surfaces, while lower-screen and controller bindings still use a
second screen. The D-pad buttons take the face buttons' size and spacing,
and an untouched saved D-pad is resized around its own centre. Classic
outlines sit where the old ones did and shoulder labels use their old size.
* fix(input): keep edited D-pads and keyboard buttons, harden the quick and adaptive toggles
Resize a saved D-pad only while its four buttons keep their original geometry, and
replace the old keyboard button only while its name and shape are untouched.
The quick toggle needs its own press before a release toggles it, and a cancelled
touch does nothing. A rejected library write no longer crashes the session or
changes the controller preference, and the Adaptive sticks switch shows whenever
the built-in gamepad is the surface in use.
* fix(input): show the adaptive sticks switch while the unfolded controls are up
* fix(input): run the gamepad restyle as library version 10 and draw the quick toggle in Gray
* fix(input): show the adaptive sticks switch whenever the built-in gamepad is the surface in use
---------
Co-authored-by: Kurt Himebauch <[email protected]>
Battle.net, store games and desktop programs start through droiddeck-proton-run
without the Steam client. When the Proton auto-update replaced the DroidDeck-Proton
build that Steam's default or a title's mapping named, the name no longer matched
an installed tool and the run fell back to Valve's Proton Experimental ARM64, which
lacks DroidDeck-Proton's patches. World of Warcraft then hung at launch.
A removed DroidDeck-Proton build now resolves to the newest installed one, the same
successor steam-compatibility moves Steam's mappings to.
* fix: library scan and download cancel crash on Android 10
The storage scan read StorageVolume.getDirectory() for every volume to
build its key, but that method only exists from Android 11, so on
Android 10 the steam-library-scan thread died with NoSuchMethodError.
The directory is now read once behind the existing Android 11 check and
reused for both the key and the candidate path; older versions fall
back to the uuid, description and /storage scan as before.
Cancelling a download closes the response stream on a background
thread. The platform okhttp can throw IllegalStateException
("Unbalanced enter/exit") from close() while a read is still in
flight, which was uncaught and killed the app. That close now ignores
IllegalStateException as well as IOException; other failures still
surface.
Fixes#2200
* test: wait for the cancel task to finish instead of polling its thread state
The cancel-close tests decided the close task was done from the worker's
thread state, which can read WAITING while the pool is still cleaning up
after a thrown exception. A close failure that escapes ends its worker
thread, and Java runs the uncaught handler before the thread ends, so the
surfacing test now joins that thread. The suppression test hands the
cancel executor more work until it runs on the same thread, which only
happens once the close task has returned without throwing.
* test: watch the close task finish instead of probing which worker runs next
A cached pool can start a second worker, so a probe task may never land
on the thread that ran the close. The suppression test now waits until
the pool has no task running and that thread is back to waiting for
work, a state a worker that is exiting after a thrown exception never
enters.
## Problem and intent
Desktop sessions need configurable mouse, keyboard and application
actions from a controller. On dual-screen devices, a lower-screen
trackpad may already handle the pointer, leaving the physical controls
available for other assignments. This PR gives physical controllers and
local touch controls a shared profile/action model instead of separate
configuration paths.
This follows the centralization direction discussed in
[#1202](https://github.com/Droid-Deck/DroidDeck/pull/1202#issuecomment-6299647180)
and addresses the controller-input portion of #860 and #1356. Those
discussions are context, **not prior maintainer approval** of this
implementation. I would appreciate architectural feedback and guidance
on splitting or coordinating this work if that would make review easier.
## Profiles and configuration
- Controller stays within the existing Setup tabs. Game and Desktop each
have a default-profile selector, followed by one profile list and device
settings.
- Named profiles reference three independently reusable parts: Physical
controller, Main screen and Second screen. Layout geometry and action
bindings are stored separately. Profiles can share physical controls
while assigning different touch actions.
- The session menu selects a complete profile, or expands to mix the
three parts. A mixed selection can be saved as a new profile; Game and
Desktop retain separate selections.
- Both screens use the same full-size editor, with add, move, resize,
snapping, undo and shared properties. The main-screen editor has
movable, collapsible tools and is transparent above a running session.
When editing the secondary display, its entire area is the canvas and
the main display hosts tools/properties.
- The old fixed gamepad, fixed Deck controls and separate Screen keys
editor are replaced with editable presets and the shared renderer.
Existing positions, bindings, shapes, symbols, imported images and
toggle behavior migrate; legacy Screen keys become additional profiles
without replacing the user's selected profile. Migration retains a
backup before removing old preferences.
- Saved layouts have thumbnails and screen targeting. Actions include
compatible native output/remapping, keys/combinations, mouse buttons,
pointer movement, vertical/horizontal scrolling, directional keys,
existing menu/keyboard commands, and disabled. Buttons can use symbols,
shapes and toggle behavior.
## Architecture and latency
```mermaid
flowchart LR
Library[Profiles + reusable layouts and bindings] --> Router[InputRouter]
Physical[Physical controller adapters] --> Router
Touch[Main + secondary InputSurfaceView] --> Sources[Owned pad / Deck sources]
Sources --> Router
BLE[Steam Controller BLE adapter] --> Router
Router --> Native[Existing controller publication]
Native --> Steam[Steam Input / guest controller]
Router --> Held[Shared key and mouse ownership]
Held --> Guest[Existing guest input outputs]
Router --> UI[Existing menu / keyboard hosts]
```
Bindings resolve when the profile changes, outside event dispatch.
Native forwarding has no additional frame/timer queue; identity routes
reuse existing state/reports. Only synthesized continuous stick
pointer/scroll motion uses a frame callback. One source releasing a
key/button does not release another source's hold. Menu, profile,
suspension and removal transitions release old outputs and gate held
controls until neutral.
Steam continues to own game-specific mappings and action sets. Steam
Mobile Touch and VR retain their specialized paths. Raw device adapters
remain responsible for transport/calibration and must join routing
before publishing consumed controls; this does not claim that every
adapter has been unified.
Historical Thor measurements on the native Game path found
kernel-event-to-guest-observer upper-bound medians of **3.061 ms
baseline versus 2.999–3.403 ms candidate**, with a subsequent
installed-APK check at **3.039 ms / 300 matched edges**. These are
relative software-path measurements, not button-to-photon latency. Some
tails increased, simultaneous BLE contention remains unmeasured, and
**this UI revision has not had a new latency measurement**. The previous
cursor fix changed visual update intervals from roughly 33.3 ms to 16.7
ms without changing sensitivity or adding smoothing.
[Full design, migration and
boundaries](https://github.com/Delete228/DroidDeck/blob/codex/unified-controller-input/docs/development/unified-input-proposal.md).
## Validation
Updated through upstream 542bbf89c; current PR head 08526f828. The
latest CodeRabbit review generated no actionable comments.
- **1,017 tests passed in 133 suites after the latest upstream merge**,
with no failures, errors or skips: all input/UI tests, global reset,
component updates, PiP policy, runtime auto-update and Proton menu
auto-selection. The pre-merge review-fix run passed **967
input/UI/global-reset tests**. The subsequent second-screen refresh fix
passed **35 focused surface/presentation tests**, including three new
state-preservation regressions.
- Full local run before the final review follow-ups: **3,598 tests,
3,573 passed, 25 failed**. The 25 are unchanged upstream macOS
limitations: log-export tests requiring SecureDirectoryStream (24) and a
case-sensitive WoW add-on test (1). The global-reset regression also
passes. Earlier disk-exhausted attempts were discarded; the completed
run bounded temporary resources with one worker and JVM recycling,
without excluding tests or changing repository test configuration.
- APK build, runtime/session assets, native dependencies, alignment and
v1/v2/v3 signatures passed. Installed over the separate Input Test app
on **AYN Thor** with its environment retained. Local packaging omits
Rust store engines; the [full Build APK workflow for 08526f828
passed](https://github.com/Droid-Deck/DroidDeck/actions/runs/42295403480),
including Android/JVM, Python/native, Rust store-engine and Xbox checks.
- Review follow-ups keep migration IDs stable, preserve unreadable
supported-version libraries before recovery, retain forward-version
protection, release keyboard owners on focus loss, handle unavailable
presentation displays, and allow controller confirmation/cancellation
after key capture. Deck router cleanup checks ownership; BLE teardown
synchronously blocks late routing/publication, and shared Triton slot
ownership protects a replacement controller from stale instances. Legacy
Select-to-Steam migrates to Desktop without replacing saved bindings;
mouse backend transitions are serialized with publication. Profile
refresh now updates custom controls in place, preserving
terminal/dashboard state, the field keyboard and WoW chat. No physical
BLE-controller test or new latency measurement was performed.
- The latest 08526f828 APK is installed on Thor. Profile preferences
stayed byte-for-byte identical and the retained Steam environment
reached READY at 1280×720 / 60 Hz. The earlier capture-dialog fix was
also exercised on Thor. APK SHA-256:
`ed6ef888648468b4ebeb94e8f1ae74a195bb1f0a3f27e4fb3646d5462a4beb6b`.
- Upstream integration preserves the new WoW chat panel,
platform-pointer and PiP fixes. The fast-double-tap stick-click rule and
its six regressions were adapted to the shared renderer; WoW field
selection also preserves Ctrl held by a profile.
- Latest Thor UI pass: existing Setup navigation, profile/layout
previews, action persistence, centered element creation, main-screen
editing over Steam, secondary canvas with primary tools/properties, and
X returning from the docked menu to controls.
- Across the documented device sessions, the user tested native physical
buttons/sticks/triggers, Deck pads/grips, desktop
pointer/clicks/scrolling/arrows, Android keyboard with physical input,
Ctrl+F and Ctrl+A/X/V/S, simultaneous physical/touch holds, menu release
and Home/resume. These historical checks are distinguished from the
latest UI-only pass.
[Revision-specific results, APK hashes and
limitations](https://github.com/Delete228/DroidDeck/blob/codex/unified-controller-input/docs/development/unified-input-validation.md).
## Coordination and remaining work
- #1202 (jmarti326): shortcut capture/device identity and the
shared-assignment discussion informed this work.
- #1305 (kingokksa): shared PC-key definitions were adapted from that
PR; this does not absorb its entire feature set.
- #1414, #581, #1226 and #1150 remain relevant for external-device
input, calibration, keyboard routing and DualSense integration. The
DualSense coordination discussion in this PR describes adapter
boundaries and duplicate-stream suppression.
Import/export, automatic per-app selection, macros, multi-button
activation chords and automatic text-field keyboard detection remain out
of scope. A single binding emitting a key combination is implemented.
The configuration UI is now implemented and exercised on Thor. I am
happy to continue UX/accessibility/localization refinements here before
merge, or in follow-up PRs, according to maintainer feedback. The main
review questions remain the shared model, source ownership/lifecycle
contract, migration policy and integration with other input work.
## UI walkthrough on AYN Thor
Actual captures from the installed Input Test build. In paired images,
the main display is above the second display. The test application name
and local build badge are not production branding changes.
<details>
<summary>Controller settings: separate defaults for Desktop and Game,
followed by the profile library.</summary>
<img width="960" alt="Controller settings: separate defaults for Desktop
and Game, followed by the profile library."
src="https://github.com/user-attachments/assets/a4c156d6-9ded-43df-b0f8-e5f34028806d"
/>
</details>
<details>
<summary>Physical controller: bindings grouped by control type, with
each current action visible.</summary>
<img width="960" alt="Physical controller: bindings grouped by control
type, with each current action visible."
src="https://github.com/user-attachments/assets/a8a840e3-19ac-473f-9f55-b06ae9c637b5"
/>
</details>
<details>
<summary>Reusable layouts: bounded previews and separate screen
targets.</summary>
<img width="960" alt="Reusable layouts: bounded previews and separate
screen targets."
src="https://github.com/user-attachments/assets/cf0cf7d1-42f3-46cd-b50b-f01e1fa8ed41"
/>
</details>
<details>
<summary>Main-screen editor over the live Steam UI, with the movable
tools expanded. The background is Steam artwork, not a running
game.</summary>
<img width="960" alt="Main-screen editor over the live Steam UI, with
the movable tools expanded. The background is Steam artwork, not a
running game."
src="https://github.com/user-attachments/assets/0781f7c8-fd10-47e5-8a23-817f945a6289"
/>
</details>
<details>
<summary>Dual-screen editor: tools and selection on the main display,
full-size placement canvas on the second display.</summary>
<img width="720" alt="Dual-screen editor: tools and selection on the
main display, full-size placement canvas on the second display."
src="https://github.com/user-attachments/assets/f13d4ef6-be6d-4d95-9d42-e047e0a63b8a"
/>
</details>
<details>
<summary>The selected lower-screen trackpad stays visible while its
properties open on the main display.</summary>
<img width="720" alt="The selected lower-screen trackpad stays visible
while its properties open on the main display."
src="https://github.com/user-attachments/assets/26baf597-f037-419e-9601-e48f086c0d4f"
/>
</details>
<details>
<summary>Session menu: expand Profile to select the physical,
main-screen and second-screen parts independently.</summary>
<img width="720" alt="Session menu: expand Profile to select the
physical, main-screen and second-screen parts independently."
src="https://github.com/user-attachments/assets/27ac4d9c-68b3-4257-8353-23104e2343de"
/>
</details>
* fix(hud): skip relayout when size is unchanged and cap graph redraws
* fix(hud): limit the redraw and relayout changes to the strip
* fix(hud): draw the strip background from a cached bitmap like the panels
* fix(hud): drop the strip-only redraw cap and relayout skip
* fix(hud): the strip graph redraws at most 15 times a second
* fix(hud): isolate strip rendering in a software layer
---------
Co-authored-by: Kurt Himebauch <[email protected]>
Track explicit whole-profile choices, including Auto, separately from inherited profiles so physical and main-screen overrides do not suppress legacy panel migration. Persist this distinction in library version 8 and read older selection records compatibly.
Keep saved Deck panels during unavailable-controller fallbacks and retain legacy preferences when migration fails. Add regressions for all three review findings, explicit Auto selection and older saved libraries.
Validation: all 992 input/UI tests passed.
Only migrate stock controls for explicitly saved legacy panel modes. Automatic fallbacks retain the active profile layout and bindings without persisting an override.
Add session regressions for automatic fallbacks, unavailable panels, explicit legacy migration and existing quick selections. Validation: all 986 input/UI tests passed.
* fix(library): Keep one Play request per game and show Starting while it prepares
* Track launch preparation per game
* Keep each game preparing until its own worker finishes
* Drop store lookups that finish after the activity ends
* feat(components): Auto updates for FEX, DXVK and VKD3D-Proton
Each component gets an Auto or Manual mode on the Default Components page.
Auto installs the newest DroidDeck-Components nightly into every Proton that
ships the component, matching each Proton's ARM64EC or x64 flavour, keeps the
current and replaced builds, and removes older builds it downloaded.
A game that ran on the previous build and exits within 30 seconds twice on the
new one rolls the line back; the failed build is skipped until a newer one is
published. Existing manual choices keep that component on Manual, and DXVK
stays on Manual while a GPU rule pins its version.
* fix(components): Auto notes per line, Original label and first-update rollback
Boot history on the Original is recorded before the first update so that
update can roll back, rollback notes stay with their own component, the
Original shows by name, and new Protons get the current build even when no
newer one is published.
* fix(components): Auto owns only its own downloads and follows in-place Proton updates
Auto deletes only builds it downloaded, re-swaps a Proton whose version
changed under an existing choice, reports a missing nightly listing as a
failed check, writes its state atomically so the page can read it without
waiting on a download, and words the rollback and download messages to
match what it does.
* fix(components): Auto keeps a rollback and reads updated Protons' own files
After a rollback only a build newer than every rejected one is taken, so an
older build still listed in the nightly cannot replace the restored one.
Flavour detection treats a choice saved for an older Proton version as not
applied, so a Proton Steam updated in place is read from its own files and
re-swapped. A failed state rename falls back to a direct write and is logged.
* fix(components): Keep the last good Auto state when a save fails
* fix(components): Serialise Auto state reads and writes
* Install the Linux runtime by itself on first launch
The runtime auto-update skipped devices with no runtime, so FEX, Proton,
DXVK and VKD3D waited for the first Steam launch to install it. Claim a
first install when the device never had a runtime, on a usable network,
outside battery saver and with a supported GPU, and check Proton as
soon as the runtime is ready. A runtime the user removed stays removed.
* fix(components): Check components as soon as Proton is installed
The Auto check stamped its hourly timer even when no Proton was
installed yet, so a fresh device waited an hour after Proton arrived.
Skip the stamp until a Proton exists and run the check right after a
Proton install finishes.
* fix(components): Apply stored builds on every check and retry unfinished rollbacks
A held or failed download no longer skips applying the stored build or
stamps the hourly timer. Stored builds are applied on every check, so a
new Proton is reconciled inside the hour, and a check requested while
one runs is queued. A rollback that could not restore or swap a Proton
is retried and reported only once it finishes. Removing the runtime
marks it as seen so it is not installed again, and the failed-save test
skips where the process can still write read-only files.
* fix(components): Keep rollback restores pending until applied and save state on failure
* fix(components): Hold newer builds until a pending rollback is applied
* fix(components): Drop pending restores for removed Protons
* fix(components): Never auto-install a non-ARM64X build over a Proton's own ARM64X DXVK or VKD3D
* fix(components): Read probe archives outside the lock and block auto updates while the original build is unknown
* fix(components): Require ARM64X when any Proton on the line needs it, and before each swap
* fix(input): Keep the Android pointer hidden over the game after hovering session menus
* fix(input): Defer the overlay pointer icon from a container, since ComposeView is final
* fix(input): Click a touch stick only on a fast double tap
* fix(input): Count the release position in stick tap travel
* fix(input): Treat cancelled pointer-ups as cancelled stick touches
* Add voice and keyboard chat input for World of Warcraft
Open a chat bar with an editable text field when a WoW client focuses
a text box (reported by Proton's winex11 IME context hook through
agent/wine-ime.json). First use asks for Voice type or Android keyboard
with a remember toggle; the choice is also in the side menu's Keyboard
group while a WoW client runs. Voice type uses the device's speech
recognition service. On dual-screen devices the bar and keyboard open
on the second screen. Send types the text and presses Enter; Cancel
presses Escape.
* Cover the Arm64 WoW client in the chat match test
* Give the second-screen chat bar the session's result and back owners
* Open the WoW chat bar with the game field's current text
When the bar opens, select and copy the game's text field and fill the
bar with it through the session clipboard bridge, then put the guest
clipboard back. Send selects the field first, so the bar's text
replaces what was there instead of being appended.
* Keep WoW chat consistent across screen changes and failed field reads
Close the bar when the game field closes even after the program name
is cleared, move the bar to the top screen when the second screen drops
it, and drop any pending second-screen keyboard request when the bar
opens. A field read that gets no copy of its own leaves the game text
unselected and Send then types without replacing it. Ctrl is only
pressed and released when nothing else holds it, and a pending read
ends when the clipboard bridge stops.
* fix(session): Close WoW chat for other programs and keep the game cursor on cancelled reads
Keep migration binding IDs stable, preserve unreadable libraries before recovery, and retain forward-version protection. Release keyboard owners on focus loss, handle unavailable settings displays, and allow controller confirmation or cancellation after physical key capture. Stop BLE routing and publication synchronously before asynchronous disconnect. Add regression coverage and record local and Thor validation.
* Show Steam launch options on the Launch tab
* fix: Launch options is one row on the Launch tab, like the shortcut fields
* fix(launch): reread Steam's launch options when the game page resumes
* fix(launch): keep launch options disabled while they reload on resume
* fix(launch): a saved launch option is not replaced by a read that started before it
* fix(launch): an unreadable Steam config keeps the shown launch options instead of clearing them
* fix(launch): an unparsable Steam config and a pending reload keep the shown launch options
* fix(launch): the resume reload follows the game the panel shows
---------
Co-authored-by: maxjivi05 <[email protected]>
* fix: Auto picks the upstream RADV pair on Xclipse 950 and 960
* fix: Auto reads the Xclipse model after the Vulkan query
* fix: Auto treats an unnamed Xclipse like the 950 and 960
* fix: Auto reads any three digit Xclipse model number
* feat: the PowerVR runtime is a downloaded add-on pack, not bundled in the APK
* fix: a stopped session cancels the PowerVR pack download
* fix: a session stopped during runtime unpacking ends before staging
* fix: a failed PowerVR pack download ends the session with a message
* Retain compositor resources when GPU completion is unconfirmed
* Stop quarantined GPU callers before retries and mapped writes
* Support Android hardware-buffer presentation over Wayland
* Support PowerVR swapchains and linear image imports in Gamescope
* Bundle verified PowerVR graphics components through existing build preparation
* Detect and select the system Vulkan backend on PowerVR
* Apply PowerVR Steam and game compatibility during normal launches
* Show PowerVR support through existing driver and component controls
* Update existing hardware guidance for system Vulkan support
* Import the shared system Vulkan readiness check
* Discover and propagate the actual PowerVR render node
* Prune obsolete PowerVR runtimes after guest processes exit
* Retain external DMA-BUF ownership after GPU quarantine
* Name the PowerVR HAL selection explicitly
* Isolate component UI tests from application startup
* Cover the inherited FEX preset in PowerVR profile tests
* Log test worker failures for CI diagnosis
* Remove temporary test logging after diagnostic CI
* Prefer system dependencies for the Android Vulkan loader
* Capture raw stderr before test worker termination
* Remove the temporary test worker crash probe
* Import pinned DXVK recipe prerequisite from PowerVR PR570
Copy only the standalone build recipe, source pin, BC-format prerequisite
and presentation-sync patch/check from wallentx/DroidDeck at d0f7264ea8.
Source: https://github.com/Droid-Deck/DroidDeck/pull/570
Preserve that proposal attribution; no Android app, compositor, managed
pack, private data or fork development history is imported.
* Add opt-in low32 GPU mapping adapter and integrity probes
* Add opt-in D3D9 single-sample workaround and fix HUD texel format
* Document verified production game workaround and publication scope
* Restrict reset diagnostics and create private output atomically
* Target official runtime review and document PR570 prerequisite provenance
* Fix runtime tool output isolation, mmap64 chaining and DST ambiguity
* Normalize swapchain backbuffers for the opt-in D3D9 single-sample path
* Experimental Samsung Xclipse support on the system Vulkan path
Detect Samsung's Vulkan HAL (vulkan.samsung.so) and its sgpu render node
alongside PowerVR, and run Xclipse through the same libhybris bridge:
- VK_LAYER_DROIDDECK_xclipse (tools/linuxfs/xclipse-layer), built with the
glibc preloads and enabled only on Xclipse: sets bionic's environ for the
driver's shader compiler, avoids immutable YCbCr samplers in compute
layouts, forwards the KHR calibrated-timestamp entry points to the EXT
ones, and reports textureCompressionBC and BC4/BC5 filtering over the
compat layer's BC emulation.
- Upstream DXVK 2.6.2, pinned as a tar.gz component and selected on Xclipse:
the driver has 128 bytes of push constants, DXVK 3 needs 256.
- android_wlegl accepts a zero stride and passes it through unchanged:
Samsung's gralloc records none for GPU-only buffers.
Tested on a Galaxy A56 (Xclipse 540, driver 24.0.560): Steam Big Picture
and PICO PARK (D3D9).
* Xclipse layer: copy the device chain and track samplers per device
- Clear textureCompressionBC in a copy of the device-create chain (every
structure up to the VkPhysicalDeviceFeatures2 that enables it) instead of
writing the caller's structure; a chain led by a structure of unknown size
is passed on unchanged.
- Track YCbCr samplers per device in a growable list, drop them on
vkDestroySampler and device teardown, and fail sampler creation rather
than leave one untracked when the list cannot grow.
- Fail descriptor set layout creation with VK_ERROR_OUT_OF_HOST_MEMORY when
a replacement sampler array cannot be allocated, instead of passing the
original YCbCr samplers to the driver.
* Xclipse layer: copy any device-chain structure the headers define
The chain copy that clears the emulated textureCompressionBC only knew a
handful of structure sizes, so a valid chain such as
VkPhysicalDeviceTimelineSemaphoreFeatures -> VkPhysicalDeviceFeatures2 was
passed on unchanged and the compat layer refused the bit. Size every
structure that may extend VkDeviceCreateInfo from a table generated from the
Vulkan registry (gen_device_chain.py, Vulkan-Headers 1.3.275, the headers
the build compiles with), and copy onto the heap instead of a fixed buffer.
Only a structure newer than those headers still passes the chain unchanged.
Add a regression test that runs the layer between a simulated loader and a
mock driver: the timeline-semaphore chain, pEnabledFeatures, an unknown
structure, the caller's structures left untouched, per-device YCbCr sampler
tracking with handle reuse, and the KHR calibrated-timestamp forwarding. CI
installs libvulkan-dev for it.
* Mali bridge (#517, by ASEbrahim) on the non-Adreno test branch
Squash of Droid-Deck/DroidDeck#517 (ASEbrahim/DroidDeck mali-bridge @ 6b823341): the Vulkan
bridge (tools/vkbridge) that runs the guest's Vulkan on the phone's own driver, with BC1-BC7,
clip/cull distance, vertex-divisor and depth-clip emulation and dma-heap memory sharing for Mali.
All of the bridge is ASEbrahim's work; squashed only to carry it on this test branch.
Combined with #570 on this branch, a device draws through exactly one path:
- Adreno: Turnip, unchanged (checked first; the bridge setting is ignored there).
- PowerVR / Xclipse with the system HAL and a render node: the system-Vulkan path (#570).
- Anything else with the Setup toggle on (Mali): the bridge.
* Test branch: keep #570's DXVK build recipe so its pinned PowerVR components verify
#602 adds output checks to tools/dxvk/build.sh; tools/powervr/fetch.py pins the exact
recipe the published PowerVR DXVK was built from, so the branch carries #570's copy.
* Test branch: make #570, #602 and #560/#561 pass each other's tests
- tools/dxvk/build.sh: #602's copy (its output checks) and the PowerVR pin in
tools/powervr/release.json moved to that file's hash; the recipe's outputs are unchanged.
- droiddeck-session: the presentation block sets _bl_cef for #570's PowerVR path, and the UI
block keeps main's order, defaulting to -cef-use-angle=vulkan when the block has not run.
- Compositor test harnesses: AHardwareBuffer stubs for #570's AHB import, and a stub for
vkp_effects_forget_image (main #597) that #561's caller harness predates.
* Auto drivers for Mali and Xclipse on the non-Adreno test branch
Auto now picks an open driver pair for the GPU, the way it does for Adreno:
- Mali (no KGSL, /dev/mali0): the GPU's name ("Mali-G720-Immortalis MC12", from the vendor
Vulkan driver or kbase's gpuinfo) picks the PanVK build: G52-G78 (Job Manager), G610/G710,
G615 or G720. A Mali none of them covers (G925...), or one whose pair is not published yet,
draws through the Mali bridge, switched on by Auto.
- Xclipse keeps the system-driver path as Auto; the RADV pair is offered as a manual choice.
- The pairs come from The412Banner/droiddeck-nonadreno-drivers (one pre-release per driver,
each a DriverBundle zip). Only a device without an Adreno reads that repo.
- The importers accept PanVK and RADV libraries (libvulkan_panfrost / libvulkan_radeon).
- An open driver pair and the Mali bridge are alternatives: a selected pair turns the bridge off.
- TU_DEBUG is only set for Turnip, on an Adreno.
Every new branch is reached only when the device has no Adreno; an Adreno's detection, release
sources, Auto choice and session environment are unchanged.
* Auto: the one-binary PanVK kbase build for Mali v9-v13 on Android 15+
The412Banner/droiddeck-nonadreno-drivers adds panvk-mali-kbase: one PanVK build for
G57/G68/G77/G78, G610/G710, G615/G715, G720/G725 and G925, whose display half needs Android 15.
On Android 15+ Auto prefers it for every Mali it covers; below 15, and whenever its release is
not listed, Auto falls back to the GPU generation's own pair. G52/G76 keep the Job Manager
build; a G715/G725/G925 below Android 15, or any Mali no pair covers, draws through the bridge.
Every pair stays selectable by hand.
* Test: the G925 is in the kbase build's family now
* Offer faux123's device PanVK builds (RP4 Pro, Ace, RG556) as manual pairs for their Mali
* Detailed graphics logs for non-Adreno test reports
- graphics-report.txt in every session folder: the graphics path the session draws with and why
(Turnip, system Vulkan #570, Mali bridge #517, or an open driver pair with its release), the
driver mode and choices, what Auto would pick, the GPU (family, Vulkan device name, kbase
gpuinfo, Android, SoC, kernel), the GPU nodes the app can see (access, device numbers, SELinux
label; nothing is opened) and the guest's graphics environment. At the end it adds the
compositor's driver lines from app.log and the game launches that were logged.
- vulkan-summary.txt: the session's own vulkaninfo --summary with the driver as exported, in the
background with a 10 s timeout.
- Per-game logs under games/<time>-<appid>/: Proton's log, DXVK's and vkd3d-proton's, Mesa's and
PanVK's startup messages and the launch environment, from droiddeck-game-env. Each file is cut
to 20 MB and the folder to 120 MB at the session's end.
- The per-game logs and the Vulkan summary are on for a GPU that is not an Adreno, and on an
Adreno only with Setup's new "Detailed graphics logs" switch. Share logs already takes the
whole session folder, so all of it is in the zip, scrubbed as before.
* Mali pairs: a stand-in render node, the driver's real library name, and a Mali known by its chip
- Auto on Jim's Dimensity 9300+ chose the bridge: the GPU had no name (the app had not asked
Vulkan yet and kbase's gpuinfo is unreadable under SELinux), so a Mali-G720 counted as a Mali
no build covers. The name now falls back to the GPU the chip is known to carry (Dimensity,
Helio, Kompanio, Tensor); and when nothing names it, Auto asks the vendor Vulkan driver off the
main thread before it settles for the bridge. Auto logs what it decided and why.
- An open Mali driver pair gets /dev/mali0 as the guest's render node, as the bridge does
(bindGpuNode, BL_DRM_STANDIN): gamescope needs the node the driver reports. The session shim
only answers libdrm's PRIME and GEM-close calls on it, which PanVK on kbase never makes.
- A PanVK or RADV runtime half is installed as libvulkan_panfrost.so / libvulkan_radeon.so rather
than under Turnip's name; Turnip keeps libvulkan_freedreno.so.
- graphics-report.txt names the render node the guest got, adds the session script's driver lines
and flags a failed Mali-bridge self-test at the top.
* Auto takes the RADV pair on a Samsung Xclipse, with the system route as its fallback
A tester's Galaxy S24 FE (Xclipse 940) boots to Steam with the RADV-Xclipse pair, where the
system-driver route fails at Steam's OpenGL (glamor and GLX cannot load a driver). Auto now
downloads and selects RADV on an Xclipse like the Mali pairs, and falls back to the system route
only when RADV is not published. #570's reset of both drivers at each start no longer undoes an
open driver pair. PowerVR (vulkan.powervr.so) keeps the system driver as before.
* An Exynos with a Mali is a Mali, not an Xclipse
The system route took vulkan.samsung.so as Samsung's Xclipse, but an Exynos 1280/1380 (Galaxy Tab
S9 FE: Mali-G68) may name its HAL the same; with /dev/mali0 present that HAL is not the system
route's, and the device is detected as the Mali it is. The chip table adds the Exynos 1380 and
1280 (s5e8835, s5e8825: Mali-G68).
* Auto fetches the open drivers itself; Xclipse named as such; sound and frame-gen in the logs
- Auto looks online again when its saved release list predates this device's open drivers, so a
Samsung or Mali phone gets its pair without pressing refresh first.
- A Samsung Xclipse is labelled "Samsung Xclipse" (not PowerVR), with its own support line.
- Per-game logs add audio-streams.txt: PulseAudio's sink inputs 20 s and 60 s into the launch,
so a silent game shows whether its stream reached Pulse.
- graphics-report.txt states the frame-generation setting and keeps the LSFG gate, FrameGen and
audio lines from app.log.
* Client sound retries a briefly disconnected AAudio; kbase ioctl refusals explained in the report
A Galaxy S24 FE's session had no sound at all: the DirectAudio sink's AAudio stream opened with
AAUDIO_ERROR_DISCONNECTED while the output route was still changing at session start, and the
plain AAudio fallback failed the same moment. The sink guard now retries DirectAudio, then the
plain sink, five times over ten seconds and says which one it got. graphics-report.txt keeps the
client-sink and kbase lines, and explains a KBASE_IOCTL_MEM_EXEC_INIT EPERM as the kbase driver's
own answer rather than the session's sandbox.
* Mali pairs draw the display on the phone's own Mali driver; Xwayland falls back without glamor
- Testers' Mali pairs got the guest drawing (gamescope on the stand-in node, Zink on PanVK) but
the compositor's PanVK Android half failed on Mali gralloc: it probes AHB formats 0x38/0x3b that
mali_gralloc does not have, and "vkCreateImage w/ native buffer failed". Under Auto a Mali pair
now leaves the display on the phone's own Vulkan driver (dma-buf, modifiers and AHB all pass the
testers' checks) and runs the pair's PanVK in the guest; the Android half stays installed for a
manual pick. "Pair in use" now follows the runtime half.
- A session after one whose Xwayland crashed (segfault, then gamescope's X11 I/O error) starts
Xwayland with XWAYLAND_NO_GLAMOR=1 on a non-Adreno device, and says so in app.log.
- graphics-report.txt names the display driver the compositor used, and keeps gralloc, dma-buf,
modifier and import lines from app.log and Xwayland's own lines from the session log.
* Non-Adreno sound: the daemon falls back on its own; the build's commit in device.txt and the report
The S24 FE's session still had no sound: its AAudio refuses DirectAudio's low-latency stream with
AAUDIO_ERROR_DISCONNECTED, and the sink guard never got to retry because pactl did not reach the
daemon in time ("the daemon did not answer in 15 s"). On a GPU that is not an Adreno the daemon's
own start-up script now asks DirectAudio again without low latency and loads the plain AAudio sink
underneath it, so a default sink exists whatever pactl does. device.txt and graphics-report.txt
name the build label and source commit, so a log says which build it came from.
* Non-Adreno sound: no second DirectAudio load (it hung the daemon); the plain AAudio sink underneath
On the S24 FE the daemon stopped after DirectAudio's failed low-latency load: the second
DirectAudio load in the same daemon never returned, so nothing after it ran and pactl never
connected. The daemon now loads only the plain AAudio sink (no low latency) after DirectAudio,
inside .nofail, and sets whichever exists as the default.
* gamescope: explicit sync off where the kernel or the stand-in node cannot signal its timelines
Adds tools/gamescope/patches/0126 (GAMESCOPE_DISABLE_EXPLICIT_SYNC=1 for the nested backends) and
sets it on a GPU that is not an Adreno when the kernel is older than 6.6 (no
DRM_IOCTL_SYNCOBJ_EVENTFD) or the guest's render node is Mali's stand-in. The graphics report
lists it with the rest of the guest's graphics environment.
* Test branch: gamescope with the explicit-sync switch (0126), pinned by sha
Built by build-gamescope.yml run 38335744351 from 0136fb61 and published as a component release
of The412Banner/droiddeck-nonadreno-drivers; the apk build fetches it by tag and checks the sha.
* Sound: nothing that can block in the daemon's start-up; Steam's web compositing in software on open pairs
- The S24 FE (US) daemon hung loading the plain AAudio sink from default.pa (its AAudio open
blocks rather than fails), so it never finished starting. default.pa is back to DirectAudio
alone inside .nofail; the fallbacks are loaded from the app once the daemon answers, each pactl
bounded by its timeout (DirectAudio, then the plain sink with and without low latency). The
guard waits 30 s and logs, with timestamps, how long the daemon took, whether its socket and
pactl exist, and what it finally got.
- With an open driver pair on a GPU that is not an Adreno, Steam's CEF composites in software
(-cef-disable-gpu-compositing) with GPU raster kept: RADV on Xclipse showed stale, half-written
tiles, as #570 saw on PowerVR. The session log says so.
* Mali under Auto: the display always on Android's own Vulkan loader; a reboot hint for refused audio
Axel's Tab S9 FE still drew its display with the PanVK Android half, set by an earlier build, and
stalled at "Waiting for Steam's first frame" on Mali gralloc's format probes. Under Auto a Mali's
display now goes to Android's own loader whatever was set before (DriverMenus resets it, and the
compositor's driver choice ignores a pair's Android half), with no HAL path to find first. A pair
counts as in use when its runtime half is. A manual pick of the PanVK display is kept.
When audio.log shows AAUDIO_ERROR_DISCONNECTED, the report and the client-sink line say Android
refused the output and that rebooting the phone usually fixes it.
* Fix: call SessionPrefs.gpuDriverMode through its instance from Java
* Compositor: log each new dma-buf format/modifier/size it imports (for colour and swizzle reports)
Steam's software web compositing already covers every open driver pair, Mali included.
* Open driver pairs: vkd3d-proton reports the driver's caps; buffer attach/release tracing; Mesa log
- With a PanVK or RADV pair (BL_OPEN_DRIVER=1), droiddeck-game-env drops the app-wide
VKD3D_FEATURE_LEVEL=12_2 and VKD3D_SHADER_MODEL=6_9 so vkd3d-proton reports what the driver has
(an Unreal DX12 game crashed at start on PanVK with the overrides); a game's own setting still
wins, and launch.txt records DROIDDECK_VKD3D_CAPS=driver.
- The compositor traces, on a GPU that is not an Adreno, the first 400 dma-buf attaches and buffer
releases by wl_buffer id and sending line, and logs any release of a buffer already released
and not attached again (gamescope's "Compositor released us but we were not acquired" on G925).
- Per-game logs no longer set MESA_DEBUG=silent, which kept mesa.log empty.
* Tests: the dma-buf quarantine harness follows send_buffer_release's tracing macro
* Mali (Venus): David Cimo's Venus route sources and recipes
From DavidCimo/droiddeck-mali, branch mali-venus @ 4f838891 (prototype 13; his 12 commits on top
of DroidDeck 0.3.1, GPL-3.0), by David Cimo:
- tools/venus: Mesa Venus vtest patch (incl. the per-queue submit_mutex fix and the cached-memory
frame readback), virglrenderer Android patch, the Mali compatibility layer (the six features
DXVK needs: fill modes, multi-viewport, robustness2, clip/cull distance, BC1-BC7) and his tools.
- tools/linuxfs/preload/venus.c: keeps Zink's kopper under Venus.
- tools/gamescope/patches/0127 (his 0114): shm buffers uploaded on every commit.
- docs/development/mali-venus.md: his design notes.
- tools/venus/ci-build.sh and build-venus.yml: his build recipes as one CI job that packs the
component (vtest server, ICD, layer) as venus.tzst for the apk.
Co-authored-by: Dersim Abbas <[email protected]>
* Mali (Venus) as a selectable route, by David Cimo
Ports David Cimo's Venus route (DavidCimo/droiddeck-mali, mali-venus @ 4f838891) onto this branch
as "Mali (Venus)", one tap in Setup next to the Mali bridge:
- VenusComponent (his): stages the Venus ICD and runs virglrenderer's vtest server over the
phone's own Vulkan driver; the guest gets VK_ICD_FILENAMES, VN_DEBUG=vtest, VTEST_SOCKET_NAME and
MESA_VK_WSI_DEBUG=sw.
- Selection rule: Venus when Setup's "Mali (Venus)" is on, on a GPU that is neither an Adreno nor
a system-driver one (PowerVR, Xclipse), and the apk carries it. It then replaces the Mali bridge
and any open driver pair for the session, and the display stays on the phone's own driver.
Auto is unchanged (the PanVK pair). Adreno never takes any of it.
- The compositor enables only the dma-buf extensions the driver has, logs a vendor driver's
extension list, and gives a zero row pitch a packed row (his vk_present.c changes).
- The Mali compatibility layer is staged into the runtime on non-Adreno devices only.
- The component (vtest server, ICD, layer) comes from build-venus.yml as venus.tzst, published on
The412Banner/droiddeck-nonadreno-drivers and pinned by sha in tools/venus/release.env.
- gamescope 3.16.29-p15 adds his shm upload patch (0127) next to 0126; pinned by sha.
- device.txt and graphics-report.txt name the route.
Co-authored-by: Dersim Abbas <[email protected]>
* Mali: Venus gamescope without robustness2, the bridge switch honoured and kept, clean driver switches
- gamescope 0128: VK_EXT_robustness2 is optional. A Mali driver through Venus has none, and
gamescope stopped at "Missing required extension: VK_EXT_robustness2" (the wayland_surface error
after it was only the knock-on). Without it, unused descriptor slots point at a 1x1 texture
instead of VK_NULL_HANDLE. (David Cimo's Pixel 9 driver has robustness2, so his build never
needed this.) gamescope component rebuilt and pinned by sha.
- Open-files limit: on a GPU that is not an Adreno the session script raises the soft limit to the
hard limit for the whole session tree (a PanVK-kbase session froze on "Too many open files"
after ten minutes) and graphics-report.txt shows before and after. A stopgap; the driver leak is
fixed separately.
- The Mali bridge switch is honoured: when on and usable it wins over a PanVK pair for the
session (Venus still wins over it). Selecting a pair no longer switches the bridge off, which is
why the switch did not survive an app restart (Auto re-selected the pair at start). Setup says
when this phone cannot use the bridge at all.
- Driver switching: picking a Mali pair by hand keeps the display on the phone's own driver, as
Auto does (a PanVK Android display stalls on Mali gralloc at "Waiting for Steam's first frame",
and stayed set after switching back); installing a bundle again replaces it instead of adding a
"-2" copy beside the old one.
- graphics-report.txt names the runtime driver's folder and libraries, and the previous session's
drivers, flagged when they changed.
* gamescope p16 (0128 robustness2 optional), pinned by sha; graphics-report says who chose the display driver
* Bundle replace only for open non-Adreno pairs; Adreno bundles keep their numbered copies
* Rebuilt open drivers reach phones: re-downloaded when their digest changes, release list hourly on non-Adreno
The non-Adreno driver releases replace their assets in place (the PanVK kbase sync-file fd leak
fix). A download is now remembered with its GitHub sha256, and an asset whose digest changed
counts as not installed, so Auto fetches it again and the bundle replaces the old one; the release
list is re-read after an hour instead of a day on a GPU that is not an Adreno.
* Mali compatibility layer: multiDrawIndirect, depthClipEnable and robustness2 for DXVK on Venus
A Galaxy A15 (Mali-G57) on Venus got gamescope and Steam up, but DXVK refused the GPU:
"Device does not support required feature 'multiDrawIndirect'". DXVK 3.1 stops at the first
missing feature, so the rest of its required list (dxvk_device_info.cpp) was checked against what
the testers' Mali drivers cannot have, and covered in the same pass, on top of David Cimo's six:
- multiDrawIndirect: reported, taken off the device, and an indirect draw with drawCount > 1 is
recorded as single draws at offset + i * stride; maxDrawIndirectCount reported as 2^30.
(drawIndirectCount is optional for DXVK and stays as the driver says.)
- VK_EXT_depth_clip_enable (required): offered when the driver lacks it; depthClipEnable off in a
pipeline becomes depthClampEnable, and the extension's structures never reach the driver.
- VK_EXT_robustness2 missing entirely (gamescope saw the same on these phones): offered with
robustBufferAccess2 (as robustBufferAccess, as before) and nullDescriptor, emulated by small
stand-ins (a zeroed buffer, a 1x1 image) for VK_NULL_HANDLE buffers, image views and vertex
buffers in plain, template and push descriptor writes and vertex-buffer binds. A null
texel-buffer view stays null (its format is not known).
The layer logs every feature it emulates per device.
* Venus component 22694a38 (layer: multiDrawIndirect, depthClipEnable, robustness2), pinned by sha
* Non-Adreno round 3: bridge images, Venus GL, shared buffers, Steam GPU drawing
- vkbridge: the memory model skips lazily allocated types. Mali-G615 lists
DEVICE_LOCAL|LAZILY_ALLOCATED first; taken as the device-local type it left
images with no memory type (self-test "image clear/draw: setup" FAIL,
gamescope "No compatible image memory type").
- Session: a bridge self-test that fails images or draws falls back to the
runtime's driver pair for that session (BL_VKBRIDGE_FALLBACK_ICD); Setup
and the graphics report say so.
- Venus: the Mali compatibility layer runs in every process but gamescope
(DROIDDECK_MALI_COMPAT=1), so Zink in Steam and Xwayland gets
nullDescriptor; null texel-buffer views get stand-ins too. Without
nullDescriptor the session's GL goes to llvmpipe.
- gamescope 0129: a refused compute pipeline is named with its SPIR-V
capabilities.
- Compositor: a buffer on two surfaces at once (gamescope's planes while a
notification is up) is released only when the last one lets go.
- Open driver pairs: processes.txt timeline for the first three minutes;
Setup "Steam interface: GPU drawing" turns off software web compositing.
* Pin gamescope p17 (0129) and Venus 55b30fbf
* Non-Adreno round 4: Mali GPU-drawing block, gamescope YCbCr retry, GL probe, bridge draw sync
- Setup's "Steam interface: GPU drawing" is hidden on Mali and a saved "on" is
cleared: with PanVK it rebooted an Edge 60 (G615) as steamwebhelper started.
Kept for Xclipse.
- gamescope 0130: a composite pipeline refused at start-up is retried with a
layout without immutable YCbCr samplers (G615 through Venus refused every
composite shader; their SPIR-V has no Float16, so fp16 was not the cause).
- Venus: droiddeck-glprobe opens GL through GLX the way the Steam client does,
with the loader's reasons logged; when it fails the client's GL goes to
llvmpipe.
- vkbridge self-test: the draw check waits for the render pass's stores before
copying out (a tiler read zeros without the dependency).
* Pin gamescope p18 (0130)
* Non-Adreno round 5: gamescope memory fallback, Venus destroy guard, OpenAL depot
- gamescope 0131: DEVICE_LOCAL|HOST_VISIBLE requests fall back to host-visible
memory (the bridge's unified-memory model lists none with both), with the
request, type bits and memory types logged on failure; 0130's probe pipeline
is kept and skipped when forced.
- Venus: GAMESCOPE_NO_YCBCR_SAMPLERS=1 from the start; virglrenderer ignores
the destroy of a pipeline it never created instead of marking the context
fatal (vtest server exit 137 on a Mali-G615).
- Steam's OpenAL depot (229020) maps to oalinst_dll, so games that list it
(Hotline Miami) get OpenAL in their prefix.
* Pin gamescope p19 (0131) and Venus 6199a93f
* Non-Adreno round 6: PowerVR PRIME on old kernels, thermal, hang-up names, bridge flippable images, Venus Zink variants
- drm.c: PR #570's current PRIME ownership check (F_DUPFD_QUERY, then kcmp,
then the F_SETOWN_EX owner marker): on a kernel before 6.10 with kcmp
blocked, drmPrimeFDToHandle answered EOPNOTSUPP for every PowerVR import
(Pixel 10, Android 17: Zink "failed drmPrimeFDToHandle", Xwayland error 7,
gamescope X11 I/O error). The Mali stand-in node is kept.
- A /sys/class/thermal the kernel will not list gets an empty directory, so
mangoapp no longer aborts on it.
- gamescope 0132: a hung-up waitable is named before the abort; 0133: the
bridge's flippable images use supported-but-unexportable modifiers instead
of asserting.
- Venus: when Zink does not start, the loader's view is logged and Zink is
retried without ZINK_DEBUG=compact, with ZINK_DESCRIPTORS=auto and without
kopper before falling back to llvmpipe.
* Pin gamescope p20 (0132, 0133)
* Non-Adreno round 7: bridge command-buffer guard, Venus Zink trace, PowerVR mangoapp, Xclipse label
- vkbridge server: every command buffer a device hands out is tracked; Begin,
End (stream replay) and QueueSubmit refuse one it never allocated or has
freed (VK_ERROR_DEVICE_LOST, logged) instead of passing it to the driver,
where Mali's vkBeginCommandBuffer crashed the app (SIGSEGV 0x10, Steam's
Zink on an Edge 60 and a Tab S11).
- vkbridge server: a fault inside the driver while serving a request cuts
that client off (SIGSEGV/SIGBUS guard per request) and leaves its objects
alone; the app and the other clients carry on. Other faults go to the
previous handler.
- vkbridge server: the pipeline cache directory is copied; the JNI string it
pointed at was freed (garbage in the cache path).
- Venus: DROIDDECK_MALI_COMPAT_TRACE=1 makes the Mali compatibility layer log
what an application queries before vkCreateDevice; a failed glprobe adds a
traced run and vulkaninfo --summary under the same environment.
- PowerVR: mangoapp draws with llvmpipe (Zink could not make it a swapchain
and it crashed at every start).
- Setup: an Xclipse GPU reads "Experimental: RADV or the system Xclipse
driver", not the PowerVR line.
* Pin Venus e40cd361 (layer trace)
* Non-Adreno round 8: bridge sharing fallback and crash detail, Venus diagnostics and 1.2 gate, compositor disconnect reasons, RADV upstream
- Mali bridge: the self-test fails "memory sharing" when the server offers
no host-visible memory type (no sharing method passed, RP4 Pro G77); the
session then falls back to the PanVK pair like a failed draw, and Setup
says the bridge cannot share memory with that driver. Without a pair to
fall back on, the log says so.
- Mali bridge: a guarded driver crash now logs the sub-command it was in,
the fault address and pc (library + offset via dladdr), the command
buffer's level, begin flags, pool flags and state, and the last 32
replayed commands. Begin on an executable buffer from a pool without
RESET_COMMAND_BUFFER_BIT is logged once (implicit reset).
- Venus: vulkan-summary.txt gets the device extension list and key
features (full vulkaninfo in vulkan-full.txt); a failed GL probe runs
once with VN_DEBUG=init,result, ZINK_DEBUG=mem, MESA_LOG_LEVEL=debug and
the layer trace into glprobe-trace.txt. The Mali compatibility layer's
trace covers every pre-device query (devices, memory, queue families,
formats, image formats, external handles) and names the extensions.
- Venus: off when the phone's driver reports Vulkan below 1.2 (gamescope
skips such a device); Setup and the graphics report say why and the
session uses the PanVK pair.
- Compositor: libwayland's own disconnect reasons go to wayland.log, and a
client's buffer may grow to 1 MB before a slow reader is dropped. A
session that ended without teardown records Android's exit reasons.
- Drivers: RADV-Xclipse upstream (radv-xclipse-upstream) is a manual
choice for Xclipse; Auto keeps radv-xclipse.
* Tests: the terminal-callers harness stubs the Quest layer and Cardboard modules and Android's system properties
* Pin gamescope p21 (main's patches + 0120-0133) and Venus a4d47cf3 (layer trace)
* Display driver Auto: Quest's patched Turnip first; the system driver only for a Mali, other GPUs keep the generic fallback
* Open driver pairs: the installed build's version, its upload date, and an Update when the release is rebuilt in place
- The release check records each asset's GitHub id and updated_at; a
download records them (with the digest) for an open non-Adreno pair.
- Components > GPU drivers: an installed open pair shows its manifest
version ("android <tag> + linux <commit>"), one not yet installed shows
"built YYYY-MM-DD". A different digest, asset id or upload time on the
same tag marks the installed pair "Update available" with an Update
action: the pair is downloaded again and replaced in place, and stays
selected only if it was. Adreno and DD-Turnip pairs are unchanged.
- graphics-report.txt: the pair release line gives the installed and the
listed upload dates and flags a newer build; an exact bundle id match is
preferred (RADV-Xclipse-upstream is not RADV-Xclipse).
* Session preload: drop oom_score_adj raises so lmkd does not treat games as cached apps
steam-launch-wrapper starts every game with --oom-score-adjust 900; on
Android 900 is CACHED_APP_MIN_ADJ, so lmkd kills the running game first
when memory runs short (and on Linux 6.1 the raise becomes the process's
permanent minimum). A process in the app cannot go below the score it
inherits, so the writes that succeed from inside are raises: every
write-open of /proc/<pid|self|thread-self>[/task/<tid>]/oom_score_adj (or
oom_adj), through open/openat or fopen, goes to /dev/null, logged once.
Based on the oom_score_adj redirect in the preload's opens.c by jdmg28 (https://github.com/jdmg28/DroidDeck-Mali/commit/6be00e00)
* Display: why presenting failed on the session-ended screen, and the display driver that really loaded
- The compositor records why it cannot present (no Vulkan driver loaded,
vkCreateInstance/vkCreateDevice failed, no GPU or graphics queue, no
swapchain on the screen with the driver's reason, device lost, GPU work
unconfirmed). Each is a DD_PRESENT_FAULT line in wayland.log, read back
over JNI or from wayland.log; the session-ended screen shows it instead
of a black screen, and graphics-report.txt gets "!! DISPLAY FAILED". A
first frame or a working swapchain clears it.
- After the display device is created, the driver that answered is logged
from VkPhysicalDeviceDriverProperties (device, driverName, driverInfo,
driver id, Vulkan version) and reported in graphics-report.txt. When a
driver pair's display half was requested but a vendor driver answered
(adrenotools falls back to the system driver when the requested one does
not load), it says so.
Based on DD_PRESENT_FAULT in vk_present.c / waylandcomp_jni.c / SessionActivity by jdmg28 (https://github.com/jdmg28/DroidDeck-Mali/blob/arena/cd8ab18a-droiddeck-mali/app/src/main/cpp/waylandcomp/src/vk_present.c)
Based on the adrenotools silent-fallback finding by ryanistr (https://github.com/ryanistr/DroidDeck-Mali/blob/main/docs/development/panvk-mali-g77-bringup.md)
* tests: steam presentation test sets 0.5.0's steam_client
* Non-Adreno: find every driver pair, Venus before the bridge, fix the bridge's begin info
From the logs testers sent since c9ebb247:
- The app read the drivers repo's newest 15 releases only; the component releases (gamescope,
Venus) pushed every PanVK pair and RADV-Xclipse past them, so Auto found no pair on any Mali
and switched the Mali bridge on. The non-Adreno repo is now read in one page of 100.
- Auto's stand-in for a missing pair: Mali (Venus) where the apk carries it (Steam runs on it
from the G57 to the G1-Ultra), the bridge otherwise, nothing on a driver below Vulkan 1.2. It
no longer clears the Linux driver, does nothing when the list could not be read, records that
it chose the route, and turns it off again once a pair is installed. A bridge that earlier
builds left on under Auto is taken as Auto's.
- Mali bridge: vkBeginCommandBuffer sent its begin info without the count the server decodes, so
Mali got a zeroed begin info with stale flags and crashed at 0x10 (Pixel 8 Pro G715, Lenovo
G615, Xiaomi G925, Tab S11).
- Mali-G1 series (MT6993, arch v14) known by its chip; its label says Venus, not the bridge.
- A session Android killed for memory is said so on the next start (the S24 FE "crashes" were
low-memory kills, not driver faults).
- The in-app updater never installs an official (Adreno-only) build over this test build.
* Venus: destroy the device's object mutex after the objects that lock it
Bionic aborts on locking a destroyed mutex ("pthread_mutex_lock called on a destroyed mutex",
Tab S9 FE G68, Pixel 9a), so a client that exited holding objects took the render worker down.
From BattleTempest's DroidDeck-Pixel (kevinbryant551-ux/DroidDeck-Pixel, GPL-3.0).
* release check: a session helper may be extracted to another runtime path
droiddeck-steam-taskset (#570) goes to usr/local/libexec/droiddeck-steam/taskset, where PATH puts
it in front of Steam's own taskset for the client only; 0.5.0's check wanted every droiddeck-*
helper at its own path.
* Venus component 93038b7d (destroyed-mutex fix)
* Update MaliBridge to v0.5.1 (#1135)
* Mali Auto: never leave a session on the runtime's Turnip
Logs from a Retroid Pocket 4 Pro (G77), Galaxy S21 Ultra (G78), Tab S11 Ultra
(G925) and Xiaomi G610 showed Auto ending on the runtime's Turnip, where
gamescope finds no GPU ("failed to find physical device"): after a pair's
trial rolled back, when the pair is on the bad list, and when the driver
list could not be read. In each case Auto now switches on its stand-in
(Venus, or the bridge) when no open driver, Venus or bridge is set.
Under Auto the Mali display also leaves any imported driver, not only a
pair's PanVK half (a lone PanVK_G720 import gave a black screen).
* Mali bridge: MaliBridge 224b6e36 (v0.5.3 source, by ASEbrahim)
From c65062b6 (v0.5.1). v0.5.2/v0.5.3: Steam mode on older Mali drivers
(G77/G710/G610/G57) gets past "starting display and audio", OpenGL 4.6 on
most Malis through dynamic-rendering and vertex-format emulation, Dimensity
9500 texture fixes, async pipeline compile, RE2 flicker on G925, DirectX on
the Pixel 8 family. JNI entry and session env names are unchanged.
* gamescope 0134: linear when the backend offers no modifier; Venus on Mesa 26.2.5
0134: CVulkanTexture::BInit asserted !modifiers.empty() when the nested Wayland
backend offered no modifier for a flippable image's format (NieR Replicant on a
Pixel 9a under Venus, session exit 134). It now tries linear and logs it.
Venus ICD: mesa-26.2.5 (venus TLS teardown from an unloaded driver, virtio-gpu
devices without 3D skipped); virglrenderer stays on 1.3.0.
* gamescope p22-nonadreno (0134 + main's 0116 cursor fix; build run 40159674290)
* CI: build the non-Adreno additions before the preload check
* build.gradle: drop verifyHandModel (main moved the hand models into the tracking pack)
* Venus component venus-proto13-e24ed8ab (Mesa 26.2.5; build run 40161447324)
* CI: fetch the Mali (Venus) component again (lost when build.yml took main's jobs)
Build a7a1ebf1 (run 40161734101) had no assets/venus or libvirgl_test_server.so, so
VenusComponent.available() was false and Auto's stand-in would have been the bridge.
* Auto: the Mali bridge stands in on a Vulkan 1.1 Mali driver
Auto refused both Venus and the bridge when the phone's driver reports Vulkan
below 1.2, so on the G77, G78 and G610 (r32p1/r38, Vulkan 1.1) a rolled-back or
missing pair left the runtime's Turnip: "failed to find physical device"
(Retroid Pocket 4 Pro after the JM pair's trial, Xiaomi 13T). Only Venus needs
1.2; the bridge raises the driver for gamescope and runs on these phones. A
Venus switch that cannot start on such a driver no longer counts as a route.
* Tests: stub main's g_release_unpaced in the DMA-BUF quarantine probe
The probe copies release_buffer out of compositor.c; since the merge it reads
g_release_unpaced, which the harness did not declare (setUpClass failed to compile).
* Update MaliBridge to v0.5.4 (ASEbrahim)
48477fe6 (tag v0.5.4): Dimensity 9500 green squares and red-tinted textures,
Balatro and other OpenGL text, fewer crashes when memory runs low, one screen
update per game frame, picture kept after a long background pause (SIGHUP no
longer stops the server), wrong colours after changing graphics settings on
the G720, more in the session report. The app-side changes of his DroidDeck
Mali app (Steam menus setting, per-game setup, Send logs, updates) are not
part of the MaliBridge repository and are not in this build.
* gamescope 0135: linear when the driver rejects every offered modifier
An r32p1 Mali-G610 (Vulkan 1.1) behind the Mali bridge answered both of the
backend's modifiers for AR24 with VK_ERROR_FORMAT_NOT_SUPPORTED, so the first
flippable image failed and gamescope asserted in vulkan_create_flat_texture
(Xiaomi 13T). Linear, when offered, is tried anyway; the bridge makes such
images without the handle type and exports them through a dma-heap. If the
image cannot be made, BInit fails as before.
* gamescope: pin the non-Adreno p23 build (0135)
* Tests: the terminal-callers harness stubs take_shm's game-frame detection
Main's take_shm now reads surface damage, parent/role/mapped and the HUD state
to spot game frames; the harness declares them so the copied function compiles
(setUpClass failed in Build APK run 40505286127). No damage, so it stays off.
* gamescope: number the test branch's patches 0920-0935
Main now has its own 0121-0125 (window override walk, host-event wake, commit fences, done at
import, HUD frame times), which sorted in between this branch's 0120-0135. The branch's sixteen
patches move to 0920-0935 with the same last two digits, so they apply after main's set and stay
clear of its next numbers. Applying all 38 to gamescope 3.16.29 in the old order and the new one
gives the same tree (checked with patch -p1, as build-in-arch.sh does). PATCHES.md and the one
code comment that named 0130 follow the new numbers.
* check_session_assets: helpers count in usr/local/bin or the Steam taskset folder only
The merge kept this branch's pattern (any usr/ target), which main's new test rejects: a helper
extracted into a subfolder of usr/local/bin must not count. The pattern now takes usr/local/bin
under any name, as on main, plus usr/local/libexec/droiddeck-steam for droiddeck-steam-taskset.
Every helper in the overlay is still found extracted in SessionFiles.kt.
* gamescope: pin 3.16.29 p24 (non-Adreno)
Built from this branch @d13b9d88 (run 41119284267): main a5b035b2's patches plus this branch's,
now 0920-0935. All 38 applied, gamescope's tests 2/2. sha256 1d1088d4.
* Update MaliBridge to v0.5.4.2 (ASEbrahim)
95a1e600 (tag v0.5.4.2), was v0.5.4 (48477fe6). For the bridge: memory sharing without the handle
type where that is all the self-test passed (unflagged buffers, client/memory.c, server_memory.c),
the older-Mali (G52-G77) game-mode start fixes, the r38p1 beta geometry-shader erratum (gs_hide),
present timeouts and an API cap per process. Client, server and the in-app library all build from
the submodule with MaliBridge's own scripts, which did not change; the protocol only gained
fields an older server sends as 0.
* Session end: the compositor fault query no longer fails the hint when the native library is absent
presentationFault() (6778a224) called WaylandCompositor.compositorFault() directly. Main's new
SessionEndHintTest runs sessionEndHint() in Robolectric, where libdroiddeckwayland cannot load, so
all six cases ended in UnsatisfiedLinkError (Build APK run 41125749537). The call is now wrapped
like runtimeDamageHint()'s, and without an answer the hint reads wayland.log as before.
* test: stub the GPU busy reader in the caller harness
* fix: release build is not debuggable, Mali uses the system display driver, staging failure ends its own session
* fix: address non-Adreno review findings
* fix: preserve current gamescope and GPU warning behavior
* fix: retain graphics failure hints and launch diagnostics
* fix: identify non-Adreno GPUs in driver failure hints
* test: harnesses follow the external client barrier and effects upscaling changes
* fix: client sources return to foreign ownership after the overlay draws
---------
Co-authored-by: Leask Wong <[email protected]>
Co-authored-by: wallentx <[email protected]>
Co-authored-by: Stefan Zijlstra <[email protected]>
Co-authored-by: William Allen <[email protected]>
Co-authored-by: The412Banner <[email protected]>
Co-authored-by: Dersim Abbas <[email protected]>
Co-authored-by: root <[email protected]>
Co-authored-by: ASEbrahim <[email protected]>
Co-authored-by: The412Banner <[email protected]>
Co-authored-by: The412Banner <[email protected]>
* fix: an unplugged game drive falls back to internal storage
When the drive chosen for game storage is not connected, installs, sessions and the install picker use internal storage instead of failing with "Game storage is unavailable". The saved drive is kept and used again once it is plugged back in.
* fix: store installs set to external use internal storage while the drive is unplugged
With default storage set to external, a missing drive or folder no longer stops installs at the location dialog or leaves the Epic launcher pointing at it. Installs go to internal storage and switch back to the drive once it is connected again.
* fix: external installs do not move to another card while the saved drive is unplugged
* fix(storage): verify saved volume is mounted
* fix(storage): reject unknown external volumes
---------
Co-authored-by: Kurt Himebauch <[email protected]>
* 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 <[email protected]>
* perf: reclaim launcher memory under system pressure
* docs: record Caravan save-load OOM with spare swap
* fix: wait for completed reclaim before increasing backoff
* fix(preload): answer NETLINK_ROUTE queries inside a session
ip(8) could not run in a session: the app's SELinux domain has no
netlink_route_socket bind permission, so iproute2 failed at bind and never saw
the link that getifaddrs() and /proc/net already expose.
Serve NETLINK_ROUTE from the same etc/droiddeck-net snapshot: an AF_UNIX
socketpair backs the netlink fd, link/addr/route dumps and route get are
synthesised, writes return EPERM, and multicast subscriptions keep failing
with EACCES so consumers that wait for link events keep their fallback.
Replies carry the port id reported by getsockname() and recvmsg() rewrites
the source address to AF_NETLINK/pid 0. iproute2's dump loop requires both
before it accepts a message or NLMSG_DONE; when either is missing every
message is skipped silently and the process blocks in recvmsg().
* fix(preload): keep socket() and send() semantics for emulated netlink
* fix(preload): deliver netlink replies with the caller's blocking mode
* fix(preload): preserve NETLINK_ROUTE protocol and fd semantics
- Send ACKs for successful queries and honor NETLINK_CAP_ACK
- Reject unsupported subscriptions and apply socket timeouts
- Track socket identity and shared references across dup and close
- Support kernel connect and rewrite recvfrom source addresses
- Resolve local and connected routes before default gateways
- Add netlink and iproute2 regression tests
* fix(preload): handle netlink request limits and socket lifecycle
- Collect large sendmsg requests without falling back to UNIX sockets
- Reject oversized replies explicitly instead of returning partial results
- Allocate unique port IDs and reject duplicate bindings
- Honor socket flags and restore inherited sockets across exec
- Validate socket types and report netlink socket identity
- Replace the fixed socket table with a dynamic registry
- Add regression tests for batching, port conflicts and exec inheritance
* fix(preload): optimize netlink tracking and support link name queries
- Add lock-free fd tracking to bypass netlink locks on ordinary sockets
- Move descriptor restoration to initialization and explicit discovery paths
- Support RTM_GETLINK lookups via IFLA_IFNAME with correct error codes
- Add regression coverage for hot-path overhead and name-based queries
* fix(preload): accept unterminated netlink interface names
- Copy IFLA_IFNAME into a bounded, NUL-terminated buffer before lookup
- Match kernel string length validation and ERANGE errors
- Add regression coverage for unterminated names and boundary cases
* fix(preload): pin peers for netlink replies
* fix(preload): make autobind idempotent
* fix(preload): isolate forked netlink cleanup
* fix(preload): avoid interposed fd scans
* perf(preload): skip PID checks for untracked fds
* fix(preload): verify peer before replay
---------
Co-authored-by: uubooster-team <[email protected]>
* fix(input): release held mouse buttons and keys when the session loses focus
A mouse button or key held in the guest stayed down when another window took focus or pointer capture ended before the release, because the release went to that window. A game that hides the cursor while a button is held, such as mouselook in World of Warcraft, then kept turning the camera with the cursor gone until the button was pressed again.
Buttons sent to the guest are now tracked like keys, and every held button and key is released when the session window loses focus or pointer capture, and when picture-in-picture opens.
* fix(input): release a held button where it was pressed
A button pressed through the compositor was released through the raw mouse channel if that channel turned on while it was held, and the reverse, so the guest kept it down. Each held button now remembers where it was pressed. A scroll-touch drag is also cancelled when the session loses focus.
* fix(input): release trackpad buttons on focus loss
---------
Co-authored-by: Kurt Himebauch <[email protected]>
* fix(components): a native-only override falls back to builtin when the prefix has no native DLL
A component that asks for a DLL native-only (for example .NET 4.8's mscoree=n) left the DLL unloadable when its native copy never reached the prefix. On ARM64 prefixes the .NET installer leaves Wine's builtin mscoree in system32, so every program that needs .NET exited at launch with 'mscoree.dll not found'.
At launch, an override of n for a DLL whose system32 or syswow64 copy is missing or is Wine's own (a link into the Proton build, or a builtin or placeholder stub) becomes n,b, and the session log says so. A real native copy is still loaded first, and the saved component state is unchanged, so the override returns to n once the native DLL is there.
* Treat any prefix symlink as builtin when checking native overrides
* Treat an unreadable prefix DLL as missing for native overrides
fix: the glasses pointer reaches the whole guest screen
Absolute input was clamped to the letterboxed picture rect computed for the phone view, while the compositor reads the input space against the glasses output. With different shapes that limited the guest pointer to a middle band of the scene, so the bottom and top edges were unreachable.
Send absolute pointer positions as the full-view fraction (the compositor already clamps to the scene) and use the whole view for the touchpad bounds and relative scale while the glasses own the output.
Fixes#2055
Co-authored-by: Moroni Granja <[email protected]>
Extend the VRAM setting to Vulkan memory-budget reports for DXVK and D3D12 while preserving real heap capacity and allocation results, removing the hard rejection that caused game crashes in FIT and Discord tests.
#2037 added gamescope patch 0126, but the app does not build gamescope.
It stages the prebuilt binary pinned in `tools/gamescope/release.env`,
which is still `gamescope-3.16.29-p19`, built on Oct 29 before the patch
existed. A fresh install of a main build therefore still draws the EA
Steam Launcher's "Authenticating…" splash over the EA "Verify your
identity" sign-in. The APK of main 951270b carries gamescope
`1a9c836e…`, the same executable as p19.
This points `release.env` at `gamescope-3.16.29-p20`.
**Needs a release first:** please publish the `gamescope-patched`
artifact of the main build of 951270bc, [run
41637838020](https://github.com/Droid-Deck/DroidDeck/actions/runs/41637838020),
as `gamescope-3.16.29-p20` in Droid-Deck/DroidDeck-Components. Until
that tag exists, the runtime component build here cannot download it.
- That run applied patches 0002–0126 and passed gamescope's tests (Ok 2,
Fail 0).
- Archive SHA256:
`4537fa58b042f84e605123c4778ee7089442591cd3bfe0648ad96e113ef6117d`
- Gamescope executable SHA256:
`b22078c80c7262a24339105b2b02c90a8cf1a7929f6e10c212f465eb772dbb6c`
- WSI layer SHA256:
`eabc9dcab72dc52dcbd7def2df9ab99e2a26e662eba2c387059ed655801825c5`
The executable is byte-identical to a local build of the same patch
series.
## Testing
AYANEO Pocket FIT Elite, DroidDeck 0.6.9 (main 951270b), Burnout
Paradise Remastered installed fresh:
- With p19 as shipped, the EA app installed and opened its sign-in, and
the Burnout splash covered most of it.
- With this run's executable staged over `/usr/local/bin/gamescope`,
`EASteamLauncher.exe` (the splash) was running. `gamescopectl
focus_info` showed focus, input and keyboard on the EA sign-in with no
override window, and the sign-in was fully visible.
- `tools.tests.test_prepare_windows_runtime`,
`test_gamescope_override_stacking` and `test_hud_frames` pass.
* fix(input): taps land where touched while a game holds the pointer
Games that hide the system cursor and draw their own, such as Stardew Valley, hold the pointer the same way first-person games do. Every touch on the default touch mode then went through the touchpad, so a tap clicked wherever the cursor already was: tapping New opened Load, and a tap after Back could press Exit.
A touch now stays a direct touch while the pointer is held. A tap clicks where the finger is, and once a swipe moves past the touch slop it turns the camera with relative motion as before. Touchpad mode is unchanged.
* fix(input): keep the swipe anchor when the first finger lifts
When the first finger of a locked-pointer swipe lifts and another stays down, the next move is reported for the remaining finger. The anchor now moves to that finger, so the camera does not jump.
## Summary
- Keep raw diagnostic staging in private cache and give each exported
archive a unique request directory.
- Remove that request directory after the CLI pull, including failed
transfers.
- Replace files from the previous extracted snapshot while preserving
unrelated destination files.
- Document that the old rooted-device fallback is not scrubbed.
Follow-up to #2061. The refreshed export tests now cover independent
archive outputs and stale snapshot replacement.
## Validation
- `wsl python3 -m unittest tools.tests.test_droiddeckctl` — 49 passed.
- `:app:testDebugUnitTest -x buildVkBridgeServer --tests
com.droiddeck.launcher.session.SessionLogShareZipTest.anArchiveCanBeWrittenOutsideTheShareFolder`
— passed.
- `git diff --check` — passed.
Refs #1774.
Maldhalla (4385780) ships its x64 and arm64 Linux builds side by side in
one depot. Its launcher starts the arm64 build when it runs on an Arm
host, natively or as an x86 shell under FEX. Steamworks offers no arm64
value for a launch option's CPU architecture, so the appinfo still reads
`oslist` linux+windows and `osarch` 64. `native_targets()` therefore
leaves the title on the Proton ARM64 default, which runs the Windows
build.
This PR adds `FEX_DEFAULT_APPS`, an explicit allowlist (the opt-in
suggested in #1774). A listed title defaults to `droiddeck-fex` instead
of `NATIVE_CHOICE`. Nothing else changes:
- A player's own pick is kept, as before.
- Unlisted titles keep today's behaviour, including the Factorio
native-choice case.
On the Snapdragon 8+ Gen 1 tablet from #1774, our benchmark scores 8–11
with the Windows build under Proton ARM64 and above 100 with the native
arm64 build.
Test:
`ProtonDefaultTest.test_a_listed_title_with_a_bundled_arm64_linux_build_defaults_to_fex`
in `tools/tests/test_game_environment.py`. It fails without the change.
`ProtonDefaultTest`, `test_droiddeck_proton_default`,
`test_steam_compat_mapping`, `test_appinfo_lookup` and
`test_steam_compat_live` pass.
A listed title whose automatic FEX mapping the player clears now records
the empty mapping as a native choice, as titles with NATIVE_CHOICE do,
instead of getting FEX again on the next run.
Steamworks has no arm64 launch-option architecture, so a title that ships
x64 and arm64 Linux builds side by side still reads as osarch 64 and gets
the Proton ARM64 default. List such titles in FEX_DEFAULT_APPS so they
default to Linux x86 (FEX), whose launch runs the bundled arm64 build. A
player's own pick is kept, and unlisted titles are unchanged.
Refs #1774
ClockLogComponent was only created inside startVkBridge(), so it never ran
on Adreno. Create it for every session (clocks.log, was vkbridge-clocks.log)
and append the battery temperature from ACTION_BATTERY_CHANGED to each
sample.
Part 1 of #1837.
A ranking a scan took from added-games-scan.json was also put in the
60-second in-memory cache with the current time, so for the next minute
single(), addFolder() and the exe picker, which ask for a fresh walk, got
a ranking that could be a week old. Kept rankings are now returned without
entering that cache, so it only ever holds rankings from real walks.
A record whose fields could not be decoded threw while the cache loaded,
and since every scan loads it first, one bad record blocked every scan;
each record is now decoded on its own and skipped if it is malformed.
name() returned names older than a week that were still in memory; it now
applies the same age limit as rank().
save() cleared its dirty flag before writing and called startWrite outside
its try, so a write that failed was never retried and could replace the
scan's result with an exception; a failed write now leaves the cache dirty
and is tried again on the next save.
Every Steam session scans the added games before the Linux side starts,
walking each game folder for its exe and reading the exe's version info.
On an AYANEO Pocket FIT Elite with 40 games on an exFAT SD card that walk
took 10 s with a warm cache and up to 155 s after Android had dropped it,
and the front end's art pass and library load ran the same scan at the
same time, so the session waited on all of them.
Scans now run one at a time, so a waiting scan reuses what the one before
it just found. Each game folder's exe ranking and identified name are kept
in added-games-scan.json; a scan reuses a ranking while the folder's
modification time is unchanged, its first exe still exists and it is under
a week old, and walks the folder again otherwise. The exe picker, single
game refreshes and adding a folder still walk the folder fresh.
seed_install read the app's manifest from every library before checking
that it was the linked DroidDeck install, so a manifest in another library
that is not UTF-8 raised past the caller and stopped routing for later
games. It now returns before reading anything but the linked manifest.
An untouched manifest from an older version was migrated even while
steamapps/downloading/<appid> existed; that migration now waits until
Steam is not staging the app, like the rewrite of a queued import.
An added game the account owns is linked into Steam as DroidDeck-<appid>.
When the folder carries no Steam build, Steam marks it Update Required and
checks every file before updating. That check reads the whole game, and an
interrupted one starts over, so on an SD card it ran again in every session
and kept the card busy: Lords of the Fallen (33 GB) restarted 51 times in a
day on an AYANEO Pocket FIT Elite.
The manifest for such an import now asks Steam to update the game when it
is launched. Imports that Steam already rewrote, but whose update has not
started, switch from "always keep updated" to "update when launched"; a
priority of 2, an update in progress, or an import with a recorded build is
left alone.
Stream each archive entry to disk instead of reading it into memory,
refuse an archive from a session that started after the state call, and
delete the exported zip from shared storage once it has been pulled.
`logs latest` pulled the session folder under getFilesDir(), which adb
cannot read on an unrooted device, so it always failed with "Permission
denied" and droiddeck-scenario kept no logs for failed runs.
The agent bridge gets a `logs` method that writes the Share logs archive
to the app's external files folder (agent-logs/), and droiddeckctl pulls
that zip and unpacks it into the destination with the session's files at
the top, as before. Builds without the method still get the old pull.
Adds the 83 Russian strings that showed in English: extra Steam libraries, session logs, dual screen launcher and sessions, PC keyboard options, the bottom screen desktop, screen swap, the stats HUD and the Mali bridge GPU info.
Address review: a navigation-bar Back that arrives while an injected mouse
Back is held now stays Back instead of releasing the right button early.
A held right button is released on pause, focus loss and input-device
removal, like the other held Back handling. While the session is
suspended or still loading, Back is no longer sent to the guest.
REDMAGIC's cast mode replaces a mouse's right button with a Back key that
names no device and lacks the virtual-hard-key flag that the navigation
bar and back gesture set. During a session, a Back from a mouse, or that
injected form while a mouse is connected, now presses the guest's right
button. The navigation bar, back gesture and controller Back still act
as Back, and so does a right click while a DroidDeck menu is open.
A mouse's Back and Forward buttons no longer also send a left click;
Android delivers them as key events.
Forward extraction and installation messages from the x64 bootstrap log and replace stale download percentages with the applying-update state. Add regression coverage for phase ordering and log forwarding.
Fixes#812
Blizzard's China client records some products under a CN uid, so the installs
it makes never reach the Library and their Stores card keeps offering Install:
Diablo II: Resurrected osic (catalogue had osi)
StarCraft II s2_cn (catalogue had s2)
Diablo Immortal anbs_cn (catalogue had anbs)
title() now resolves those uids to the international title, so the Library, the
Stores "installed" state, the cover folder and the launch code all agree, while
Game.installUid keeps the uid the Agent recorded for updates and uninstalls.
TITLES and STORE are untouched.
Provide compatible gateway metadata for IPv4 VPN links without changing
VPN routing, and keep text and JSON network snapshots consistent.
Add regression tests for gateway selection and network snapshots.
Zink's rebind_image() and zink_rebind_all_images() refreshed only a sampler
view's main image view after the resource got a new backing object. The
view's zs_view and cube_array surfaces stayed on the old object and were
freed with it, so the next descriptor update wrote a garbage VkImageView.
On PowerVR (needs_zs_shader_swizzle) every RGBX/BGRX view has a zs_view, and
Steam's web helper segfaulted in vkUpdateDescriptorSetWithTemplate within
seconds of fast Right presses on the Home shelf. The new Mesa patch rebuilds
all of the view's surfaces with init_sampler_view().
Avoid decoding every app when Start without Steam resolves one game launch. Use wanted-app selection in the single-game helpers while keeping the existing field decoder and full-reader default for bulk callers.
Track partial coverage separately from complete results, share one immutable byte snapshot for incremental queries, and keep timestamp invalidation and warm hits. Preserve the last valid duplicate record and release the raw snapshot after a full decode.
Add format/cache/structural-budget and launch-resolution regressions, reuse the existing binary fixture encoder, and document the host-only performance scope.
* fix: upscale game buffers before native-resolution composition
* fix: drop an added code comment from the effects source draw
* fix: effects source keeps pillarboxed games and near-full source rectangles
* fix: a larger opaque draw above keeps the full composition path
---------
Co-authored-by: maxjivi05 <[email protected]>
The Agent's install request pointed at http://cn.patch.battle.net:1119/<product>.
That host only 301-redirects to the country's version service today; Blizzard's
China client asks cn.patch.battlenet.com.cn:1119 directly.
The update check built <branch>.version.battle.net, so a China install (branch
cn) asked cn.version.battle.net, which does not answer; the China service is
cn.version.battlenet.com.cn.
Only region cn changes.
fix(components): the library sweep refreshes the catalog when it is safe to
A game job reads the cached catalog, and the installed check compares against
the cached revision, so a repaired component never reached a prefix that
already had it. The sweep now fetches the catalog first when online and not on
a metered or constrained connection.
World of Warcraft, Classic and Forever get an Addons tab in the Library. It imports addon zips into the right Interface/AddOns folder, lists the installed addons, and deletes one after a confirmation.
* 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
* fix(desktop): Steam's own header moves and resizes its windows
Steam never asks KWin to move or resize its windows, and its maximize
button can only maximize. Each normal Steam window now reports presses
on its header and edges, and KWin handles them natively. No extra title
bar is added and other apps are unchanged.
* fix(desktop): drag regions cover the elements inside them
* fix(desktop): find Steam's drag regions by position
The Friends window's drag region ignores pointer events, so a hit test
never finds it. The check now runs once a press moves, so plain clicks
cost nothing.
* fix(desktop): download language packs from the release that has them
The Ubuntu runtime's release has no language packs or index, so the
desktop stayed in English for every language.
* fix(desktop): reinstall language packs when their release changes
* fix(desktop): a game's window follows its own window mode
Desktop games ran in a titled gamescope window whatever mode they asked for. gamescope now fills the desktop for a fullscreen or borderless game and matches the window size of a windowed one.
* Use the p30 gamescope test build
* fix(desktop): a windowed game too big for the desktop is maximised
* fix(desktop): a decorated game window stays a window
* Use the published gamescope p21
* fix(battlenet): use the right artwork for each game
World of Warcraft: Forever took retail art and its logo from a loose name lookup, and only fetched its own art when the Store tab was opened. Shop pages also supplied trailer stills and seasonal promos.
Each game now has chosen Blizzard key art with the shop page as a fallback, installed games fetch their art without opening the store, and lookups by name must match exactly. Art found by the old lookup is fetched again once.
* fix(battlenet): keep cached art if the new art cannot be fetched
Classic's variants fetch through the shared title, and a failing chosen image falls back to the shop page.
* feat(gamescope): a three way switch for relative mouse motion
Games that confine the cursor and warp it back to read movement never
reach the nested hints rule, so their aim stays bounded by the screen.
The convar defaults to 0 and leaves that rule alone; 1 forces relative
motion for the focused game, 2 keeps the pointer absolute even when the
rule would apply.
(cherry picked from commit db3d7555e0)
* feat(input): the session carries a relative mouse mode per game
Reads the mode from the game's gesture config, hands it to gamescope as
gamescope_relative_mouse, and pushes a change to the running session
through gamescopectl.
(cherry picked from commit e350d88464)
* feat(drawer): the relative mouse switch is a three way choice
Auto leaves it to the game, on forces relative motion, off keeps the
pointer absolute. Named in all 32 locales so none of the three falls
back to English.
(cherry picked from commit e96a187e02)
* fix(input): follow the focused game's relative mouse mode
Switching to a game whose saved mode differs left the session on the
previous game's mode, since only the drawer state was refreshed. The
requests also ran on a thread per selection, so two quick picks could
reach gamescope out of order.
(cherry picked from commit 7ff8c5d568)
* fix(input): retry the relative mouse mode gamescope did not take
The drawer state moved before the request ran, so a failed request left
the two out of step and the next focus change skipped the resend.
(cherry picked from commit cf0f82d8cd)
* fix(input): the relative mouse mode tracks the latest choice
The guest agent answers ok when it handled the exec, so a failing
gamescopectl was recorded as applied. Reconcile the newest desired mode
on the worker thread after each request, and only mark applied when the
command really exited zero.
(cherry picked from commit 5f5e3bd52e)
* gamescope: a hidden cursor under any pointer constraint is relative
Games that hide the cursor and confine the pointer had no lock hint, so the
nested pointer stayed absolute and the camera froze.
* Treat a hardware mouse as a real mouse in sessions
A mouse now moves the guest pointer by relative motion for the whole session
through pointer capture, so it no longer stops at screen edges or reaches
system gestures. Its cursor stays visible in every touch mode, follows the
game showing or hiding it, and clicks no longer press on-screen controls.
* gamescope: use the mouse test release
* Direct touch turns the camera while a game holds the pointer
A swipe in direct touch now sends relative motion when a game has the pointer
locked, including when the lock starts during the swipe, as when a game
hides the cursor while a button is held.
* Address review findings on mouse and touch input
Keep touchpad taps on on-screen controls, cancel a pending long press during a
locked swipe, let the three-finger gesture cancel a locked swipe, refresh
capture when the output changes, and use the menu's key for the relative
mouse setting.
* Keep locked touches inside the game picture and free the mouse in touch editors
* End touch editor ownership when the editor goes away
* Bring the mouse cursor back when a touch editor closes
* Release a pointer lock at the game's pointer
* gamescope: use the pointer position test release
* Fit the X screen to a fullscreen window larger than it
* gamescope: use the X screen fit test release
* Return the X screen when no game window has focus
* gamescope: use the X screen return test release
* ci: avoid redundant gamescope builds
* fix: hardware mouse in Desktop and pointer warping games
KWin nested on the host only reads relative motion while its own pointer lock is on, so a program that locked the pointer in Desktop (Blender orbit and pan) stopped receiving motion. A captured hardware mouse now turns that lock on, and a real Right Ctrl no longer toggles it.
Gamescope follows a game's pointer warps instead of overriding them, and absolute motion also sends raw motion. Also retries relative mouse requests, tidies the unplug path, passes side buttons and fits a second oversized window correctly.
* chore: use the gamescope build with patch 0131
* fix: Desktop mouse lock keeps retrying after early attempts fail
* fix: hold the key that turns on the Desktop mouse lock
KWin ignores a press and release delivered together.
* fix: gamescope enters warp following sooner and keeps steady motion
* chore: use gamescope p26
* Capture system keys in a focused session and keep the first Desktop mouse motion in place
* Use the p27 gamescope build
* Update the system keys row test
* Recognise a captured mouse so the Desktop grab can start
* Keep touch warps out of relative motion
* Send Esc from keyboards that report it as Back
* Use the p28 gamescope build
* Release captured keys on pause, forward motion during a grab and wait for a pending screen fit
* Use the p29 gamescope build
* fix(input): keep held system keys consumed until they are released
Copy each captured key event, since the service owns the original only for the callback, and consume the physical release of a key already released to the guest.
Use the published gamescope p20.
* fix(input): capture a system key only from its first press
---------
Co-authored-by: kingokksa <[email protected]>
Co-authored-by: maxjivi05 <[email protected]>
fix(battlenet): keep games installed inside Battle.net's folder running
A game installed under C:\Program Files (x86)\Battle.net\ (for example
World of Warcraft: Forever in "Battle.net\World of Warcraft\_forever_")
was counted as part of Battle.net itself. Launched from DroidDeck, the
launch never saw a game, said "Battle.net closed before a game started",
stopped everything left in the prefix, game included, and exited 1.
Battle.net's own files are now only those directly in its folder, in its
Battle.net.<build> folders and under ProgramData\Battle.net.
Co-authored-by: Albert <[email protected]>
* feat: desktop and scrolling wall live wallpapers selectable from Android settings
* fix: render the wall wallpaper off the main thread and give it its own thumbnail
* fix: serialize wall wallpaper visibility and wait out in-flight draws on surface destroy
* fix: restart the wall loop on surface recreation and keep queued visibility changes
* fix: keep the wall loop alive across a failed canvas lock and finish redraws before returning
* fix: post the wall redraw asynchronously on surface change
* fix: redraw the desktop wallpaper logo from the real mark and show game covers on the wall
* chore: mount the git dir in build containers and seed new worktrees from the main checkout
* fix: decode wall covers as RGB_565 and seed worktree assets only into empty dirs
* feat: drop the desktop wallpaper and add frame rate and speed settings to the wall
* feat: wall settings over the live wallpaper with a match-screen rate, and drop the center vignette
* perf: pace the wall on vsync and draw it on a hardware canvas
* fix: stage worktree seed copies and publish them only after they succeed
* fix: point the wall settings link at the activity class so the picker shows the gear
* fix: dedupe wall covers by app and space repeats like the in-app wall, keep the frame loop alive across surface gaps
* fix: show the picker preview behind wall settings and drop the display-rate lookup
* fix: stop wall frame callbacks when the surface goes and only replace an empty seed destination
* fix: skip a seed when the destination reappears before the staged copy is moved in
Blood Omen ships dxconfig.exe beside kain.exe and the shortcut pointed at
the config tool: pickExe kept the first .exe the folder listing returned
when no title match was found. Read the game's play task from GOG's own
goggame-<id>.info manifest first - a primary game task, or a primary
launcher task for launcher games, and the product's manifest ahead of any
other goggame-*.info - then the product's temp_executable; without either,
rank the folder's exes so a word of the title wins, config and tool names
lose, and depth then size break the remaining ties.
GOG's galaxy_*.exe support installer is not the game either. The Witcher:
Enhanced Edition ships galaxy_the_witcher_enhanced_edition_2.1.0.15.exe
beside launcher.exe, and its build metadata names that file as the
product's temp executable, so launching it starts a 32-bit installer that
stops at "Path not found" under ARM64 FEX - the same dialog as the setup
stub #1174 removed. Play tasks, the hint and the name search skip it.
Fixes#680Fixes#694
Only a primary launcher task counts as a launch task: GOG files the
configuration tool beside a game as a non-primary launcher, so it now falls
through to the executable search instead of becoming the game.
The temp_executable hint is matched by basename with either separator and
must resolve inside the install folder, so a nested galaxy_*.exe is skipped
too and a hint cannot point outside the game.
GOG runs a task from the working directory it declares, and from the install
root when it declares none. That directory now reaches the sidecar and the
shortcut's Start In: a game in a subfolder (System/witcher.exe) starts where
GOG starts it instead of in System/, where it cannot find its Data folder.
Nothing in droiddeckctl reported temperature, so a before/after
comparison could start one run hot and the next cold.
`thermal` reads the battery temperature from dumpsys battery, the
thermal status from dumpsys thermalservice, and each CPU policy's
scaling_max_freq against cpuinfo_max_freq (capped when lower), all
readable over plain adb. `wait cool --below <C>` polls it every 10 s
until the battery is under that temperature, or exits 5 at --timeout.
With the overlay hidden at session start the blocked feeds have no value,
so cur_freq and perf_now were never written before the binds were taken
and stayed unbound for the whole session.
* Reduce on-screen stick load and honour Low latency off
On-screen controls requested unbuffered touch on every press, so turning
Low latency off had no effect on them. They now follow the setting.
Every stick move also redrew the whole overlay. Redraws during movement are
capped at 30 per second in both overlays, and stick moves under a pixel
are dropped in the standard overlay.
* Keep full stick deflection reachable
Moves under a pixel were dropped even at the edge of the stick, so the pad
could stop just short of full range.
Patch 0126 (#2037) is in the source tree, but the app still stages the
p19 binary pinned here, built on Oct 29 before the patch existed. A fresh
install keeps drawing the EA Steam Launcher splash over the EA sign-in.
p20 is the gamescope-patched archive from the main build of 951270bc
(Droid-Deck/DroidDeck actions run 41637838020): patches 0002-0126
applied, gamescope tests Ok 2 / Fail 0.
* gamescope: EA sign-in stays above the EA launcher's splash
The EA app's Steam launcher shows the game's splash as a layered tool
window, and the EA app, another process, opens its sign-in window
topmost. gamescope took the splash as the sign-in's override window, so
the splash was painted over the sign-in and became gamescope's keyboard
focus window (Burnout Paradise Remastered). With patch 0126 a window from
another process that is neither topmost nor transient for a topmost
focus window is no longer an override candidate for it, as on Windows.
map_win now also reads _WINE_HWND_EXSTYLE: gamescope only took it from a
PropertyNotify, which it listens for from map on, so a style Wine set
before mapping was never known.
* gamescope: the topmost rule needs both windows' pids
gamescope keeps a window's pid at -1 when XRes cannot name its client,
so an unknown pid next to a known one is not evidence of two processes.
Patch 0126 now applies the rule only when both pids are known. The patch
carries function names in its hunk headers, and the test also checks
that its map_win hunk reads _WINE_HWND_EXSTYLE into hasHwndStyleEx (a
check of the patch text, not of a running gamescope).
The redistributable seeder marks every process in a game's install scripts
as already run, except Vulkan runtime and Ubisoft Connect installers. EA
games on Steam list the EA app installer (EADesktopSetup) there and start
through steam2ea://, which only the EA app handles. With the installer
skipped, Proton could not open that link (ret 31) and the game closed
without a window (Burnout Paradise Remastered). The EA app installer is
now left to run like Ubisoft Connect's, and a marker an earlier seeding
wrote is taken back out, so an existing prefix installs it on its next
launch.
* fix(runtime): keep the Linux desktop installed across a runtime update
A runtime update replaces the whole runtime tree, and the desktop pack
is built against one runtime, so every update removed the installed
desktop. Since the runtime started updating by itself, this happened
without the user doing anything: the next Open showed "Install & open"
and downloaded the whole desktop again.
When the desktop was installed before the update, the same update job
now installs the new runtime's desktop pack right after the runtime,
with the update's progress. If that install fails, the runtime update
still counts as done and Open offers the install as before. A repair
from the session no longer installs the desktop a second time when the
runtime install already did.
* fix(runtime): roll back a failed desktop restore after a runtime update
If putting the desktop back fails after its archive started unpacking,
the install journal stayed behind, and every later launch, Steam
included, tried to resume it and stopped when that failed. The update
now rolls back the partial desktop with the desktop removal, which
restores the runtime's own files and clears the journal. Open then
offers the desktop install as before.
Whether the desktop was installed is now checked on its own, so a
repair of a damaged runtime also restores it.
* fix(session): do not install the desktop again after resuming it
Opening the desktop with an interrupted desktop install resumed that
install and then installed the desktop a second time. The second
install is now skipped when the resume already completed the desktop.
* fix: a stale session's startup failure no longer stops the newer session
* fix: stale session failures and queued-launch cleanup are checked against the current session under the stop lock
* fix: download monitor polls off the main thread so slow storage can't ANR the session
* fix: cancel the queued download poll before the service shuts its executor down
* fix: ignore download monitor submissions that arrive after the service shut it down
* feat: suggest Steam libraries found on internal storage and cards
* fix: add found card libraries by their readable path and drop superseded scan results
Forever was missing for two reasons. Its install was not a known title,
so the scan skipped it as "not listed". And every WoW version installs
into the same World of Warcraft folder, so once one version was listed
the Library dropped the others as the same game.
- Forever is a title: launch code WoWF, product wowf, folder _forever_.
- An install whose uid is not known is matched by the product code
product.db records for it.
- When versions share the folder, the one listed first keeps it and the
others use their own version folder (_forever_, _classic_, ...). Each
title's folder is remembered, so a shortcut keeps its app id when
another version is installed or removed.
Games installed before this update are listed on the next Library scan.
They do not need to be installed again.
Swap screens for dual-screen handhelds: the game moves to the built-in
bottom screen while the top screen shows the Dashboard or the KDE desktop.
Swap button in the Dashboard and Desktop bars and a Show the game on
row in the session menu. The desktop resizes to the screen it is on,
Follow screen and Match screen sessions take the bottom screen's shape,
the keyboard opens on the top screen, and the game swaps back on session
end, picture-in-picture, glasses, or when the bottom screen is taken.
Review follow-ups cover desktop restart, refresh pacing, key and pointer
ownership, touch cancellation and bottom surface recovery.
On phones where Android blocks the GPU's sysfs files for apps, the HUD
reads the GPU without root (#1969). On Adreno 8xx that showed no GPU
clock and only the partial load from Vulkan timing (OnePlus 13, Adreno
830, reported by iamTooShaar).
- GPU clock: the clock job used the Adreno 6xx/7xx registers. The
register layout is now picked from the KGSL chip id: on 8xx
CP_ALWAYS_ON_COUNTER is 0x8e7 and the RBBM cycle countable is 1.
Unknown later generations read nothing.
- GPU load: hardware-scheduled kgsl kernels (every 8xx, and some 7xx
such as the AYANEO Pocket FIT's) never write the memstore ringbuffer
timestamps. When those stay 0, the reader samples the contexts' own
memstore entries (every context that has submitted work, up to 64,
contexts with new or running work first).
- HUD diagnostics report: a "GPU readers without root" line with the
reason each reader works or fails, "none" instead of "null", and the
per-core CPU % source named honestly.
Tested: iamTooShaar's OnePlus 13 shows GPU clock and load with the PR
test build (06be2c5). Pocket FIT standalone build of the final readers
against the kernel's sysfs numbers: idle 1-2 % vs 3-4 %, GPU job loop
25-29 % vs 28-30 %, clock 1050 MHz vs 1050 MHz. The review fixes after
the test build were not tested on the OnePlus 13.
* feat(dual-screen): step aside on the Retroid Pocket Duo's bottom screen while another window holds it
When the Duo's double-Home performance panel or another app (Chrome, YouTube) is on the bottom screen, DroidDeck switches to single-screen mode: the launcher's bottom panel and the in-game bottom controls close and the top screen takes its single-screen layout. When the bottom screen is back on its home screen, dual-screen mode returns, with the in-game controls reopening in the mode they had.
The DroidDeck shortcuts accessibility service watches the bottom screen's windows on the Duo only. An app opened under DroidDeck's panel is caught by its window opening on that display; the Retroid panel and any app on top are read from the window list.
* fix(dual-screen): log the bottom screen's holders only when they change
* fix(dual-screen): the topmost window other than DroidDeck's decides who holds the bottom screen
Apps opened under DroidDeck's bottom panel are still listed beneath it, so DroidDeck's own windows are skipped and the next full-size window decides. Fallback home screens such as Settings' boot-time one no longer count as a home screen.
* fix(dual-screen): catch apps opened under the in-game bottom controls
The in-game bottom controls hide the windows beneath them from the window list, so an app opening a window on the bottom screen while only DroidDeck is listed now holds the screen until the list can see it.
* fix(dual-screen): keyboards never hold the bottom screen; watch the built-in panel only
Keyboard windows opening on the bottom screen no longer count as an app arriving, so typing with the in-game bottom keyboard keeps the controls up. Only the built-in bottom panel is watched, so an external display can't close the controls. An arrival recorded while a check runs is kept for the next check, and stopping the watcher clears the held state after any running check.
* feat(dual-screen): step aside on the AYN Thor's bottom screen too
The bottom-screen watch now runs on every handheld in DualScreen's known dual-screen models, the Retroid Pocket Duo and the AYN Thor, including model names that extend them such as AYN Thor Max.
* fix(dual-screen): only the current bottom-screen watch publishes; Thor variants match when the display type is hidden
* fix: session drawer top row scrolls and Stop stays an icon-only button
* fix: docked drawer drops the second-screen page; tapping the active mode returns to none
* fix: docked drawer drops the bottom-screen mode icons; the Mode page keeps them
* fix: restore the second-screen drawer page when docked
* feat: dual screen launcher and dual screen sessions are separate Automatic/On/Off settings
* fix: session drawer top row keeps its right alignment
---------
Co-authored-by: Kurt Himebauch <[email protected]>
* fix: remove the Bottom screen HUD descriptions from the performance HUD settings
* fix: remove the telemetry blurb, speed units row and tap hint from the bottom stats page
* fix: keep steamClientLabel
DOOM (2016) asks for exactly two swapchain images and stops with "FATAL
ERROR: getSwapchainImagesKHR failed with error (VK_SUCCESS)" when it gets
more. Every game launch gets GAMESCOPE_WSI_MIN_IMAGE_COUNT=8 and
GAMESCOPE_WSI_ENSURE_MIN_IMAGE_COUNT=1, so gamescope's WSI layer gave it 8
images (3 with only the count removed) and the game crashed on start.
When DOOMx64vk.exe sits beside the launched program (DOOMx64.exe for a GOG
install, or DOOMx64vk.exe), both variables are removed and
vk_x11_strict_image_count=true is set, the option Mesa's driconf gives the
game. Other games keep the defaults. A per-game value in the environment
editor still wins.
Tested on an AYANEO Pocket FIT with DroidDeck 0.6.9 and the same values set
per game: the swapchain gets 2 images and the game reaches its main menu at
60 fps; before, it crashed on every launch.
- In-game stats shows a bottom-screen HUD while no game sends dashboard data: 11 styles, tap to change; it reads the game's frames and the device itself, so it works with the top-screen HUD off.
- Line strip: one line like the top HUD's Strip (API + FPS, frame-time graph, 1 / 0.1 / 0.01 % lows, GPU, CPU with temperature, VRAM, RAM, clock); default strip above a game's dashboard; numbers that do not fit are left out by importance.
- The same strip under the tabs on every other bottom page during a session.
- Bottom screen HUD settings, shown even with the top HUD off: one switch for the whole bottom HUD, strip on every tab, and which numbers the Line strip shows.
- The bottom HUD redraws about 10 times a second (was about 3) and reads device stats twice a second.
- GPU load, clock and VRAM readers also run while the bottom HUD shows them (BottomHudPresence).
- Night style stays dim instead of flickering with the FPS.
- Review fixes: slow-frame ring 2048, tab strip redraw without gamescope frames, Proton name per game, Graphs' duration from the kept readings, Fahrenheit in Tiles, Rings and Cores.
Auto now prefers the newest DD-Turnip release on the Adreno 7xx, 710/720/722 included, and falls back to the previous Banners-Turnip pick when no DD-Turnip pair is published or the build fails to start a session on the device. The 8 Gen 2 on One UI keeps its patched Banners build and the 6xx keep Banners-Turnip.
* feat: manage several Steam libraries beside internal and the card
* fix: serialize Steam library registry edits, refuse folders inside a Games root, refresh games after changes
* fix: rename refreshes games, added x86-64 libraries join redist seeding and direct mapping
* 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
* feat: KDE desktop as a bottom-screen mode beside the top-screen session
* test: bottom-desktop launch test sets DISPLAY instead of inheriting it
* fix: drop the bottom desktop's launch when the session stops while preparing it
* fix: bottom desktop restarts on a fresh surface, trackpad/touch toggle, loading throbber, DroidDeck's display driver
* fix: bottom desktop pauses with the session and opens the keyboard for a tapped text field
* fix: no bottom desktop guest starts or keeps running unfrozen while the session is paused
* fix: bottom desktop treats a freeze in progress as paused
* fix: only the session that began a freeze clears its pausing flag
* fix: bottom desktop launch is stored and cleared only for its own session generation
* fix: a stopped session tears down the bottom desktop it started even after a newer launch is prepared
* fix: a new session's bottom desktop replaces one an earlier session left running
* fix: release the bottom desktop's keyboard watcher when its compositor dies
* fix: bottom desktop starts its guest only for the launch its compositor was attached for
* fix: bottom desktop ignores compositor replies to an earlier session's attach
From Android 14 the Material theme leaves the navigation bar
transparent, so with launcher fullscreen off a white system bar showed
under the dark launcher. AppTheme now sets the navigation bar to black,
matching the status bar.
On some Adreno devices (the Retroid Pocket Duo) SELinux hides kgsl's sysfs
nodes from apps, so the HUD showed no GPU % and no GPU clock. Devices whose
nodes are readable keep using them; nothing changes there or on GPUs
without kgsl.
Where the nodes are blocked:
- GPU %: a native reader maps kgsl's memstore read-only (any kgsl client
may) and samples the ringbuffer start/end timestamps once a millisecond
while a HUD is shown. That covers all GPU work, Android's own
composition included.
- Backup for GPU %: an implicit Vulkan layer in the guest
(tools/gpuload) and the compositor time their own GPU work with
timestamp queries. It runs until the memstore stamps are seen moving,
and for the whole session if they never move.
- GPU clock: a small GPU job of our own reads the always-on and RBBM
cycle counters (libgpuclock).
The HUD diagnostics export lists which source each GPU value came from.
tools/gpuload/build.sh takes the Vulkan headers from build_local.sh's NDK
first and keeps NDK paths with spaces intact.
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.
Codemasters EGO games (DiRT 3, DiRT Showdown, GRID, F1 ...) pick their thread plan by core count from system/hardware_settings_restrictions.xml, which only has plans for 2, 3, 4, 6 and 8+ cores. When DroidDeck leaves a phone's slow cores out Windows can see 5 (Retroid Pocket Duo), and DiRT 3 then puts every worker on one core and stops on a loading screen; Showdown closes at start.
When a game starts with WINE_CPU_TOPOLOGY set and its restrictions have no cpu rule for that count, droiddeck-game-env adds one copied from the nearest smaller plan (5 -> the game's 4-core plan), only if that plan's worker map file exists. The file as it was is kept as .droiddeck-original; later launches change nothing. Only ASCII digits count as a core count, and the edited file is staged under a unique name.
Tested on a Retroid Pocket Duo: with DiRT 3's shipped file, the plan is added at launch and the game loads to its menus and races.
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.
On dual-screen handhelds the bottom-screen Dashboard only opened by itself in the first Steam session after DroidDeck started. SessionActivity.onCreate checks for a new session twice; the first block calls SessionEvents.begin(), which moves the phase to PREPARING, so the second block (which held the per-session reset of secondScreenAutoOpened / secondScreenProvisional) never ran. The reset now happens in the first block, right after begin().
Tested on a Retroid Pocket Duo: two Steam sessions in one app process, Dashboard on the bottom screen both times.
A Dashboard mode for the second screen of dual-screen handhelds (developed on a Retroid Pocket Duo), the default on dual-screen devices. It opens on Chat at each Steam start and switches to In-game stats when a game starts.
- Downloads: Steam downloads and updates (SD-card libraries too), shader caches and DroidDeck store downloads.
- Notifications: other apps' notifications once access is allowed, kept in memory only.
- Screenshots: a game screenshot without the overlay, and a gallery of DroidDeck's and Steam's screenshots.
- Chat & voice: mic and game volume buttons, and Steam's web chat signed in with the Steam client's own session.
- In-game stats: FPS, CPU, GPU, RAM and battery plus the running game or Steam client type, and a racing dash from Codemasters, Forza and OutGauge games' data output, with War Thunder and Elite Dangerous layouts; speed in km/h or mph.
- Send dashboard data: turns a Codemasters game's data output on for the dash (per game or Automatic) and keeps the original setting to put back.
- DiRT 3 / DiRT Showdown: with their data output on they stop with error 10038 at race end; a ws2_32.dll stand-in (tools/dirt-ws2) is swapped into the prefix while the game runs, skipped on DroidDeck-Proton 11-11.10 which has the fix built in. Same behaviour as DiRTTelemetryErrorFix by RoccoC, based on Carsten Gottschling's fix.
About "telemetry": here it means a racing game's own live driving data (speed, gear, rpm, pedals), not app analytics. For In-game stats no information leaves the device and nothing is shared: the game sends its data to 127.0.0.1 on the same device, DroidDeck only listens there while the page is open and keeps the numbers in memory just long enough to draw them. DroidDeck sends no data about the user or their use of the app; logs leave the device only when the user taps Share logs.
The bottom-screen panel handed focus back to the main screen only when it opened or was touched within the last second. When the panel became the active screen later, the paused launcher kept no focused window, so controller input waited until Android reported the app as not responding. The panel now hands focus back whenever it becomes the active screen or gains window focus, unless bottom-screen typing is open or an app the launcher opened is in front.
* docs: DroidDeck 0.6.9 release notes
* docs: explain the banking app fix in the 0.6.9 notes
* docs: banking app line in the 0.6.9 summary
* docs: dual-screen line in the 0.6.9 summary
* docs: move the dual-screen line into the 0.6.9 intro
* Update release notes for version 0.6.9
Reformatted the release notes for clarity and improved readability.
* docs: add #1888 and #1896 to the 0.6.9 notes
* docs: add #1903 to the 0.6.9 notes
* fix: dual-screen launcher keeps the update prompt on screen and stays out of Recents as home; Quest panel opens landscape
Outside screens the launcher opens (the update prompt, Settings, pickers) now hold the top screen when the bottom screen is touched, so a bottom-screen tap no longer buries the Android install prompt and leaves updates stuck waiting for confirmation (DD-368).
When DroidDeck is the home app its launcher task is excluded from Recents, so it can no longer be swiped closed; a running game session is still listed (DD-377).
The launcher declares a 1024x640 dp default panel size so Meta Quest opens it landscape again after the orientation manifest change in #1606 (DD-360).
* fix: Recents exclusion follows the top DroidDeck activity and only undoes its own change
The launcher task is hidden from Recents only while FrontEndActivity is on top, DroidDeck is home and no session runs; the file manager and a running session show it again, the check repeats when the launcher pauses, and tasks hidden by someone else are left alone.
* fix: Recents ownership survives process death and never claims a task someone else excluded
* Run x86_64 Flatpak apps through FEX
* proot: run an execveat with an absolute path like execve
* Find x86_64 Flatpak installs and resolve sandbox paths like the sandbox does
* 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
* Translate the strings added since the last translation pass
* Fix translation wording from review
Use the Android settings labels for the pairing guide in bg, cs, da, de, fi, ru and zh-CN, correct the frame limit divisibility wording in de and nl, describe owned games without implying purchase in hi and ru, fix the VRAM label in es-419, describe GOG replacement files correctly in tr and name the Guide button in zh-CN.
* fix: library menus open toward the side with room, so in portrait they are no longer cut off
In portrait the Filter, Sort, Display and Collections buttons sit at the left of the screen, but their menus still grew to the left and were squeezed into the space before the button, cutting the labels. A menu now opens the other way when its own side is narrower than the menu and the other side has more room.
* fix: step-out menus keep their outline with the content when a resize flips the side
* fix: step-out menus still take keyboard focus when the layout shifts while opening
* fix: a folder of games still splits into its games when it also holds a folder with no game, as with Rance 01 in a VNs folder
A visual-novel folder on game storage held a steamapps folder with no exe in it. The group check asked every subfolder to hold a game, so the whole folder stayed one game and a pick in Rance 01 switched that game instead of adding its own. Folders without a game and Steam library folders no longer count, and old games folders being moved to entries split such a folder the same way.
* Check a folder's own entry, removal and store ownership before splitting it, and keep folders with redist or extras as one game
An old Games folder that was removed keeps its games removed after the split, a folder already added stays as it is, and a store game's folder is never split.
* fix: a split games folder keeps the game it launched and that game's settings
When an old games folder entry is split into one entry per game, the
folder holding the selected exe is kept even if ranking left it out (or
the parent stays whole when the exe sits directly in it), and the
parent's name, app ID, launch options and other settings carry over to
that game without overwriting settings it already has.
* fix: a split games folder never migrates steamapps and keeps the launched exe
A saved exe under a steamapps or hidden folder keeps the old entry whole
instead of adding that folder as a game, and the game that inherits the
old entry's settings keeps the exe the old entry launched when nothing
was picked.
* fix: banking apps open again with DroidDeck installed; the shortcuts accessibility service is listed only once the user sets it up
0.6.7 added HomeButtonService, a key-filtering accessibility service, enabled in the manifest. Android lists it as an installed accessibility service on every phone, and banking apps such as UBS refuse to start while one from outside the Play Store is installed. The service is now disabled by default, turned on when the user opens the Shortcuts setup, turned back off if they leave it unset, and kept on for anyone who already uses it.
* Keep the shortcuts service declared and turn it off at start unless the user chose it, so an update never drops their choice
A failed read of the enabled-services setting now changes nothing.
* Hide the unchosen shortcuts service right after an update, before DroidDeck is opened
A package-replaced receiver in its own light process runs the same listing sync as startup. The manifest default stays enabled because Android drops an unavailable service from the enabled-services setting, which would erase the choice of users updating from 0.6.7.
* Hide the shortcuts service as soon as the user turns it off in Accessibility settings
* fix(audio): DirectAudio no longer crackles where Android refuses low-latency output
On devices where Android won't give the game a low-latency output (AYN Thor,
Android 13), DirectAudio kept Wine's 10 ms period and 12 ms buffer. An
emulated game audio thread can't keep that pace there, so audio crackled.
The session now checks for low-latency output and, only when it is refused,
asks DirectAudio for a 20 ms period and a 60 ms buffer.
DirectAudio's settings are now AUDIO_DIRECT_* everywhere in DroidDeck and are
handed to the driver under the names it reads when DirectAudio is set up.
AUDIO_DIRECT_MS and AUDIO_DIRECT_PERIOD_MS can be set per game.
* fix(audio): game profile DirectAudio settings and the microphone-off setting reach the driver
* fix(dual-screen): keep the bottom-screen controls up while a game loads
The session opened its bottom-screen trackpad only at the game's first frame. The launcher's bottom menu closes when a game starts, so on dual-screen handhelds the bottom screen showed whatever Android app was behind it for the whole load, which is long for Xbox games while Xodus prepares them. The controls now open when the session starts. Devices without a built-in second screen, and players who turned the in-game controls off, are unchanged.
* fix(dual-screen): pick the bottom-screen panel again once the session's controller is known
Opening the bottom-screen controls at session start can run before SessionService has prepared the Steam Deck pad, while SessionState.deckPad is still false, so the trackpad could be chosen where the Deck controls belong. That early choice is now provisional: at the game's first frame it switches to the panel the remembered choice and the real deckPad give, unless the player has already picked one.
* fix(dual-screen): keep the loading-time panel choice through PiP, background and new sessions
* fix(dual-screen): retry a terminal panel opened before it was ready and keep a manual close made during loading
* fix(desktop): video wallpapers no longer race ahead after DroidDeck returns from the background
* test: give the vrclient loader harness the clock syscall hook
fix: the bottom-screen panel no longer pulls DroidDeck back over an app opened on the main screen from outside DroidDeck
On dual-screen handhelds the panel took focus and moved DroidDeck's task forward whenever another app opened over it on the main screen, unless DroidDeck had opened that app itself. A frontend such as iisu, or Settings opened from a shortcut, was covered again within a fraction of a second. The panel now stops reclaiming once the launcher is paused, and starts again when it resumes.
* fix(runtime): ship liblzo2-2 so the x86-64 Steam client can install FEX
FEX's unsquashfs links liblzo2. The Ubuntu runtime did not ship it, so the FEX check failed on every build, the x86-64 client was never set up, and the session went straight back to the home screen. Only phones with the desktop installed had it.
* fix: the x86-64 Steam client starts again after it updates itself under FEX
On its first start the x86-64 client downloads a 500 MB update and, under FEX, ends on an illegal instruction (rc=132) instead of asking for a restart with rc=42, so the session ended there. The client is started again once.
* fix: a session in picture-in-picture draws at 30 fps instead of the full refresh
Since auto picture-in-picture became the default (#1727), pressing Home leaves the session running in a small window that takes no input. A game kept drawing at its full rate there: on a REDMAGIC Astra 2 Among Us in picture-in-picture drew 3.31 W, the same as playing it and about 1.05 W more than a suspended session. While in picture-in-picture the compositor now paces the session at 30 fps (or a lower limit already set). Mirroring to glasses keeps the glasses rate, and the display mode request ignores the picture-in-picture limit so the app in front is not slowed.
* fix: keep the no-argument output limit the glasses pacing tests read
* fix: hold scene renders to the output limit and keep frame generation off in picture-in-picture
A frame limit only paced dmabuf buffer releases, so a shared-memory client was still drawn on every vsync with its frame callbacks sent at the panel's rate. The vsync tick now waits until the limit's interval, less half a refresh, has passed since the last scene. Frame generation is disarmed while picture-in-picture is shown and armed again when it ends; frame generation requests are applied in order.
* Give the commit render harness the frame limit hold
* Hold scene renders to a frame limit deadline, also on the fallback timer, and keep frame generation for glasses mirroring in picture in picture
* fix: AYN Odin handhelds are single-screen in automatic dual-screen mode
* Use a second screen only when more than one physical screen is lit, instead of excluding AYN models
* fix: open the session panel when the second screen turns on, and keep the On mode's display choice
* fix: let the session panel open again after its screen goes away and returns
* fix: Automatic dual screen needs a touchscreen for the second panel instead of counting lit displays
* fix: check the second screen again when a touchscreen comes back after the display
* fix: check the second screen again when a touchscreen is added or removed, and on resume
* Keep the second screen panel closed once the session has ended
#1809 held Steam's own UI to an even share of the refresh near 60 fps whenever it had focus. On 120 Hz and faster screens Big Picture then felt locked at 60 while games ran at the full rate, and players reported it as a regression. The compositor now releases Steam UI buffers unpaced again, like a game's. The larger swapchain stays limited to games, so the client UI still uses gamescope's default image count.
* fix(xbox): keep the game in its own folder on the short drive instead of at the drive root
* fix(xbox): SD card Xbox games can read their own package files
Xodus mounts a store library on the selected SD card at /mnt/droiddeck-stores/<card> and links the launch view to it, but game sessions only bound that library as /mnt/droiddeck-sd. The links were dangling inside the game, so MicrosoftGame.Config, appxmanifest.xml and XCurl.dll could not be read and GDK games stopped with "Failed to initialize GDK". Game sessions now bind the same store roots Xodus uses.
* fix(xbox): give the SD Steam library its own store path when another store root shares the card
externalRootsForXodus added the selected Steam library's Games folder with the card's guest path even when another store root on the same card already used it, so both bound to one destination. It now takes a path-derived suffix in that case, the same way externalRoots separates its own roots.
Preserve profile bindings, shared held-input ownership and custom panels alongside upstream physical shortcuts, mouse routing and docked menus. Add integration regressions and record the full 2,918-test JVM run.
fix(runtime): ship libva-drm2 and libva-x11-2 so DroidDeck-Proton's video demuxer loads
DroidDeck-Proton's ffmpeg links libva-drm and libva-x11. The runtime only had libva2, so winedmo.so failed to load and Media Foundation video stopped: Resident Evil Revelations hung on its loading screen with "Failed to create demuxer, status 0xc000007a", and RE engine and Kingdom Hearts cutscenes broke. Installing the desktop hid it because Plasma pulls both in.
* perf(epic): keep the hidden launcher from loading its store and Fab pages, and compress it while games run
The hidden Epic Games Launcher that stays up for sign-in while a game runs pre-warmed its store and Fab pages on a display nobody sees. Under FEX each page used about one core and grew past a gigabyte, crashing and reloading in a loop. The hidden instance now skips the store pre-warm, gets a blank page in place of Fab, and is paged out to zRAM with the Steam client once a game has focus. The visible launcher is unchanged.
* test(zram): keep the scan fixture PIDs above the kernel PID range
Pill: battery level, temperature and watts share one "BAT" row, as on the
other sizes, and each part follows its own switch. Turning Memory off no
longer hides the battery temperature (reported by Keegan), and watts show
without the battery level.
Touch (part of #1752, reported by ZZCSK): a locked HUD lets a touch through
where an on-screen control under it takes it: controller buttons and sticks,
Steam touch controls, screen keys and the controls panel below the game.
Each layer answers takesTouchAt with its own touch checks. Everywhere else
the HUD keeps the touch, so a long press still locks or unlocks it. A new
HUD starts locked. The "Lock in place" hint says so in every language.
Tests: Pill battery row per switch combination; takesTouchAt agrees with a
real touch over a grid for the controller buttons, screen keys and both
controls panel layouts (Deck and gamepad, landscape and portrait); a locked
HUD passes touches to a control under it, keeps the rest, and unlocks on a
long press.
* Use the second screen on dual-screen handhelds
Detect built-in second screens such as the Retroid Pocket Duo and AYN Thor.
The sections move to a menu on the bottom screen, L2 and R2 switch sections,
and text fields type on the bottom screen so the top keeps the full view.
Sessions open the bottom-screen trackpad and keyboard when they start.
Setup gains a Dual screen choice (Automatic, On, Off) with toggles for the
bottom menu and the in-game controls. Closing the bottom menu no longer ends
a running session.
* Type into Steam from the bottom screen
While the bottom-screen trackpad and keyboard controls are open in a Steam
session, Steam's own on-screen keyboard no longer covers the top screen.
The agent keeps Steam's text target and asks the app instead, which opens
the Android keyboard on the bottom screen and hands focus back to the game
when it closes. Modal and VR keyboards, and sessions without the bottom
controls, keep Steam's keyboard. The Show keyboard button now follows the
keyboard when it is closed another way.
* Open the bottom controls after the first frame and tidy dual-screen translations
* Move the in-game menu to the bottom screen on dual-screen handhelds
Fix controller input and focus on the second screen: keys and sticks reach the game after touching the bottom controls, closing the bottom controls returns focus to the game, B closes bottom typing, and the typing panel keeps a focusable window until focus has left it.
* Let B close the open layer of the session menu first and focus the selected bottom-screen mode
* Let the Retroid Pocket Duo Home double press open its quick menu during sessions
* Open the keyboard on the bottom screen for Steam text fields and hide it on Enter or when the field loses focus
* Keep controller focus on the top screen when a section is picked on the bottom screen
* Keep LB and RB on the store tabs and let Back return from a store to its tab
* Show the Adreno 722 as supported on the Retroid Pocket Duo
* Label the Steam integration suggestion's close button Close and cover that closing it is permanent
* Close the docked menu with its presentation, keep a focus picked during the stop delay, and ignore panel picks on process-settings pages
* 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
Apply planned compatibility changes in one traversal instead of copying and searching the whole token list for each game. Wire retired-Proton replacement, default/tool changes and x86-64 repointing through the batch writer.
Share entry and name updates to preserve metadata, first-match semantics and reverse insertion order. Retain the single-item path and sequential alias/non-string behavior without changing the planner, tool policy, file locks or atomic writes.
Add reference, work-budget and integration regressions, and document the host-only bulk configuration benefit.
* Warn when a drive used as a game library is linked at USB 2.0 speed
Reads the drive's USB link speed from sysfs through its mount, and shows a
dismissible warning with the USB generation, link speed and a tip for a
faster cable or drive. The warning returns after the drive is plugged in
again unless Don't show this again is switched on.
* Keep slow USB dismissals per plug-in across restarts and recheck after a dismiss
Dismissals are stored with the boot count and the USB bus and device number,
so they survive a process restart, end when that drive is plugged in again
even while the launcher is stopped, and never clear other drives. A dismiss
looks for the next slow drive, stale scans are dropped, and an unknown port
speed is no longer treated as a USB 2.0 port.
* Drop slow USB scans that a newer library or mount check has superseded
* Invalidate pending slow USB scans as soon as a media broadcast arrives
* Revalidate an open slow USB warning on every check and match raw mount library paths
Every check now rescans, so a warning that is already showing is replaced or
cleared when its drive is removed or the library changes. Library paths saved
under /mnt/media_rw are treated as their /storage volume.
* Keep the Don't show this again choice when a rescan closes the slow USB warning
* Save the slow USB opt-out before the launcher activity is torn down
* feat: art-first store cards that share the library display settings
* fix: keep price and installed state on store cards when size is hidden
* fix: darken the compact store badge for contrast on light artwork
* feat: art-first library cards with a display menu for size, title and storage
* build: use native arm64 docker on Apple Silicon and add a fast deploy loop
* fix: address review feedback on library cards and local build scripts
The session HUD is FusionHUD (Bannerlator's HUD) with every reading taken from
the same source, and worked out the same way, as the MangoHud build that runs
Steam's performance overlay in a DroidDeck session.
- gamescope patch 0125 (gamescope-3.16.29-p19) shares the frame times that
mangoapp reads (one per signalled commit of connector 0) in a ring named by
DROIDDECK_HUD_FRAMES; the app reads it once per display frame and counts the
compositor's frames when gamescope sends none.
- MangoHud's rules: FPS over periods of at least 500 ms, frame time = 1000/FPS,
200-frame graph on a fixed 0-50 ms scale; CPU, GPU, temperatures, RAM, swap,
VRAM and battery from MangoHud's sources. Zones 22/26/28 are typed as GPU and
DDR sensors, so Steam's overlay CPU temperature no longer includes them.
- Five sizes in a panel shaped like the DroidDeck mark, with a centred header,
one left edge, neon outline and FPS; Minimal is the default size. Settings on
a new "Performance HUD" drawer page, with Bannerlator's options.
- The HUD reads sensors and polls frames only while it is drawn (not while
hidden, in picture-in-picture or behind another app), keeps its place after
picture-in-picture, and the diagnostics export has its own reader.
Device-tested on the AYANEO Pocket FIT: same readings as Steam's overlay side
by side in Dead Cells; the HUD updates in a game, hides in picture-in-picture
and comes back in place, no crashes.
Some Microsoft accounts get a delegation-compact (d=) ticket, which the
pinned Xbox backend (Nexus 0.2.8) rejected: the library stayed empty
(store-ticket-format) and downloads failed. This moves the backend to
Nexus 0.2.12, which has the upstream store-ticket fix, rebases the
DroidDeck credentials, launch-view and cloud-token patches, and strips
the d= prefix from compact JWE tickets so downloads work again.
Tested by seven users in the Discord Xbox Store Issues thread on the
pr-1729 build at d55e05a0.
* Update Xbox backend to 0.2.12
* Fix cloud offering fallback and add regression tests
* Fix Xbox authentication with compact JWE tickets
* ci: run unit tests in four forks
* fix(ui): keep the ring veil count per test so tests do not share snapshot state
* ci: keep two unit test forks, four were slower
* fix: stop stale Android readbacks snapping Steam's brightness slider back during a held stick
* fix: let deploy_fast sign with the release env on macOS and Linux
* fix: honor DROIDDECK_SIGNING_ENV first and cover the env signer in tests
* fix: honor DROIDDECK_SIGNING_ENV first and cover the env signer in tests
* fix: retry held brightness readbacks, use a monotonic hold clock and resolve the signing env path
* fix: resolve the build-tools path before signing changes directory
* fix: pick the newest build-tools numerically and keep the env file from replacing them
* fix: only pick build-tools that have aapt, zipalign and apksigner
* feat(setup): guide build number and the child-process toggle when developer options are off
* fix(setup): only guide the child-process toggle on devices that have it
Integrate upstream 213c2da5, keeping Home/Guide player-slot handling compatible with remapped controls and disabled routing. Preserve the input-settings overlay gate for Home and Steam volume keys.
Validation: 2,707 JVM tests across 389 suites pass. Device validation and latency measurements were not repeated.
Integrate screen keys, portrait controls, and Home/Back shortcuts with routed input. Preserve remapped axes, neutral gating, and explicit Back bindings; discard stale queued chord reports when routing changes.
Validation: 2,632 debug JVM tests across 385 suites pass with Java 17 and skipRust. Device testing and latency measurements were not repeated for this merge.
On a 2.9 GB Mali-G76 MC4 phone the GPU-drawn Steam UI took about 400 MB,
and Android's low-memory killer ended DroidDeck itself while The Binding
of Isaac loaded; with the UI in software Isaac reached its title screen.
Below 4 GB of RAM, bridge sessions now default BL_VKBRIDGE_CEF_GPU to 0;
a value already set is kept. The software UI takes about a core while it
animates and idles while a game has focus.
Through the Mali bridge the Steam UI flickered: frames alternated with
and without its footer while gamescope handed its layers to the phone's
compositor as separate planes. Turning gamescope's overlay planes off
nearly stopped it (2 flips), and a composited image has none (Mali-G76
MC4 r26p0 at rest: 145 footer flips in 6 s, 0 composited). Where in the
plane path it goes wrong is not known. Steam sessions on the bridge set
gamescope_composite_force=1 unless it is already set.
v0.5.4 backs the exported memory with AHardwareBuffers on phones without a
dma-buf heap (older Mali such as the G68, G77 and G78) and fixes green and
red textures on the Dimensity 9000 series.
A device with /dev/mali0 and no KGSL starts with the bridge on, so it draws
without a trip to Setup. An Adreno keeps Turnip and its automatic driver
route whatever the setting says, other GPUs keep the switch off until it is
turned on, and a choice made in Setup always wins. The GPU checks are read
once per process.
Setup has a "DXVK" row under the bridge's safe mode: automatic (the default, as before) or
Proton's own (SessionPrefs.bridgeProtonDxvk). With Proton's own, nothing is chosen and an earlier
automatic choice goes back to the Proton's copy.
Automatic choices also go back when the session is not a bridge session (the bridge turned off),
even when nothing is known of the Vulkan; a queued automatic swap is dropped with them, the
player's own queued swaps stay. Turning the bridge off, or choosing Proton's own, does it right
away in the background instead of at the next session. A failed detection in a bridge session
still keeps the last choice.
When nothing could be read (no apps-vulkan file and VulkanInfo failing), every automatic choice was
undone: a 1.1 phone went back to a DXVK that cannot start, and the next session flipped it again.
Now an automatic choice is undone only when the Vulkan is known and wants none.
Each Proton's swap or undo is on its own: a failure (a missing saved copy, an I/O error) is said in
the result and the remaining Protons still get theirs. When the Proton is the same but its saved copy
is gone, the choice stays and is said, instead of being forgotten with DXVK 1.10.3 still installed.
apps-vulkan: the version counts only with promotion=on; otherwise, and in files from servers before
the field (which wrote a promoted version even where it was not trusted), driver_api does. A file
whose driver_api is not the driver's own version (a driver update, a server rolled back) is left out.
Proton's DXVK 3 refuses a device below Vulkan 1.3, without shaderInt64 or
with less than 256 bytes of push constants, so on a Mali with a 1.1 driver
(OnePlus Ace 2V, Mali-G710, r32p1) every D3D9-11 game exits. On a bridge
session the forced-package step now picks the newest DXVK that starts on
what games see: 1.10.3 below Vulkan 1.3, 2.6.2 on 1.3 without shaderInt64
or 256 bytes of push constants, Proton's own otherwise.
What games see comes from <bridge cache dir>/apps-vulkan (a bridge with
Vulkan promotion writes it after its self-test) or else from the phone's
driver (vkinfo now reports shader_int64 and max_push_constants). These
choices are marked automatic: a later session with another answer swaps or
restores them, and the player's own choice in Components is never touched.
With BL_VKBRIDGE_CEF_ANGLE=vulkan the CEF GPU process stopped crashing but
Chromium still composited in software (gpu_compositing: disabled_software,
Steam "Disabling GPU acceleration: Disabled/RuntimeDetect", VizCompositorThread
at a full core). The GPU report shows why: "eglCreateWindowSurface failed with
error EGL_BAD_CONFIG". ANGLE's Vulkan backend on X11 gives every EGL config the
root window's visual; Chromium's X11 surface asks for a config whose
EGL_NATIVE_VISUAL_ID is the ARGB visual whenever a compositing manager owns
_NET_WM_CM_S0 (gamescope does), finds none and passes a null config.
bl_webhelper_layout() now adds +Vulkan +VulkanFromANGLE +DefaultANGLEVulkan
for ANGLE on Vulkan, so Skia composites through an xcb swapchain on ANGLE's
Vulkan device and no EGL window surface is needed. BL_VKBRIDGE_CEF_VULKAN=0
leaves it off; unset BL_VKBRIDGE_CEF_ANGLE still means Steam's stock layout.
Steam drops -cef-use-angle: on the bridge the CEF GPU process reports
ANGLE_OPENGL (ANGLE on Zink) with -cef-use-angle=vulkan on the command line.
An explicit BL_VKBRIDGE_CEF_ANGLE now also becomes --use-gl=angle
--use-angle=<backend> on steamwebhelper's own argv (stock process layout kept);
unset, nothing changes.
FrontEndActivity read vkBridge and vkBridgeSafe only when the Performance page
opened, so after an app restart (every APK update) Setup showed
"Mali bridge: off" while sessions used the bridge. refresh() now reads them.
VkBridgeComponent logs how long the server took to listen.
The bridge check tested the server's socket once. It now polls every 50 ms
(BL_VKBRIDGE_WAIT_MS, default 10000) on the first pass, logs how long the
socket took, stops waiting when the session is told to stop, and names what
is missing (icd, library, socket) when it falls back.
A second row under the GPU check, shown only when the bridge is on: "Safe mode" keeps the
bridge's proven workarounds wherever its startup probe has not verified this GPU and driver;
off is performance mode, native paths wherever the probe passed. It reaches the server as
<cache dir>/mode ("safe" or "performance", written atomically before the server starts or is
reused, so the in-app server's JNI start stays as it is) and the guest as VKBRIDGE_MODE, which
/root/.vkbridge-env can still override. A server without modes ignores the file.
Android empties an app's cacheDir when storage runs low; it took the bridge's pipeline
caches, the probe profile, stuck-pipelines and the env file from under a running server.
The directory is now files/vkbridge-pipelines (--cache-dir and the in-app start alike), and
an existing cache/vkbridge-pipelines is moved there once. Nothing trims filesDir, so the
server's own limits bound it (64 MB per pipeline cache file, a total limit in newer servers).
Steam's web helper now runs its GPU work inside the browser process
(--in-process-gpu --no-zygote, #566). On the Mali bridge that browser stops
answering after its first window, Steam restarts it every 10 s and then quits
before the login page. Bridge sessions keep the stock process layout
(BL_WEBHELPER_ARGS=stock); BL_VKBRIDGE_WEBHELPER_ARGS overrides it for tests.
Adreno sessions are unchanged.
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.
A non-QTI gralloc handle (Arm/MediaTek gralloc on Mali) has no layout
the compositor can read, so the pool import guessed a linear dma-buf and
failed. On drivers other than Qualcomm's the compositor device enables
VK_ANDROID_external_memory_android_hardware_buffer and
VK_EXT_queue_family_foreign and imports such buffers as AHardwareBuffers;
the linear dma-buf stays as the last resort. Adreno/Turnip keeps the
same device and the dma-buf path.
The components job checks out external/MaliBridge and builds the guest
ICD, its manifest and the self-test as the vkbridge recipe; a submodule
counts in a recipe's identity by its pinned commit.
The server is built with the NDK into libvkbridge_server.so (a program
the app execs from its native library directory); the Linux ICD, its
manifest and the self-test go into the runtime assets with the other
glibc preloads, locally and in CI.
MaliBridge (https://github.com/ASEbrahim/MaliBridge) is a Vulkan
command bridge that lets the Linux side draw on a non-Adreno GPU (Mali)
through the phone's own Vulkan driver. Pinned at v0.5.3.1.
Keep Select-as-Steam in the existing one-time profile migration and remove the obsolete bridge flag. Raw SELECT state stays stable across router attachment changes, while explicit native, disabled and local profile bindings remain authoritative.
Let the panel close its shared DeckControls source. Detached sticks, D-pads and face buttons release only their own controls, preserving other trackpad contacts and clicks.
Add seven regressions. All 2,246 JVM tests pass; the debug APK build, session assets and runtime inputs validate. Thor validation remains pending.
Keep unused pad rings closed during profile and enable changes. Route Steam menu and VR stick-switch pulses independently of primary bindings, preserving physical holds and cancelling stale releases.
Seed replacement routes with retained source state and require held controls to return to neutral. Add nine regressions and document validation: 2,239 JVM tests and the debug APK build pass; device validation remains pending.
--notes "KDE Plasma desktop package for DroidDeck's Linux runtime ($GITHUB_REPOSITORY tools/desktop-kde, $GITHUB_SHA). size $(stat -c %s out/desktop-kde.tar.zst), sha256 $(cat out/desktop-kde.sha256). desktop-kde.packages.txt lists the signature-checked Arch Linux ARM packages it was built from." \
--notes "Ubuntu 26.04 LTS arm64 runtime for DroidDeck ($GITHUB_REPOSITORY tools/ubuntu, $GITHUB_SHA): size $(stat -c %s "out/$TAG.tar.zst"), sha256 $(cat "out/$TAG.sha256"). $TAG-desktop.tar.zst is the Kubuntu Plasma desktop built on top of it, holding only what the runtime does not already have: size $(stat -c %s "out/$TAG-desktop.tar.zst"), sha256 $(cat "out/$TAG-desktop.sha256"). The .packages.txt files list every installed package and version." \
# Partial clone: a merged blob that only exists in a fork's history can't be lazily fetched (GitHub: "not our ref"); merge-tree writes it locally first.
git merge-tree --write-tree HEAD origin/main >/dev/null || true
- name:Note when the proot pack pin is behind this build
run:|
@@ -508,6 +607,21 @@ jobs:
echo "::warning::built proot pack $sha does not match tools/packs/proot/release.env ($PROOT_PACK_SHA256). This build publishes the pack it just built. Source builds keep downloading the pinned pack until release.env is updated."
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.
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!
<p align="center"><a href="https://discord.gg/JRGAvawjsm"><img src="https://img.shields.io/badge/Discord-Join%20the%20community-5865F2?logo=discord&logoColor=white" alt="Join the DroidDeck Discord"></a></p>
<p align="center"><b>100% open source. No ads. No telemetry. No proprietary components. No source withheld.</b></p>
<p align="center">
<a href="https://www.star-history.com/droid-deck/droiddeck"><img src="https://api.star-history.com/badge?repo=Droid-Deck/DroidDeck&type=rank&theme=dark" alt="Star History Rank" /></a>
<p align="center"><img src="docs/releases/media/0.2.0/launch-into-steam.gif" width="80%" alt="Tapping DroidDeck on the Android home screen and landing in Steam Big Picture"></p>
<p align="center"><img src="docs/media/steam-to-desktop.gif" width="100%" alt="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"></p>
> Note: DroidDeck does not have a stand-alone website. Do not click on any download links from websites claiming to be the DroidDeck team.
> DroidDeck has no website. Only download it from this repository's [Releases](https://github.com/Droid-Deck/DroidDeck/releases).
## Requirements and install
<p align="center">
<img src="docs/releases/media/0.6.7/library.gif" width="49%" alt="The Library switching source chips, changing the card size and opening the Filter menu">
<img src="docs/releases/media/0.3.0/in-game.gif" width="49%" alt="A Windows game running through Proton with the on-screen controls">
<img src="docs/releases/media/0.4.0/multi-controller.gif" width="49%" alt="Two controllers playing split screen on one handheld">
<img src="docs/releases/media/0.6.7/ubuntu-desktop-discover.jpg" width="49%" alt="The Kubuntu desktop with Discover open">
</p>
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](https://github.com/Droid-Deck/DroidDeck/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.
## Install
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.
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](https://github.com/Droid-Deck/DroidDeck/releases/download/0.6.9/DroidDeck-0.6.9.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
Smart glasses setup is in the [support matrix](docs/smart-glasses-support.md). Release notes are in [docs/releases](docs/releases).
Use **Setup → Smart glasses** for app-wide virtual-screen settings across DroidDeck menus, launchers, desktop sessions, and games. Supported VITURE, RayNeo, XREAL, and Rokid 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](docs/smart-glasses-support.md) for supported devices, controls, setup, and limitations.
## Community
Join the [DroidDeck Discord](https://discord.gg/JRGAvawjsm) 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.
Help, Preview builds and device reports are on [Discord](https://discord.gg/JRGAvawjsm). For bugs, post in **#bug-reports** with the zip from **Share logs**.
## 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`.
Needs Docker, Java 17, the Android SDK and NDK, Rust via rustup, GitHub CLI, Python 3 and `zstd`.
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.
```bash
tools/build_local.sh
```
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`.
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`.
`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.
On Windows, see `tools/run_windows.ps1` and `tools/prepare_windows_runtime.py`.
## 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.
Compatibility and performance vary by device. Desktop compositing is software rendered, and Firefox sandboxing is reduced under proot.
## 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](https://github.com/phobos665) adaptation of [Legendary](https://github.com/legendary-gl/legendary) and [gogdl](https://github.com/Heroic-Games-Launcher/heroic-gogdl), respectively. Battle.net support is [@kayakyakr](https://github.com/kayakyakr)'s: it runs Blizzard's own Battle.net client, downloaded from Blizzard and not included, in Proton, and shows the key art from each game's [Battle.net Shop](https://shop.battle.net) page; without the Steam client its games see DroidDeck's pad as an Xbox 360 controller through `PROTON_SPOOF_STEAMINPUT_VIDPID` in [Proton](https://github.com/ValveSoftware/wine)'s `dlls/winebus.sys`. Its games install from Stores through Blizzard's own Agent and the local HTTP API it serves, driven the way [@barncastle](https://github.com/barncastle)'s [Battle.Net-Installer](https://github.com/barncastle/Battle.Net-Installer) drives it, with the requests described on [wowdev.wiki](https://wowdev.wiki/Agent); how other launchers find, install and start Blizzard games was read in [@mubaraknumann](https://github.com/mubaraknumann)'s [unifideck](https://github.com/mubaraknumann/unifideck), [@djmittens](https://github.com/djmittens)'s [neo-mittens](https://github.com/djmittens/neo-mittens) `bnetswitch`, [@BCD1210](https://github.com/BCD1210)'s [soju](https://github.com/BCD1210/soju) and [Lutris](https://github.com/lutris/lutris)'s `product.db` reader, without taking their code. The registry entries a GOG game's install script (`goggame-<id>.script`) sets are read the way [Heroic Games Launcher](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher)'s GOG setup (GPL-3.0) reads them. LSFG frame generation is from the work of Camille LaVey and the [Eden](https://eden-emu.dev) emulator project, following [lsfg-vk](https://github.com/PancakeTAS/lsfg-vk), ported to WinNative and DroidDeck by [@maxjivi05](https://github.com/maxjivi05); it needs your own copy of [Lossless Scaling](https://store.steampowered.com/app/993090/) and ships none of its shaders. x86 AppImages, and any whose own runtime cannot unpack them, are unpacked with [uruntime](https://github.com/VHSgunzo/uruntime) by VHSgunzo (MIT), shipped unmodified with its licence, which also starts the arm64 emulator AppImages whose own runtime cannot run its extractor on Android. x86 AppImages packed with [sharun](https://github.com/VHSgunzo/sharun) by VHSgunzo (MIT) start because DroidDeck's own [FEX](https://github.com/FEX-Emu/FEX) build (MIT) puts the platform name under `AT_PLATFORM`, which sharun's [userland-execve](https://crates.io/crates/userland-execve) loader (MIT) requires; FEX filed it under `AT_BASE_PLATFORM`. The Steam client's audio goes through [@The412Banner](https://github.com/The412Banner)'s [DirectAudio](https://github.com/The412Banner/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](https://github.com/WinNative-Emu/WinNative/pull/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](https://github.com/deadlock-api/deadlock-api-ingest). x86 programs that link OpenSSL 3 under FEX use the copy in the [Steam Linux Runtime 4.0](https://gitlab.steamos.cloud/steamrt/steamrt) platform. Hooks written with `WriteProcessMemory` into code [FEX](https://github.com/FEX-Emu/FEX) has translated go through `/proc/<pid>/mem` again when the kernel refuses `process_vm_writev`, as [Wine](https://gitlab.winehq.org/wine/wine)'s `server/ptrace.c` wrote them before Wine 11. x86 Linux games under FEX get the same `/etc/asound.conf` that [pressure-vessel](https://gitlab.steamos.cloud/steamrt/steam-runtime-tools) writes into the Steam Linux Runtime, so ALSA plays through PulseAudio. Games render with [Mesa](https://gitlab.freedesktop.org/mesa/mesa) Turnip's own per-render-pass choice between tile memory and system memory, while the Steam client keeps `TU_DEBUG=sysmem`; [FEX](https://github.com/FEX-Emu/FEX)'s code disk cache was checked in its source and is already on through Proton. The proot fast path picks the guest name of a host file bound under several names in the order [PRoot](https://github.com/proot-me/proot)'s `path/binding.c` (GPL-2.0-or-later) sorts its bindings, so links and `/proc/self/fd` read back as they do through proot. The session's [PipeWire](https://gitlab.freedesktop.org/pipewire/pipewire) (MIT) opens its sockets with the per-socket access settings documented in its `module-access.c`, and [WirePlumber](https://gitlab.freedesktop.org/pipewire/wireplumber) (MIT) keeps those clients at full permissions through its `access.rules`. Wine write watches use the userfaultLine truncated
DroidDeck is [GPL-3.0](LICENSE). 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.
XR glasses support builds on WinNative's VITURE glasses work. VITURE protocol details were checked against [XRLinuxDriver](https://github.com/wheaney/XRLinuxDriver) by [@wheaney](https://github.com/wheaney), including the VITURE protocol header it ships, [ar-drivers-rs](https://github.com/badicsalex/ar-drivers-rs) by [@badicsalex](https://github.com/badicsalex) and the [go-macos/iokit VITURE package](https://pkg.go.dev/github.com/go-macos/iokit/viture); the optional VITURE SDK belongs to VITURE and is not included. RayNeo support adapts [@informalTechCode](https://github.com/informalTechCode)'s [RayNeo-Air-Series-3DoF-Scaffold](https://github.com/informalTechCode/RayNeo-Air-Series-3DoF-Scaffold) (Apache-2.0) and references [@verncat](https://github.com/verncat)'s [RayNeo-Air-3S-Pro-OpenVR](https://github.com/verncat/RayNeo-Air-3S-Pro-OpenVR) (MIT) and [Monado](https://gitlab.freedesktop.org/monado/monado) (BSL-1.0); see [docs/third-party/rayneo](docs/third-party/rayneo/README.md). XREAL 1S support references [XRLinuxDriver](https://github.com/wheaney/XRLinuxDriver) and [xrealOneDeviceKit](https://github.com/wheaney/xrealOneDeviceKit) by @wheaney, [xreal_one_driver](https://github.com/rohitsangwan01/xreal_one_driver) by [@rohitsangwan01](https://github.com/rohitsangwan01) (MIT) and [xr-tools](https://github.com/0xcaff/xr-tools) by [@0xcaff](https://github.com/0xcaff). Rokid support follows the Rokid protocol notes in [ar-drivers-rs](https://github.com/badicsalex/ar-drivers-rs) and @badicsalex's [Void Computing write-ups](https://voidcomputing.hu/blog/worse-better-prettier/), and [Monado](https://gitlab.freedesktop.org/monado/monado)'s Rokid driver. The REDMAGIC glasses-display findings draw on [@mekhontsev](https://github.com/mekhontsev)'s [MagicDesk investigation](https://github.com/mekhontsev/magicdesk/blob/main/docs/xr-resolution-fix.md) and [@Coding-BR](https://github.com/Coding-BR)'s [decompiled Nubia kernel](https://github.com/Coding-BR/android_kernel_nubia_sm8850_qwjujube). VITURE, RayNeo, XREAL, Rokid and REDMAGIC are trademarks of their owners; this project is not affiliated with them.
<details>
<summary><b>Runtime, Wine and Proton</b></summary>
Joy-Con VR controllers port the arm model of Google's [Google VR SDK for Unity](https://github.com/googlevr/gvr-unity-sdk) (Apache-2.0), adapt the hand skeleton of Valve's [OpenVR](https://github.com/ValveSoftware/openvr) `handskeletonsimulation` sample (BSD-3-Clause), which follows Moshi Turner's hand simulation in [Monado](https://gitlab.freedesktop.org/monado/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](https://github.com/torvalds/linux/blob/master/drivers/hid/hid-nintendo.c) driver by Daniel J. Ogorchock, [@dekuNukem](https://github.com/dekuNukem)'s [Nintendo_Switch_Reverse_Engineering](https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering), [SDL](https://github.com/libsdl-org/SDL) and the Android Open Source Project, and checked against [ALVR](https://github.com/alvr-org/ALVR), [OpenComposite](https://gitlab.com/znixian/OpenOVR), [PSMoveSteamVRBridge](https://github.com/HipsterSloth/PSMoveSteamVRBridge), [@F8LEFT](https://github.com/F8LEFT)'s [JoyConVr](https://github.com/F8LEFT/JoyConVr), [@krnelpan1c](https://github.com/krnelpan1c)'s [Daydream-Controller-SteamVR](https://github.com/krnelpan1c/Daydream-Controller-SteamVR), [@yoyobuae](https://github.com/yoyobuae)'s [joyconvr](https://github.com/yoyobuae/joyconvr), [Moonlight](https://github.com/moonlight-stream/moonlight-android) and [Dolphin](https://github.com/dolphin-emu/dolphin); see [docs/third-party/joycon](docs/third-party/joycon/README.md). Nintendo Switch and Joy-Con are trademarks of Nintendo; this project is not affiliated with Nintendo.
- Runtime, shim, input and controller work build on WinNative and Bannerlator by maxjivi05.
- mmap ring buffer controller input by ribbit384 ([WinNative #345](https://github.com/WinNative-Emu/WinNative/pull/345)).
- [FEX](https://github.com/FEX-Emu/FEX) (MIT) runs x86 code. DroidDeck's build puts the platform name under `AT_PLATFORM` so [sharun](https://github.com/VHSgunzo/sharun) AppImages by VHSgunzo (MIT) start through its [userland-execve](https://crates.io/crates/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](https://github.com/VHSgunzo/uruntime) by VHSgunzo (MIT), shipped unmodified with its licence.
- x86 programs linking OpenSSL 3 use the copy in the [Steam Linux Runtime 4.0](https://gitlab.steamos.cloud/steamrt/steamrt). x86 Linux games get the `/etc/asound.conf` that [pressure-vessel](https://gitlab.steamos.cloud/steamrt/steam-runtime-tools) writes.
-`WriteProcessMemory` hooks fall back to `/proc/<pid>/mem` the way [Wine](https://gitlab.winehq.org/wine/wine)'s `server/ptrace.c` did before Wine 11.
- Wine write watches use userfaultfd support in [Proton](https://github.com/ValveSoftware/wine)'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](https://github.com/torvalds/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](https://github.com/torvalds/linux) ntsync semantics in `Documentation/userspace-api/ntsync.rst` (GPL-2.0).
- The proot fast path orders bindings like [PRoot](https://github.com/proot-me/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.
- Zink on PowerVR keeps colour textures in colour with Icenowy Zheng's upstream [Mesa](https://gitlab.freedesktop.org/mesa/mesa) fix d55418dae7 (MIT), carried as a runtime patch.
- Games and AppImage programs such as emulators use [Mesa](https://gitlab.freedesktop.org/mesa/mesa) Turnip's per render pass memory choice. The Steam client and Chromium-based apps keep `TU_DEBUG=sysmem`.
- Black Ops III and Dark Souls Remastered start without [umu-protonfixes](https://github.com/Open-Wine-Components/umu-protonfixes) (BSD-2-Clause), whose `311210.py` and `570940.py` fail under ARM64 Proton.
- [ReSkate](https://github.com/Dingo-Shenanigans/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](https://github.com/deadlock-api/deadlock-api-ingest).
Camera hand tracking runs Google's [MediaPipe](https://github.com/google-ai-edge/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](https://developer.android.com/jetpack/androidx/releases/camera) (Apache-2.0); MediaPipe's usage reports are switched off. The design was checked against [@F8LEFT](https://github.com/F8LEFT)'s [JoyConVr](https://github.com/F8LEFT/JoyConVr) camera app (MIT), and the short Joy-Con movements follow the accelerometer mode of the [Google VR SDK for Unity](https://github.com/googlevr/gvr-unity-sdk)'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](https://github.com/alvr-org/ALVR)'s `hand_gestures.rs` (MIT) and Meta Quest's menu pinch, with distances checked on photos from the [HaGRID](https://github.com/hukenovs/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](docs/third-party/handtracking/README.md).
</details>
The Store installs and runs apps with [Flatpak](https://github.com/flatpak/flatpak) (LGPL-2.1). Its sandbox goes through DroidDeck's own bubblewrap shim, which follows how Flatpak hands bwrap its environment. Chromium and Electron apps from Flathub start their browser processes through [zypak](https://github.com/refi64/zypak) by [@refi64](https://github.com/refi64) (BSD-3-Clause); its source was read to see that it always spawns them through Flatpak's spawn portal, which is why DroidDeck starts them directly without Chromium's sandbox instead.
<details>
<summary><b>Stores</b></summary>
Remote Play from a PC to the arm64 Steam client goes through a stand-in for the V4L2 decoder and the libdrm dumb buffers that Steam's `streaming_client` expects: each frame is decoded by Android's MediaCodec through the app, or by the [FFmpeg](https://ffmpeg.org) (LGPL-2.1-or-later) that ships in Steam's own runtime, into dma-heap buffers that its Vulkan renderer imports. The Qualcomm low-latency decoder settings follow [Moonlight](https://github.com/moonlight-stream/moonlight-android) (GPL-3.0).
- Epic Games and GOG build on [Phobos](https://github.com/phobos665)'s adaptation of [Legendary](https://github.com/legendary-gl/legendary) and [gogdl](https://github.com/Heroic-Games-Launcher/heroic-gogdl). GOG install script registry entries are read the way [Heroic Games Launcher](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher) (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](https://github.com/kayakyakr). It runs Blizzard's own client in Proton (downloaded from Blizzard, not included) and shows key art from the [Battle.net Shop](https://shop.battle.net). Without Steam, its games see the pad as an Xbox 360 controller via `PROTON_SPOOF_STEAMINPUT_VIDPID` in [Proton](https://github.com/ValveSoftware/wine)'s `dlls/winebus.sys`.
- Battle.net installs drive Blizzard's Agent the way [@barncastle](https://github.com/barncastle)'s [Battle.Net-Installer](https://github.com/barncastle/Battle.Net-Installer) does, with requests from [wowdev.wiki](https://wowdev.wiki/Agent). Read without taking code: [@mubaraknumann](https://github.com/mubaraknumann)'s [unifideck](https://github.com/mubaraknumann/unifideck), [@djmittens](https://github.com/djmittens)'s [neo-mittens](https://github.com/djmittens/neo-mittens) `bnetswitch`, [@BCD1210](https://github.com/BCD1210)'s [soju](https://github.com/BCD1210/soju) and [Lutris](https://github.com/lutris/lutris)'s `product.db` reader.
- World of Warcraft addon imports replace an addon's folder from a temporary copy, as [WowUp](https://github.com/WowUp/WowUp) does, and read flavour folders the way [WoWDeck](https://github.com/sigboe/wowdeck) does. No code from either is used.
- The World of Warcraft addon Download window searches [WoWInterface](https://www.wowinterface.com) through its MMOUI API. [CurseForge](https://www.curseforge.com/wow) opens in the user's browser, with no API and no key.
- Xbox games load [@domsvoboda](https://github.com/domsvoboda)'s [xodus-nexus](https://github.com/domsvoboda/xodus-nexus) modules (LGPL-2.1-or-later), including `wintypes.dll`. Minecraft's GameInput setup was checked against [@Wyze3306](https://github.com/Wyze3306)'s [BedrockOnLinux](https://github.com/Wyze3306/BedrockOnLinux) (MIT).
- The Xbox Cloud Gaming player uses the WebRTC client of [@unknownskl](https://github.com/unknownskl)'s [Greenlight](https://github.com/unknownskl/greenlight) (MIT), and its offer tweaks and sharpening follow [@redphx](https://github.com/redphx)'s [Better xCloud](https://github.com/redphx/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](https://www.7-zip.org) 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](https://github.com/flatpak/flatpak) (LGPL-2.1) through DroidDeck's own bubblewrap shim. Chromium and Electron apps start directly, since [zypak](https://github.com/refi64/zypak) by [@refi64](https://github.com/refi64) (BSD-3-Clause) always uses Flatpak's spawn portal.
- Folder sync runs [Syncthing](https://github.com/syncthing/syncthing) (MPL-2.0), built from [@researchxxl](https://github.com/researchxxl)'s [syncthing-android](https://github.com/researchxxl/syncthing-android) fork (MPL-2.0).
Steam's Browse buttons open a GTK 3 file chooser through PyGObject (LGPL-2.1-or-later). It runs under Steam's own `reaper` with Steam's app ID, which [gamescope](https://github.com/ValveSoftware/gamescope) (BSD-2-Clause) reads from the process tree (`GetAppIdFromReaper` in `src/Utils/Process.cpp`) to show it over Steam.
</details>
Black Ops III and Dark Souls Remastered start without [umu-protonfixes](https://github.com/Open-Wine-Components/umu-protonfixes) (BSD-2-Clause), whose fixes for them (`311210.py` sets ALSA audio, `570940.py` installs VC++ 2017) fail under ARM64 Proton.
<details>
<summary><b>Audio, video and HUD</b></summary>
- Steam audio goes through [@The412Banner](https://github.com/The412Banner)'s [DirectAudio](https://github.com/The412Banner/directaudio) PulseAudio sink (LGPL-2.1-or-later), built from `tools/directaudio/sink`.
- The DroidDeck HUD is [@The412Banner](https://github.com/The412Banner)'s [FusionHUD](https://github.com/The412Banner/FusionHUD) (GPL-3.0 with an attribution term). Its readings match Valve's [MangoHud](https://github.com/flightlessmango/MangoHud) build (MIT), with frame times from gamescope patch 0125.
- The HUD's CPU, GPU load, thermal and battery readers come from [@xXJSONDeruloXx](https://github.com/xXJSONDeruloXx)'s work.
- LSFG frame generation comes from Camille LaVey and the [Eden](https://eden-emu.dev) project, following [lsfg-vk](https://github.com/PancakeTAS/lsfg-vk), ported by [@maxjivi05](https://github.com/maxjivi05). It needs your own copy of [Lossless Scaling](https://store.steampowered.com/app/993090/) and ships none of its shaders.
- Remote Play decodes through Android's MediaCodec or the [FFmpeg](https://ffmpeg.org) (LGPL-2.1-or-later) in Steam's runtime. Qualcomm low latency settings follow [Moonlight](https://github.com/moonlight-stream/moonlight-android) (GPL-3.0).
- [PipeWire](https://gitlab.freedesktop.org/pipewire/pipewire) (MIT) sockets use the settings in its `module-access.c`, and [WirePlumber](https://gitlab.freedesktop.org/pipewire/wireplumber) (MIT) keeps those clients at full permissions.
</details>
<details>
<summary><b>Desktop</b></summary>
- The KDE pacman repository snapshots [Arch Linux ARM](https://archlinuxarm.org) and installs with [pacman](https://gitlab.archlinux.org/pacman/pacman) (GPL-2.0-or-later).
- [KWin](https://invent.kde.org/plasma/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](https://code.qt.io/cgit/qt/qtbase.git)'s `qwaylandinputdevice.cpp` and `qguiapplication.cpp`, [Konsole](https://invent.kde.org/utilities/konsole)'s `TerminalDisplay.cpp`, and [Mesa](https://gitlab.freedesktop.org/mesa/mesa)'s `u_threaded_context.c` and `zink_resource.c`.
- The Steam Keyboard window rule follows Valve's `steamdeck-kde-presets` with [Bazzite](https://github.com/ublue-os/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](https://github.com/ValveSoftware/gamescope) (BSD-2-Clause) through `GetAppIdFromReaper`.
- The AYN Odin 2 phantom second display was confirmed by [@SapphireRhodonite](https://github.com/SapphireRhodonite)'s [WatermelonDS 0.0.5](https://github.com/SapphireRhodonite/WatermelonDS/releases/tag/0.0.5) notes and [iiSU #363](https://github.com/iisu-network/iiSU/issues/363), without taking their code.
</details>
<details>
<summary><b>Smart glasses</b></summary>
- Builds on WinNative's VITURE work. Protocol checked against [@wheaney](https://github.com/wheaney)'s [XRLinuxDriver](https://github.com/wheaney/XRLinuxDriver), [@badicsalex](https://github.com/badicsalex)'s [ar-drivers-rs](https://github.com/badicsalex/ar-drivers-rs) and [go-macos/iokit](https://pkg.go.dev/github.com/go-macos/iokit/viture). The optional VITURE SDK is not included.
- The VITURE protocol V2 frames adapt [@elasticjava](https://github.com/elasticjava)'s [viture-v2](https://github.com/elasticjava/viture-v2) (MIT) by Holger Bartnick. See `docs/third-party/viture-v2`.
- RayNeo adapts [@informalTechCode](https://github.com/informalTechCode)'s [RayNeo-Air-Series-3DoF-Scaffold](https://github.com/informalTechCode/RayNeo-Air-Series-3DoF-Scaffold) (Apache-2.0) and references [@verncat](https://github.com/verncat)'s [RayNeo-Air-3S-Pro-OpenVR](https://github.com/verncat/RayNeo-Air-3S-Pro-OpenVR) (MIT) and [Monado](https://gitlab.freedesktop.org/monado/monado) (BSL-1.0). See [docs/third-party/rayneo](docs/third-party/rayneo/README.md).
- XREAL 1S references XRLinuxDriver and [xrealOneDeviceKit](https://github.com/wheaney/xrealOneDeviceKit) by @wheaney, [@rohitsangwan01](https://github.com/rohitsangwan01)'s [xreal_one_driver](https://github.com/rohitsangwan01/xreal_one_driver) (MIT) and [@0xcaff](https://github.com/0xcaff)'s [xr-tools](https://github.com/0xcaff/xr-tools).
- REDMAGIC display findings draw on [@mekhontsev](https://github.com/mekhontsev)'s [MagicDesk investigation](https://github.com/mekhontsev/magicdesk/blob/main/docs/xr-resolution-fix.md) and [@Coding-BR](https://github.com/Coding-BR)'s [decompiled Nubia kernel](https://github.com/Coding-BR/android_kernel_nubia_sm8850_qwjujube).
</details>
<details>
<summary><b>VR, Joy-Con and hand tracking</b></summary>
- Joy-Con VR ports the arm model from Google's [GVR SDK for Unity](https://github.com/googlevr/gvr-unity-sdk) (Apache-2.0) and adapts the hand skeleton from Valve's [OpenVR](https://github.com/ValveSoftware/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](https://github.com/torvalds/linux/blob/master/drivers/hid/hid-nintendo.c) by Daniel J. Ogorchock, [@dekuNukem](https://github.com/dekuNukem)'s [Nintendo_Switch_Reverse_Engineering](https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering), [SDL](https://github.com/libsdl-org/SDL) and AOSP. Checked against [ALVR](https://github.com/alvr-org/ALVR), [OpenComposite](https://gitlab.com/znixian/OpenOVR), [PSMoveSteamVRBridge](https://github.com/HipsterSloth/PSMoveSteamVRBridge), [@F8LEFT](https://github.com/F8LEFT)'s [JoyConVr](https://github.com/F8LEFT/JoyConVr), [@krnelpan1c](https://github.com/krnelpan1c)'s [Daydream-Controller-SteamVR](https://github.com/krnelpan1c/Daydream-Controller-SteamVR), [@yoyobuae](https://github.com/yoyobuae)'s [joyconvr](https://github.com/yoyobuae/joyconvr), [Moonlight](https://github.com/moonlight-stream/moonlight-android) and [Dolphin](https://github.com/dolphin-emu/dolphin). See [docs/third-party/joycon](docs/third-party/joycon/README.md).
- Camera hand tracking runs Google's [MediaPipe](https://github.com/google-ai-edge/mediapipe) Pose Landmarker (Lite), BlazePose GHUM 3D and Hand Landmarker (Apache-2.0) on [CameraX](https://developer.android.com/jetpack/androidx/releases/camera) 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](https://github.com/alvr-org/ALVR)'s `hand_gestures.rs` (MIT) and Meta Quest's menu pinch, tuned on [HaGRID](https://github.com/hukenovs/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](docs/third-party/handtracking/README.md).
// LSFG from Camille LaVey / the Eden Emulator Project, following upstream lsfg-vk (PancakeTAS).
// Ported to WinNative and DroidDeck by @maxjivi05. GPL-3.0-or-later.
// LSFG from Camille LaVey / the Eden Emulator Project, following upstream lsfg-vk (PancakeTAS). Ported to WinNative and DroidDeck by @maxjivi05. GPL-3.0-or-later.
// LSFG from Camille LaVey / the Eden Emulator Project, following upstream lsfg-vk (PancakeTAS).
// Ported to WinNative and DroidDeck by @maxjivi05. GPL-3.0-or-later.
// LSFG from Camille LaVey / the Eden Emulator Project, following upstream lsfg-vk (PancakeTAS). Ported to WinNative and DroidDeck by @maxjivi05. GPL-3.0-or-later.
Loaded 100 of 1571 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.