From 99176a83884b9bf476ca5b4da4809ed107e3d741 Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Mon, 23 Jun 2025 10:00:51 -0500 Subject: [PATCH] Move menu bits to MenuHandler --- src/graphics/Screen.cpp | 238 +---------------------------- src/graphics/Screen.h | 10 +- src/graphics/draw/MenuHandler.cpp | 242 ++++++++++++++++++++++++++++++ src/graphics/draw/MenuHandler.h | 19 +++ 4 files changed, 269 insertions(+), 240 deletions(-) create mode 100644 src/graphics/draw/MenuHandler.cpp create mode 100644 src/graphics/draw/MenuHandler.h diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 492ff2ae9..f94a8d991 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -31,6 +31,7 @@ along with this program. If not, see . #include "TimeFormatters.h" #include "draw/ClockRenderer.h" #include "draw/DebugRenderer.h" +#include "draw/MenuHandler.h" #include "draw/MessageRenderer.h" #include "draw/NodeListRenderer.h" #include "draw/NotificationRenderer.h" @@ -560,7 +561,7 @@ int32_t Screen::runOnce() if (displayHeight == 0) { displayHeight = dispdev->getHeight(); } - handleMenuSwitch(); + menuHandler::handleMenuSwitch(); // Show boot screen for first logo_timeout seconds, then switch to normal operation. // serialSinceMsec adjusts for additional serial wait time during nRF52 bootup @@ -589,7 +590,7 @@ int32_t Screen::runOnce() #ifndef DISABLE_WELCOME_UNSET if (!NotificationRenderer::isOverlayBannerShowing() && config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) { - LoraRegionPicker(0); + menuHandler::LoraRegionPicker(0); } #endif if (!NotificationRenderer::isOverlayBannerShowing() && rebootAtMsec != 0) { @@ -1213,7 +1214,7 @@ int Screen::handleInputEvent(const InputEvent *event) setFastFramerate(); // Draw ASAP ui->update(); - handleMenuSwitch(); + menuHandler::handleMenuSwitch(); return 0; } /* @@ -1315,12 +1316,12 @@ int Screen::handleInputEvent(const InputEvent *event) : 2); // set inital selection #endif } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.clock) { - clockMenu(); + menuHandler::clockMenu(); } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.lora) { - LoraRegionPicker(); + menuHandler::LoraRegionPicker(); } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage && devicestate.rx_text_message.from) { - messageResponseMenu(); + menuHandler::messageResponseMenu(); } else if (framesetInfo.positions.firstFavorite != 255 && this->ui->getUiState()->currentFrame >= framesetInfo.positions.firstFavorite && this->ui->getUiState()->currentFrame <= framesetInfo.positions.lastFavorite) { @@ -1375,231 +1376,6 @@ bool Screen::isOverlayBannerShowing() return NotificationRenderer::isOverlayBannerShowing(); } -void Screen::LoraRegionPicker(uint32_t duration) -{ - static const char *optionsArray[] = {"Back", - "US", - "EU_433", - "EU_868", - "CN", - "JP", - "ANZ", - "KR", - "TW", - "RU", - "IN", - "NZ_865", - "TH", - "LORA_24", - "UA_433", - "UA_868", - "MY_433", - "MY_" - "919", - "SG_" - "923", - "PH_433", - "PH_868", - "PH_915", - "ANZ_433"}; - showOverlayBanner( - "Set the LoRa region", duration, optionsArray, 23, - [](int selected) -> void { - if (selected != 0 && config.lora.region != _meshtastic_Config_LoRaConfig_RegionCode(selected)) { - config.lora.region = _meshtastic_Config_LoRaConfig_RegionCode(selected); - // This is needed as we wait til picking the LoRa region to generate keys for the first time. - if (!owner.is_licensed) { - bool keygenSuccess = false; - if (config.security.private_key.size == 32) { - // public key is derived from private, so this will always have the same result. - if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) { - keygenSuccess = true; - } - } else { - LOG_INFO("Generate new PKI keys"); - crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); - keygenSuccess = true; - } - if (keygenSuccess) { - config.security.public_key.size = 32; - config.security.private_key.size = 32; - owner.public_key.size = 32; - memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32); - } - } - config.lora.tx_enabled = true; - initRegion(); - if (myRegion->dutyCycle < 100) { - config.lora.ignore_mqtt = true; // Ignore MQTT by default if region has a duty cycle limit - } - service->reloadConfig(SEGMENT_CONFIG); - rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000); - } - }, - 0); -} - -void Screen::TwelveHourPicker() -{ - static const char *optionsArray[] = {"Back", "12-hour", "24-hour"}; - showOverlayBanner("12/24 display?", 30000, optionsArray, 3, [](int selected) -> void { - if (selected == 0) { - screen->menuQueue = screen->clock_menu; - } else if (selected == 1) { - config.display.use_12h_clock = true; - } else { - config.display.use_12h_clock = false; - } - service->reloadConfig(SEGMENT_CONFIG); - }); -} - -void Screen::TZPicker() -{ - static const char *optionsArray[] = {"Back", - "US/Hawaii", - "US/Alaska", - "US/Pacific", - "US/Mountain", - "US/Central", - "US/Eastern", - "UTC", - "EU/Western", - "EU/" - "Central", - "EU/Eastern", - "Asia/Kolkata", - "Asia/Hong_Kong", - "AU/AWST", - "AU/ACST", - "AU/AEST", - "Pacific/NZ"}; - showOverlayBanner("Pick Timezone", 30000, optionsArray, 17, [](int selected) -> void { - if (selected == 0) { - screen->menuQueue = screen->clock_menu; - } else if (selected == 1) { // Hawaii - strncpy(config.device.tzdef, "HST10", sizeof(config.device.tzdef)); - } else if (selected == 2) { // Alaska - strncpy(config.device.tzdef, "AKST9AKDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); - } else if (selected == 3) { // Pacific - strncpy(config.device.tzdef, "PST8PDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); - } else if (selected == 4) { // Mountain - strncpy(config.device.tzdef, "MST7MDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); - } else if (selected == 5) { // Central - strncpy(config.device.tzdef, "CST6CDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); - } else if (selected == 6) { // Eastern - strncpy(config.device.tzdef, "EST5EDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); - } else if (selected == 7) { // UTC - strncpy(config.device.tzdef, "UTC", sizeof(config.device.tzdef)); - } else if (selected == 8) { // EU/Western - strncpy(config.device.tzdef, "GMT0BST,M3.5.0/1,M10.5.0", sizeof(config.device.tzdef)); - } else if (selected == 9) { // EU/Central - strncpy(config.device.tzdef, "CET-1CEST,M3.5.0,M10.5.0/3", sizeof(config.device.tzdef)); - } else if (selected == 10) { // EU/Eastern - strncpy(config.device.tzdef, "EET-2EEST,M3.5.0/3,M10.5.0/4", sizeof(config.device.tzdef)); - } else if (selected == 11) { // Asia/Kolkata - strncpy(config.device.tzdef, "IST-5:30", sizeof(config.device.tzdef)); - } else if (selected == 12) { // China - strncpy(config.device.tzdef, "HKT-8", sizeof(config.device.tzdef)); - } else if (selected == 13) { // AU/AWST - strncpy(config.device.tzdef, "AWST-8", sizeof(config.device.tzdef)); - } else if (selected == 14) { // AU/ACST - strncpy(config.device.tzdef, "ACST-9:30ACDT,M10.1.0,M4.1.0/3", sizeof(config.device.tzdef)); - } else if (selected == 15) { // AU/AEST - strncpy(config.device.tzdef, "AEST-10AEDT,M10.1.0,M4.1.0/3", sizeof(config.device.tzdef)); - } else if (selected == 16) { // NZ - strncpy(config.device.tzdef, "NZST-12NZDT,M9.5.0,M4.1.0/3", sizeof(config.device.tzdef)); - } - if (selected != 0) { - setenv("TZ", config.device.tzdef, 1); - service->reloadConfig(SEGMENT_CONFIG); - } - }); -} - -void Screen::clockMenu() -{ - static const char *optionsArray[] = {"Back", "12-hour", "Timezone"}; - showOverlayBanner("Clock Menu", 30000, optionsArray, 3, [](int selected) -> void { - if (selected == 1) { - screen->menuQueue = screen->twelve_hour_picker; - screen->setInterval(0); - runASAP = true; - } else if (selected == 2) { - screen->menuQueue = screen->TZ_picker; - screen->setInterval(0); - runASAP = true; - } - }); -} - -void Screen::messageResponseMenu() -{ - - static const char **optionsArrayPtr; - int options; - if (kb_found) { - const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset", "Reply via Freetext"}; - optionsArrayPtr = optionsArray; - options = 4; - } else { - const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset"}; - optionsArrayPtr = optionsArray; - options = 3; - } -#ifdef HAS_I2S - const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset", "Reply via Freetext", "Read Aloud"}; - optionsArrayPtr = optionsArray; - options = 5; -#endif - showOverlayBanner("Message Action?", 30000, optionsArrayPtr, options, [](int selected) -> void { - if (selected == 1) { - screen->dismissCurrentFrame(); - } else if (selected == 2) { - if (devicestate.rx_text_message.to == NODENUM_BROADCAST) { - cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST, devicestate.rx_text_message.channel); - } else { - cannedMessageModule->LaunchWithDestination(devicestate.rx_text_message.from); - } - } else if (selected == 3) { - if (devicestate.rx_text_message.to == NODENUM_BROADCAST) { - cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST, devicestate.rx_text_message.channel); - } else { - cannedMessageModule->LaunchFreetextWithDestination(devicestate.rx_text_message.from); - } - } -#ifdef HAS_I2S - else if (selected == 4) { - const meshtastic_MeshPacket &mp = devicestate.rx_text_message; - const char *msg = reinterpret_cast(mp.decoded.payload.bytes); - - audioThread->readAloud(msg); - } -#endif - }); -} - -void Screen::handleMenuSwitch() -{ - switch (menuQueue) { - case menu_none: - break; - case lora_picker: - LoraRegionPicker(); - break; - case TZ_picker: - TZPicker(); - break; - case twelve_hour_picker: - TwelveHourPicker(); - break; - case clock_menu: - clockMenu(); - break; - } - menuQueue = menu_none; -} - } // namespace graphics #else diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 6c339475c..8a1a71334 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -201,9 +201,6 @@ class Screen : public concurrency::OSThread size_t frameCount = 0; // Total number of active frames ~Screen(); - enum screenMenus { menu_none, lora_picker, TZ_picker, twelve_hour_picker, clock_menu }; - screenMenus menuQueue = menu_none; - // Which frame we want to be displayed, after we regen the frameset by calling setFrames enum FrameFocus : uint8_t { FOCUS_DEFAULT, // No specific frame @@ -604,12 +601,6 @@ class Screen : public concurrency::OSThread void handleShowNextFrame(); void handleShowPrevFrame(); void handleStartFirmwareUpdateScreen(); - void handleMenuSwitch(); - void clockMenu(); - void TZPicker(); - void TwelveHourPicker(); - void LoraRegionPicker(uint32_t duration = 30000); - void messageResponseMenu(); // Info collected by setFrames method. // Index location of specific frames. @@ -685,5 +676,6 @@ class Screen : public concurrency::OSThread // Extern declarations for function symbols used in UIRenderer extern std::vector functionSymbol; extern std::string functionSymbolString; +extern graphics::Screen *screen; #endif \ No newline at end of file diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp new file mode 100644 index 000000000..c68ebfcc2 --- /dev/null +++ b/src/graphics/draw/MenuHandler.cpp @@ -0,0 +1,242 @@ +#include "configuration.h" +#if HAS_SCREEN +#include "MenuHandler.h" +#include "MeshRadio.h" +#include "MeshService.h" +#include "NodeDB.h" +#include "graphics/Screen.h" +#include "main.h" +#include "modules/CannedMessageModule.h" + +namespace graphics +{ +menuHandler::screenMenus menuHandler::menuQueue = menu_none; + +void menuHandler::LoraRegionPicker(uint32_t duration) +{ + static const char *optionsArray[] = {"Back", + "US", + "EU_433", + "EU_868", + "CN", + "JP", + "ANZ", + "KR", + "TW", + "RU", + "IN", + "NZ_865", + "TH", + "LORA_24", + "UA_433", + "UA_868", + "MY_433", + "MY_" + "919", + "SG_" + "923", + "PH_433", + "PH_868", + "PH_915", + "ANZ_433"}; + screen->showOverlayBanner( + "Set the LoRa region", duration, optionsArray, 23, + [](int selected) -> void { + if (selected != 0 && config.lora.region != _meshtastic_Config_LoRaConfig_RegionCode(selected)) { + config.lora.region = _meshtastic_Config_LoRaConfig_RegionCode(selected); + // This is needed as we wait til picking the LoRa region to generate keys for the first time. + if (!owner.is_licensed) { + bool keygenSuccess = false; + if (config.security.private_key.size == 32) { + // public key is derived from private, so this will always have the same result. + if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) { + keygenSuccess = true; + } + } else { + LOG_INFO("Generate new PKI keys"); + crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); + keygenSuccess = true; + } + if (keygenSuccess) { + config.security.public_key.size = 32; + config.security.private_key.size = 32; + owner.public_key.size = 32; + memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32); + } + } + config.lora.tx_enabled = true; + initRegion(); + if (myRegion->dutyCycle < 100) { + config.lora.ignore_mqtt = true; // Ignore MQTT by default if region has a duty cycle limit + } + service->reloadConfig(SEGMENT_CONFIG); + rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000); + } + }, + 0); +} + +void menuHandler::TwelveHourPicker() +{ + static const char *optionsArray[] = {"Back", "12-hour", "24-hour"}; + screen->showOverlayBanner("12/24 display?", 30000, optionsArray, 3, [](int selected) -> void { + if (selected == 0) { + menuHandler::menuQueue = menuHandler::clock_menu; + } else if (selected == 1) { + config.display.use_12h_clock = true; + } else { + config.display.use_12h_clock = false; + } + service->reloadConfig(SEGMENT_CONFIG); + }); +} + +void menuHandler::TZPicker() +{ + static const char *optionsArray[] = {"Back", + "US/Hawaii", + "US/Alaska", + "US/Pacific", + "US/Mountain", + "US/Central", + "US/Eastern", + "UTC", + "EU/Western", + "EU/" + "Central", + "EU/Eastern", + "Asia/Kolkata", + "Asia/Hong_Kong", + "AU/AWST", + "AU/ACST", + "AU/AEST", + "Pacific/NZ"}; + screen->showOverlayBanner("Pick Timezone", 30000, optionsArray, 17, [](int selected) -> void { + if (selected == 0) { + menuHandler::menuQueue = menuHandler::clock_menu; + } else if (selected == 1) { // Hawaii + strncpy(config.device.tzdef, "HST10", sizeof(config.device.tzdef)); + } else if (selected == 2) { // Alaska + strncpy(config.device.tzdef, "AKST9AKDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); + } else if (selected == 3) { // Pacific + strncpy(config.device.tzdef, "PST8PDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); + } else if (selected == 4) { // Mountain + strncpy(config.device.tzdef, "MST7MDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); + } else if (selected == 5) { // Central + strncpy(config.device.tzdef, "CST6CDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); + } else if (selected == 6) { // Eastern + strncpy(config.device.tzdef, "EST5EDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); + } else if (selected == 7) { // UTC + strncpy(config.device.tzdef, "UTC", sizeof(config.device.tzdef)); + } else if (selected == 8) { // EU/Western + strncpy(config.device.tzdef, "GMT0BST,M3.5.0/1,M10.5.0", sizeof(config.device.tzdef)); + } else if (selected == 9) { // EU/Central + strncpy(config.device.tzdef, "CET-1CEST,M3.5.0,M10.5.0/3", sizeof(config.device.tzdef)); + } else if (selected == 10) { // EU/Eastern + strncpy(config.device.tzdef, "EET-2EEST,M3.5.0/3,M10.5.0/4", sizeof(config.device.tzdef)); + } else if (selected == 11) { // Asia/Kolkata + strncpy(config.device.tzdef, "IST-5:30", sizeof(config.device.tzdef)); + } else if (selected == 12) { // China + strncpy(config.device.tzdef, "HKT-8", sizeof(config.device.tzdef)); + } else if (selected == 13) { // AU/AWST + strncpy(config.device.tzdef, "AWST-8", sizeof(config.device.tzdef)); + } else if (selected == 14) { // AU/ACST + strncpy(config.device.tzdef, "ACST-9:30ACDT,M10.1.0,M4.1.0/3", sizeof(config.device.tzdef)); + } else if (selected == 15) { // AU/AEST + strncpy(config.device.tzdef, "AEST-10AEDT,M10.1.0,M4.1.0/3", sizeof(config.device.tzdef)); + } else if (selected == 16) { // NZ + strncpy(config.device.tzdef, "NZST-12NZDT,M9.5.0,M4.1.0/3", sizeof(config.device.tzdef)); + } + if (selected != 0) { + setenv("TZ", config.device.tzdef, 1); + service->reloadConfig(SEGMENT_CONFIG); + } + }); +} + +void menuHandler::clockMenu() +{ + static const char *optionsArray[] = {"Back", "12-hour", "Timezone"}; + screen->showOverlayBanner("Clock Menu", 30000, optionsArray, 3, [](int selected) -> void { + if (selected == 1) { + menuHandler::menuQueue = menuHandler::twelve_hour_picker; + screen->setInterval(0); + runASAP = true; + } else if (selected == 2) { + menuHandler::menuQueue = menuHandler::TZ_picker; + screen->setInterval(0); + runASAP = true; + } + }); +} + +void menuHandler::messageResponseMenu() +{ + + static const char **optionsArrayPtr; + int options; + if (kb_found) { + const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset", "Reply via Freetext"}; + optionsArrayPtr = optionsArray; + options = 4; + } else { + const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset"}; + optionsArrayPtr = optionsArray; + options = 3; + } +#ifdef HAS_I2S + const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset", "Reply via Freetext", "Read Aloud"}; + optionsArrayPtr = optionsArray; + options = 5; +#endif + screen->showOverlayBanner("Message Action?", 30000, optionsArrayPtr, options, [](int selected) -> void { + if (selected == 1) { + screen->dismissCurrentFrame(); + } else if (selected == 2) { + if (devicestate.rx_text_message.to == NODENUM_BROADCAST) { + cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST, devicestate.rx_text_message.channel); + } else { + cannedMessageModule->LaunchWithDestination(devicestate.rx_text_message.from); + } + } else if (selected == 3) { + if (devicestate.rx_text_message.to == NODENUM_BROADCAST) { + cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST, devicestate.rx_text_message.channel); + } else { + cannedMessageModule->LaunchFreetextWithDestination(devicestate.rx_text_message.from); + } + } +#ifdef HAS_I2S + else if (selected == 4) { + const meshtastic_MeshPacket &mp = devicestate.rx_text_message; + const char *msg = reinterpret_cast(mp.decoded.payload.bytes); + + audioThread->readAloud(msg); + } +#endif + }); +} + +void menuHandler::handleMenuSwitch() +{ + switch (menuQueue) { + case menu_none: + break; + case lora_picker: + LoraRegionPicker(); + break; + case TZ_picker: + TZPicker(); + break; + case twelve_hour_picker: + TwelveHourPicker(); + break; + case clock_menu: + clockMenu(); + break; + } + menuQueue = menu_none; +} + +} // namespace graphics + +#endif \ No newline at end of file diff --git a/src/graphics/draw/MenuHandler.h b/src/graphics/draw/MenuHandler.h new file mode 100644 index 000000000..a4c9d6fe6 --- /dev/null +++ b/src/graphics/draw/MenuHandler.h @@ -0,0 +1,19 @@ +#include "configuration.h" +namespace graphics +{ + +class menuHandler +{ + public: + enum screenMenus { menu_none, lora_picker, TZ_picker, twelve_hour_picker, clock_menu }; + static screenMenus menuQueue; + + static void LoraRegionPicker(uint32_t duration = 30000); + static void handleMenuSwitch(); + static void clockMenu(); + static void TZPicker(); + static void TwelveHourPicker(); + static void messageResponseMenu(); +}; + +} // namespace graphics \ No newline at end of file