diff --git a/include/globals.h b/include/globals.h index b7f01c73a1..66fbdc601a 100644 --- a/include/globals.h +++ b/include/globals.h @@ -109,6 +109,13 @@ struct Option { void *hoverPointer; bool hovered; // return to the remote (webui or app) if it is hovered on the loopoptions + // Optional trailing icons drawn by loopOptions (opt-in; both default to + // "off" so existing menus are unchanged): + // iconRssi != 0 -> four-bar signal meter for that dBm value + // iconLock: 0 none, 1 locked/secured, 2 open + int iconRssi = 0; + uint8_t iconLock = 0; + Option( const char *lbl, const std::function &op, bool sel = false, bool (*hov)(void *hoverPointer, bool shouldRender) = nullptr, void *ptr = nullptr, bool hvrd = false, diff --git a/src/core/display.cpp b/src/core/display.cpp index 3b4f210baa..e6462a369b 100644 --- a/src/core/display.cpp +++ b/src/core/display.cpp @@ -825,12 +825,18 @@ Opt_Coord drawOptions( else tft.setTextColor(fgcolor, bgcolor); if (!options[i].enabled) tft.setTextColor(TFT_DARKGREY, bgcolor); + // Reserve room on the right for any opt-in trailing icons + int iconReserve = 0; + if (options[i].iconRssi != 0) iconReserve += 14; + if (options[i].iconLock != 0) iconReserve += 11; + int maxChars = (tftWidth * 0.8 - 10 - iconReserve) / (LW * FM) - 1; + String text = ""; if (i == index) { text += ">"; coord.x = tftWidth * 0.10 + 5 + FM * LW; coord.y = tft.getCursorY() + 4; - coord.size = (tftWidth * 0.8 - 10) / (LW * FM) - 1; + coord.size = maxChars; coord.fgcolor = fgcolor; coord.bgcolor = bgcolor; } else text += " "; @@ -839,11 +845,27 @@ Opt_Coord drawOptions( // Draw text with appropriate colors for selection if (i == index) { tft.setTextColor(bgcolor, bruceConfig.priColor); } - tft.println(text.substring(0, (tftWidth * 0.8 - 10) / (LW * FM) - 1)); + tft.println(text.substring(0, maxChars)); // Reset text color for next item tft.setTextColor(fgcolor, bgcolor); + // Optional trailing icons (signal strength / lock), right-aligned + if (options[i].iconRssi != 0 || options[i].iconLock != 0) { + uint16_t iconCol = (i == index) ? bgcolor : fgcolor; + int iconY = cursorY + 4 + (FM * LH - 8) / 2; + int ix = tftWidth * 0.10 + tftWidth * 0.8 - 6; + if (options[i].iconRssi != 0) { + ix -= 11; + drawSignalBars(ix, iconY, options[i].iconRssi, iconCol); + ix -= 3; + } + if (options[i].iconLock != 0) { + ix -= 9; + drawLockIcon(ix, iconY, options[i].iconLock == 1, iconCol); + } + } + cont++; } if (cont > MAX_MENU_SIZE) break; @@ -1737,6 +1759,33 @@ uint16_t getColorVariation(uint16_t color, int delta, int direction) { return compl_color; } +void drawSignalBars(int x, int y, int rssi, uint16_t color) { + int bars = 0; + if (rssi > -55) bars = 4; + else if (rssi > -68) bars = 3; + else if (rssi > -80) bars = 2; + else if (rssi > -92) bars = 1; + for (int i = 0; i < 4; i++) { + int h = 2 + i * 2; + if (i < bars) tft.fillRect(x + i * 3, y + 8 - h, 2, h, color); + else tft.drawFastHLine(x + i * 3, y + 7, 2, color); + } +} + +void drawLockIcon(int x, int y, bool locked, uint16_t color) { + // Solid body (drawn in `color` so it reads on any row background) + tft.fillRect(x, y + 4, 9, 6, color); + if (locked) { + // Closed shackle centered over the body + tft.drawRoundRect(x + 2, y, 5, 6, 2, color); + } else { + // Open shackle: an unlatched hook to the right + tft.drawFastHLine(x + 3, y, 4, color); + tft.drawFastVLine(x + 6, y, 5, color); + tft.drawPixel(x + 2, y + 1, color); + } +} + // Draw BITMAP files // These read 16- and 32-bit types from the SD card file. // BMP data is stored little-endian, Arduino is little-endian too. diff --git a/src/core/display.h b/src/core/display.h index 97c276e354..50db8bfa41 100644 --- a/src/core/display.h +++ b/src/core/display.h @@ -108,6 +108,16 @@ uint16_t getComplementaryColor(uint16_t color); uint16_t getComplementaryColor2(uint16_t color); uint16_t getColorVariation(uint16_t color, int delta = 10, int direction = 0); +// Small four-bar signal-strength meter icon (RSSI, in dBm). Draws in an +// ~11x8 px box with its top-left at (x, y); stronger signal fills more bars. +// Shared by the WiFi/BLE scan lists so signal reads at a glance per entry. +void drawSignalBars(int x, int y, int rssi, uint16_t color); + +// Small padlock icon (~9x10 px, top-left at x,y). `locked` draws a closed +// shackle, otherwise an open one — flags secured vs open networks. Drawn in +// `color`, so it stays visible on any row background. +void drawLockIcon(int x, int y, bool locked, uint16_t color); + void resetTftDisplay( int x = 0, int y = 0, uint16_t fc = bruceConfig.priColor, int size = FM, uint16_t bg = bruceConfig.bgColor, uint16_t screen = bruceConfig.bgColor diff --git a/src/core/wifi/wifi_common.cpp b/src/core/wifi/wifi_common.cpp index 65c921ca73..1d2a7924c6 100644 --- a/src/core/wifi/wifi_common.cpp +++ b/src/core/wifi/wifi_common.cpp @@ -218,8 +218,6 @@ bool wifiConnectMenu(wifi_mode_t mode) { int encryptionType = WiFi.encryptionType(i); int32_t rssi = WiFi.RSSI(i); int32_t ch = WiFi.channel(i); - // Check if the network is secured - String encryptionPrefix = (encryptionType == WIFI_AUTH_OPEN) ? "" : "#"; String encryptionTypeStr; switch (encryptionType) { case WIFI_AUTH_OPEN: encryptionTypeStr = "Open"; break; @@ -233,13 +231,15 @@ bool wifiConnectMenu(wifi_mode_t mode) { default: encryptionTypeStr = "Unknown"; break; } - String optionText = encryptionPrefix + ssid + "(" + String(rssi) + "|" + - encryptionTypeStr + "|ch." + String(ch) + ")"; + String optionText = ssid + " (" + encryptionTypeStr + "|ch." + String(ch) + ")"; - options.push_back({optionText.c_str(), [&selSsid, &selEnc, ssid, encryptionType]() { - selSsid = ssid; - selEnc = encryptionType; - }}); + Option opt(optionText.c_str(), [&selSsid, &selEnc, ssid, encryptionType]() { + selSsid = ssid; + selEnc = encryptionType; + }); + opt.iconRssi = rssi; + opt.iconLock = (encryptionType == WIFI_AUTH_OPEN) ? 2 : 1; + options.push_back(opt); } } WiFi.scanDelete(); diff --git a/src/modules/NRF24/nrf_common.cpp b/src/modules/NRF24/nrf_common.cpp index 7578344f62..07a8ae3119 100644 --- a/src/modules/NRF24/nrf_common.cpp +++ b/src/modules/NRF24/nrf_common.cpp @@ -7,17 +7,13 @@ HardwareSerial NRFSerial = HardwareSerial(2); // Uses UART2 for External NRF's SPIClass *NRFSPI; void nrf_info() { - tft.fillScreen(bruceConfig.bgColor); - tft.setTextSize(FM); - tft.setTextColor(TFT_RED, bruceConfig.bgColor); - tft.drawCentreString("_Disclaimer_", tftWidth / 2, 10, 1); - tft.setTextColor(TFT_WHITE, bruceConfig.bgColor); + drawMainBorderWithTitle("NRF24 INFO"); + printSubtitle("Disclaimer"); tft.setTextSize(FP); - tft.setCursor(15, 33); + padprintln(""); padprintln("These functions were made to be used in a controlled environment for STUDY only."); padprintln(""); padprintln("DO NOT use these functions to harm people or companies, you can go to jail!"); - tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); padprintln(""); padprintln( "This device is VERY sensible to noise, so long wires or passing near VCC line can make " diff --git a/src/modules/badusb_ble/ducky_typer.cpp b/src/modules/badusb_ble/ducky_typer.cpp index c1a2f5ed73..cba7e42be7 100644 --- a/src/modules/badusb_ble/ducky_typer.cpp +++ b/src/modules/badusb_ble/ducky_typer.cpp @@ -839,7 +839,7 @@ void key_input(FS fs, const String &bad_script, HIDInterface *_hid) { ); if (!bruceConfig.badUSBBLEShowOutput) { tft.setTextSize(FP); - tft.setTextColor(TFT_RED); + tft.setTextColor(getColorVariation(bruceConfig.priColor, 8, -1)); tft.setCursor(BORDER_OFFSET_FROM_SCREEN_EDGE * 2, tftHeight / 2); tft.print("Script output disabled"); } @@ -954,10 +954,10 @@ void key_input(FS fs, const String &bad_script, HIDInterface *_hid) { } else if (PriCmd->type != DuckyCommandType_Comment) { printTFTBadUSBBLE(Command, bruceConfig.priColor); if (Argument.length() > 0) { - printTFTBadUSBBLE(" " + Argument, (ArgCmd == nullptr ? TFT_WHITE : TFT_WHITE), true); - } else printTFTBadUSBBLE("", TFT_WHITE, true); + printTFTBadUSBBLE(" " + Argument, bruceConfig.priColor, true); + } else printTFTBadUSBBLE("", bruceConfig.priColor, true); } else if (PriCmd->type == DuckyCommandType_Comment) { - printTFTBadUSBBLE(Argument, TFT_DARKGREEN, true); + printTFTBadUSBBLE(Argument, getColorVariation(bruceConfig.priColor, 8, 1), true); } } @@ -1410,13 +1410,7 @@ void PresenterMode(HIDInterface *&hid, bool ble) { bool timerStarted = false; auto drawStaticUI = [&]() { - tft.fillScreen(bruceConfig.bgColor); - - tft.setTextSize(FM); - tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); - tft.drawCentreString("PRESENTER", tftWidth / 2, 10, 1); - - tft.drawFastHLine(10, 35, tftWidth - 20, bruceConfig.priColor); + drawMainBorderWithTitle("PRESENTER"); tft.setTextSize(FM); tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); @@ -1431,7 +1425,7 @@ void PresenterMode(HIDInterface *&hid, bool ble) { tft.fillRect(0, tftHeight / 2 - 35, tftWidth, 40, bruceConfig.bgColor); tft.setTextSize(4); - tft.setTextColor(TFT_WHITE, bruceConfig.bgColor); + tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); String slideStr = "Slide " + String(currentSlide); tft.drawCentreString(slideStr, tftWidth / 2, tftHeight / 2 - 30, 1); lastDisplayedSlide = currentSlide; @@ -1454,7 +1448,10 @@ void PresenterMode(HIDInterface *&hid, bool ble) { tft.fillRect(0, tftHeight / 2 + 30, tftWidth, 30, bruceConfig.bgColor); tft.setTextSize(3); - tft.setTextColor(timerStarted ? TFT_GREEN : TFT_DARKGREY, bruceConfig.bgColor); + tft.setTextColor( + timerStarted ? bruceConfig.priColor : getColorVariation(bruceConfig.priColor, 8, -1), + bruceConfig.bgColor + ); tft.drawCentreString(timeBuffer, tftWidth / 2, tftHeight / 2 + 35, 1); lastDisplayedSeconds = elapsed; diff --git a/src/modules/ble/ble_common.cpp b/src/modules/ble/ble_common.cpp index e814d033ec..0eac23d4d1 100644 --- a/src/modules/ble/ble_common.cpp +++ b/src/modules/ble/ble_common.cpp @@ -217,7 +217,9 @@ void ble_scan() { if (bt_title.isEmpty()) bt_title = bt_address; if (options.size() < MAX_DISPLAY_DEVICES) { - options.emplace_back(bt_title.c_str(), [=]() { ble_info(bt_name, bt_address, bt_signal); }); + Option opt(bt_title.c_str(), [=]() { ble_info(bt_name, bt_address, bt_signal); }); + opt.iconRssi = advertisedDevice->getRSSI(); + options.push_back(opt); processedCount++; } } diff --git a/src/modules/ble/ble_ninebot.cpp b/src/modules/ble/ble_ninebot.cpp index b7a6b16b57..d217be89e6 100644 --- a/src/modules/ble/ble_ninebot.cpp +++ b/src/modules/ble/ble_ninebot.cpp @@ -185,6 +185,7 @@ void BLENinebot::loop() { loopOptions(deviceSelection); }} ); + deviceSelection.back().iconRssi = adv->getRSSI(); } bool returnToMenu = false; diff --git a/src/modules/ethernet/HostInfo.cpp b/src/modules/ethernet/HostInfo.cpp index 3ba43f7f25..31c1d87c03 100644 --- a/src/modules/ethernet/HostInfo.cpp +++ b/src/modules/ethernet/HostInfo.cpp @@ -94,8 +94,7 @@ void HostInfo::setup(const Host &host) { }; const int portCount = sizeof(portNumbers) / sizeof(portNumbers[0]); - // Initialize display - drawMainBorder(); + // Initialize display — ScrollableTextArea draws its own titled frame. tft.setTextSize(FP); ScrollableTextArea area = ScrollableTextArea("HOST INFO"); diff --git a/src/modules/ir/ir_jammer.cpp b/src/modules/ir/ir_jammer.cpp index 23c045e5c8..b58b7c6943 100644 --- a/src/modules/ir/ir_jammer.cpp +++ b/src/modules/ir/ir_jammer.cpp @@ -123,7 +123,7 @@ void renderModeSettings(JammerState &state, int &curY, int ySpacing) { curY += ySpacing; tft.setCursor(10, curY); tft.setTextColor( - (state.settingIndex == 3) ? TFT_YELLOW : bruceConfig.priColor, bruceConfig.bgColor + (state.settingIndex == 3) ? bruceConfig.priColor : getColorVariation(bruceConfig.priColor, 8, -1), bruceConfig.bgColor ); padprint("TIMING: "); tft.print(String(state.markTiming)); @@ -135,7 +135,7 @@ void renderModeSettings(JammerState &state, int &curY, int ySpacing) { curY += ySpacing; tft.setCursor(10, curY); tft.setTextColor( - (state.settingIndex == 3) ? TFT_YELLOW : bruceConfig.priColor, bruceConfig.bgColor + (state.settingIndex == 3) ? bruceConfig.priColor : getColorVariation(bruceConfig.priColor, 8, -1), bruceConfig.bgColor ); padprint("MARK: "); tft.print(String(state.markTiming)); @@ -144,7 +144,7 @@ void renderModeSettings(JammerState &state, int &curY, int ySpacing) { curY += ySpacing; tft.setCursor(10, curY); tft.setTextColor( - (state.settingIndex == 4) ? TFT_YELLOW : bruceConfig.priColor, bruceConfig.bgColor + (state.settingIndex == 4) ? bruceConfig.priColor : getColorVariation(bruceConfig.priColor, 8, -1), bruceConfig.bgColor ); padprint("SPACE: "); tft.print(String(state.spaceTiming)); @@ -153,7 +153,7 @@ void renderModeSettings(JammerState &state, int &curY, int ySpacing) { curY += ySpacing; tft.setCursor(10, curY); tft.setTextColor( - (state.settingIndex == 5) ? TFT_YELLOW : bruceConfig.priColor, bruceConfig.bgColor + (state.settingIndex == 5) ? bruceConfig.priColor : getColorVariation(bruceConfig.priColor, 8, -1), bruceConfig.bgColor ); padprint("POWER: "); tft.println(String(state.jamDensity)); @@ -164,7 +164,7 @@ void renderModeSettings(JammerState &state, int &curY, int ySpacing) { curY += ySpacing; tft.setCursor(10, curY); tft.setTextColor( - (state.settingIndex == 3) ? TFT_YELLOW : bruceConfig.priColor, bruceConfig.bgColor + (state.settingIndex == 3) ? bruceConfig.priColor : getColorVariation(bruceConfig.priColor, 8, -1), bruceConfig.bgColor ); padprint("MIN: "); tft.print(String(state.minTiming)); @@ -173,7 +173,7 @@ void renderModeSettings(JammerState &state, int &curY, int ySpacing) { curY += ySpacing; tft.setCursor(10, curY); tft.setTextColor( - (state.settingIndex == 4) ? TFT_YELLOW : bruceConfig.priColor, bruceConfig.bgColor + (state.settingIndex == 4) ? bruceConfig.priColor : getColorVariation(bruceConfig.priColor, 8, -1), bruceConfig.bgColor ); padprint("MAX: "); tft.print(String(state.maxTiming)); @@ -182,7 +182,7 @@ void renderModeSettings(JammerState &state, int &curY, int ySpacing) { curY += ySpacing; tft.setCursor(10, curY); tft.setTextColor( - (state.settingIndex == 5) ? TFT_YELLOW : bruceConfig.priColor, bruceConfig.bgColor + (state.settingIndex == 5) ? bruceConfig.priColor : getColorVariation(bruceConfig.priColor, 8, -1), bruceConfig.bgColor ); padprint("SPEED: "); tft.println(String(state.sweepSpeed)); @@ -190,7 +190,7 @@ void renderModeSettings(JammerState &state, int &curY, int ySpacing) { curY += ySpacing; tft.setCursor(10, curY); tft.setTextColor( - (state.settingIndex == 6) ? TFT_YELLOW : bruceConfig.priColor, bruceConfig.bgColor + (state.settingIndex == 6) ? bruceConfig.priColor : getColorVariation(bruceConfig.priColor, 8, -1), bruceConfig.bgColor ); padprint("POWER: "); tft.println(String(state.jamDensity)); @@ -202,7 +202,7 @@ void renderModeSettings(JammerState &state, int &curY, int ySpacing) { curY += ySpacing; tft.setCursor(10, curY); tft.setTextColor( - (state.settingIndex == 3) ? TFT_YELLOW : bruceConfig.priColor, bruceConfig.bgColor + (state.settingIndex == 3) ? bruceConfig.priColor : getColorVariation(bruceConfig.priColor, 8, -1), bruceConfig.bgColor ); padprint("POWER: "); tft.println(String(state.jamDensity)); @@ -222,7 +222,7 @@ void displayStats(JammerState &state, int x, int y) { // Set text properties for the stats display tft.setTextSize(FP); tft.setCursor(tftWidth / 2, tftHeight / 2); - tft.setTextColor(TFT_GREEN, bruceConfig.bgColor); + tft.setTextColor(getColorVariation(bruceConfig.priColor, 8, 1), bruceConfig.bgColor); // Calculate running time in seconds uint32_t runtime = (millis() - state.startTime) / 1000; @@ -380,8 +380,8 @@ void setupJammer(IRsend &irsend) { // Configure IR LED pin as output setup_ir_pin(bruceConfigPins.irTx, OUTPUT); - // Draw UI border on the display - drawMainBorder(); + // Draw the standard Bruce frame with title + drawMainBorderWithTitle("IR JAMMER"); } /** @@ -401,46 +401,43 @@ void renderJammerUI(JammerState &state) { // Calculate layout dimensions based on screen size int contentWidth = tftWidth - 20; - int yStart = 35; + int yStart = BORDER_PAD_Y + 8 * FM + 1; // just below the Bruce frame title int ySpacing = 10; int rightColumnX = tftWidth / 2 + 10; // Full screen redraw only when necessary if (state.redraw) { - // Clear content area - tft.fillRect(10, yStart, contentWidth, tftHeight - 55, bruceConfig.bgColor); - - // Draw title - tft.setCursor(10, yStart); - tft.setTextSize(FM); - tft.setTextColor(TFT_CYAN, bruceConfig.bgColor); - padprint("IR Jammer"); - tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); + // Clear content area (below the frame title) + tft.fillRect(10, yStart, contentWidth, tftHeight - yStart - 15, bruceConfig.bgColor); } - // Show activity indicator when jamming is active - if (state.jamming_active && blinkState) { - tft.setCursor(tftWidth / 2, tft.getCursorY()); - tft.setTextColor(TFT_MAGENTA, bruceConfig.bgColor); - tft.println("*"); - } else { - tft.setCursor(tftWidth / 2, tft.getCursorY()); - tft.println(" "); - } + // Blinking activity dot (top-right of the content) while jamming + tft.setTextSize(FP); + tft.setCursor(tftWidth - 20, yStart); + tft.setTextColor( + (state.jamming_active && blinkState) ? bruceConfig.priColor : bruceConfig.bgColor, bruceConfig.bgColor + ); + tft.print("*"); // Display current status with highlighting for selected setting - int curY = yStart + 20; + int curY = yStart; tft.setCursor(10, curY); tft.setTextSize(FP); - tft.setTextColor((state.settingIndex == 0) ? TFT_YELLOW : bruceConfig.priColor, bruceConfig.bgColor); + tft.setTextColor( + (state.settingIndex == 0) ? bruceConfig.priColor : getColorVariation(bruceConfig.priColor, 8, -1), + bruceConfig.bgColor + ); padprint("STATUS: "); - tft.setTextColor(state.jamming_active ? TFT_RED : TFT_WHITE, bruceConfig.bgColor); + tft.setTextColor( + state.jamming_active ? bruceConfig.priColor : getColorVariation(bruceConfig.priColor, 8, -1), + bruceConfig.bgColor + ); tft.println(state.jamming_active ? "ACTIVE " : "PAUSED "); // Display frequency setting curY += ySpacing + 10; tft.setCursor(10, curY); - tft.setTextColor((state.settingIndex == 1) ? TFT_YELLOW : bruceConfig.priColor, bruceConfig.bgColor); + tft.setTextColor((state.settingIndex == 1) ? bruceConfig.priColor : getColorVariation(bruceConfig.priColor, 8, -1), bruceConfig.bgColor); padprint("FREQ: "); tft.print(String(getFrequency(state.current_freq_idx) / 1000)); tft.println(" kHz "); @@ -448,7 +445,7 @@ void renderJammerUI(JammerState &state) { // Display mode selection curY += ySpacing; tft.setCursor(10, curY); - tft.setTextColor((state.settingIndex == 2) ? TFT_YELLOW : bruceConfig.priColor, bruceConfig.bgColor); + tft.setTextColor((state.settingIndex == 2) ? bruceConfig.priColor : getColorVariation(bruceConfig.priColor, 8, -1), bruceConfig.bgColor); padprint("MODE: "); tft.println(getModeName(state.currentMode)); @@ -462,13 +459,8 @@ void renderJammerUI(JammerState &state) { int instructionsY = tftHeight - 20; tft.setCursor(10, instructionsY); tft.setTextSize(FP); - tft.setTextColor(TFT_BLUE, bruceConfig.bgColor); - padprintln("[SEL] Change Set. | [NEXT/PREV] Adjust Val. "); - - // Display exit instruction in top-right corner - tft.setTextColor(TFT_RED, bruceConfig.bgColor); - tft.setCursor(tftWidth - 70, 30); - tft.print("[ESC] Exit"); + tft.setTextColor(getColorVariation(bruceConfig.priColor, 8, -1), bruceConfig.bgColor); + padprintln("[SEL] Change | [NEXT/PREV] Adjust"); tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); // Reset redraw flag now that UI is updated diff --git a/src/modules/ir/ir_read.cpp b/src/modules/ir/ir_read.cpp index 2f9cc95680..013226f432 100644 --- a/src/modules/ir/ir_read.cpp +++ b/src/modules/ir/ir_read.cpp @@ -202,18 +202,13 @@ void IrRead::begin() { } void IrRead::cls() { - drawMainBorder(); - tft.setCursor(10, 28); + drawMainBorderWithTitle("IR READ"); tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); } void IrRead::display_banner() { cls(); - tft.setTextSize(FM); - padprintln("IR Read"); - tft.setTextSize(FP); - padprintln("--------------"); padprintln("Signals captured: " + String(signals_read)); tft.println(""); } diff --git a/src/modules/others/clicker.cpp b/src/modules/others/clicker.cpp index fd56550383..41029675ed 100644 --- a/src/modules/others/clicker.cpp +++ b/src/modules/others/clicker.cpp @@ -131,7 +131,7 @@ void drawMenuItem( // Draw selection border (green when editing, white when selected) if (isSelected && itemIndex != ITEM_START) { - uint16_t borderColor = isEdit ? TFT_GREEN : bruceConfig.priColor; + uint16_t borderColor = isEdit ? getColorVariation(bruceConfig.priColor, 8, 1) : bruceConfig.priColor; int borderWidth = isEdit ? 2 : 1; for (int i = 0; i < borderWidth; i++) { @@ -164,7 +164,7 @@ void drawMenuItem( int numWidth = strlen(delayStr) * 6 * layout.text_size_large; tft.setCursor(unitX - numWidth - 12, contentY); - if (isEdit && isSelected) tft.setTextColor(TFT_YELLOW, bruceConfig.bgColor); + if (isEdit && isSelected) tft.setTextColor(getColorVariation(bruceConfig.priColor, 8, 1), bruceConfig.bgColor); tft.print(delayStr); tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); @@ -182,7 +182,7 @@ void drawMenuItem( int textWidth = strlen(btnName) * 6 * layout.text_size_large; tft.setCursor(unitX - textWidth, contentY); - if (isEdit && isSelected) tft.setTextColor(TFT_YELLOW, bruceConfig.bgColor); + if (isEdit && isSelected) tft.setTextColor(getColorVariation(bruceConfig.priColor, 8, 1), bruceConfig.bgColor); tft.print(btnName); break; } @@ -198,14 +198,14 @@ void drawMenuItem( const char *customText = "Custom"; int textWidth = strlen(customText) * 6 * layout.text_size_large; tft.setCursor(unitX - textWidth, contentY); - if (isEdit && isSelected) tft.setTextColor(TFT_YELLOW, bruceConfig.bgColor); + if (isEdit && isSelected) tft.setTextColor(getColorVariation(bruceConfig.priColor, 8, 1), bruceConfig.bgColor); tft.print(customText); } else if (config.max_clicks == 0) { // Show "Infinite" or "INF" for unlimited clicks const char *infText = (tftWidth > 200) ? "Infinite" : "INF"; int textWidth = strlen(infText) * 6 * layout.text_size_large; tft.setCursor(unitX - textWidth, contentY); - if (isEdit && isSelected) tft.setTextColor(TFT_YELLOW, bruceConfig.bgColor); + if (isEdit && isSelected) tft.setTextColor(getColorVariation(bruceConfig.priColor, 8, 1), bruceConfig.bgColor); tft.print(infText); } else { // Show numeric value @@ -213,7 +213,7 @@ void drawMenuItem( snprintf(clicksStr, sizeof(clicksStr), "%d", config.max_clicks); int numWidth = strlen(clicksStr) * 6 * layout.text_size_large; tft.setCursor(unitX - numWidth, contentY); - if (isEdit && isSelected) tft.setTextColor(TFT_YELLOW, bruceConfig.bgColor); + if (isEdit && isSelected) tft.setTextColor(getColorVariation(bruceConfig.priColor, 8, 1), bruceConfig.bgColor); tft.print(clicksStr); } break; @@ -221,13 +221,13 @@ void drawMenuItem( case ITEM_START: { // Draw styled button - uint16_t btnColor = isSelected ? TFT_DARKGREEN : TFT_DARKGREY; + uint16_t btnColor = isSelected ? bruceConfig.priColor : getColorVariation(bruceConfig.priColor, 8, -1); tft.fillRoundRect( layout.margin, yPos, tftWidth - 2 * layout.margin, layout.button_height, 8, btnColor ); // Centered text - tft.setTextColor(TFT_WHITE, btnColor); + tft.setTextColor(bruceConfig.bgColor, btnColor); const char *btnText = (tftWidth > 200) ? "START CLICK" : "START"; int textWidth = strlen(btnText) * 6 * layout.text_size_large; tft.setCursor( @@ -276,9 +276,9 @@ void drawClickingScreen(const LayoutConfig &layout, const char *buttonName) { // Animated header to show active state const int headerHeight = (tftHeight > 200) ? 40 : 30; - tft.fillRect(0, 0, tftWidth, headerHeight, TFT_DARKGREEN); + tft.fillRect(0, 0, tftWidth, headerHeight, bruceConfig.priColor); tft.setTextSize((tftWidth > 200) ? 2 : 1); - tft.setTextColor(TFT_WHITE, TFT_DARKGREEN); + tft.setTextColor(bruceConfig.bgColor, bruceConfig.priColor); const char *headerText = (tftWidth > 200) ? "-> CLICKING <-" : "-> CLICK <-"; int headerWidth = strlen(headerText) * 6 * ((tftWidth > 200) ? 2 : 1); @@ -328,7 +328,7 @@ void updateCPSDisplay(const LayoutConfig &layout, int currentCPS, unsigned long char cpsStr[16]; snprintf(cpsStr, sizeof(cpsStr), "%d", currentCPS); tft.setTextSize(cpsSize); - tft.setTextColor(TFT_GREEN, bruceConfig.bgColor); + tft.setTextColor(getColorVariation(bruceConfig.priColor, 8, 1), bruceConfig.bgColor); int cpsWidth = strlen(cpsStr) * 6 * cpsSize; tft.setCursor((tftWidth - cpsWidth) / 2, cpsY); tft.print(cpsStr); @@ -376,9 +376,9 @@ void drawSummaryScreen( tft.fillScreen(bruceConfig.bgColor); // Header shows completion status - uint16_t headerColor = completed ? TFT_DARKGREEN : TFT_DARKGREY; + uint16_t headerColor = completed ? bruceConfig.priColor : getColorVariation(bruceConfig.priColor, 8, -1); tft.fillRect(0, 0, tftWidth, layout.header_height, headerColor); - tft.setTextColor(TFT_WHITE, headerColor); + tft.setTextColor(bruceConfig.bgColor, headerColor); tft.setTextSize(layout.text_size_large); const char *statusText = completed ? "COMPLETED" : "STOPPED"; @@ -425,9 +425,9 @@ void drawUSBInitScreen(const LayoutConfig &layout) { // Header const int headerHeight = (tftHeight > 200) ? 40 : 30; - tft.fillRect(0, 0, tftWidth, headerHeight, TFT_ORANGE); + tft.fillRect(0, 0, tftWidth, headerHeight, bruceConfig.priColor); tft.setTextSize((tftWidth > 200) ? 2 : 1); - tft.setTextColor(TFT_WHITE, TFT_ORANGE); + tft.setTextColor(bruceConfig.bgColor, bruceConfig.priColor); const char *headerText = "USB INIT"; int headerWidth = strlen(headerText) * 6 * ((tftWidth > 200) ? 2 : 1); @@ -451,7 +451,7 @@ void drawUSBInitScreen(const LayoutConfig &layout) { // Countdown animation (3 seconds) tft.setTextSize((tftWidth > 200) ? 3 : 2); - tft.setTextColor(TFT_YELLOW, bruceConfig.bgColor); + tft.setTextColor(getColorVariation(bruceConfig.priColor, 8, 1), bruceConfig.bgColor); for (int i = 3; i > 0; i--) { // Clear previous number @@ -830,7 +830,7 @@ void clicker_setup() { delay(2000); // Show restart/exit prompt - tft.setTextColor(TFT_WHITE, bruceConfig.bgColor); + tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); tft.setTextSize(layout.text_size_small); int promptY = tftHeight - 20; tft.fillRect(0, promptY - 5, tftWidth, 25, bruceConfig.bgColor); diff --git a/src/modules/others/timer.cpp b/src/modules/others/timer.cpp index 9443d7e64d..2a6f7d7452 100644 --- a/src/modules/others/timer.cpp +++ b/src/modules/others/timer.cpp @@ -186,7 +186,7 @@ void Timer::loop() { } } - drawMainBorder(false); + drawMainBorderWithTitle("TIMER", false); tft.setTextSize(f_size); tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); tft.drawCentreString(timeString, timerX, timerY, 1); diff --git a/src/modules/others/u2f.cpp b/src/modules/others/u2f.cpp index 982e47aa5a..a1b6a3f743 100644 --- a/src/modules/others/u2f.cpp +++ b/src/modules/others/u2f.cpp @@ -1668,27 +1668,24 @@ U2fHidDevice &u2fDevice() { } void drawU2fStatusScreen() { - tft.fillScreen(bruceConfig.bgColor); - tft.setTextSize(2); - tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); - tft.setCursor(6, 8); - tft.print("USB U2F"); - - tft.setTextSize(1); - tft.setCursor(6, 36); - tft.print("Ready for registration/login"); - tft.setCursor(6, 50); - tft.print("Press center when prompted"); - tft.setCursor(6, 64); - tft.print("ESC: Back"); + drawMainBorderWithTitle("USB U2F"); + printSubtitle("FIDO / U2F Authenticator"); + tft.setTextSize(FP); + padprintln(""); + padprintln("Ready for registration/login"); + padprintln("Press center when prompted"); + padprintln(""); + padprintln("ESC: Back"); } void updateU2fRuntimeInfo(const U2fHidDevice &device) { - tft.fillRect(0, 84, tftWidth, tftHeight - 84, bruceConfig.bgColor); - tft.setTextSize(2); - tft.setCursor(6, 94); + int y = tftHeight - 34; + tft.fillRect(BORDER_PAD_X, y - 2, tftWidth - 2 * BORDER_PAD_X, 20, bruceConfig.bgColor); + tft.setTextSize(FM); + tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); + tft.setCursor(BORDER_PAD_X, y); tft.print(device.waitingForPresence() ? "Confirm now" : "Waiting..."); - tft.setTextSize(1); + tft.setTextSize(FP); } } // namespace diff --git a/src/modules/reverseShell/reverseShell.cpp b/src/modules/reverseShell/reverseShell.cpp index 7aaeb978a1..a6ba06b9d3 100644 --- a/src/modules/reverseShell/reverseShell.cpp +++ b/src/modules/reverseShell/reverseShell.cpp @@ -66,33 +66,28 @@ void ReverseShell() { }; // ── Setup ────────────────────────────────────────────────── - tft.fillScreen(bruceConfig.bgColor); - tft.setTextSize(FM); - tft.setTextColor(TFT_RED, bruceConfig.bgColor); - tft.drawCentreString("Reverse Shell", tftWidth / 2, 10, 1); - tft.setTextColor(TFT_WHITE, bruceConfig.bgColor); - tft.setTextSize(FP); - tft.setCursor(15, 33); - tft.println("Developed by Fourier & Ninja-jr"); - tft.println("Starting reverse shell server..."); + drawMainBorderWithTitle("REVERSE SHELL"); + printSubtitle("by Fourier & Ninja-jr"); + padprintln(""); + padprintln("Starting reverse shell server..."); WiFi.mode(WIFI_AP); if (!WiFi.softAPConfig(apGateway, apGateway, IPAddress(255, 255, 255, 0))) { - tft.println("Failed to configure AP"); + padprintln("Failed to configure AP"); return; } // ── AP Password: bruce ───────────────────────────────────── if (!WiFi.softAP("BruceShell", "bruce")) { - tft.println("Failed to start AP"); + padprintln("Failed to start AP"); return; } - tft.println("Wi-Fi AP Started: BruceShell (pass: bruce)"); - tft.println("IP: " + apGateway.toString()); + padprintln("Wi-Fi AP Started: BruceShell (pass: bruce)"); + padprintln("IP: " + apGateway.toString()); tcpServer.begin(); - tft.println("TCP server started on port 23."); + padprintln("TCP server started on port 23."); // ── Web Interface ────────────────────────────────────────── ws.onEvent(onWsEvent); @@ -162,8 +157,8 @@ void ReverseShell() { }); webServer.begin(); - tft.println("Web server started on port 80!"); - tft.println("WebSocket server started on /ws"); + padprintln("Web server started on port 80!"); + padprintln("WebSocket server started on /ws"); dnsServer.start(53, "*", apGateway); @@ -175,7 +170,7 @@ void ReverseShell() { if (!shellConnected) { tcpClient = tcpServer.accept(); if (tcpClient) { - tft.println("Client connected."); + padprintln("Client connected."); tcpClient.println("~Welcome to BruceShell."); tcpClient.println("~Developed by Fourier & Ninja-jr"); tcpClient.println("~Type 'help' for available commands"); @@ -184,13 +179,13 @@ void ReverseShell() { } if (shellConnected && !tcpClient.connected()) { - tft.println("Client disconnected."); + padprintln("Client disconnected."); shellConnected = false; tcpClient.stop(); } if (check(EscPress)) { - tft.println("Exiting reverse shell server..."); + padprintln("Exiting reverse shell server..."); tcpServer.stop(); ws.closeAll(); webServer.end(); diff --git a/src/modules/rf/emit.cpp b/src/modules/rf/emit.cpp index c89003447b..bf2b5971a7 100644 --- a/src/modules/rf/emit.cpp +++ b/src/modules/rf/emit.cpp @@ -15,16 +15,13 @@ TaskHandle_t rf_raw_emit_draw_handle = NULL; // FreeRTOS task to handle periodic updates void rf_raw_emit_draw(void *parameter) { - tft.fillScreen(bruceConfig.bgColor); - drawMainBorder(); - tft.setCursor(20, 38); + drawMainBorderWithTitle("RF EMIT"); + printSubtitle("Replaying signal"); tft.setTextSize(FP); - tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); - tft.print("Emitting: "); - tft.print(frequency); - tft.print(" MHz"); + padprintln(""); + padprintln("Emitting: " + String(frequency, 2) + " MHz"); tft.setTextColor(getColorVariation(bruceConfig.priColor), bruceConfig.bgColor); - tft.println(" Press [OK] to stop "); + padprintln("Press [OK] to stop"); tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); while (1) { diff --git a/src/modules/rf/record.cpp b/src/modules/rf/record.cpp index f79d42873b..77773dea13 100644 --- a/src/modules/rf/record.cpp +++ b/src/modules/rf/record.cpp @@ -37,7 +37,7 @@ void sinewave_animation() { } void rf_raw_record_draw(RawRecordingStatus status) { - tft.setCursor(20, 38); + tft.setCursor(20, 46); tft.setTextSize(FP); if (status.frequency <= 0) { tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); @@ -132,7 +132,7 @@ static void rf_raw_record_accept_capture( status.firstSignalTime = receivedTime; status.recordingStarted = true; tft.drawPixel(0, 0, 0); - tft.fillRect(10, 30, tftWidth - 20, tftHeight - 40, bruceConfig.bgColor); + tft.fillRect(10, 45, tftWidth - 20, tftHeight - 55, bruceConfig.bgColor); } status.lastSignalTime = receivedTime; } @@ -214,13 +214,11 @@ void rf_raw_record_create(RawRecording &recorded, bool &returnToMenu) { bool rssiFeature = false; rssiFeature = bruceConfigPins.rfModule == CC1101_SPI_MODULE; - tft.fillScreen(bruceConfig.bgColor); - drawMainBorder(); + drawMainBorderWithTitle("RF RECORD"); if (rssiFeature) rf_range_selection(bruceConfigPins.rfFreq); - tft.fillScreen(bruceConfig.bgColor); - drawMainBorder(); + drawMainBorderWithTitle("RF RECORD"); rf_raw_record_draw(status); // Initialize RF module and update display @@ -242,7 +240,7 @@ void rf_raw_record_create(RawRecording &recorded, bool &returnToMenu) { // Erase sinewave animation tft.drawPixel(0, 0, 0); - tft.fillRect(10, 30, tftWidth - 20, tftHeight - 40, bruceConfig.bgColor); + tft.fillRect(10, 45, tftWidth - 20, tftHeight - 55, bruceConfig.bgColor); rf_raw_record_draw(status); // Start recording diff --git a/src/modules/rfid/rfid125.cpp b/src/modules/rfid/rfid125.cpp index eb791337cc..754b30dd61 100644 --- a/src/modules/rfid/rfid125.cpp +++ b/src/modules/rfid/rfid125.cpp @@ -128,49 +128,19 @@ void RFID125::set_state(RFID125_State state) { } void RFID125::cls() { - drawMainBorder(); - tft.setCursor(10, 28); + drawMainBorderWithTitle("RFID 125KHZ"); tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); } void RFID125::display_banner() { cls(); - tft.setTextSize(FM); - padprintln("RFID 125kHz"); tft.setTextSize(FP); switch (_current_state) { - case READ_MODE: - padprintln(" READ MODE"); - padprintln(" ---------"); - break; - // case LOAD_MODE: - // padprintln(" LOAD MODE"); - // padprintln(" ---------"); - // break; - // case CLONE_MODE: - // padprintln(" CLONE MODE"); - // padprintln(" ----------"); - // break; - // case ERASE_MODE: - // padprintln(" ERASE MODE"); - // padprintln(" ----------"); - // break; - // case WRITE_MODE: - // padprintln(" WRITE DATA MODE"); - // padprintln(" ---------------"); - // break; - // case WRITE_NDEF_MODE: - // padprintln(" WRITE NDEF MODE"); - // padprintln(" ---------------"); - // break; - case SAVE_MODE: - padprintln(" SAVE MODE"); - padprintln(" ---------"); - break; + case READ_MODE: printSubtitle("READ MODE"); break; + case SAVE_MODE: printSubtitle("SAVE MODE"); break; } - tft.setTextSize(FP); padprintln(""); padprintln("Press [OK] to change mode."); padprintln(""); diff --git a/src/modules/wifi/deauther.cpp b/src/modules/wifi/deauther.cpp index c400b45036..10dc0cda1f 100644 --- a/src/modules/wifi/deauther.cpp +++ b/src/modules/wifi/deauther.cpp @@ -828,22 +828,25 @@ void deauthAllFromScan() { int rssi = WiFi.RSSI(i); String displayName = ssid.length() > 0 ? ssid : ""; - String optionText = displayName + " (" + String(rssi) + "dBm|ch" + String(channel) + ")"; - - options.push_back({optionText.c_str(), [=]() { - uint8_t targetMAC[6]; - memcpy(targetMAC, WiFi.BSSID((uint8_t)i), 6); - int ch = WiFi.channel((uint8_t)i); - WiFi.scanDelete(); - - SelPress = false; - EscPress = false; - PrevPress = false; - NextPress = false; - delay(100); + String optionText = displayName + " (ch" + String(channel) + ")"; + + Option opt(optionText.c_str(), [=]() { + uint8_t targetMAC[6]; + memcpy(targetMAC, WiFi.BSSID((uint8_t)i), 6); + int ch = WiFi.channel((uint8_t)i); + WiFi.scanDelete(); + + SelPress = false; + EscPress = false; + PrevPress = false; + NextPress = false; + delay(100); - runDeauthAll(targetMAC, ch); - }}); + runDeauthAll(targetMAC, ch); + }); + opt.iconRssi = rssi; + opt.iconLock = (WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? 2 : 1; + options.push_back(opt); } options.push_back({"Back", []() { returnToMenu = true; }}); @@ -1017,22 +1020,25 @@ void showAPSelectionForClientDeauth() { int rssi = WiFi.RSSI(i); String displayName = ssid.length() > 0 ? ssid : ""; - String optionText = displayName + " (" + String(rssi) + "dBm|ch" + String(channel) + ")"; - - options.push_back({optionText.c_str(), [=]() { - uint8_t targetMAC[6]; - memcpy(targetMAC, WiFi.BSSID((uint8_t)i), 6); - int ch = WiFi.channel((uint8_t)i); - WiFi.scanDelete(); - - SelPress = false; - EscPress = false; - PrevPress = false; - NextPress = false; - delay(100); + String optionText = displayName + " (ch" + String(channel) + ")"; + + Option opt(optionText.c_str(), [=]() { + uint8_t targetMAC[6]; + memcpy(targetMAC, WiFi.BSSID((uint8_t)i), 6); + int ch = WiFi.channel((uint8_t)i); + WiFi.scanDelete(); + + SelPress = false; + EscPress = false; + PrevPress = false; + NextPress = false; + delay(100); - scanClientsOnAP(targetMAC, ch); - }}); + scanClientsOnAP(targetMAC, ch); + }); + opt.iconRssi = rssi; + opt.iconLock = (WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? 2 : 1; + options.push_back(opt); } options.push_back({"Back", []() { returnToMenu = true; }}); diff --git a/src/modules/wifi/wifi_atks.cpp b/src/modules/wifi/wifi_atks.cpp index 26ae1ccee3..df6c499a8a 100644 --- a/src/modules/wifi/wifi_atks.cpp +++ b/src/modules/wifi/wifi_atks.cpp @@ -286,7 +286,6 @@ void wifi_atk_menu() { int encryptionType = WiFi.encryptionType(i); int32_t rssi = WiFi.RSSI(i); int32_t ch = WiFi.channel(i); - String encryptionPrefix = (encryptionType == WIFI_AUTH_OPEN) ? "" : "#"; String encryptionTypeStr; switch (encryptionType) { case WIFI_AUTH_OPEN: encryptionTypeStr = "Open"; break; @@ -303,17 +302,17 @@ void wifi_atk_menu() { String displaySSID = ssid; if (displaySSID.length() == 0) { displaySSID = " " + WiFi.BSSIDstr(i); } - String optionText = encryptionPrefix + displaySSID + " (" + String(rssi) + "|" + - encryptionTypeStr + "|ch." + String(ch) + ")"; - - options.push_back({optionText.c_str(), [=]() { - ap_record = ap_records[i]; - target_atk_menu( - WiFi.SSID(i).c_str(), - WiFi.BSSIDstr(i), - static_cast(WiFi.channel(i)) - ); - }}); + String optionText = displaySSID + " (" + encryptionTypeStr + "|ch." + String(ch) + ")"; + + Option opt(optionText.c_str(), [=]() { + ap_record = ap_records[i]; + target_atk_menu( + WiFi.SSID(i).c_str(), WiFi.BSSIDstr(i), static_cast(WiFi.channel(i)) + ); + }); + opt.iconRssi = rssi; + opt.iconLock = (encryptionType == WIFI_AUTH_OPEN) ? 2 : 1; + options.push_back(opt); } addOptionToMainMenu();