Merge remote-tracking branch 'refs/remotes/pr/497' into integration/all-open-prs-20260913

This commit is contained in:
Michael A. Cojocari committed 2026-09-13 18:58:22 -04:00
commit fdde0dd0cc
6 files changed
+105 -12

No files matched your search

+3
View File
@@ -36,6 +36,9 @@ because that is the root.
**HOME** is the shortcut past all of that: it goes straight to the root and
clears the trail, so Back from there won't jump you back out again.
When **App drawer as home** is enabled, the drawer cog's **Lock home to drawer**
option makes HOME stay in or return to the drawer. The **Cmdr** tile still
opens the Commander screen when you need it.
The only exception is deliberate: progress overlays (SD format, bulk delete)
block all keys until the operation finishes — Back will not navigate out from
+7 -3
View File
@@ -9,7 +9,7 @@
namespace TouchPrefsSchema {
static constexpr uint16_t MAGIC = 0x5743; // 'WC' (WadaCfg)
static constexpr uint8_t CURRENT_VERSION = 59; // v49: fem_lna default flip on the V4-R8; v50: map tile z/x/y line off by default (no new fields); v51: console_mode (boot into the text console; new trailing field, default OFF); v52: console_monitor (show incoming messages in the console, default ON); v54: boot_wifi_time + boot_wifi_open (cold-boot saved-Wi-Fi time sync, #383; both OFF); v55: loud_alerts (resonant-pitch chime, #388; OFF); v56: theme_mode (Day/Night firmware theme, #296; default Night); v57: gps_fuzz_m (displace the advertised position, #399; 0 = off); v58: Attaky notification blink enable + room/DM color indexes (#423); v59: telem_loc_exact (answer position requests from chosen contacts with the REAL fix instead of the advert displacement; 0 = keep it displaced)
static constexpr uint8_t CURRENT_VERSION = 60; // v49: fem_lna default flip on the V4-R8; v50: map tile z/x/y line off by default (no new fields); v51: console_mode (boot into the text console; new trailing field, default OFF); v52: console_monitor (show incoming messages in the console, default ON); v54: boot_wifi_time + boot_wifi_open (cold-boot saved-Wi-Fi time sync, #383; both OFF); v55: loud_alerts (resonant-pitch chime, #388; OFF); v56: theme_mode (Day/Night firmware theme, #296; default Night); v57: gps_fuzz_m (displace the advertised position, #399; 0 = off); v58: Attaky notification blink enable + room/DM color indexes (#423); v59: telem_loc_exact (answer position requests from chosen contacts with the REAL fix instead of the advert displacement; 0 = keep it displaced); v60: home_key_keeps_drawer (#491; default OFF)
static constexpr uint8_t BROKEN_MID_INSERT_VERSION = 44;
// Persisted byte layout. New fields must be appended at the end: older blobs
@@ -116,6 +116,7 @@ struct __attribute__((packed)) Config {
// you already picked, not a broadcast, so applying the advert displacement to it is
// arguably too blunt (honza_87628). OFF keeps the displacement, matching the advert.
uint8_t telem_loc_exact;
uint8_t home_key_keeps_drawer; // v60: M9 Home key stays in/returns to the app drawer
};
static constexpr size_t HEADER_SIZE = offsetof(Config, bright);
@@ -128,10 +129,10 @@ static_assert(offsetof(Config, web_mirror) == offsetof(Config, rx_queue) + sizeo
// rejection is silent: cfgFlush just returns false and every preference stops
// persisting, with nothing in the log to say why. The struct grows most releases,
// so guard the ceiling here rather than discover it as "settings do not save" on
// whichever board is using the file backend. 117 bytes today.
// whichever board is using the file backend. 121 bytes today.
static_assert(sizeof(Config) <= 2048,
"Config exceeds the SdNvsPrefs value cap; prefs would silently stop saving");
static_assert(offsetof(Config, telem_loc_exact) + sizeof(Config::telem_loc_exact) == sizeof(Config),
static_assert(offsetof(Config, home_key_keeps_drawer) + sizeof(Config::home_key_keeps_drawer) == sizeof(Config),
"new preference fields must remain trailing");
// lang_file must stay immediately before the tail, or a v48-era blob overlays
// onto the wrong bytes. Checking both ends means the next person to append is
@@ -169,6 +170,9 @@ static_assert(offsetof(Config, attaky_notify_dm_color) ==
static_assert(offsetof(Config, telem_loc_exact) ==
offsetof(Config, attaky_notify_dm_color) + sizeof(Config::attaky_notify_dm_color),
"telem_loc_exact must follow attaky_notify_dm_color");
static_assert(offsetof(Config, home_key_keeps_drawer) ==
offsetof(Config, telem_loc_exact) + sizeof(Config::telem_loc_exact),
"home_key_keeps_drawer must follow telem_loc_exact");
// Overlay a persisted blob on caller-provided defaults. Beta 57 wrote v44 with
// retry_echo inserted before web_mirror, shifting every later value. That blob
+12
View File
@@ -149,6 +149,7 @@ static void cfgSetDefaults(TouchCfg& c) {
c.attaky_notify_room_color = 0; // red
c.attaky_notify_dm_color = 1; // green
c.compact_chat = 0; // OFF: bubble chat layout (opt-in IRC-style dense rows)
c.home_key_keeps_drawer = 0; // OFF: preserve Home-key Commander/drawer toggle
c.clock_floor = 0; // no persisted send-timestamp floor yet
c.rx_queue = 1; // ON: buffered receive (test-channel default; opt-out toggle in Radio & Mesh)
c.retry_echo = 0; // OFF: auto-retry is opt-in (toggle in Radio & Mesh)
@@ -264,6 +265,7 @@ static void cfgLoadOrMigrate() {
if (stored_version < 56) { s_cfg.theme_mode = 0; } // Night: unchanged appearance
if (stored_version < 57) { s_cfg.gps_fuzz_m = 0; } // OFF: real position
if (stored_version < 59) { s_cfg.telem_loc_exact = 0; } // OFF: answers stay displaced
if (stored_version < 60) { s_cfg.home_key_keeps_drawer = 0; } // preserve Home-key toggle
if (stored_version < 58) {
s_cfg.attaky_notify_enabled = 0;
s_cfg.attaky_notify_room_color = 0;
@@ -1398,6 +1400,16 @@ bool touchPrefsSetHomeIsDrawer(bool on) {
return cfgFlush();
}
bool touchPrefsGetHomeKeyKeepsDrawer() {
if (!s_begun) touchPrefsBegin();
return s_cfg.home_key_keeps_drawer != 0;
}
bool touchPrefsSetHomeKeyKeepsDrawer(bool on) {
if (!s_begun) touchPrefsBegin();
s_cfg.home_key_keeps_drawer = on ? 1 : 0;
return cfgFlush();
}
// Store ALL device data (identity, prefs, contacts, channels) on the SD card
// under /meshcomod instead of internal SPIFFS. Read at boot (main.cpp) BEFORE
// the data loads, so changing it needs a reboot. Key "use_sd" in the "touch"
+5
View File
@@ -289,6 +289,11 @@ bool touchPrefsSetNavDirKey(int idx, uint8_t ch);
bool touchPrefsGetHomeIsDrawer();
bool touchPrefsSetHomeIsDrawer(bool on);
/* M9 Home-key behavior: when true and the app drawer is configured as Home,
* pressing Home stays in or returns to the drawer instead of toggling Commander. */
bool touchPrefsGetHomeKeyKeepsDrawer();
bool touchPrefsSetHomeKeyKeepsDrawer(bool on);
/** Hide the device/profile name in the status bar and move the clock to the
* left where the name used to be. Default false (name shown). */
bool touchPrefsGetHideNodeName();
+54 -6
View File
@@ -41797,12 +41797,15 @@ static bool m9HandleNavKey(int key) {
}
if (getActiveTab() == HOME_TAB_INDEX) {
const bool was_open = s_home_drawer_mode; // read BEFORE dismissing anything
// Stop on a key-blocker row (SD format / bulk delete progress) instead of
// spinning eight times closing nothing and then toggling the drawer out
// from under a running operation.
for (int i = 0; i < 8 && anyPopupOpen(); i++) { if (!hwKeyDismissTopPopup()) break; }
if (anyPopupOpen()) { s_nav_show = true; if (g_lv.task) g_lv.task->noteUserInput(); return true; }
setHomeDrawer(!was_open); // decide from the snapshot, not the now-mutated flag
const bool keep_drawer = s_home_is_drawer && touchPrefsGetHomeKeyKeepsDrawer();
if (!(keep_drawer && was_open && s_appdrawer_root && !appDrawerCovered())) {
// Stop on a key-blocker row (SD format / bulk delete progress) instead of
// spinning eight times closing nothing and then toggling the drawer out
// from under a running operation.
for (int i = 0; i < 8 && anyPopupOpen(); i++) { if (!hwKeyDismissTopPopup()) break; }
if (anyPopupOpen()) { s_nav_show = true; if (g_lv.task) g_lv.task->noteUserInput(); return true; }
setHomeDrawer(keep_drawer ? true : !was_open); // optional sticky drawer; otherwise preserve toggle
}
} else {
s_m9_map_pan = false; // leaving the Map: pan must not outlive the tab (a stale flag ate the next Back press)
if (!navGoToMainTab(HOME_TAB_INDEX)) { // a blocker popup refused: nothing moved, so keep the trail
@@ -46026,7 +46029,21 @@ static void appHomeIsDrawerCb(lv_event_t* e) {
#endif
s_home_is_drawer = on;
if (on) s_home_drawer_mode = true; // make the drawer the live Home view too (persists on return + reboot)
#if defined(HAS_THINKNODE_M9)
lv_obj_t* dependent_row = static_cast<lv_obj_t*>(lv_event_get_user_data(e));
if (dependent_row && lv_obj_is_valid(dependent_row)) {
if (on) lv_obj_clear_flag(dependent_row, LV_OBJ_FLAG_HIDDEN);
else lv_obj_add_flag(dependent_row, LV_OBJ_FLAG_HIDDEN);
}
#endif
}
#if defined(HAS_THINKNODE_M9)
static void appHomeKeyKeepsDrawerCb(lv_event_t* e) {
if (lv_event_get_code(e) != LV_EVENT_VALUE_CHANGED) return;
touchPrefsSetHomeKeyKeepsDrawer(
lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED));
}
#endif
static void openAppGridSheet() {
closeAppGridSheet();
lv_coord_t sw = lv_disp_get_hor_res(nullptr), sh = lv_disp_get_ver_res(nullptr);
@@ -46056,6 +46073,9 @@ static void openAppGridSheet() {
lv_obj_remove_style_all(card);
{
int card_h = hdr + 2 * btn_h + gap + home_row + gap + 2 * pad;
#if defined(HAS_THINKNODE_M9)
card_h += home_row + gap;
#endif
if (card_h > card_h_max) card_h = card_h_max;
lv_obj_set_size(card, card_w, card_h);
}
@@ -46064,7 +46084,13 @@ static void openAppGridSheet() {
lv_obj_set_style_bg_opa(card, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_set_style_radius(card, 8, LV_PART_MAIN);
lv_obj_set_style_pad_all(card, pad, LV_PART_MAIN);
#if defined(HAS_THINKNODE_M9)
lv_obj_set_scroll_dir(card, LV_DIR_VER);
lv_obj_set_scrollbar_mode(card, LV_SCROLLBAR_MODE_AUTO);
lv_obj_add_event_cb(card, scrollClampOnEndCb, LV_EVENT_SCROLL_END, nullptr);
#else
lv_obj_clear_flag(card, LV_OBJ_FLAG_SCROLLABLE);
#endif
lv_obj_t* ttl = lv_label_create(card);
lv_label_set_text(ttl, TR("App icon size"));
@@ -46109,7 +46135,29 @@ static void openAppGridSheet() {
#if defined(ESP32)
if (touchPrefsGetHomeIsDrawer()) lv_obj_add_state(sw, LV_STATE_CHECKED);
#endif
#if defined(HAS_THINKNODE_M9)
const int ky = ty + home_row + gap;
lv_obj_t* key_row = lv_obj_create(card);
lv_obj_remove_style_all(key_row);
lv_obj_set_size(key_row, card_w - 2 * pad, home_row);
lv_obj_set_pos(key_row, 0, ky);
lv_obj_clear_flag(key_row, LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_SCROLLABLE);
lv_obj_t* kl = lv_label_create(key_row);
lv_label_set_long_mode(kl, LV_LABEL_LONG_DOT);
lv_obj_set_width(kl, lv_pct(68));
lv_label_set_text(kl, TR("Lock home to drawer"));
lv_obj_set_style_text_font(kl, &g_font_12, LV_PART_MAIN);
lv_obj_set_style_text_color(kl, lv_color_hex(COLOR_TEXT), LV_PART_MAIN);
lv_obj_align(kl, LV_ALIGN_LEFT_MID, 0, 0);
lv_obj_t* key_sw = lv_switch_create(key_row);
lv_obj_align(key_sw, LV_ALIGN_RIGHT_MID, 0, 0);
if (touchPrefsGetHomeKeyKeepsDrawer()) lv_obj_add_state(key_sw, LV_STATE_CHECKED);
if (!touchPrefsGetHomeIsDrawer()) lv_obj_add_flag(key_row, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_event_cb(key_sw, appHomeKeyKeepsDrawerCb, LV_EVENT_VALUE_CHANGED, nullptr);
lv_obj_add_event_cb(sw, appHomeIsDrawerCb, LV_EVENT_VALUE_CHANGED, key_row);
#else
lv_obj_add_event_cb(sw, appHomeIsDrawerCb, LV_EVENT_VALUE_CHANGED, nullptr);
#endif
}
}
static void appDrawerSettingsCb(lv_event_t* e) {
+24 -3
View File
@@ -30,6 +30,7 @@ static Config safeDefaults() {
c.attaky_notify_enabled = 0;
c.attaky_notify_room_color = 0;
c.attaky_notify_dm_color = 1;
c.home_key_keeps_drawer = 0;
return c;
}
@@ -125,7 +126,8 @@ int main() {
+ sizeof(Config::gps_fuzz_m) + sizeof(Config::attaky_notify_enabled)
+ sizeof(Config::attaky_notify_room_color)
+ sizeof(Config::attaky_notify_dm_color)
+ sizeof(Config::telem_loc_exact) == sizeof(Config),
+ sizeof(Config::telem_loc_exact)
+ sizeof(Config::home_key_keeps_drawer) == sizeof(Config),
"v53 is the current layout minus every byte appended since");
Config v53 = safeDefaults();
@@ -167,6 +169,7 @@ int main() {
current.attaky_notify_enabled = 1;
current.attaky_notify_room_color = 6;
current.attaky_notify_dm_color = 4;
current.home_key_keeps_drawer = 1;
migrated = safeDefaults();
assert(TouchPrefsSchema::overlayStored(migrated, &current, sizeof(current), &stored_version));
assert(stored_version == TouchPrefsSchema::CURRENT_VERSION);
@@ -198,7 +201,8 @@ int main() {
+ sizeof(Config::attaky_notify_enabled)
+ sizeof(Config::attaky_notify_room_color)
+ sizeof(Config::attaky_notify_dm_color)
+ sizeof(Config::telem_loc_exact) == sizeof(Config),
+ sizeof(Config::telem_loc_exact)
+ sizeof(Config::home_key_keeps_drawer) == sizeof(Config),
"v55 is the current layout minus every byte appended since");
Config v55 = safeDefaults();
v55.ver = 55;
@@ -220,7 +224,8 @@ int main() {
static_assert(v57_size + sizeof(Config::attaky_notify_enabled)
+ sizeof(Config::attaky_notify_room_color)
+ sizeof(Config::attaky_notify_dm_color)
+ sizeof(Config::telem_loc_exact) == sizeof(Config),
+ sizeof(Config::telem_loc_exact)
+ sizeof(Config::home_key_keeps_drawer) == sizeof(Config),
"v57 is the current layout minus the Attaky notification fields");
Config v57 = safeDefaults();
v57.ver = 57;
@@ -236,6 +241,22 @@ int main() {
assert(migrated.attaky_notify_room_color == 0);
assert(migrated.attaky_notify_dm_color == 1);
// #491 appends the M9 Home-key drawer behavior at v60. A v59 blob keeps
// every prior setting, while the new option defaults OFF to preserve the
// established Commander/drawer toggle until the user opts in.
constexpr size_t v59_size = offsetof(Config, home_key_keeps_drawer);
static_assert(v59_size + sizeof(Config::home_key_keeps_drawer) == sizeof(Config),
"v59 is the current layout minus the Home-key drawer option");
Config v59 = safeDefaults();
v59.ver = 59;
v59.telem_loc_exact = 1;
v59.home_key_keeps_drawer = 1; // outside the stored v59 extent
migrated = safeDefaults();
assert(TouchPrefsSchema::overlayStored(migrated, &v59, v59_size, &stored_version));
assert(stored_version == 59);
assert(migrated.telem_loc_exact == 1);
assert(migrated.home_key_keeps_drawer == 0);
Config invalid = safeDefaults();
uint8_t garbage[sizeof(Config)] = {};
assert(!TouchPrefsSchema::overlayStored(invalid, garbage, sizeof(garbage)));