diff --git a/components/retro-go/rg_display.c b/components/retro-go/rg_display.c index b9421a95..d1b6eac4 100644 --- a/components/retro-go/rg_display.c +++ b/components/retro-go/rg_display.c @@ -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(); diff --git a/components/retro-go/rg_system.c b/components/retro-go/rg_system.c index 6d7033f5..ed3ef323 100644 --- a/components/retro-go/rg_system.c +++ b/components/retro-go/rg_system.c @@ -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; diff --git a/components/retro-go/rg_system.h b/components/retro-go/rg_system.h index 1ee6f394..7810e65f 100644 --- a/components/retro-go/rg_system.h +++ b/components/retro-go/rg_system.h @@ -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);