diff --git a/firmware/application/apps/ble_tx_app.cpp b/firmware/application/apps/ble_tx_app.cpp index 3140da1f..81a08fb4 100644 --- a/firmware/application/apps/ble_tx_app.cpp +++ b/firmware/application/apps/ble_tx_app.cpp @@ -189,19 +189,20 @@ void BLETxView::start() { } char advertisementData[63] = {0}; - strcpy(advertisementData, packets[current_packet].advertisementData); - if (!randomString.empty()) { - // Check if the substring exists within the larger string - const char* result = strstr(advertisementData, randomString.c_str()); + // TODO: Make this a checkbox. + if (!markedBytes.empty()) { + for (size_t i = 0; i < strlen(advertisementData); i++) { + bool found = false; - if (result != NULL) { - // Calculate the start and end positions of the substring - int startPos = result - advertisementData; - int endPos = startPos + randomString.length(); + auto it = std::find(markedBytes.begin(), markedBytes.end(), i); - for (int i = startPos; i < endPos; i++) { + if (it != markedBytes.end()) { + found = true; + } + + if (found) { int min = 0; int max = 15; @@ -317,9 +318,11 @@ BLETxView::BLETxView(NavigationView& nav) &label_mac_address, &text_mac_address, &label_data_packet, + &label_data_index, + &text_data_index, + &dataEditView, &button_save_packet, - &button_switch, - &dataEditView}); + &button_switch}); field_frequency.set_step(0); @@ -383,6 +386,26 @@ BLETxView::BLETxView(NavigationView& nav) button_switch.on_select = [&nav](Button&) { nav.replace(); }; + + dataEditView.on_select = [this] { + // Save last selected cursor. + cursor_pos.line = dataEditView.line(); + cursor_pos.col = dataEditView.col(); + + uint16_t dataBytePos = ((dataEditView.line()) * 30) + dataEditView.col(); + + auto it = std::find(markedBytes.begin(), markedBytes.end(), dataBytePos); + + if (it != markedBytes.end()) { + markedBytes.erase(it); + } else { + markedBytes.push_back(dataBytePos); + } + + dataEditView.cursor_mark_selected(); + + text_data_index.set(to_string_dec_uint(dataBytePos)); + }; } BLETxView::BLETxView( @@ -460,10 +483,6 @@ void BLETxView::on_data(uint32_t value, bool is_data) { } } -void BLETxView::on_random_data_change(std::string value) { - randomString = value; -} - void BLETxView::update_current_packet(BLETxPacket packet, uint32_t currentIndex) { std::string formattedMacAddress = to_string_formatted_mac_address(packet.macAddress); @@ -497,6 +516,7 @@ void BLETxView::update_current_packet(BLETxPacket packet, uint32_t currentIndex) dataEditView.set_font_zoom(true); dataEditView.set_file(*dataFileWrapper); + dataEditView.redraw(true, true); } void BLETxView::set_parent_rect(const Rect new_parent_rect) { diff --git a/firmware/application/apps/ble_tx_app.hpp b/firmware/application/apps/ble_tx_app.hpp index 14cb6819..647cb2bc 100644 --- a/firmware/application/apps/ble_tx_app.hpp +++ b/firmware/application/apps/ble_tx_app.hpp @@ -158,12 +158,16 @@ class BLETxView : public View { bool random_mac = false; bool file_override = false; - TextViewer dataEditView{ - {0, 9 * 16, 240, 240}}; + typedef struct { + uint16_t line; + uint16_t col; + } CursorPos; std::unique_ptr dataFileWrapper{}; File dataFile{}; std::filesystem::path dataTempFilePath{u"BLETX/dataFileTemp.TXT"}; + std::vector markedBytes{}; + CursorPos cursor_pos{}; static constexpr uint8_t mac_address_size_str{12}; static constexpr uint8_t max_packet_size_str{62}; @@ -171,15 +175,12 @@ class BLETxView : public View { static constexpr uint32_t max_packet_repeat_count{UINT32_MAX}; static constexpr uint32_t max_num_packets{32}; - std::string randomBuffer{}; - std::string randomString{}; - BLETxPacket packets[max_num_packets]; PKT_TYPE pduType = {PKT_TYPE_DISCOVERY}; static constexpr auto header_height = 9 * 16; - static constexpr auto switch_button_height = 3 * 16; + static constexpr auto switch_button_height = 5 * 16; Button button_open{ {0 * 8, 0 * 16, 10 * 8, 2 * 16}, @@ -258,10 +259,10 @@ class BLETxView : public View { "-"}; Labels label_packets_sent{ - {{0 * 8, 12 * 8}, "Packets Left:", Color::light_grey()}}; + {{0 * 8, 12 * 8}, "Repeat Count:", Color::light_grey()}}; Text text_packets_sent{ - {13 * 8, 6 * 16, 14 * 8, 16}, + {13 * 8, 6 * 16, 12 * 8, 16}, "-"}; Labels label_mac_address{ @@ -277,6 +278,16 @@ class BLETxView : public View { Console console{ {0, 9 * 16, 240, 240}}; + TextViewer dataEditView{ + {0, 9 * 16, 240, 240}}; + + Labels label_data_index{ + {{0 * 8, 28 * 8}, "Data Index:", Color::light_grey()}}; + + Text text_data_index{ + {11 * 8, 14 * 16, 8 * 8, 16}, + "-"}; + Button button_save_packet{ {1 * 8, 16 * 16, 13 * 8, 2 * 16}, "Save Packet"}; diff --git a/firmware/application/apps/ui_text_editor.cpp b/firmware/application/apps/ui_text_editor.cpp index 797a715d..2133ac4e 100644 --- a/firmware/application/apps/ui_text_editor.cpp +++ b/firmware/application/apps/ui_text_editor.cpp @@ -72,6 +72,11 @@ void TextViewer::paint(Painter& painter) { paint_state_.redraw_text = false; } + if (paint_state_.redraw_marked) { + paint_marked(painter); + paint_state_.redraw_marked = false; + } + paint_cursor(painter); } @@ -118,8 +123,9 @@ bool TextViewer::on_encoder(EncoderEvent delta) { return updated; } -void TextViewer::redraw(bool redraw_text) { +void TextViewer::redraw(bool redraw_text, bool redraw_marked) { paint_state_.redraw_text = redraw_text; + paint_state_.redraw_marked = redraw_marked; set_dirty(); } @@ -140,6 +146,27 @@ void TextViewer::cursor_end() { redraw(); } +void TextViewer::cursor_set(uint16_t line, uint16_t col) { + cursor_.line = line; + cursor_.col = col; +} + +void TextViewer::cursor_mark_selected() { + LineColPair newMarker = std::make_pair(cursor_.line, cursor_.col); + auto it = std::find(pairedVector.begin(), pairedVector.end(), newMarker); + + if (it != pairedVector.end()) { + pairedVector.erase(it); + } else { + pairedVector.push_back(newMarker); + } + + // Mark pending change. + cursor_.mark_change = false; + + redraw(); +} + uint16_t TextViewer::line_length() { return file_->line_length(cursor_.line); } @@ -247,12 +274,52 @@ void TextViewer::paint_cursor(Painter& painter) { }; if (paint_state_.line != UINT32_MAX) // only XOR old cursor if it still appears on the screen - xor_cursor(paint_state_.line, paint_state_.col); + { + // Only reset previous cursor if we aren't marking. + if (paint_state_.mark_change) { + xor_cursor(paint_state_.line, paint_state_.col); + } + } xor_cursor(cursor_.line, cursor_.col); paint_state_.line = cursor_.line; paint_state_.col = cursor_.col; + paint_state_.mark_change = cursor_.mark_change; + + // Reset marking and wait for new change. + cursor_.mark_change = true; +} + +void TextViewer::paint_marked(Painter& painter) { + auto xor_cursor = [this, &painter](int32_t line, uint16_t col) { + int cursor_width = char_width + 1; + int x = (col - paint_state_.first_col) * char_width - 1; + if (x < 0) { // cursor is one pixel narrower when in left column + cursor_width--; + x = 0; + } + int y = screen_rect().top() + (line - paint_state_.first_line) * char_height; + + // Converting one row at a time to reduce buffer size + auto pbuf8 = cursor_.pixel_buffer8; + auto pbuf = cursor_.pixel_buffer; + for (auto col = 0; col < char_height; col++) { + // Annoyingly, read_pixels uses a 24-bit pixel format vs draw_pixels which uses 16-bit + portapack::display.read_pixels({x, y + col, cursor_width, 1}, pbuf8, cursor_width); + for (auto i = 0; i < cursor_width; i++) + pbuf[i] = Color(pbuf8[i].r, pbuf8[i].g, pbuf8[i].b).v ^ 0xFFFF; + portapack::display.draw_pixels({x, y + col, cursor_width, 1}, pbuf, cursor_width); + } + }; + + auto it = pairedVector.begin(); + + while (it != pairedVector.end()) { + LineColPair entry = (LineColPair)*it; + xor_cursor(entry.first, entry.second); + it++; + } } void TextViewer::reset_file(FileWrapper* file) { diff --git a/firmware/application/apps/ui_text_editor.hpp b/firmware/application/apps/ui_text_editor.hpp index e393ef1f..85dc00de 100644 --- a/firmware/application/apps/ui_text_editor.hpp +++ b/firmware/application/apps/ui_text_editor.hpp @@ -59,7 +59,7 @@ class TextViewer : public Widget { bool on_key(KeyEvent key) override; bool on_encoder(EncoderEvent delta) override; - void redraw(bool redraw_text = false); + void redraw(bool redraw_text = false, bool redraw_marked = false); void set_file(FileWrapper& file) { reset_file(&file); } void clear_file() { reset_file(); } @@ -71,6 +71,11 @@ class TextViewer : public Widget { void cursor_home(); void cursor_end(); + void cursor_set(uint16_t line, uint16_t col); + void cursor_mark_selected(); + + typedef std::pair LineColPair; + std::vector pairedVector{}; // Gets the length of the current line. uint16_t line_length(); @@ -97,6 +102,7 @@ class TextViewer : public Widget { void paint_text(Painter& painter, uint32_t line, uint16_t col); void paint_cursor(Painter& painter); + void paint_marked(Painter& painter); void reset_file(FileWrapper* file = nullptr); @@ -111,6 +117,8 @@ class TextViewer : public Widget { uint32_t first_line{}; uint16_t first_col{}; bool redraw_text{true}; + bool redraw_marked{false}; + bool mark_change{true}; } paint_state_{}; struct { @@ -121,6 +129,8 @@ class TextViewer : public Widget { // Pixel buffer used for cursor XOR'ing - Max cursor width = Max char width + 1 ColorRGB888 pixel_buffer8[ui::char_width + 1]{}; Color pixel_buffer[ui::char_width + 1]{}; + + bool mark_change{true}; } cursor_{}; };