Files
tactility/.claude/skills/tactility-firmware/references/display-settings.md
T
Adolfo Reyna 12035f2f39 chore: move firmware skills in-repo + AGENTS.md
- Migrates tactility-firmware and tactility-bluetooth from
  ~/.hermes/skills/ into .claude/skills/ for repo co-location
- Adds AGENTS.md with local workstation context, board table,
  board-direct build rule, Hermes PYTHONPATH pitfall, common
  Tactility pitfalls, and in-repo skill index

Refs: personal Gitea mirror
2026-07-17 09:51:08 -04:00

65 lines
2.7 KiB
Markdown

# DisplaySettings Extension Recipe
Header: `Tactility/Include/Tactility/settings/DisplaySettings.h`
Impl: `Tactility/Source/settings/DisplaySettings.cpp`
## Field Types
Struct as of 2026-07-11:
```cpp
struct DisplaySettings {
Orientation orientation;
uint8_t gammaCurve;
uint8_t backlightDuty;
bool backlightTimeoutEnabled;
uint32_t backlightTimeoutMs;
ScreensaverType screensaverType = BouncingBalls;
bool disableScreensaverWhenCharging = false; // new
};
enum ScreensaverType { None, BouncingBalls, Mystify, MatrixRain, StackChan, McpScreen, Count };
```
## Adding a New Field — 5 Places in CPP
1. `SETTINGS_KEY_*` constant:
```cpp
constexpr auto* SETTINGS_KEY_DISABLE_SCREENSAVER_WHEN_CHARGING = "disableScreensaverWhenCharging";
```
2. `load()` — parse:
```cpp
bool disable_when_charging = false;
auto e = map.find(KEY_DISABLE);
if (e != map.end()) disable_when_charging = (e->second=="1"||e->second=="true"||e->second=="True");
...
settings.disableScreensaverWhenCharging = disable_when_charging;
```
3. `getDefault()` aggregate includes field.
4. `save()`:
```cpp
map[KEY_DISABLE] = settings.disableScreensaverWhenCharging ? "1":"0";
```
5. For enums: add `toString/fromString` cases. Sentinel `Count` is bounds check.
## Persistence Details
- File: `getUserDataPath()+"/settings/display.properties"` (Internal or SD depending on device.properties storage.userDataLocation)
- `loadPropertiesFile``map<string,string>`, tolerant parsing `atoi`/`strtoul`
- `savePropertiesFile` writes `key=value` lines. Parent dir created via `findOrCreateParentDirectory(path,0755)`
- `loadOrGetDefault()` tries load, else getDefault()
- Settings thread-safety: save dispatched off UI via `getMainDispatcher().dispatch([settings]{ save(settings); findService()->reloadSettings(); })`
## Reload into DisplayIdleService
`reloadSettings()` sets `atomic<bool> settingsReloadRequested`. Next `tick()` (50ms timer, Lower priority) checks `exchange(false)` and does `cachedDisplaySettings = loadOrGetDefault()` while holding LVGL lock.
## Display App UI Wiring
- Members stored as `lv_obj_t*` raw pointers, initialized nullptr
- `onShow()` loads settings `loadOrGetDefault()`, builds wrappers column flex
- Each row pattern: create wrapper, label left aligned, control right aligned (switch/dropdown/slider)
- Switch state: `lv_obj_has_state(sw, LV_STATE_CHECKED)`; set via `lv_obj_add_state(sw, LV_STATE_CHECKED)` before aligning
- Dependent disabling: parent `timeoutSwitch` toggles `timeoutDropdown`, `screensaverDropdown`, `disableWhenChargingWrapper` via `LV_STATE_DISABLED`
- Event callbacks static, retrieve app via `lv_event_get_user_data(event)` cast to DisplayApp*
- `onHide()` only saves if `displaySettingsUpdated==true`, dispatches async