OSD: stop any settings change from wiping the active OSD mode

Changing any setting at all — brightness, a speedhack, a controller binding —
made the on-screen display disappear.

The OSD has two independent controls that both write the same native flags. The
per-stat selection in settings, and the MODE picked from the in-game menu or the
hotkey (Full / Minimal / Custom / Off). Settings.applyTo() pushes the per-stat
osdShow* flags unconditionally, and applyTo runs on EVERY settings change, so it
was overwriting whatever mode was active with the Custom flag set. On most
setups the Custom set is mostly off, which is why the symptom reads as the OSD
vanishing rather than as it changing.

The mode STATE was never lost — InGameOverlay.osdMode still said Full, and the
in-game menu still showed Full. Only the native flags had been replaced, so the
UI and the screen disagreed and nothing looked wrong from the app's side.

Fixed at the applyTo choke point rather than at its five call sites: a re-assert
that reapplies the mode when it is anything other than Custom. Custom is left
alone deliberately — applyTo has just written exactly what Custom means, and
re-applying would be a redundant round trip through the CPU thread.

This is the same shape as the boot-time applyStoredOsdMode() and the
second-display reapplyOsdMode(), which already restore the mode after something
else has pushed flags underneath it. applyTo was the third place that needed it
and the only one that had no such guard.
This commit is contained in:
jpolo1224
2026-08-21 00:25:08 -04:00
parent 54d2850295
commit 517fa69c4c
2 changed files with 25 additions and 0 deletions
@@ -969,6 +969,12 @@ data class Settings(
NativeApp.osdShowVersion(osdShowVersion)
NativeApp.osdShowSettings(osdShowSettings)
NativeApp.osdShowInputs(osdShowInputs)
// ★ The OSD MODE overrides every flag just written. Full / Minimal / Off are a separate
// choice from the per-stat selection above, and this function runs on every settings
// change — so without this line, changing any unrelated setting quietly swaps an active
// mode for the Custom flags, which reads to the user as "the OSD disappeared when I
// changed a setting". Custom is already correct and is left alone.
com.armsx2.ui.InGameOverlay.reassertOsdModeAfterSettingsApply()
// USB keyboard (#254): live attach/detach on the running VM. A plain
// setSetting("USB1","Type",...) write is persisted but doesn't reattach
// USB devices, so drive the device (re)creation explicitly. No-op before
@@ -61,6 +61,25 @@ object InGameOverlay {
}
}
/**
* Re-assert a non-Custom OSD mode after [com.armsx2.config.Settings.applyTo] has run.
*
* applyTo pushes the per-stat osdShow* flags unconditionally, and it runs on EVERY settings
* change — so changing anything at all, brightness, a speedhack, a controller binding, would
* silently replace an active Full / Minimal / Off mode with the Custom flag set. For most
* users the Custom set is mostly off, so what that looks like is the OSD vanishing whenever
* you touch settings.
*
* Custom needs nothing done: applyTo just wrote exactly what Custom means. Only the three
* modes that OVERRIDE the per-stat flags have to be restored, which is what
* [reapplyOsdMode] already does for the second-display case.
*/
fun reassertOsdModeAfterSettingsApply() {
ensureOsdLoaded()
if (osdMode.value != OsdMode.Custom)
runCatching { applyOsdMode(osdMode.value) }
}
/** Short label for [mode], shown by the hotkey toast and the menu selector. */
fun osdModeLabel(mode: OsdMode): String = when (mode) {
OsdMode.Full -> "Full"