From a81363d743b341a5dcf0d374df20db07b8deef67 Mon Sep 17 00:00:00 2001 From: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com> Date: Wed, 30 Sep 2026 13:06:12 -0400 Subject: [PATCH] feat: turn Deck mode on for new installs and add a performance overlay toggle (#101) * feat: turn Deck mode on for new installs and add a performance overlay toggle * feat: hide the session fps HUD where Deck mode's performance overlay runs --- .../main/java/com/droiddeck/launcher/App.kt | 2 + .../com/droiddeck/launcher/MainActivity.kt | 4 ++ .../launcher/session/SessionPrefs.kt | 39 +++++++++++++++++-- .../launcher/session/SessionService.kt | 1 + .../launcher/ui/ModeSettingsDialog.kt | 8 ++++ 5 files changed, 50 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/droiddeck/launcher/App.kt b/app/src/main/java/com/droiddeck/launcher/App.kt index 8caeae182..19e13cc88 100644 --- a/app/src/main/java/com/droiddeck/launcher/App.kt +++ b/app/src/main/java/com/droiddeck/launcher/App.kt @@ -12,6 +12,8 @@ class App : Application() { override fun onCreate() { super.onCreate() CrashHandler.install(this) + // Before anything else touches the session prefs: a new install's first read decides it. + com.droiddeck.launcher.session.SessionPrefs.settleDeckModeDefault(this) BwrapSpawner.start(this) } } diff --git a/app/src/main/java/com/droiddeck/launcher/MainActivity.kt b/app/src/main/java/com/droiddeck/launcher/MainActivity.kt index 5026b97c4..ea1bad720 100644 --- a/app/src/main/java/com/droiddeck/launcher/MainActivity.kt +++ b/app/src/main/java/com/droiddeck/launcher/MainActivity.kt @@ -105,6 +105,7 @@ class MainActivity : ComponentActivity() { private var glThread by mutableStateOf(true) private var noGlError by mutableStateOf(true) private var steamDeckMode by mutableStateOf(false) + private var mangoapp by mutableStateOf(true) private var steamController by mutableStateOf(SessionPrefs.CONTROLLER_DECK) private var showProtons by mutableStateOf(false) // Components page: FEX / DXVK / VKD3D-Proton per Proton (ComponentsManager). @@ -899,6 +900,7 @@ class MainActivity : ComponentActivity() { fexPreset = if (mode == SessionService.MODE_STEAM) fexPreset else null, steamChannel = if (mode == SessionService.MODE_STEAM) steamChannel else null, steamDeckMode = mode == SessionService.MODE_STEAM && steamDeckMode, + mangoapp = mangoapp, steamController = if (mode == SessionService.MODE_STEAM) steamController else null, runSteamAtStartup = mode == SessionService.MODE_STEAM && runSteamAtStartup, addedGamesDirs = if (mode == SessionService.MODE_STEAM) addedGamesDirs else null, @@ -956,6 +958,7 @@ class MainActivity : ComponentActivity() { steamDeckMode = on steamChannel = SessionPrefs.steamChannel(this) }, + onMangoapp = { on -> SessionPrefs.setMangoapp(this, on); mangoapp = on }, onSteamController = { id -> SessionPrefs.setSteamController(this, id); steamController = id }, onRunSteamAtStartup = { on -> SessionPrefs.setRunSteamAtStartup(this, on) @@ -1042,6 +1045,7 @@ class MainActivity : ComponentActivity() { fexPreset = SessionPrefs.fexPreset(this) steamChannel = SessionPrefs.steamChannel(this) steamDeckMode = SessionPrefs.steamDeckMode(this) + mangoapp = SessionPrefs.mangoapp(this) steamController = SessionPrefs.steamController(this) runSteamAtStartup = SessionPrefs.runSteamAtStartup(this) addedGamesDirs = SessionPrefs.addedGamesDirs(this) diff --git a/app/src/main/java/com/droiddeck/launcher/session/SessionPrefs.kt b/app/src/main/java/com/droiddeck/launcher/session/SessionPrefs.kt index ae382bd5b..4a0312cad 100644 --- a/app/src/main/java/com/droiddeck/launcher/session/SessionPrefs.kt +++ b/app/src/main/java/com/droiddeck/launcher/session/SessionPrefs.kt @@ -44,12 +44,23 @@ object SessionPrefs { prefs(context).edit().putBoolean("appImagesEnabled", on).apply() } - fun hudEnabled(context: Context): Boolean = prefs(context).getBoolean("hud", true) + /** + * The session's performance HUD (the fps box). A Deck-mode Steam session with the performance + * overlay has Steam's own (mangoapp, from the QAM), so there the HUD is off unless turned on + * in one - a choice kept apart from every other session's, where it stays on by default. + */ + fun hudEnabled(context: Context): Boolean = prefs(context).getBoolean(hudKey(context), !mangoappSession(context)) fun setHudEnabled(context: Context, on: Boolean) { - prefs(context).edit().putBoolean("hud", on).apply() + prefs(context).edit().putBoolean(hudKey(context), on).apply() } + private fun hudKey(context: Context) = if (mangoappSession(context)) "hudDeck" else "hud" + + /** A Steam session that runs Deck mode with its performance overlay (mangoapp). */ + private fun mangoappSession(context: Context) = + SessionState.mode == SessionService.MODE_STEAM && steamDeckMode(context) && mangoapp(context) + /** When enabled, a single Back opens Steam QAM and a double Back opens the session menu. */ fun backActionsInverted(context: Context): Boolean = prefs(context).getBoolean("backActionsInverted", false) @@ -292,10 +303,30 @@ object SessionPrefs { prefs(context).getString("steamController", CONTROLLER_DECK) ?: CONTROLLER_DECK fun setSteamController(context: Context, id: String) { prefs(context).edit().putString("steamController", id).apply() } - /** Runs the SteamOS gamepad client with its Quick Access performance controls. */ - fun steamDeckMode(context: Context): Boolean = prefs(context).getBoolean("steamDeckMode", false) + /** Runs the SteamOS gamepad client with its Quick Access performance controls. On for new installs (settleDeckModeDefault). */ + fun steamDeckMode(context: Context): Boolean = prefs(context).getBoolean("steamDeckMode", true) fun setSteamDeckMode(context: Context, on: Boolean) { prefs(context).edit().putBoolean("steamDeckMode", on).apply() } + /** + * Deck mode became the default for new installs; an install from before keeps what it ran with + * (off), so an update never changes its interface or restarts the client on its own. Run once at + * process start, before anything reads or writes these prefs: a new install has neither prefs + * nor a runtime yet. The answer is written down, so it is decided once. + */ + fun settleDeckModeDefault(context: Context) { + val p = prefs(context) + if (p.contains("steamDeckMode")) return + val existing = p.all.isNotEmpty() || java.io.File(context.filesDir, "linuxfs").exists() + p.edit().putBoolean("steamDeckMode", !existing).apply() + } + + /** + * Deck mode's performance overlay (mangoapp, beside gamescope): the QAM's Overlay Level draws + * through it. Off is the way out where Valve's mangoapp crashes (one Turnip build did). + */ + fun mangoapp(context: Context): Boolean = prefs(context).getBoolean("mangoapp", true) + fun setMangoapp(context: Context, on: Boolean) { prefs(context).edit().putBoolean("mangoapp", on).apply() } + /** * Zink's lazy descriptor mode (ZINK_DESCRIPTORS=lazy) with its compact set layout * (ZINK_DEBUG=compact) for the client's GL-on-Vulkan UI, as WinNative runs it. On by default. diff --git a/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt b/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt index 3e4f76399..27dc9ac5f 100644 --- a/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt +++ b/app/src/main/java/com/droiddeck/launcher/session/SessionService.kt @@ -528,6 +528,7 @@ class SessionService : Service() { if (SessionPrefs.glThread(this)) guest.add("mesa_glthread=true") if (SessionPrefs.noGlError(this)) guest.add("MESA_NO_ERROR=1") if (SessionState.mode == MODE_STEAM) guest.add("BL_STEAMDECK=" + (if (SessionPrefs.steamDeckMode(this)) "1" else "0")) + if (SessionState.mode == MODE_STEAM) guest.add("BL_MANGOAPP=" + (if (SessionPrefs.mangoapp(this)) "1" else "0")) if (steamHere) guest.add("BL_STEAM_CHANNEL=" + SessionPrefs.steamChannel(this)) if (SessionState.mode == MODE_STEAM) { guest.add("BL_GAMESCOPE_FORCE_FULLSCREEN=" + (if (SessionPrefs.forceFullscreen(this)) "1" else "0")) diff --git a/app/src/main/java/com/droiddeck/launcher/ui/ModeSettingsDialog.kt b/app/src/main/java/com/droiddeck/launcher/ui/ModeSettingsDialog.kt index 62ae9ea1d..fc41cd81e 100644 --- a/app/src/main/java/com/droiddeck/launcher/ui/ModeSettingsDialog.kt +++ b/app/src/main/java/com/droiddeck/launcher/ui/ModeSettingsDialog.kt @@ -70,6 +70,8 @@ class ModeSettings( val steamChannel: String? = null, /** Steam only: enable the SteamOS client interface and its performance controls. */ val steamDeckMode: Boolean = false, + /** Steam, Deck mode: the QAM's performance overlay (mangoapp) is started. */ + val mangoapp: Boolean = true, /** Steam only: what the pad is to the client (SessionPrefs.CONTROLLER_*); null outside Steam. */ val steamController: String? = null, /** Steam only: start a Steam session when DroidDeck opens. */ @@ -128,6 +130,7 @@ class ModeSettingsActions( val onForceFullscreen: (Boolean) -> Unit = {}, val onSteamChannel: (String) -> Unit = {}, val onSteamDeckMode: (Boolean) -> Unit = {}, + val onMangoapp: (Boolean) -> Unit = {}, val onSteamController: (String) -> Unit = {}, val onRunSteamAtStartup: (Boolean) -> Unit = {}, val onPickAddedGamesDir: () -> Unit = {}, @@ -359,6 +362,11 @@ fun ModeSettingsPage(s: ModeSettings, a: ModeSettingsActions) { "Enables Steam's Deck interface and Quick Access performance overlay controls. Applies next session.", s.steamDeckMode, onChange = a.onSteamDeckMode, ) + if (s.steamDeckMode) ToggleRow( + host, "mangoapp", "Performance overlay", + "The Quick Access Menu's frame-rate and usage overlay. Turn off if games crash or go black with Deck mode on. Applies next session.", + s.mangoapp, onChange = a.onMangoapp, + ) // Deck mode fixes the branch (SessionPrefs.steamChannel); the choice is for Deck mode off. if (s.steamDeckMode) SettingsRow("Client branch", "Steam Deck public beta: Deck mode needs it, so Steam doesn't keep offering an update") {} else ChoiceRow(