feat(oled): fold CtrlWake + brightness persistence onto native-trigger firmware (v0.6.12)

Cherry-picks ONLY the two separable good features from the withdrawn
v0.6.11 (commit 71cead4) — the `controller_wakes_display` (CtrlWake /
OLED-sleep-while-playing) toggle and `screen_brightness` persistence —
onto the verified-working native-trigger build.

The v0.6.11 regressions are intentionally NOT carried over:
  - 71cead4's state_mgr.cpp trigger-FFB allow-bit mirror (#11 suspect)
  - all of 84393c0's audio resampler retiming + config audio-defaults
    + chunked-SPI flush (#12 suspect)

Touches only src/config.{h,cpp} and src/oled.cpp, so the trigger and
audio code paths compile byte-identical to the tested firmware and
cannot regress. Two new Config_body fields append at the struct tail
(erased flash -> clamped to defaults by config_valid, clean upgrade).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MarcelineVPQ
2026-06-07 12:23:21 -06:00
co-authored by Claude Opus 4.8
parent 00da3d6a18
commit 9cabdca3f5
4 changed files with 50 additions and 7 deletions
+5 -1
View File
@@ -6,7 +6,9 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Version
---
## [Unreleased]
## [0.6.12-oled-edition] — 2026-06-07
> **Supersedes the withdrawn v0.6.11.** v0.6.11 bundled the two OLED quality-of-life features below with two regressions (constant haptics, issue #11; an audio retiming change, issue #12) and is not recommended. v0.6.12 is built on the verified-working native-trigger firmware and folds in **only** the two separable good features — `CtrlWake` and brightness persistence — leaving the trigger and audio code paths byte-identical to the tested build. The v0.6.11 regression changes (the `state_mgr.cpp` trigger-FFB allow-bit mirror and the `audio.cpp` resampler retiming) are intentionally **not** carried over.
Headline: **native DualSense adaptive triggers now fire in real PC games on Linux/Proton — through the dongle, 1:1 with a wired controller.** For weeks the triggers only ever worked in the on-dongle OLED self-test, never in a game: the dongle was *recognised*, but games fell back to a generic/Xbox pad and never sent trigger effects. The root cause was three ways the dongle's USB presentation differed from genuine Sony hardware — each one accepted by Linux's `hid_playstation` (which only checks size + CRC) but **rejected by a game's native DualSense detection, which validates the actual content**. Closing all three makes the dongle byte-for-byte indistinguishable from a real DS5 to the game.
@@ -15,6 +17,8 @@ Headline: **native DualSense adaptive triggers now fire in real PC games on Linu
### Added
- **Native adaptive triggers & haptics in games on Linux/Proton, through the dongle.** Games with native DualSense support (Cyberpunk 2077, etc.) now drive the controller's adaptive triggers via the dongle, identical to a directly-wired DualSense — verified 1:1 by A/B against the same controller plugged in over USB-C. No Steam Input, no per-game hacks beyond the host recipe above. The firmware already proxies the host's trigger output reports to the controller (unchanged); the missing piece was getting games to *recognise* the dongle as a genuine DS5 in the first place (below).
- **`CtrlWake` setting — let the OLED sleep while you play.** New Settings toggle (default **on**, preserving the old behaviour). With it **off**, controller input no longer keeps the panel awake — only the OLED's own KEY0/KEY1 do — so the auto-dim / auto-off timers actually count down during gameplay and the screen can sleep while the controller is in active use. (folded from v0.6.11; issues #8/#9)
- **OLED brightness now persists across a power cycle.** The KEY1-long-press brightness choice is saved to config and restored on boot, instead of resetting to full every power-on. (folded from v0.6.11; issue #9)
### Changed
+8
View File
@@ -113,6 +113,14 @@ void config_valid() {
body->bt_mic_enable = 1;
printf("[Config] bt_mic_enable invalid, defaulting to 1 (on)\n");
}
if (body->screen_brightness > 3) { // kBrightLevels has 4 entries (0..3)
body->screen_brightness = 0; // full brightness
printf("[Config] screen_brightness invalid, defaulting to 0 (full)\n");
}
if (body->controller_wakes_display > 1) { // 0xFF erased / upgrade → default ON
body->controller_wakes_display = 1;
printf("[Config] controller_wakes_display invalid, defaulting to 1 (on)\n");
}
if (body->config_version != CONFIG_VERSION) {
body->config_version = CONFIG_VERSION;
printf("[Config] Warning: Config may breaking change\n");
+10
View File
@@ -44,6 +44,16 @@ struct __attribute__((packed)) Config_body {
// over BT and the dongle decodes it to the USB capture endpoint. Costs extra
// DS5 battery (keeps its audio subsystem awake), hence the toggle.
uint8_t bt_mic_enable;
// OLED brightness, as an index into kBrightLevels[] (src/oled.cpp). Persisted
// so the KEY1-long-press brightness choice survives a power cycle. Erased
// flash (0xFF) → clamped to 0 (full brightness) by config_valid. Issue #9.
uint8_t screen_brightness;
// When 0, controller input no longer keeps the OLED awake — only the OLED's
// own KEY0/KEY1 do — so the dim/off timers actually count down during
// gameplay and the panel can sleep while the controller is in use. Default 1
// preserves the original "any controller activity wakes the screen"
// behavior. Issues #8 (dim timeout never fired during play) and #9.
uint8_t controller_wakes_display;
};
struct __attribute__((packed)) Config {
+27 -6
View File
@@ -126,15 +126,16 @@ constexpr int kLbModeHost = 8;
constexpr int kNumLbModes = 9;
// Settings screen state
constexpr int kNumSettingsItems = 16; // 8 fields + 3 auto-haptic + 2 screen-timeout + BT mic + Reset + Wipe
constexpr int kNumSettingsItems = 17; // 8 fields + 3 auto-haptic + 2 screen-timeout + BT mic + Ctrl-wake + Reset + Wipe
constexpr int kSettingsAutoHapEnaIdx = 8;
constexpr int kSettingsAutoHapGainIdx = 9;
constexpr int kSettingsAutoHapLpIdx = 10;
constexpr int kSettingsScrDimIdx = 11;
constexpr int kSettingsScrOffIdx = 12;
constexpr int kSettingsBtMicIdx = 13;
constexpr int kSettingsResetIdx = 14;
constexpr int kSettingsWipeSlotsIdx = 15;
constexpr int kSettingsCtrlWakeIdx = 14;
constexpr int kSettingsResetIdx = 15;
constexpr int kSettingsWipeSlotsIdx = 16;
Config_body settings_local{};
int settings_sel = 0;
bool settings_dirty = false;
@@ -521,6 +522,14 @@ void handle_buttons() {
last_activity_us = time_us_64();
if (held > kLongPressUs) {
bright_idx = (bright_idx + 1) % kNumBrightLevels;
// Persist so the choice survives a power cycle (issue #9). Keep
// settings_local in sync too, so a later Settings-screen save can't
// clobber screen_brightness with its stale snapshot.
Config_body b = get_config();
b.screen_brightness = (uint8_t)bright_idx;
set_config(b);
config_save();
settings_local.screen_brightness = (uint8_t)bright_idx;
} else {
current_screen = (current_screen - 1 + kNumScreens) % kNumScreens;
last_render_us = 0;
@@ -1547,6 +1556,7 @@ void settings_adjust(int delta) {
break;
}
case 13: c.bt_mic_enable ^= 1; break; // BT mic on/off
case 14: c.controller_wakes_display ^= 1; break; // controller activity wakes OLED on/off
}
}
@@ -1649,8 +1659,9 @@ __attribute__((noinline)) void format_settings_item(int idx, char* line, size_t
else snprintf(line, n, "%s ScrOff %umin", cur, c.screen_off_timeout);
break;
case 13: snprintf(line, n, "%s BT Mic %s", cur, c.bt_mic_enable ? "on" : "off"); break;
case 14: snprintf(line, n, "%s Reset to defaults", cur); break;
case 15: snprintf(line, n, "%s Wipe all slots", cur); break;
case 14: snprintf(line, n, "%s CtrlWake %s", cur, c.controller_wakes_display ? "on" : "off"); break;
case 15: snprintf(line, n, "%s Reset to defaults", cur); break;
case 16: snprintf(line, n, "%s Wipe all slots", cur); break;
}
}
@@ -1822,6 +1833,11 @@ void oled_init() {
// Restore the persisted lightbar mode + favorites (config_load() already ran
// in main() before this). Defaults to HOST passthrough on a fresh flash.
lightbar_load_config();
// Restore the persisted OLED brightness (KEY1-long-press choice). config_valid
// clamps screen_brightness to a legal kBrightLevels index, so this is safe to
// use directly. Fresh flash → 0 (full brightness). Issue #9.
bright_idx = get_config().screen_brightness;
}
// Dim-tier renderer: blank the panel and draw a tiny "I'm alive" dot that
@@ -1885,7 +1901,12 @@ void oled_loop() {
}
if (hash != last_input_hash) {
last_input_hash = hash;
last_activity_us = time_us_64();
// Controller input only keeps the panel awake when the user has left
// "CtrlWake" on (the default). With it off, the dim/off timers count
// down during gameplay and only KEY0/KEY1 wake the screen — see
// handle_buttons(), which bumps last_activity_us unconditionally.
// Issues #8 / #9.
if (get_config().controller_wakes_display) last_activity_us = time_us_64();
}
// Rising-edge: BT-connect itself counts as activity, so the screen wakes
// the moment a controller pairs rather than waiting for the first input.