diff --git a/esp32_marauder/MenuFunctions.cpp b/esp32_marauder/MenuFunctions.cpp index 798ca64b6..1d3a52ef2 100644 --- a/esp32_marauder/MenuFunctions.cpp +++ b/esp32_marauder/MenuFunctions.cpp @@ -1,4 +1,5 @@ #include "MenuFunctions.h" +#include "MenuMarquee.h" #include "CommandLine.h" #include "lang_var.h" @@ -13,7 +14,7 @@ extern LinkedList* ble_devices; extern CommandLine cli_obj; #ifdef HAS_MINI_SCREEN -void MenuFunctions::drawMiniMenuButton(int b, int x, bool selected) { +void MenuFunctions::drawMiniMenuButton(int b, int x, bool selected, uint16_t text_offset) { if (!current_menu || !current_menu->list || x < 0 || x >= current_menu->list->size()) return; @@ -32,10 +33,101 @@ void MenuFunctions::drawMiniMenuButton(int b, int x, bool selected) { display_obj.tft.fillRect(button_x, button_y - 4, KEY_W, KEY_H, background); display_obj.tft.setTextColor(text_color, background); display_obj.tft.setCursor(button_x + BUTTON_PADDING, button_y + (KEY_H / 2) - 8); - display_obj.tft.print(current_menu->list->get(x).name); + display_obj.tft.print(this->menuLabelWindow(current_menu->list->get(x).name, text_offset)); } #endif +String MenuFunctions::menuLabelWindow(const String& name, uint16_t offset) { + const int16_t available_width = max(1, KEY_W - (2 * BUTTON_PADDING)); + + #ifdef HAS_MINI_SCREEN + display_obj.tft.setFreeFont(NULL); + display_obj.tft.setTextSize(1); + #else + display_obj.tft.setFreeFont(MENU_FONT); + display_obj.tft.setTextSize(KEY_TEXTSIZE); + #endif + + if (offset >= name.length()) + offset = 0; + + String visible = name.substring(offset); + while (visible.length() > 1 && display_obj.tft.textWidth(visible) > available_width) + visible.remove(visible.length() - 1); + + return visible; +} + +uint16_t MenuFunctions::menuLabelMaxOffset(const String& name) { + const int16_t available_width = max(1, KEY_W - (2 * BUTTON_PADDING)); + uint16_t max_offset = 0; + + #ifdef HAS_MINI_SCREEN + display_obj.tft.setFreeFont(NULL); + display_obj.tft.setTextSize(1); + #else + display_obj.tft.setFreeFont(MENU_FONT); + display_obj.tft.setTextSize(KEY_TEXTSIZE); + #endif + + if (display_obj.tft.textWidth(name) > available_width) { + max_offset = name.length() > 0 ? name.length() - 1 : 0; + for (uint16_t offset = 1; offset < name.length(); offset++) { + if (display_obj.tft.textWidth(name.substring(offset)) <= available_width) { + max_offset = offset; + break; + } + } + } + + #ifdef HAS_FULL_SCREEN + display_obj.tft.setFreeFont(NULL); + #endif + + return max_offset; +} + +void MenuFunctions::updateMenuMarquee(uint32_t current_time) { + const bool menu_visible = (wifi_scan_obj.currentScanMode == WIFI_SCAN_OFF) || + (wifi_scan_obj.currentScanMode == WIFI_CONNECTED) || + (wifi_scan_obj.currentScanMode == OTA_UPDATE); + + if (!menu_visible || !current_menu || !current_menu->list || + current_menu->list->size() == 0 || current_menu->selected >= current_menu->list->size()) { + marquee_menu = nullptr; + marquee_selected = 0xFFFF; + marquee_rendered_offset = 0; + marquee_max_offset = 0; + return; + } + + if (marquee_menu != current_menu || marquee_selected != current_menu->selected) { + marquee_menu = current_menu; + marquee_selected = current_menu->selected; + marquee_selected_since = current_time; + marquee_rendered_offset = 0; + marquee_max_offset = this->menuLabelMaxOffset( + current_menu->list->get(current_menu->selected).name + ); + return; + } + + const uint16_t next_offset = MenuMarquee::offsetForElapsed( + current_time - marquee_selected_since, + marquee_max_offset + ); + + if (next_offset == marquee_rendered_offset) + return; + + marquee_rendered_offset = next_offset; + this->buttonSelected( + current_menu->selected - menu_start_index, + current_menu->selected, + marquee_rendered_offset + ); +} + void MenuFunctions::buttonNotSelected(int b, int x) { if (x == -1) x = b; @@ -53,7 +145,7 @@ void MenuFunctions::buttonNotSelected(int b, int x) { #ifdef HAS_FULL_SCREEN display_obj.tft.setFreeFont(MENU_FONT); display_obj.key[b].initButton(&display_obj.tft, KEY_X, KEY_Y + b * (KEY_H + KEY_SPACING_Y), KEY_W, KEY_H, TFT_BLACK, TFT_BLACK, color, (char*)"", KEY_TEXTSIZE); - display_obj.key[b].drawButton(false, current_menu->list->get(x).name); + display_obj.key[b].drawButton(false, this->menuLabelWindow(current_menu->list->get(x).name)); if ((current_menu->list->get(x).name != text09) && (current_menu->list->get(x).icon != 255)) display_obj.tft.drawXBitmap(0, KEY_Y + (b * (KEY_H + KEY_SPACING_Y)) - (ICON_H / 2), @@ -66,7 +158,7 @@ void MenuFunctions::buttonNotSelected(int b, int x) { #endif } -void MenuFunctions::buttonSelected(int b, int x) { +void MenuFunctions::buttonSelected(int b, int x, uint16_t text_offset) { if (x == -1) x = b; @@ -76,7 +168,7 @@ void MenuFunctions::buttonSelected(int b, int x) { uint16_t color = this->getColor(current_menu->list->get(x).color); #ifdef HAS_MINI_SCREEN - this->drawMiniMenuButton(b, x, true); + this->drawMiniMenuButton(b, x, true, text_offset); #endif #ifdef HAS_FULL_SCREEN @@ -84,7 +176,7 @@ void MenuFunctions::buttonSelected(int b, int x) { if (current_menu->list->get(x).icon == SETTINGS && current_menu->list->get(x).color == TFTLIGHTGREY) { uint16_t setting_color = current_menu->list->get(x).selected ? TFT_GREEN : TFT_RED; display_obj.key[b].initButton(&display_obj.tft, KEY_X, KEY_Y + b * (KEY_H + KEY_SPACING_Y), KEY_W, KEY_H, TFT_BLACK, TFT_LIGHTGREY, setting_color, (char*)"", KEY_TEXTSIZE); - display_obj.key[b].drawButton(false, current_menu->list->get(x).name); + display_obj.key[b].drawButton(false, this->menuLabelWindow(current_menu->list->get(x).name, text_offset)); display_obj.tft.drawXBitmap(0, KEY_Y + (b * (KEY_H + KEY_SPACING_Y)) - (ICON_H / 2), menu_icons[current_menu->list->get(x).icon], @@ -93,7 +185,7 @@ void MenuFunctions::buttonSelected(int b, int x) { TFT_BLACK, TFT_LIGHTGREY); } else { - display_obj.key[b].drawButton(true, current_menu->list->get(x).name); + display_obj.key[b].drawButton(true, this->menuLabelWindow(current_menu->list->get(x).name, text_offset)); if ((current_menu->list->get(x).name != text09) && (current_menu->list->get(x).icon != 255)) display_obj.tft.drawXBitmap(0, KEY_Y + (b * (KEY_H + KEY_SPACING_Y)) - (ICON_H / 2), @@ -205,6 +297,8 @@ void MenuFunctions::main(uint32_t currentTime) } } + this->updateMenuMarquee(currentTime); + boolean pressed = false; // This is code from bodmer's keypad example @@ -4616,6 +4710,10 @@ void MenuFunctions::changeMenu(Menu* menu, bool simple_change) { current_menu = menu; current_menu->selected = 0; + marquee_menu = nullptr; + marquee_selected = 0xFFFF; + marquee_rendered_offset = 0; + marquee_max_offset = 0; buildButtons(menu); @@ -4697,6 +4795,10 @@ void MenuFunctions::buildButtons(Menu *menu, int starting_index, const char* but void MenuFunctions::displayCurrentMenu(int start_index) { //Serial.println(F("Displaying current menu...")); + marquee_menu = nullptr; + marquee_selected = 0xFFFF; + marquee_rendered_offset = 0; + marquee_max_offset = 0; display_obj.clearScreen(); display_obj.updateBanner(current_menu->name); display_obj.tft.setTextColor(TFT_LIGHTGREY, TFT_DARKGREY); @@ -4723,7 +4825,7 @@ void MenuFunctions::displayCurrentMenu(int start_index) if (is_setting_node && current_menu->selected == i) { uint16_t setting_color = current_menu->list->get(i).selected ? TFT_GREEN : TFT_RED; display_obj.key[i - start_index].initButton(&display_obj.tft, KEY_X, KEY_Y + (i - start_index) * (KEY_H + KEY_SPACING_Y), KEY_W, KEY_H, TFT_BLACK, TFT_LIGHTGREY, setting_color, (char*)"", KEY_TEXTSIZE); - display_obj.key[i - start_index].drawButton(false, current_menu->list->get(i).name); + display_obj.key[i - start_index].drawButton(false, this->menuLabelWindow(current_menu->list->get(i).name)); display_obj.tft.drawXBitmap(0, KEY_Y + (i - start_index) * (KEY_H + KEY_SPACING_Y) - (ICON_H / 2), menu_icons[current_menu->list->get(i).icon], @@ -4732,7 +4834,7 @@ void MenuFunctions::displayCurrentMenu(int start_index) TFT_BLACK, TFT_LIGHTGREY); } else if ((!is_setting_node && current_menu->list->get(i).selected) || (current_menu->selected == i)) { - display_obj.key[i - start_index].drawButton(true, current_menu->list->get(i).name); + display_obj.key[i - start_index].drawButton(true, this->menuLabelWindow(current_menu->list->get(i).name)); if ((current_menu->list->get(i).name != text09) && (current_menu->list->get(i).icon != 255)) display_obj.tft.drawXBitmap(0, KEY_Y + (i - start_index) * (KEY_H + KEY_SPACING_Y) - (ICON_H / 2), @@ -4742,7 +4844,7 @@ void MenuFunctions::displayCurrentMenu(int start_index) TFT_BLACK, color); } else { - display_obj.key[i - start_index].drawButton(false, current_menu->list->get(i).name); + display_obj.key[i - start_index].drawButton(false, this->menuLabelWindow(current_menu->list->get(i).name)); if ((current_menu->list->get(i).name != text09) && (current_menu->list->get(i).icon != 255)) display_obj.tft.drawXBitmap(0, KEY_Y + (i - start_index) * (KEY_H + KEY_SPACING_Y) - (ICON_H / 2), diff --git a/esp32_marauder/MenuFunctions.h b/esp32_marauder/MenuFunctions.h index f7ad63498..8e692d98f 100644 --- a/esp32_marauder/MenuFunctions.h +++ b/esp32_marauder/MenuFunctions.h @@ -151,9 +151,14 @@ class MenuFunctions float _graph_scale = 1.0; uint32_t initTime = 0; int menu_start_index = 0; - uint8_t mini_kb_index = 0; - uint8_t old_gps_sat_count = 0; + uint8_t mini_kb_index = 0; + uint8_t old_gps_sat_count = 0; uint8_t max_graph_value = 0; + Menu* marquee_menu = nullptr; + uint16_t marquee_selected = 0xFFFF; + uint16_t marquee_rendered_offset = 0; + uint16_t marquee_max_offset = 0; + uint32_t marquee_selected_since = 0; void buildWiFiFoxHuntMenu(); void buildBluetoothFoxHuntMenu(); @@ -274,12 +279,15 @@ class MenuFunctions void battery(bool initial = false); void battery2(bool initial = false); const char* callSetting(const char* key); - void displaySetting(const char* key, Menu* menu, int index); - void buttonSelected(int b, int x = -1); - void buttonNotSelected(int b, int x = -1); - #ifdef HAS_MINI_SCREEN - void drawMiniMenuButton(int b, int x, bool selected); - #endif + void displaySetting(const char* key, Menu* menu, int index); + void buttonSelected(int b, int x = -1, uint16_t text_offset = 0); + void buttonNotSelected(int b, int x = -1); + String menuLabelWindow(const String& name, uint16_t offset = 0); + uint16_t menuLabelMaxOffset(const String& name); + void updateMenuMarquee(uint32_t current_time); + #ifdef HAS_MINI_SCREEN + void drawMiniMenuButton(int b, int x, bool selected, uint16_t text_offset = 0); + #endif //#if (!defined(HAS_ILI9341) && defined(HAS_BUTTONS)) #ifdef HAS_MINI_KB String miniKeyboard(Menu * targetMenu, bool do_pass = false); diff --git a/esp32_marauder/MenuMarquee.cpp b/esp32_marauder/MenuMarquee.cpp new file mode 100644 index 000000000..661c36872 --- /dev/null +++ b/esp32_marauder/MenuMarquee.cpp @@ -0,0 +1 @@ +#include "MenuMarquee.h" diff --git a/esp32_marauder/MenuMarquee.h b/esp32_marauder/MenuMarquee.h new file mode 100644 index 000000000..be6358176 --- /dev/null +++ b/esp32_marauder/MenuMarquee.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +namespace MenuMarquee { + +constexpr uint32_t START_DELAY_MS = 1000; +constexpr uint32_t STEP_INTERVAL_MS = 150; +constexpr uint16_t END_PAUSE_STEPS = 5; + +inline uint16_t offsetForElapsed(uint32_t elapsed_ms, uint16_t max_offset) { + if (max_offset == 0 || elapsed_ms <= START_DELAY_MS) + return 0; + + const uint32_t scroll_step = 1 + ((elapsed_ms - START_DELAY_MS - 1) / STEP_INTERVAL_MS); + const uint32_t cycle_steps = static_cast(max_offset) + 1 + END_PAUSE_STEPS; + const uint32_t cycle_step = scroll_step % cycle_steps; + + if (cycle_step > max_offset) + return max_offset; + + return static_cast(cycle_step); +} + +} // namespace MenuMarquee diff --git a/esp32_marauder/ReconMission.cpp b/esp32_marauder/ReconMission.cpp index 7e49d50fe..44c0b6163 100644 --- a/esp32_marauder/ReconMission.cpp +++ b/esp32_marauder/ReconMission.cpp @@ -519,6 +519,7 @@ void ReconMission::writeManifest(bool complete) { #endif } +// GCOVR_EXCL_START -- Recon dashboard rendering requires a hardware TFT. void ReconMission::drawDashboard(uint32_t current_time) { #ifdef HAS_SCREEN if (last_dashboard && current_time - last_dashboard < 1000) return; @@ -544,7 +545,7 @@ void ReconMission::drawDashboard(uint32_t current_time) { char status[40]; if (active_mode == ReconMode::WIFI_RECON) { #if TFT_WIDTH < 200 - snprintf(status, sizeof(status), "R W %lu:%02lu A%lu S%lu P%lu", + snprintf(status, sizeof(status), "W %lu:%02lu A%lu S%lu P%lu", static_cast(seconds / 60), static_cast(seconds % 60), static_cast(ap_count), @@ -559,11 +560,19 @@ void ReconMission::drawDashboard(uint32_t current_time) { static_cast(probe_count), gps_fix ? '+' : '-'); #endif } else { - snprintf(status, sizeof(status), "REC B %lu:%02lu D%lu U%lu G%c", + #if TFT_WIDTH < 200 + snprintf(status, sizeof(status), "B %lu:%02lu D%lu U%lu", + static_cast(seconds / 60), + static_cast(seconds % 60), + static_cast(ble_count), + static_cast(repeat_count)); + #else + snprintf(status, sizeof(status), "REC B %lu:%02lu D%lu U%lu G%c", static_cast(seconds / 60), static_cast(seconds % 60), static_cast(ble_count), static_cast(repeat_count), gps_fix ? '+' : '-'); + #endif } const uint16_t color = active_mode == ReconMode::WIFI_RECON ? TFT_MAGENTA : TFT_CYAN; display_obj.tft.fillRect(0, 16, TFT_WIDTH, 16, color); @@ -577,13 +586,17 @@ void ReconMission::drawDashboard(uint32_t current_time) { while (display_obj.screen_buffer->size()) display_obj.screen_buffer->shift(); #endif - const int16_t body_top = 48; + #if TFT_HEIGHT < 160 + const int16_t body_top = 34; + #else + const int16_t body_top = 48; + #endif display_obj.tft.fillRect(0, body_top, TFT_WIDTH, TFT_HEIGHT - body_top, TFT_BLACK); #if TFT_HEIGHT < 160 - const int16_t graph_x = 7; - const int16_t graph_y = body_top + 7; - const int16_t graph_width = 50; - const int16_t graph_height = 62; + const int16_t graph_x = 4; + const int16_t graph_y = body_top + 2; + const int16_t graph_width = 54; + const int16_t graph_height = 58; #elif TFT_WIDTH < 200 const int16_t graph_x = (TFT_WIDTH - 84) / 2; const int16_t graph_y = body_top + 7; @@ -670,26 +683,25 @@ void ReconMission::drawDashboard(uint32_t current_time) { display_obj.tft.setTextColor(TFT_WHITE, TFT_BLACK); char line[34]; #if TFT_HEIGHT < 160 - const int16_t stats_x = 64; - if (active_mode == ReconMode::WIFI_RECON) - snprintf(line, sizeof(line), "AP %-4lu STA %-4lu", (unsigned long)ap_count, - (unsigned long)station_count); - else - snprintf(line, sizeof(line), "BLE %-5lu", (unsigned long)ble_count); - display_obj.tft.drawString(line, stats_x, body_top + 8, 1); - if (active_mode == ReconMode::WIFI_RECON) - snprintf(line, sizeof(line), "PROBE %-3lu UPD %-3lu", (unsigned long)probe_count, - (unsigned long)repeat_count); - else - snprintf(line, sizeof(line), "UPDATE %-3lu", (unsigned long)repeat_count); - display_obj.tft.drawString(line, stats_x, body_top + 22, 1); + const int16_t stats_x = 63; if (active_mode == ReconMode::WIFI_RECON) { + snprintf(line, sizeof(line), "AP %lu", (unsigned long)ap_count); + display_obj.tft.drawString(line, stats_x, body_top + 2, 1); + snprintf(line, sizeof(line), "STA %lu", (unsigned long)station_count); + display_obj.tft.drawString(line, stats_x, body_top + 14, 1); + snprintf(line, sizeof(line), "PRB %lu", (unsigned long)probe_count); + display_obj.tft.drawString(line, stats_x, body_top + 26, 1); + snprintf(line, sizeof(line), "UPD %lu", (unsigned long)repeat_count); + display_obj.tft.drawString(line, stats_x, body_top + 38, 1); const bool deauth_active = last_deauth && current_time - last_deauth < RECON_DEAUTH_ALERT_MS; - snprintf(line, sizeof(line), "D! %-4lu", (unsigned long)deauth_count); + snprintf(line, sizeof(line), "D! %lu", (unsigned long)deauth_count); display_obj.tft.setTextColor(deauth_active ? TFT_RED : TFT_DARKGREY, TFT_BLACK); - display_obj.tft.drawString(line, stats_x, body_top + 36, 1); - display_obj.tft.setTextColor(TFT_LIGHTGREY, TFT_BLACK); - display_obj.tft.drawString(band_label, stats_x, body_top + 50, 1); + display_obj.tft.drawString(line, stats_x, body_top + 50, 1); + } else { + snprintf(line, sizeof(line), "BLE %lu", (unsigned long)ble_count); + display_obj.tft.drawString(line, stats_x, body_top + 14, 1); + snprintf(line, sizeof(line), "UPD %lu", (unsigned long)repeat_count); + display_obj.tft.drawString(line, stats_x, body_top + 32, 1); } #elif TFT_WIDTH < 200 if (active_mode == ReconMode::WIFI_RECON) @@ -736,7 +748,31 @@ void ReconMission::drawDashboard(uint32_t current_time) { } #endif - #if TFT_HEIGHT >= 160 + #if TFT_HEIGHT < 160 + const int16_t event_y = TFT_HEIGHT - 10; + display_obj.tft.drawFastHLine(4, event_y - 5, TFT_WIDTH - 8, TFT_DARKGREY); + if (active_mode == ReconMode::WIFI_RECON && ui_relationship_head) { + const UiRelationship& relationship = + ui_relationships[(ui_relationship_head + 2) % 3]; + char ap_name[8]; + reconTruncate(relationship.ap_name, ap_name, sizeof(ap_name)); + snprintf(line, sizeof(line), "%02X:%02X>%s", relationship.station[4], + relationship.station[5], ap_name); + display_obj.tft.setTextColor(TFT_CYAN, TFT_BLACK); + display_obj.tft.drawCentreString(line, TFT_WIDTH / 2, event_y, 1); + } else { + const UiEvent& latest = ui_events[(ui_event_head + 3) % 4]; + if (latest.type == 'p') snprintf(line, sizeof(line), "PRB %.10s", latest.label); + else if (latest.type == 'b') snprintf(line, sizeof(line), "%02X:%02X>%.7s", + latest.mac[4], latest.mac[5], + latest.label[0] ? latest.label : "BLE"); + else if (latest.type) snprintf(line, sizeof(line), "%c %02X:%02X:%02X", + latest.type, latest.mac[3], latest.mac[4], latest.mac[5]); + else snprintf(line, sizeof(line), "Waiting..."); + display_obj.tft.setTextColor(latest.type == 'd' ? TFT_RED : TFT_LIGHTGREY, TFT_BLACK); + display_obj.tft.drawCentreString(line, TFT_WIDTH / 2, event_y, 1); + } + #else #if TFT_WIDTH >= 200 const int16_t relation_y = body_top + 112; display_obj.tft.drawFastHLine(6, relation_y - 6, TFT_WIDTH - 12, TFT_DARKGREY); @@ -833,6 +869,7 @@ void ReconMission::drawDashboard(uint32_t current_time) { (void)current_time; #endif } +// GCOVR_EXCL_STOP void ReconMission::observeLists() { if (!running) return; diff --git a/esp32_marauder/WiFiScan.cpp b/esp32_marauder/WiFiScan.cpp index ab4f4d24f..93cd1251c 100644 --- a/esp32_marauder/WiFiScan.cpp +++ b/esp32_marauder/WiFiScan.cpp @@ -4599,12 +4599,27 @@ void WiFiScan::RunPacketMonitor(uint8_t scan_mode, uint16_t color) { if (scan_mode == WIFI_PACKET_MONITOR) startPcap("packet_monitor"); - #if defined(HAS_SCREEN) && defined(HAS_ILI9341) + // GCOVR_EXCL_START -- Packet Monitor presentation requires a hardware TFT. + #if defined(HAS_SCREEN) && (defined(HAS_ILI9341) || (defined(MARAUDER_MINI_V3) && !defined(DUAL_MINI_C5))) if (scan_mode == WIFI_PACKET_MONITOR) this->resetPacketMonitorGraph(); #endif - #ifdef HAS_ILI9341 + #if defined(MARAUDER_MINI_V3) && !defined(DUAL_MINI_C5) + if (scan_mode == WIFI_PACKET_MONITOR) { + display_obj.init(); + display_obj.tft.setRotation(SCREEN_ORIENTATION); + display_obj.tft.fillScreen(TFT_BLACK); + display_obj.tft.setFreeFont(NULL); + display_obj.tft.setTextSize(1); + this->drawPacketMonitorControls(); + this->drawPacketMonitorGraphs(); + } + else { + this->setupScanDisplayArea(TFT_WHITE, color); + this->prepareScanStage(TFT_GREEN, TFT_BLACK); + } + #elif defined(HAS_ILI9341) if ((scan_mode != WIFI_SCAN_PACKET_RATE) && (scan_mode != WIFI_SCAN_CHAN_ANALYZER) && (scan_mode != WIFI_SCAN_CHAN_ACT)) { @@ -4681,6 +4696,7 @@ void WiFiScan::RunPacketMonitor(uint8_t scan_mode, uint16_t color) { this->prepareScanStage(TFT_GREEN, TFT_BLACK); #endif #endif + // GCOVR_EXCL_STOP //Serial.println(F("Running packet scan...")); esp_wifi_init(&cfg2); @@ -9518,18 +9534,18 @@ void WiFiScan::wifiSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type) // If we dont the buffer size is not 0, don't write or else we get CORRUPT_HEAP #ifdef HAS_SCREEN - #ifdef HAS_ILI9341 - if (snifferPacket->payload[0] == 0x80) + #if defined(HAS_ILI9341) || (defined(MARAUDER_MINI_V3) && !defined(DUAL_MINI_C5)) // GCOVR_EXCL_LINE + if (snifferPacket->payload[0] == 0x80) // GCOVR_EXCL_LINE -- requires live WiFi capture. { - num_beacon++; + num_beacon++; // GCOVR_EXCL_LINE } - else if ((snifferPacket->payload[0] == 0xA0 || snifferPacket->payload[0] == 0xC0 )) + else if ((snifferPacket->payload[0] == 0xA0 || snifferPacket->payload[0] == 0xC0 )) // GCOVR_EXCL_LINE { - num_deauth++; + num_deauth++; // GCOVR_EXCL_LINE } - else if (snifferPacket->payload[0] == 0x40) + else if (snifferPacket->payload[0] == 0x40) // GCOVR_EXCL_LINE { - num_probe++; + num_probe++; // GCOVR_EXCL_LINE } #else if (snifferPacket->payload[0] == 0x80) @@ -9545,7 +9561,7 @@ void WiFiScan::wifiSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type) } else { #ifdef HAS_SCREEN - #ifndef HAS_ILI9341 + #if !defined(HAS_ILI9341) && !(defined(MARAUDER_MINI_V3) && !defined(DUAL_MINI_C5)) // GCOVR_EXCL_LINE display_string.concat(";wht;"); #endif #endif @@ -9565,7 +9581,7 @@ void WiFiScan::wifiSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type) } #ifdef SCREEN_BUFFER - #ifndef HAS_ILI9341 + #if !defined(HAS_ILI9341) && !(defined(MARAUDER_MINI_V3) && !defined(DUAL_MINI_C5)) // GCOVR_EXCL_LINE if (display_obj.display_buffer->size() >= 10) return; @@ -9974,6 +9990,7 @@ bool WiFiScan::filterActive() { return false; } +// GCOVR_EXCL_START -- Packet Monitor graphs require a hardware TFT and live capture. #ifdef HAS_SCREEN int8_t WiFiScan::checkAnalyzerButtons(uint32_t currentTime) { boolean pressed = false; @@ -10015,7 +10032,7 @@ bool WiFiScan::filterActive() { #endif #ifdef HAS_SCREEN - #ifdef HAS_ILI9341 + #if defined(HAS_ILI9341) || (defined(MARAUDER_MINI_V3) && !defined(DUAL_MINI_C5)) void WiFiScan::resetPacketMonitorGraph() { memset(packet_monitor_beacons, 0, sizeof(packet_monitor_beacons)); memset(packet_monitor_deauths, 0, sizeof(packet_monitor_deauths)); @@ -10045,7 +10062,11 @@ bool WiFiScan::filterActive() { void WiFiScan::drawPacketMonitorGraph(const uint16_t *values, int16_t top, int16_t bottom, uint16_t color, const char *label) { - const int16_t plot_top = top + 12; + #ifdef MARAUDER_MINI_V3 + const int16_t plot_top = top + 10; + #else + const int16_t plot_top = top + 12; + #endif const int16_t graph_height = bottom - plot_top; uint16_t max_value = 1; for (uint16_t i = 0; i < PACKET_MONITOR_HISTORY_LEN; i++) @@ -10080,7 +10101,11 @@ bool WiFiScan::filterActive() { } void WiFiScan::drawPacketMonitorGraphs() { - const int16_t graph_top = 64; + #ifdef MARAUDER_MINI_V3 + const int16_t graph_top = 28; + #else + const int16_t graph_top = 64; + #endif const int16_t lane_height = (SCREEN_HEIGHT - graph_top) / 3; drawPacketMonitorGraph(packet_monitor_beacons, graph_top, graph_top + lane_height - 1, TFT_GREEN, "BCN"); @@ -10091,22 +10116,41 @@ bool WiFiScan::filterActive() { } void WiFiScan::drawPacketMonitorControls() { - display_obj.tft.fillRect(0, 0, SCREEN_WIDTH, 64, TFT_BLACK); + #ifdef MARAUDER_MINI_V3 + display_obj.tft.fillRect(0, 0, SCREEN_WIDTH, 28, TFT_BLACK); + #else + display_obj.tft.fillRect(0, 0, SCREEN_WIDTH, 64, TFT_BLACK); + #endif display_obj.tft.setTextColor(TFT_WHITE, TFT_BLACK); - display_obj.tft.drawCentreString(text_table1[45], SCREEN_WIDTH / 2, 0, 2); - display_obj.tftDrawChannelScaleButtons(set_channel, false); - display_obj.tftDrawExitScaleButtons(false); + #ifdef MARAUDER_MINI_V3 + display_obj.tft.drawCentreString(text_table1[45], SCREEN_WIDTH / 2, 0, 1); + #else + display_obj.tft.drawCentreString(text_table1[45], SCREEN_WIDTH / 2, 0, 2); + display_obj.tftDrawChannelScaleButtons(set_channel, false); + display_obj.tftDrawExitScaleButtons(false); + #endif display_obj.tft.setTextColor(TFT_WHITE, TFT_BLACK); display_obj.tft.drawCentreString(String("CH ") + set_channel, - SCREEN_WIDTH / 2, 18, 1); + SCREEN_WIDTH / 2, + #ifdef MARAUDER_MINI_V3 + 10, + #else + 18, + #endif + 1); + #ifdef MARAUDER_MINI_V3 + display_obj.tft.setTextColor(TFT_DARKGREY, TFT_BLACK); + display_obj.tft.drawCentreString("UP/DN CH L EXIT", SCREEN_WIDTH / 2, 19, 1); + #endif } #endif - #ifdef HAS_ILI9341 + #if defined(HAS_ILI9341) || (defined(MARAUDER_MINI_V3) && !defined(DUAL_MINI_C5)) void WiFiScan::packetMonitorMain(uint32_t currentTime) { - const int8_t b = this->checkAnalyzerButtons(currentTime); + #ifdef HAS_ILI9341 + const int8_t b = this->checkAnalyzerButtons(currentTime); - if (b == CHAN_MINUS_INDEX) { + if (b == CHAN_MINUS_INDEX) { #ifndef HAS_DUAL_BAND if (set_channel > 1) set_channel--; @@ -10122,8 +10166,8 @@ bool WiFiScan::filterActive() { #endif changeChannel(); this->drawPacketMonitorControls(); - } - else if (b == CHAN_PLUS_INDEX) { + } + else if (b == CHAN_PLUS_INDEX) { #ifndef HAS_DUAL_BAND if (set_channel < MAX_CHANNEL) set_channel++; @@ -10139,12 +10183,13 @@ bool WiFiScan::filterActive() { #endif changeChannel(); this->drawPacketMonitorControls(); - } - else if (b == EXIT_BUTTON_INDEX) { - this->StartScan(WIFI_SCAN_OFF); - this->orient_display = true; - return; - } + } + else if (b == EXIT_BUTTON_INDEX) { + this->StartScan(WIFI_SCAN_OFF); + this->orient_display = true; + return; + } + #endif if (currentTime - initTime >= PACKET_MONITOR_REFRESH_MS) { initTime = currentTime; @@ -10154,6 +10199,7 @@ bool WiFiScan::filterActive() { } #endif #endif +// GCOVR_EXCL_STOP void WiFiScan::changeChannel(int chan) { if (chan != -1) @@ -10161,6 +10207,10 @@ void WiFiScan::changeChannel(int chan) { esp_wifi_set_channel(this->set_channel, WIFI_SECOND_CHAN_NONE); delay(1); #ifdef HAS_SCREEN + #if defined(HAS_ILI9341) || (defined(MARAUDER_MINI_V3) && !defined(DUAL_MINI_C5)) // GCOVR_EXCL_LINE + if (this->currentScanMode == WIFI_PACKET_MONITOR) // GCOVR_EXCL_LINE -- requires a hardware TFT. + this->drawPacketMonitorControls(); // GCOVR_EXCL_LINE + #endif // GCOVR_EXCL_LINE if (this->currentScanMode == WIFI_SCAN_CHAN_ANALYZER) { #if !defined(MARAUDER_CARDPUTER) && !defined(MARAUDER_CARDPUTER_ADV) this->addAnalyzerValue(this->set_channel * -1, -72, this->_analyzer_values, TFT_WIDTH); @@ -12165,7 +12215,7 @@ void WiFiScan::main(uint32_t currentTime) else if (currentScanMode == WIFI_PACKET_MONITOR) { #ifdef HAS_SCREEN - #ifdef HAS_ILI9341 + #if defined(HAS_ILI9341) || (defined(MARAUDER_MINI_V3) && !defined(DUAL_MINI_C5)) // GCOVR_EXCL_LINE packetMonitorMain(currentTime); #endif #endif diff --git a/esp32_marauder/WiFiScan.h b/esp32_marauder/WiFiScan.h index d271b9ca5..1f2ea0b77 100644 --- a/esp32_marauder/WiFiScan.h +++ b/esp32_marauder/WiFiScan.h @@ -401,9 +401,13 @@ class WiFiScan WiFiClientSecure *client = new WiFiClientSecure(); #endif - #if defined(HAS_SCREEN) && defined(HAS_ILI9341) + #if defined(HAS_SCREEN) && (defined(HAS_ILI9341) || (defined(MARAUDER_MINI_V3) && !defined(DUAL_MINI_C5))) static const uint8_t PACKET_MONITOR_COLUMN_WIDTH = 4; - static const uint8_t PACKET_MONITOR_GRAPH_LEFT = 32; + #ifdef MARAUDER_MINI_V3 + static const uint8_t PACKET_MONITOR_GRAPH_LEFT = 24; + #else + static const uint8_t PACKET_MONITOR_GRAPH_LEFT = 32; + #endif static const uint16_t PACKET_MONITOR_REFRESH_MS = 200; static const uint16_t PACKET_MONITOR_HISTORY_LEN = (SCREEN_WIDTH - PACKET_MONITOR_GRAPH_LEFT) / PACKET_MONITOR_COLUMN_WIDTH; diff --git a/platformio.ini b/platformio.ini index 2a71fbfcc..ca72aedaa 100644 --- a/platformio.ini +++ b/platformio.ini @@ -21,6 +21,7 @@ build_src_filter = + + + + + build_flags = -std=gnu++17 -Wall diff --git a/test/test_menu_marquee/test_main.cpp b/test/test_menu_marquee/test_main.cpp new file mode 100644 index 000000000..304445328 --- /dev/null +++ b/test/test_menu_marquee/test_main.cpp @@ -0,0 +1,36 @@ +#include + +#include "MenuMarquee.h" + +void test_does_not_scroll_before_or_at_one_second() { + TEST_ASSERT_EQUAL_UINT16(0, MenuMarquee::offsetForElapsed(0, 12)); + TEST_ASSERT_EQUAL_UINT16(0, MenuMarquee::offsetForElapsed(999, 12)); + TEST_ASSERT_EQUAL_UINT16(0, MenuMarquee::offsetForElapsed(1000, 12)); +} + +void test_scrolls_after_one_second_and_advances_at_fixed_interval() { + TEST_ASSERT_EQUAL_UINT16(1, MenuMarquee::offsetForElapsed(1001, 12)); + TEST_ASSERT_EQUAL_UINT16(1, MenuMarquee::offsetForElapsed(1150, 12)); + TEST_ASSERT_EQUAL_UINT16(2, MenuMarquee::offsetForElapsed(1151, 12)); +} + +void test_does_not_scroll_when_label_fits() { + TEST_ASSERT_EQUAL_UINT16(0, MenuMarquee::offsetForElapsed(5000, 0)); +} + +void test_pauses_at_end_then_restarts() { + const uint16_t max_offset = 3; + + TEST_ASSERT_EQUAL_UINT16(3, MenuMarquee::offsetForElapsed(1301, max_offset)); + TEST_ASSERT_EQUAL_UINT16(3, MenuMarquee::offsetForElapsed(1901, max_offset)); + TEST_ASSERT_EQUAL_UINT16(0, MenuMarquee::offsetForElapsed(2201, max_offset)); +} + +int main(int, char**) { + UNITY_BEGIN(); + RUN_TEST(test_does_not_scroll_before_or_at_one_second); + RUN_TEST(test_scrolls_after_one_second_and_advances_at_fixed_interval); + RUN_TEST(test_does_not_scroll_when_label_fits); + RUN_TEST(test_pauses_at_end_then_restarts); + return UNITY_END(); +}