rg_display: Added on-screen battery icon when battery is low (<2%)

Cherry-Pick: fbe84ffb961a049d9cdabacc15eac89989308587

As of now there is no way to disable it, but I will add a toggle later, likely in the LED menu (and rename the menu to Indicators or something)..
This commit is contained in:
Alex Duchesne 2025-11-07 18:22:26 -05:00
parent 36b2ae46fc
commit 07ccb99196
3 changed files with 80 additions and 28 deletions

View File

@ -10,7 +10,6 @@
static rg_task_t *display_task_queue;
static rg_display_counters_t counters;
static rg_display_config_t config;
static rg_surface_t *osd;
static rg_surface_t *border;
static rg_display_t display;
static int16_t map_viewport_to_source_x[RG_SCREEN_WIDTH + 1];
@ -45,6 +44,38 @@ static inline void lcd_send_buffer(uint16_t *buffer, size_t length);
#include "drivers/display/dummy.h"
#endif
static int draw_on_screen_display(int region_start, int region_end)
{
static unsigned int area_dirty = 0;
rg_margins_t margins = rg_gui_get_safe_area();
int left = display.screen.width - margins.right - 28;
int top = margins.top + 4;
int border = 3;
int width = 20;
int height = 14;
if (region_end < top + height)
return top + height;
// Low battery indicator
if (rg_system_get_indicator(RG_INDICATOR_POWER_LOW) && ((counters.totalFrames / 20) & 1))
{
rg_display_clear_rect(left, top, width, height, C_RED); // Main body
rg_display_clear_rect(left + width, top + height / 4, border, height / 2, C_RED); // The tab
rg_display_clear_rect(left + border, top + border, width - border * 2, height - border * 2, C_BLACK); // The fill
// memset(&screen_line_checksum[top], 0, sizeof(uint32_t) * height);
area_dirty |= (1 << RG_INDICATOR_POWER_LOW);
}
else if (area_dirty)
{
if (display.viewport.width < display.screen.width || display.viewport.height < display.screen.height)
rg_display_clear_rect(left, top, width + border, height, C_BLACK);
memset(&screen_line_checksum[top], 0, sizeof(uint32_t) * height);
area_dirty = 0;
}
return 0;
}
static inline unsigned blend_pixels(unsigned a, unsigned b)
{
// Fast path (taken 80-90% of the time)
@ -102,6 +133,7 @@ static inline void write_update(const rg_surface_t *update)
int lines_remaining = draw_height;
int lines_updated = 0;
int window_top = -1;
int osd_next_call = 20;
for (int y = 0; y < draw_height;)
{
@ -211,13 +243,14 @@ static inline void write_update(const rg_surface_t *update)
lcd_send_buffer(line_buffer, 0);
}
lines_remaining -= lines_to_copy;
}
// Drawing the OSD as we progress reduces flicker compared to doing it once at the end
if (osd_next_call && draw_top + y >= osd_next_call)
{
osd_next_call = draw_on_screen_display(0, draw_top + y);
window_top = -1;
}
if (osd != NULL)
{
// TODO: Draw on screen display. By default it should be bottom left which is fine
// for both virtual keyboard and info labels. Maybe make it configurable later...
lines_remaining -= lines_to_copy;
}
if (lines_updated > draw_height * 0.80f)
@ -334,7 +367,7 @@ static void display_task(void *arg)
}
write_update(msg.dataPtr);
// draw_on_screen_display(0, display.screen.height);
rg_task_receive(&msg);
lcd_sync();

View File

