diff --git a/lib/hal/HalGPIO.cpp b/lib/hal/HalGPIO.cpp index 64a251de..ec0176a2 100644 --- a/lib/hal/HalGPIO.cpp +++ b/lib/hal/HalGPIO.cpp @@ -7,7 +7,14 @@ void HalGPIO::begin() { pinMode(UART0_RXD, INPUT); } -void HalGPIO::update() { inputMgr.update(); } +void HalGPIO::update() { + inputMgr.update(); + const bool connected = isUsbConnected(); + usbStateChanged = (connected != lastUsbConnected); + lastUsbConnected = connected; +} + +bool HalGPIO::wasUsbStateChanged() const { return usbStateChanged; } bool HalGPIO::isPressed(uint8_t buttonIndex) const { return inputMgr.isPressed(buttonIndex); } diff --git a/lib/hal/HalGPIO.h b/lib/hal/HalGPIO.h index 09e1fa52..a283ed60 100644 --- a/lib/hal/HalGPIO.h +++ b/lib/hal/HalGPIO.h @@ -23,6 +23,9 @@ class HalGPIO { InputManager inputMgr; #endif + bool lastUsbConnected = false; + bool usbStateChanged = false; + public: HalGPIO() = default; @@ -41,6 +44,9 @@ class HalGPIO { // Check if USB is connected bool isUsbConnected() const; + // Returns true once per edge (plug or unplug) since the last update() + bool wasUsbStateChanged() const; + enum class WakeupReason { PowerButton, AfterFlash, AfterUSBPower, Other }; WakeupReason getWakeupReason() const; diff --git a/src/components/themes/BaseTheme.cpp b/src/components/themes/BaseTheme.cpp index fc0a0156..7894aa74 100644 --- a/src/components/themes/BaseTheme.cpp +++ b/src/components/themes/BaseTheme.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -37,7 +38,11 @@ void drawBatteryIcon(const GfxRenderer& renderer, int x, int y, int battWidth, i const bool charging = gpio.isUsbConnected(); // The +1 is to round up, so that we always fill at least one pixel - constexpr int maxFillWidth = battWidth - 5; + const int maxFillWidth = battWidth - 5; + const int fillHeight = rectHeight - 4; + if (maxFillWidth <= 0 || fillHeight <= 0) { + return; + } int filledWidth = percentage * maxFillWidth / 100 + 1; if (filledWidth > maxFillWidth) { filledWidth = maxFillWidth; @@ -46,10 +51,10 @@ void drawBatteryIcon(const GfxRenderer& renderer, int x, int y, int battWidth, i // When charging, ensure minimum fill so lightning bolt is fully visible constexpr int minFillForBolt = 8; if (charging && filledWidth < minFillForBolt) { - filledWidth = minFillForBolt; + filledWidth = std::min(minFillForBolt, maxFillWidth); } - renderer.fillRect(x + 2, y + 2, filledWidth, rectHeight - 4); + renderer.fillRect(x + 2, y + 2, filledWidth, fillHeight); // Draw lightning bolt when charging (white/inverted on black fill for visibility) if (charging) { diff --git a/src/main.cpp b/src/main.cpp index 7bd21f37..75bf69c5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -379,6 +379,12 @@ void loop() { return; } + // Refresh the battery icon when USB is plugged or unplugged. + // Placed after sleep guards so we never queue a render that won't be processed. + if (gpio.wasUsbStateChanged()) { + activityManager.requestUpdate(); + } + const unsigned long activityStartTime = millis(); activityManager.loop(); const unsigned long activityDuration = millis() - activityStartTime;