ADR-0013: One C++ registry lists the 8 consoles and the modal windows — no list is ever copied into YAML¶
Page en anglais
Cette page n'existe qu'en anglais.
Status: Accepted (2026-09-08) Date: 2026-09-08 (audit of 2026-09-06, §4.1 item 5)
Context¶
Two lists were maintained by hand in several places:
- Which consoles exist. The 8 game namespaces (
Marble,Arkanoid,Lode,Go,Trivia,Draughts,Chess,Pinball) were enumerated in four YAML lambdas:tab5_games_close_all(close the running game), the 1 s interval oftab5-scripts.yaml(skip the auto-close while a game runs),tab5-imu.yamltwice (push the 3 axes to every game, and pick the 10/30 Hz poll rate from a subset of them), and thetab5_screen_nametext sensor oftab5-ha-controls.yaml(name the game to HA).Tab5/README.mditself listed six steps to add a console, three of them being "add the namespace to this list too". - Which modal windows exist. The 8 popups (+ the alarm ring layer and 3 sub-windows) were enumerated in three tables:
kPopupsin the auto-close interval,kPopupsagain in the "go to screen" select,kScreensin the text sensor, pluskTargetOf(select index → popup). The file header oftab5-ha-controls.yamlsaid a new modal "must be added to BOTH tables", and the auto-close comment said the same for its own list.
The project history already shows the failure mode: before tab5_games_close_all existed, Trivia was missing from 5 of the 8 selector cards. A 9th console or a 10th popup had five to seven places to update, none of which failed to compile when forgotten.
Decision¶
Tab5/ecran/tab5_registry.h/.cppis the single owner of both lists.GameRegistry::kGames[]is aconstexpr-style table{name, is_open, close, on_imu, imu_fast}. The game namespaces are plain C++, so the table lives entirely in C++.any_open(),open_name(),close_all(),any_imu_fast_open()anddispatch_imu()replace the four hand-written enumerations. "Neon Apron" stays last: itsclose()restoresrotation: 270and must have the last word (ADR-0012).ModalRegistryis a fixed array (16 slots, no heap) of{lv_obj_t*, name, kind}with three kinds:POPUP(named, faded out byclose_popup_if_open, auto-closed afterUIIdle::POPUP_MS),SUBWINDOW(never named, hidden flat together with the popups: calendar day detail, console confirmations),LAYER(named but never closed by the registry: the alarm ring screen).visible_name(),any_popup_visible(),find(name)andclose_all()replacekScreens, bothkPopupsandkTargetOf.- LVGL widgets are only reachable through
id()inside a YAML lambda, so the modal list is written once, in thetab5_modal_registry_initscript oftab5-scripts.yaml(moved totab5-navigation.yamlon 2026-10-08, with the opening script of each screen). The script is idempotent and every reader executes it before reading; whichever runs first after boot fills the table, the others find it ready. This avoids touching the protectedon_bootsequence oftab5-ha-hmi.yaml. - The "go to screen" select looks its target up by option label (
ModalRegistry::find(x)), so the select option and the registered name must be identical. A mismatch logs aWARNinstead of silently doing nothing. tools/check_tab5_registry.py, run bypytestin CI, enforces the decision: every*_game.his inkGamesand vice-versa; noX::is_open(),X::close()orX::on_imu(in any YAML; the legacy table names are gone; everystyle_modal_cardpopup is registered; every select option has a registered label.
Consequences¶
- Adding a console is one line in
kGames(plus its page, open script and selector card). Adding a popup is oneModalRegistry::add(...)line (plus a select option if it should be reachable from HA). Forgetting either fails the test suite before anything is flashed. - The IMU poll-rate subset is no longer a second list but a flag on each console (
imu_fast); the reasoning (shake-only games stay at 10 Hz, the pinball nudge needs 30 Hz) sits next to the table. any_popup_visible(lv_obj_t* const*, int)disappears fromtab5_custom.h: its only caller was one of the removed tables.- The registration script runs a dozen pointer stores once per boot; the readers loop over at most 12 entries per second. Nothing measurable.
- Every lambda that used to spell out a list now depends on
tab5_registry.h; a console header that changes itson_imusignature breaks the compile in one place, which is the point.