@ -221,28 +221,26 @@ static void update_statistics(void)
update_memory_statistics();
}
static void update_indicators(void)
static void update_indicators(bool reset_animation)
{
uint32_t visibleIndicators = indicators & app.indicatorsMask;
static int animation_step = 0;
rg_color_t newColor = 0; // C_GREEN
if (reset_animation)
animation_step = 0;
else
animation_step++;
if (indicators & (3 << RG_INDICATOR_CRITICAL))
newColor = C_RED; // Make it flash rapidly!
else if (visibleIndicators & (1 << RG_INDICATOR_POWER_LOW))
newColor = C_RED;
newColor = (animation_step & 1) ? C_NONE : C_RED;
else if (visibleIndicators)
newColor = C_BLUE;
// In some cases it can be costly to update the LED status, skip if unchanged
if (newColor == ledColor)
return;
#if defined(ESP_PLATFORM) && defined(RG_GPIO_LED)
// GPIO LED doesn't support colors, so any color = on
if (RG_GPIO_LED != GPIO_NUM_NC)
gpio_set_level(RG_GPIO_LED, newColor != 0);
#endif
ledColor = newColor;
if (newColor != ledColor)
rg_system_set_led_color(newColor);
}
static void system_monitor_task(void *arg)
@ -258,12 +256,10 @@ static void system_monitor_task(void *arg)
rtcValue = time(NULL);
update_statistics();
// update_indicators(); // Implicitly called by rg_system_set_indicator below
rg_battery_t battery = rg_input_read_battery();
// TODO: The flashing should eventually be handled by update_indicators instead of here...
rg_system_set_indicator(RG_INDICATOR_POWER_LOW, (battery.present && battery.level <= 2.f &&
!rg_system_get_indicator(RG_INDICATOR_POWER_LOW)));
rg_system_set_indicator(RG_INDICATOR_POWER_LOW, true);
update_indicators(false);
// Try to avoid complex conversions that could allocate, prefer rounding/ceiling if necessary.
rg_system_log(RG_LOG_DEBUG, NULL, "STACK:%d, HEAP:%d+%d (%d+%d), BUSY:%d%%, FPS:%d (S:%d R:%d+%d), BATT:%d",
@ -843,7 +839,8 @@ IRAM_ATTR int64_t rg_system_timer(void)
void rg_system_event(int event, void *arg)
{
RG_LOGV("Dispatching event:%d arg:%p\n", event, arg);
// FIXME: rg_* components should have a way to listen to events too (eg rg_gui receive RG_EVENT_GEOMETRY)
RG_LOGV("Dispatching event:%d arg:%p", event, arg);
if (app.handlers.event)
app.handlers.event(event, arg);
}
@ -1042,9 +1039,11 @@ bool rg_system_save_trace(const char *filename, bool panic_trace)
void rg_system_set_indicator(rg_indicator_t indicator, bool on)
{
uint32_t old_indicators = indicators;
indicators &= ~(1 << indicator);
indicators |= (on << indicator);
update_indicators();
if (old_indicators != indicators)
update_indicators(true);
}
bool rg_system_get_indicator(rg_indicator_t indicator)
@ -1064,6 +1063,25 @@ bool rg_system_get_indicator_mask(rg_indicator_t indicator)
return app.indicatorsMask & (1 << indicator);
}
bool rg_system_set_led_color(rg_color_t color)
{
ledColor = color;
#if defined(RG_GPIO_LED)
int value = color > 0; // GPIO LED doesn't support colors, so any color = on
#if defined(RG_GPIO_LED_INVERT)
value = !value;
#endif
if (RG_GPIO_LED != GPIO_NUM_NC)
return gpio_set_level(RG_GPIO_LED, value) == ESP_OK;
#endif
return true;
}
rg_color_t rg_system_get_led_color(void)
{
return ledColor;
}
void rg_system_set_log_level(rg_log_level_t level)
{
if (level >= 0 && level < RG_LOG_MAX)
@ -1416,8 +1434,7 @@ rg_emu_states_t *rg_emu_get_states(const char *romPath, size_t slots)
bool rg_emu_reset(bool hard)
{
if (app.speed != 1.f)
rg_system_set_app_speed(1.f);
rg_system_set_app_speed(1.f);
if (app.handlers.reset)
return app.handlers.reset(hard);
return false;

View File

@ -223,6 +223,8 @@ void rg_system_set_indicator(rg_indicator_t indicator, bool on);
bool rg_system_get_indicator(rg_indicator_t indicator);
void rg_system_set_indicator_mask(rg_indicator_t indicator, bool on);
bool rg_system_get_indicator_mask(rg_indicator_t indicator);
bool rg_system_set_led_color(rg_color_t color);
rg_color_t rg_system_get_led_color(void);
void rg_system_set_tick_rate(int tickRate);
int rg_system_get_tick_rate(void);
void rg_system_set_log_level(rg_log_level_t level);