diff --git a/components/retro-go/rg_display.c b/components/retro-go/rg_display.c index 117f3e3ae..669067290 100644 --- a/components/retro-go/rg_display.c +++ b/components/retro-go/rg_display.c @@ -10,13 +10,16 @@ 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]; static int16_t map_viewport_to_source_y[RG_SCREEN_HEIGHT + 1]; static uint32_t screen_line_checksum[RG_SCREEN_HEIGHT + 1]; +// OSD Surface Management +static bool osd_enabled = false; +static rg_osd_t osd; + #define LINE_IS_REPEATED(Y) (map_viewport_to_source_y[(Y)] == map_viewport_to_source_y[(Y) - 1]) // This is to avoid flooring a number that is approximated to .9999999 and be explicit about it #define FLOAT_TO_INT(x) ((int)((x) + 0.1f)) @@ -36,6 +39,74 @@ static const char *SETTING_CUSTOM_ZOOM = "DispCustomZoom"; #include "drivers/display/dummy.h" #endif +// TODO : make it more user-friendly -> instead of specifying a rect and a surface +// just specify the corner and the dimensions (width and height) +rg_surface_t *rg_display_init_osd(rg_corner_t corner, int width, int height, bool has_transparency) +{ + // clipping if osd > display + width = RG_MIN(width, display.screen.width); + height = RG_MIN(height, display.screen.height); + + osd.has_transparency = has_transparency; + + int left, top; + switch (corner) + { + case CORNER_TOP_LEFT: + left = 0; + top = 0; + break; + case CORNER_TOP_RIGHT: + left = display.screen.width - width; + top = 0; + break; + case CORNER_BOTTOM_LEFT: + left = 0; + top = display.screen.height - height; + break; + case CORNER_BOTTOM_RIGHT: + left = display.screen.width - width; + top = display.screen.height - height; + break; + default: + left = 0; + top = 0; + break; + } + + osd.surface = rg_surface_create(width, height, RG_PIXEL_565_LE, MEM_SLOW); + + osd.left = left; + osd.top = top; + + if (has_transparency) + { // we fill the background with the "transparent color" + uint16_t *buffer = osd.surface->data; // Treat the buffer as 16-bit values + for (int i = 0; i < osd.surface->width * osd.surface->height; i++) + buffer[i] = C_TRANSPARENT; // Assign the C_TRANSPARENT color directly to the background + } + + osd_enabled = 1; // Enable OSD if surface is valid + return osd.surface; +} + +void rg_display_set_osd_enabled(bool enabled) +{ + osd_enabled = enabled; + rg_display_force_redraw(); +} + +bool rg_display_is_osd_enabled() +{ + return osd_enabled; +} + +void deinit_osd() +{ + rg_surface_free(osd.surface); // Free the OSD surface + osd_enabled = false; +} + static inline unsigned blend_pixels(unsigned a, unsigned b) { // Fast path (taken 80-90% of the time) @@ -205,10 +276,71 @@ static inline void write_update(const rg_surface_t *update) lines_remaining -= lines_to_copy; } - if (osd != NULL) + if (osd_enabled) { // 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... + const int *buffer = osd.surface->data; + RG_ASSERT_ARG(buffer); + + const int width = osd.surface->width; + const int height = osd.surface->height; + + const int min_width = RG_MIN(draw_width, osd.surface->width); + const int min_height = RG_MIN(draw_height, osd.surface->height); + + const int x0 = RG_MAX(draw_left, osd.left); + const int y0 = RG_MAX(draw_top, osd.top); + + lcd_set_window(osd.left + display.screen.margin_left, osd.top + display.screen.margin_top, width, height); + + // FIXME: only redraw when background has changed + for (size_t y = 0; y < height;) + { + uint16_t *line_buffer = lcd_get_buffer(LCD_BUFFER_LENGTH); + size_t num_lines = RG_MIN(LCD_BUFFER_LENGTH / width, height - y); + + // Copy line by line because stride may not match width + for (size_t line = 0; line < num_lines; ++line) + { + uint16_t *osd_buffer = (void *)buffer + (y + line) * osd.surface->width * 2; + uint16_t *dst = line_buffer + (line * width); + + #define OSD_TO_SCREEN(PTR_TYPE, BACKGROUND_PIXEL) { \ + PTR_TYPE *viewport = (PTR_TYPE *)data; \ + if(y + line >= draw_top){ \ + viewport += ((map_viewport_to_source_y[y + line - draw_top] * stride) >> 1); \ + } \ + else{ \ + viewport += ((map_viewport_to_source_y[y + line] * stride) >> 1); \ + } \ + for (size_t x = 0; x < width; ++x){ \ + if(osd.has_transparency && osd_buffer[x] == C_TRANSPARENT){ \ + if((y+line)>=y0 && (x-draw_left)>=x0 && (y+line)<=min_height && (x-draw_left)<=min_width){\ + dst[x] = BACKGROUND_PIXEL; \ + } \ + else{ \ + dst[x] = 0x0000; \ + } \ + } \ + else{ \ + dst[x] = (osd_buffer[x] >> 8) | (osd_buffer[x] << 8); \ + } \ + } \ + } + + if (format & RG_PIXEL_PALETTE) + OSD_TO_SCREEN(uint8_t, palette[viewport[map_viewport_to_source_x[x - draw_left]]]) + else if (format == RG_PIXEL_565_LE) + OSD_TO_SCREEN(uint16_t, (viewport[map_viewport_to_source_x[x - draw_left]] >> 8) | (viewport[map_viewport_to_source_x[x - draw_left]] << 8)) + else + OSD_TO_SCREEN(uint16_t, viewport[map_viewport_to_source_x[x - draw_left]]) + } + + lcd_send_buffer(line_buffer, width * num_lines); + y += num_lines; + } + lcd_sync(); } if (lines_updated > draw_height * 0.80f) @@ -230,7 +362,7 @@ static void update_viewport_scaling(void) if (config.scaling == RG_DISPLAY_SCALING_FULL) { new_width = screen_width; - new_height = screen_height; + new_height = display.screen.height; } else if (config.scaling == RG_DISPLAY_SCALING_FIT) { diff --git a/components/retro-go/rg_display.h b/components/retro-go/rg_display.h index adbbfb1ca..3bf043c7d 100644 --- a/components/retro-go/rg_display.h +++ b/components/retro-go/rg_display.h @@ -99,6 +99,26 @@ typedef struct #include "rg_surface.h" +typedef struct +{ + rg_surface_t *surface; + bool has_transparency; + int top, left; +} rg_osd_t; + +typedef enum +{ + CORNER_TOP_LEFT = 0, + CORNER_TOP_RIGHT, + CORNER_BOTTOM_LEFT, + CORNER_BOTTOM_RIGHT +} rg_corner_t; + +rg_surface_t *rg_display_init_osd(rg_corner_t corner, int width, int height, bool has_transparency); +void rg_display_set_osd_enabled(bool enabled); +bool rg_display_is_osd_enabled(); +void deinit_osd(); + void rg_display_init(void); void rg_display_deinit(void); void rg_display_write_rect(int left, int top, int width, int height, int stride, const uint16_t *buffer, uint32_t flags); diff --git a/components/retro-go/rg_gui.c b/components/retro-go/rg_gui.c index 2bf8ec85b..bb8169a10 100644 --- a/components/retro-go/rg_gui.c +++ b/components/retro-go/rg_gui.c @@ -16,7 +16,6 @@ static struct uint16_t *screen_buffer, *draw_buffer; size_t draw_buffer_size; int screen_width, screen_height; - int screen_safezone; // rg_rect_t struct { const rg_font_t *font; @@ -103,13 +102,8 @@ static int get_vertical_position(int y_pos, int height) void rg_gui_init(void) { - gui.screen_width = rg_display_get_width(); - gui.screen_height = rg_display_get_height(); - #ifdef RG_SCREEN_HAS_ROUND_CORNERS - gui.screen_safezone = 20; - #else - gui.screen_safezone = 0; - #endif + gui.screen_width = rg_display_get_info()->screen.width; + gui.screen_height = rg_display_get_info()->screen.height; gui.draw_buffer = get_draw_buffer(gui.screen_width, 18, C_BLACK); rg_gui_set_language_id(rg_settings_get_number(NS_GLOBAL, SETTING_LANGUAGE, RG_LANG_EN)); rg_gui_set_font(rg_settings_get_number(NS_GLOBAL, SETTING_FONTTYPE, RG_FONT_VERA_12)); @@ -244,26 +238,52 @@ void rg_gui_set_surface(rg_surface_t *surface) gui.screen_buffer = surface ? surface->data : NULL; } -void rg_gui_copy_buffer(int left, int top, int width, int height, int stride, const void *buffer) +void rg_gui_copy_buffer(rg_surface_t *dest_surface, int left, int top, int width, int height, int stride, const void *buffer) { left = get_horizontal_position(left, width); top = get_vertical_position(top, height); - if (gui.screen_buffer) + uint16_t *target_buffer; + int target_width; + int target_height; + + if (dest_surface) + { + target_buffer = dest_surface->data; + target_width = dest_surface->width; + target_height = dest_surface->height; + } + else + { + target_buffer = gui.screen_buffer; + target_width = gui.screen_width; + target_height = gui.screen_height; + } + + if (target_buffer) { if (stride < width) stride = width * 2; - width = RG_MIN(width, gui.screen_width - left); - height = RG_MIN(height, gui.screen_height - top); + width = RG_MIN(width, target_width - left); + height = RG_MIN(height, target_height - top); for (int y = 0; y < height; ++y) { - uint16_t *dst = gui.screen_buffer + (top + y) * gui.screen_width + left; + uint16_t *dst = target_buffer + (top + y) * target_width + left; const uint16_t *src = (void *)buffer + y * stride; for (int x = 0; x < width; ++x) - if (src[x] != C_TRANSPARENT) + // FIXME: this is a bit wanky but basically it is used to make the difference between + // drawings to the OSD and to the gui buffer wich C_TRANSPARENT are handled differently + if (dest_surface) + { dst[x] = src[x]; + } + else + { + if (src[x] != C_TRANSPARENT) + dst[x] = src[x]; + } } } else @@ -364,7 +384,7 @@ static size_t get_glyph(uint32_t *output, const rg_font_t *font, int points, int return glyph_width; } -rg_rect_t rg_gui_draw_text(int x_pos, int y_pos, int width, const char *text, // const rg_font_t *font, +rg_rect_t rg_gui_draw_text(rg_surface_t *dest_surface, int x_pos, int y_pos, int width, const char *text, rg_color_t color_fg, rg_color_t color_bg, uint32_t flags) { int padding = (flags & RG_TEXT_NO_PADDING) ? 0 : 1; @@ -468,7 +488,7 @@ rg_rect_t rg_gui_draw_text(int x_pos, int y_pos, int width, const char *text, // } if (!(flags & RG_TEXT_DUMMY_DRAW)) - rg_gui_copy_buffer(x_pos, y_pos + y_offset, draw_width, line_height, 0, draw_buffer); + rg_gui_copy_buffer(dest_surface, x_pos, y_pos + y_offset, draw_width, line_height, 0, draw_buffer); y_offset += line_height; @@ -479,7 +499,8 @@ rg_rect_t rg_gui_draw_text(int x_pos, int y_pos, int width, const char *text, // return (rg_rect_t){x_pos, y_pos, draw_width, y_offset}; } -void rg_gui_draw_rect(int x_pos, int y_pos, int width, int height, int border_size, + +void rg_gui_draw_rect(rg_surface_t *dest_surface, int x_pos, int y_pos, int width, int height, int border_size, rg_color_t border_color, rg_color_t fill_color) { if (width <= 0 || height <= 0) @@ -492,10 +513,10 @@ void rg_gui_draw_rect(int x_pos, int y_pos, int width, int height, int border_si { uint16_t *draw_buffer = get_draw_buffer(border_size, RG_MAX(width, height), border_color); - rg_gui_copy_buffer(x_pos, y_pos, width, border_size, 0, draw_buffer); // Top - rg_gui_copy_buffer(x_pos, y_pos + height - border_size, width, border_size, 0, draw_buffer); // Bottom - rg_gui_copy_buffer(x_pos, y_pos, border_size, height, 0, draw_buffer); // Left - rg_gui_copy_buffer(x_pos + width - border_size, y_pos, border_size, height, 0, draw_buffer); // Right + rg_gui_copy_buffer(dest_surface, x_pos, y_pos, width, border_size, 0, draw_buffer); // Top + rg_gui_copy_buffer(dest_surface, x_pos, y_pos + height - border_size, width, border_size, 0, draw_buffer); // Bottom + rg_gui_copy_buffer(dest_surface, x_pos, y_pos, border_size, height, 0, draw_buffer); // Left + rg_gui_copy_buffer(dest_surface, x_pos + width - border_size, y_pos, border_size, height, 0, draw_buffer); // Right x_pos += border_size; y_pos += border_size; @@ -507,7 +528,7 @@ void rg_gui_draw_rect(int x_pos, int y_pos, int width, int height, int border_si { uint16_t *draw_buffer = get_draw_buffer(width, RG_MIN(height, 16), fill_color); for (int y = 0; y < height; y += 16) - rg_gui_copy_buffer(x_pos, y_pos + y, width, RG_MIN(height - y, 16), 0, draw_buffer); + rg_gui_copy_buffer(dest_surface, x_pos, y_pos + y, width, RG_MIN(height - y, 16), 0, draw_buffer); } } @@ -516,19 +537,19 @@ void rg_gui_draw_image(int x_pos, int y_pos, int width, int height, bool resampl if (img && resample && (width && height) && (width != img->width || height != img->height)) { rg_image_t *new_img = rg_surface_resize(img, width, height); - rg_gui_copy_buffer(x_pos, y_pos, width, height, new_img->width * 2, new_img->data); + rg_gui_copy_buffer(NULL, x_pos, y_pos, width, height, new_img->width * 2, new_img->data); rg_surface_free(new_img); } else if (img) { int draw_width = width ? RG_MIN(width, img->width) : img->width; int draw_height = height ? RG_MIN(height, img->height) : img->height; - rg_gui_copy_buffer(x_pos, y_pos, draw_width, draw_height, img->width * 2, img->data); + rg_gui_copy_buffer(NULL, x_pos, y_pos, draw_width, draw_height, img->width * 2, img->data); } else // We fill a rect to show something is missing instead of abort... { - rg_gui_draw_rect(x_pos, y_pos, width, height, 2, C_RED, C_BLACK); - // rg_gui_draw_text(x_pos + 2, y_pos + 2, width - 4, "No image", C_DIM_GRAY, C_BLACK, 0); + rg_gui_draw_rect(NULL, x_pos, y_pos, width, height, 2, C_RED, C_BLACK); + // rg_gui_draw_text(NULL, x_pos + 2, y_pos + 2, width - 4, "No image", C_DIM_GRAY, C_BLACK, 0); } } @@ -540,11 +561,15 @@ void rg_gui_draw_icons(void) int bar_height = txt.height; int icon_height = RG_MAX(8, bar_height - 4); int icon_top = RG_MAX(0, (bar_height - icon_height - 1) / 2); - int right = gui.screen_safezone; + int right = 0; if (battery.present) { - right += 22; + #ifdef RG_SCREEN_HAS_ROUND_CORNERS + right += 42; // This is to shift the battery icon a bit on the left + #else + right += 22; // Regular rectangle screen + #endif int width = 16; int height = icon_height; @@ -556,10 +581,10 @@ void rg_gui_draw_icons(void) rg_color_t color_border = C_SILVER; rg_color_t color_empty = C_BLACK; - rg_gui_draw_rect(x_pos, y_pos, width + 2, height, 1, color_border, C_NONE); - rg_gui_draw_rect(x_pos + width + 2, y_pos + 2, 2, height - 4, 1, color_border, C_NONE); - rg_gui_draw_rect(x_pos + 1, y_pos + 1, width_fill, height - 2, 0, 0, color_fill); - rg_gui_draw_rect(x_pos + 1 + width_fill, y_pos + 1, width - width_fill, height - 2, 0, 0, color_empty); + rg_gui_draw_rect(NULL, x_pos, y_pos, width + 2, height, 1, color_border, C_NONE); + rg_gui_draw_rect(NULL, x_pos + width + 2, y_pos + 2, 2, height - 4, 1, color_border, C_NONE); + rg_gui_draw_rect(NULL, x_pos + 1, y_pos + 1, width_fill, height - 2, 0, 0, color_fill); + rg_gui_draw_rect(NULL, x_pos + 1 + width_fill, y_pos + 1, width - width_fill, height - 2, 0, 0, color_empty); } if (network.state > RG_NETWORK_DISCONNECTED) @@ -576,13 +601,13 @@ void rg_gui_draw_icons(void) rg_color_t color_border = (network.state == RG_NETWORK_CONNECTED) ? C_SILVER : C_DIM_GRAY; y_pos += height * 0.6; - rg_gui_draw_rect(x_pos, y_pos, seg_width, height * 0.4, 1, color_border, color_fill); + rg_gui_draw_rect(NULL, x_pos, y_pos, seg_width, height * 0.4, 1, color_border, color_fill); x_pos += seg_width + 2; y_pos -= height * 0.3; - rg_gui_draw_rect(x_pos, y_pos, seg_width, height * 0.7, 1, color_border, color_fill); + rg_gui_draw_rect(NULL, x_pos, y_pos, seg_width, height * 0.7, 1, color_border, color_fill); x_pos += seg_width + 2; y_pos -= height * 0.3; - rg_gui_draw_rect(x_pos, y_pos, seg_width, height * 1.0, 1, color_border, color_fill); + rg_gui_draw_rect(NULL, x_pos, y_pos, seg_width, height * 1.0, 1, color_border, color_fill); } if (gui.show_clock) @@ -596,15 +621,14 @@ void rg_gui_draw_icons(void) struct tm *time = localtime(&time_sec); sprintf(buffer, "%02d:%02d", time->tm_hour, time->tm_min); - rg_gui_draw_text(x_pos, y_pos, 0, buffer, C_SILVER, gui.screen_buffer ? C_TRANSPARENT : C_BLACK, 0); + rg_gui_draw_text(NULL, x_pos, y_pos, 0, buffer, C_SILVER, gui.screen_buffer ? C_TRANSPARENT : C_BLACK, 0); } } void rg_gui_draw_hourglass(void) { - rg_display_write_rect( - get_horizontal_position(RG_GUI_CENTER, image_hourglass.width), - get_vertical_position(RG_GUI_CENTER, image_hourglass.height), + rg_display_write_rect((gui.screen_width / 2) - (image_hourglass.width / 2), + (gui.screen_height / 2) - (image_hourglass.height / 2), image_hourglass.width, image_hourglass.height, image_hourglass.width * 2, @@ -636,9 +660,8 @@ void rg_gui_draw_status_bars(void) else snprintf(footer, max_len, "Retro-Go %s", app->version); - // FIXME: Respect gui.safezone (draw black background full screen_width, but pad the text if needed) - rg_gui_draw_text(0, RG_GUI_TOP, gui.screen_width, header, C_WHITE, C_BLACK, 0); - rg_gui_draw_text(0, RG_GUI_BOTTOM, gui.screen_width, footer, C_WHITE, C_BLACK, 0); + rg_gui_draw_text(NULL, 0, RG_GUI_TOP, gui.screen_width, header, C_WHITE, C_BLACK, 0); + rg_gui_draw_text(NULL, 0, RG_GUI_BOTTOM, gui.screen_width, footer, C_WHITE, C_BLACK, 0); rg_gui_draw_icons(); } @@ -722,8 +745,8 @@ void rg_gui_draw_dialog(const char *title, const rg_gui_option_t *options, int s if (title) { int width = inner_width + row_padding_x * 2; - rg_gui_draw_text(x, y, width, title, gui.style.box_header, gui.style.box_background, RG_TEXT_ALIGN_CENTER); - rg_gui_draw_rect(x, y + font_height, width, 6, 0, 0, gui.style.box_background); + rg_gui_draw_text(NULL, x, y, width, title, gui.style.box_header, gui.style.box_background, RG_TEXT_ALIGN_CENTER); + rg_gui_draw_rect(NULL, x, y + font_height, width, 6, 0, 0, gui.style.box_background); y += font_height + 6; } @@ -778,49 +801,49 @@ void rg_gui_draw_dialog(const char *title, const rg_gui_option_t *options, int s } else if (options[i].value) { - rg_gui_draw_text(xx, yy, col1_width, options[i].label, fg, bg, 0); - rg_gui_draw_text(xx + col1_width, yy, sep_width, ": ", fg, bg, 0); - height = rg_gui_draw_text(xx + col1_width + sep_width, yy, col2_width, options[i].value, fg, bg, RG_TEXT_MULTILINE).height; - rg_gui_draw_rect(xx, yy + font_height, inner_width - col2_width, height - font_height, 0, 0, bg); + rg_gui_draw_text(NULL, xx, yy, col1_width, options[i].label, fg, bg, 0); + rg_gui_draw_text(NULL, xx + col1_width, yy, sep_width, ": ", fg, bg, 0); + height = rg_gui_draw_text(NULL, xx + col1_width + sep_width, yy, col2_width, options[i].value, fg, bg, RG_TEXT_MULTILINE).height; + rg_gui_draw_rect(NULL, xx, yy + font_height, inner_width - col2_width, height - font_height, 0, 0, bg); } else { - height = rg_gui_draw_text(xx, yy, inner_width, options[i].label, fg, bg, RG_TEXT_MULTILINE).height; + height = rg_gui_draw_text(NULL, xx, yy, inner_width, options[i].label, fg, bg, RG_TEXT_MULTILINE).height; } - rg_gui_draw_rect(x, yy, row_padding_x, height, 0, 0, bg); - rg_gui_draw_rect(xx + inner_width, yy, row_padding_x, height, 0, 0, bg); - rg_gui_draw_rect(x, y, inner_width + row_padding_x * 2, row_padding_y, 0, 0, bg); - rg_gui_draw_rect(x, yy + height, inner_width + row_padding_x * 2, row_padding_y, 0, 0, bg); + rg_gui_draw_rect(NULL, x, yy, row_padding_x, height, 0, 0, bg); + rg_gui_draw_rect(NULL, xx + inner_width, yy, row_padding_x, height, 0, 0, bg); + rg_gui_draw_rect(NULL, x, y, inner_width + row_padding_x * 2, row_padding_y, 0, 0, bg); + rg_gui_draw_rect(NULL, x, yy + height, inner_width + row_padding_x * 2, row_padding_y, 0, 0, bg); y += height + row_padding_y * 2; } if (y < (box_y + box_height)) { - rg_gui_draw_rect(box_x, y, box_width, (box_y + box_height) - y, 0, 0, gui.style.box_background); + rg_gui_draw_rect(NULL, box_x, y, box_width, (box_y + box_height) - y, 0, 0, gui.style.box_background); } - rg_gui_draw_rect(box_x, box_y, box_width, box_height, box_padding, gui.style.box_background, C_NONE); - rg_gui_draw_rect(box_x - 1, box_y - 1, box_width + 2, box_height + 2, 1, gui.style.box_border, C_NONE); + rg_gui_draw_rect(NULL, box_x, box_y, box_width, box_height, box_padding, gui.style.box_background, C_NONE); + rg_gui_draw_rect(NULL, box_x - 1, box_y - 1, box_width + 2, box_height + 2, 1, gui.style.box_border, C_NONE); // Basic scroll indicators are overlayed at the end... if (top_i > 0) { int x = box_x + box_width - 10; int y = box_y + box_padding + 2; - rg_gui_draw_rect(x + 0, y - 0, 6, 2, 0, 0, gui.style.scrollbar); - rg_gui_draw_rect(x + 1, y - 2, 4, 2, 0, 0, gui.style.scrollbar); - rg_gui_draw_rect(x + 2, y - 4, 2, 2, 0, 0, gui.style.scrollbar); + rg_gui_draw_rect(NULL, x + 0, y - 0, 6, 2, 0, 0, gui.style.scrollbar); + rg_gui_draw_rect(NULL, x + 1, y - 2, 4, 2, 0, 0, gui.style.scrollbar); + rg_gui_draw_rect(NULL, x + 2, y - 4, 2, 2, 0, 0, gui.style.scrollbar); } if (i < options_count) { int x = box_x + box_width - 10; int y = box_y + box_height - 6; - rg_gui_draw_rect(x + 0, y - 4, 6, 2, 0, 0, gui.style.scrollbar); - rg_gui_draw_rect(x + 1, y - 2, 4, 2, 0, 0, gui.style.scrollbar); - rg_gui_draw_rect(x + 2, y - 0, 2, 2, 0, 0, gui.style.scrollbar); + rg_gui_draw_rect(NULL, x + 0, y - 4, 6, 2, 0, 0, gui.style.scrollbar); + rg_gui_draw_rect(NULL, x + 1, y - 2, 4, 2, 0, 0, gui.style.scrollbar); + rg_gui_draw_rect(NULL, x + 2, y - 0, 2, 2, 0, 0, gui.style.scrollbar); } } @@ -1077,7 +1100,7 @@ void rg_gui_draw_keyboard(const rg_keyboard_map_t *map, size_t cursor) char buf[2] = {0}; - rg_gui_draw_rect(x_pos, y_pos, width, height, 2, gui.style.box_border, gui.style.box_background); + rg_gui_draw_rect(NULL, x_pos, y_pos, width, height, 2, gui.style.box_border, gui.style.box_background); for (size_t i = 0; i < map->columns * map->rows; ++i) { @@ -1086,7 +1109,7 @@ void rg_gui_draw_keyboard(const rg_keyboard_map_t *map, size_t cursor) if (!map->data[i]) continue; buf[0] = map->data[i]; - rg_gui_draw_text(x + 1, y + 1, 14, buf, C_BLACK, i == cursor ? C_CYAN : C_IVORY, RG_TEXT_ALIGN_CENTER); + rg_gui_draw_text(NULL, x + 1, y + 1, 14, buf, C_BLACK, i == cursor ? C_CYAN : C_IVORY, RG_TEXT_ALIGN_CENTER); } } @@ -1788,9 +1811,9 @@ static rg_gui_event_t slot_select_cb(rg_gui_option_t *option, rg_gui_event_t eve color = C_RED; } rg_gui_draw_image(0, margin, gui.screen_width, gui.screen_height - margin * 2, true, preview); - rg_gui_draw_rect(0, margin, gui.screen_width, gui.screen_height - margin * 2, border, color, C_NONE); - rg_gui_draw_rect(border, margin + border, gui.screen_width - border * 2, gui.style.font_height * 2 + 6, 0, C_BLACK, C_BLACK); - rg_gui_draw_text(border + 60, margin + border + 5, gui.screen_width - border * 2 - 120, buffer, C_WHITE, C_BLACK, RG_TEXT_ALIGN_CENTER|RG_TEXT_BIGGER|RG_TEXT_NO_PADDING); + rg_gui_draw_rect(NULL, 0, margin, gui.screen_width, gui.screen_height - margin * 2, border, color, C_NONE); + rg_gui_draw_rect(NULL, border, margin + border, gui.screen_width - border * 2, gui.style.font_height * 2 + 6, 0, C_BLACK, C_BLACK); + rg_gui_draw_text(NULL, border + 60, margin + border + 5, gui.screen_width - border * 2 - 120, buffer, C_WHITE, C_BLACK, RG_TEXT_ALIGN_CENTER|RG_TEXT_BIGGER|RG_TEXT_NO_PADDING); rg_surface_free(preview); } else if (event == RG_DIALOG_ENTER) @@ -1868,3 +1891,55 @@ void rg_gui_game_menu(void) rg_audio_set_mute(false); } + +void rg_gui_battery_indicator(rg_surface_t *osd){ + rg_battery_t battery = rg_input_read_battery(); + + if (battery.present && battery.level < 213) // 10% + { + rg_rect_t txt = TEXT_RECT("00:00", 0); + int bar_height = txt.height; + int icon_height = RG_MAX(8, bar_height - 4); + int icon_top = RG_MAX(0, (bar_height - icon_height - 1) / 2); + int height = icon_height; + int y_pos = icon_top; + int width = 16; + int width_fill = width / 100.f * battery.level; + + #ifdef RG_SCREEN_HAS_ROUND_CORNERS + int x_pos = -42; // This is to shift the battery icon a bit on the left + #else + int x_pos = -22; // Regular rectangle screen + #endif + + rg_color_t color_fill = C_RED; + rg_color_t color_border = C_SILVER; + rg_color_t color_empty = C_BLACK; + + rg_gui_draw_rect(osd, x_pos, y_pos, width + 2, height, 1, color_border, C_NONE); + rg_gui_draw_rect(osd, x_pos + width + 2, y_pos + 2, 2, height - 4, 1, color_border, C_NONE); + rg_gui_draw_rect(osd, x_pos + 1, y_pos + 1, width_fill, height - 2, 0, 0, color_fill); + rg_gui_draw_rect(osd, x_pos + 1 + width_fill, y_pos + 1, width - width_fill, height - 2, 0, 0, color_empty); + } +} + +void rg_gui_draw_status_bars_osd(rg_surface_t *osd) +{ + size_t max_len = osd->width / 8; + char header[max_len]; + + const rg_app_t *app = rg_system_get_app(); + rg_stats_t stats = rg_system_get_counters(); + + if (!app->initialized || app->isLauncher) + return; + + snprintf(header, max_len, "SPEED: %d%% (%d %d) / BUSY: %d%%", + (int)round(stats.totalFPS / app->tickRate * 100.f), + (int)round(stats.totalFPS), + (int)app->frameskip, + (int)round(stats.busyPercent)); + + rg_gui_draw_rect(osd, 0, RG_GUI_TOP, osd->width, osd->height, 0, 0, C_TRANSPARENT); + rg_gui_draw_text(osd, 0, RG_GUI_TOP, osd->width, header, C_WHITE, C_TRANSPARENT, 0); +} \ No newline at end of file diff --git a/components/retro-go/rg_gui.h b/components/retro-go/rg_gui.h index ca5a1db15..4082777fa 100644 --- a/components/retro-go/rg_gui.h +++ b/components/retro-go/rg_gui.h @@ -90,7 +90,7 @@ struct rg_gui_option_s #define RG_DIALOG_CANCELLED -0x7654321 -#define TEXT_RECT(text, max) rg_gui_draw_text(-(max), 0, 0, (text), 0, 0, RG_TEXT_MULTILINE|RG_TEXT_DUMMY_DRAW) +#define TEXT_RECT(text, max) rg_gui_draw_text(NULL, -(max), 0, 0, (text), 0, 0, RG_TEXT_MULTILINE|RG_TEXT_DUMMY_DRAW) void rg_gui_init(void); bool rg_gui_set_language_id(int index); @@ -101,11 +101,11 @@ const char *rg_gui_get_theme_name(void); rg_image_t *rg_gui_get_theme_image(const char *name); rg_color_t rg_gui_get_theme_color(const char *section, const char *key, rg_color_t default_value); rg_image_t *rg_gui_load_image_file(const char *path); -void rg_gui_copy_buffer(int left, int top, int width, int height, int stride, const void *buffer); +void rg_gui_copy_buffer(rg_surface_t *dest_surface, int left, int top, int width, int height, int stride, const void *buffer); -rg_rect_t rg_gui_draw_text(int x_pos, int y_pos, int width, const char *text, // const rg_font_t *font, +rg_rect_t rg_gui_draw_text(rg_surface_t *dest_surface, int x_pos, int y_pos, int width, const char *text, // const rg_font_t *font, rg_color_t color_fg, rg_color_t color_bg, uint32_t flags); -void rg_gui_draw_rect(int x_pos, int y_pos, int width, int height, int border_size, +void rg_gui_draw_rect(rg_surface_t *dest_surface, int x_pos, int y_pos, int width, int height, int border_size, rg_color_t border_color, rg_color_t fill_color); void rg_gui_draw_icons(void); void rg_gui_draw_dialog(const char *header, const rg_gui_option_t *options, int sel); @@ -127,6 +127,9 @@ void rg_gui_game_menu(void); void rg_gui_about_menu(void); void rg_gui_debug_menu(void); +void rg_gui_battery_indicator(rg_surface_t *osd); +void rg_gui_draw_status_bars_osd(rg_surface_t *osd); + // Creates a 565LE color from C_RGB(255, 255, 255) #define C_RGB(r, g, b) ((((r) >> 3) << 11) | (((g) >> 2) << 5) | (((b) & 0x1F))) diff --git a/components/retro-go/rg_system.c b/components/retro-go/rg_system.c index fb94d2b7e..a4182f890 100644 --- a/components/retro-go/rg_system.c +++ b/components/retro-go/rg_system.c @@ -243,7 +243,14 @@ static void update_indicators(void) } static void system_monitor_task(void *arg) -{ +{ + rg_surface_t *osd = NULL; + if(!app.isLauncher) // not working + { + // get the OSD surface + osd = rg_display_init_osd(CORNER_TOP_LEFT, 280, 15, 1); + } + int64_t nextLoopTime = 0; time_t prevTime = time(NULL); @@ -256,11 +263,18 @@ static void system_monitor_task(void *arg) update_statistics(); // update_indicators(); // Implicitly called by rg_system_set_indicator below - rg_battery_t battery = rg_input_read_battery(); + + #ifdef RG_GPIO_LED // 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))); + #endif + + if(osd){ + rg_gui_draw_status_bars_osd(osd); + rg_gui_battery_indicator(osd); + } // 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 (%d+%d+%d), BATT:%d\n", @@ -301,7 +315,7 @@ static void system_monitor_task(void *arg) { const char *message = "App unresponsive... Hold MENU to quit!"; // Drawing at this point isn't safe. But the alternative is being frozen... - rg_gui_draw_text(RG_GUI_CENTER, RG_GUI_CENTER, 0, message, C_RED, C_BLACK, RG_TEXT_BIGGER); + rg_gui_draw_text(NULL, RG_GUI_CENTER, RG_GUI_CENTER, 0, message, C_RED, C_BLACK, RG_TEXT_BIGGER); if (!rg_input_wait_for_key(RG_KEY_MENU, false, 2000)) RG_PANIC("Application terminated!"); // We're not in a nice state, don't normal exit } @@ -1294,7 +1308,7 @@ bool rg_emu_save_state(uint8_t slot) { // Save succeeded, let's take a pretty screenshot for the launcher! char *filename = rg_emu_get_path(RG_PATH_SCREENSHOT + slot, app.romPath); - rg_emu_screenshot(filename, rg_display_get_width() / 2, 0); + rg_emu_screenshot(filename, rg_display_get_info()->screen.width / 2, 0); free(filename); emu_update_save_slot(slot); } diff --git a/launcher/main/gui.c b/launcher/main/gui.c index ef4297097..b84bd10df 100644 --- a/launcher/main/gui.c +++ b/launcher/main/gui.c @@ -434,7 +434,7 @@ void gui_draw_background(tab_t *tab, int shade) if (tab->background) rg_gui_draw_image(0, 0, gui.width, gui.height, false, tab->background); else - rg_gui_draw_rect(0, 0, gui.width, gui.height, 0, 0, gui.theme->background); + rg_gui_draw_rect(NULL, 0, 0, gui.width, gui.height, 0, 0, gui.theme->background); } void gui_draw_header(tab_t *tab, int offset) @@ -448,17 +448,17 @@ void gui_draw_header(tab_t *tab, int offset) if (tab->banner) rg_gui_draw_image(LOGO_WIDTH + 1, offset + 8, 0, HEADER_HEIGHT - 8, false, tab->banner); else - rg_gui_draw_text(LOGO_WIDTH + 8, offset + 8, 0, tab->desc, gui.theme->foreground, C_TRANSPARENT, RG_TEXT_BIGGER); + rg_gui_draw_text(NULL, LOGO_WIDTH + 8, offset + 8, 0, tab->desc, gui.theme->foreground, C_TRANSPARENT, RG_TEXT_BIGGER); } void gui_draw_tab_indicator(void) { char buffer[64] = {0}; memset(buffer, '-', gui.tabs_count); - rg_gui_draw_text(RG_GUI_CENTER, RG_GUI_BOTTOM, 0, buffer, C_DIM_GRAY, C_TRANSPARENT, RG_TEXT_BIGGER|RG_TEXT_MONOSPACE); + rg_gui_draw_text(NULL, RG_GUI_CENTER, RG_GUI_BOTTOM, 0, buffer, C_DIM_GRAY, C_TRANSPARENT, RG_TEXT_BIGGER|RG_TEXT_MONOSPACE); memset(buffer, ' ', gui.tabs_count); buffer[gui.selected_tab] = '-'; - rg_gui_draw_text(RG_GUI_CENTER, RG_GUI_BOTTOM, 0, buffer, C_SNOW, C_TRANSPARENT, RG_TEXT_BIGGER|RG_TEXT_MONOSPACE); + rg_gui_draw_text(NULL, RG_GUI_CENTER, RG_GUI_BOTTOM, 0, buffer, C_SNOW, C_TRANSPARENT, RG_TEXT_BIGGER|RG_TEXT_MONOSPACE); } void gui_draw_status(tab_t *tab) @@ -468,8 +468,8 @@ void gui_draw_status(tab_t *tab) char *txt_left = tab->status[tab->status[1].left[0] ? 1 : 0].left; char *txt_right = tab->status[tab->status[1].right[0] ? 1 : 0].right; - rg_gui_draw_text(status_x, status_y, gui.width - status_x, txt_right, gui.theme->foreground, C_TRANSPARENT, RG_TEXT_ALIGN_LEFT); - rg_gui_draw_text(status_x, status_y, 0, txt_left, gui.theme->foreground, C_TRANSPARENT, RG_TEXT_ALIGN_RIGHT); + rg_gui_draw_text(NULL, status_x, status_y, gui.width - status_x, txt_right, gui.theme->foreground, C_TRANSPARENT, RG_TEXT_ALIGN_LEFT); + rg_gui_draw_text(NULL, status_x, status_y, 0, txt_left, gui.theme->foreground, C_TRANSPARENT, RG_TEXT_ALIGN_RIGHT); rg_gui_draw_icons(); } @@ -487,7 +487,7 @@ void gui_draw_list(tab_t *tab) { char buffer[64]; snprintf(buffer, 63, "[%s]", tab->navpath); - top += rg_gui_draw_text(0, top, gui.width, buffer, gui.theme->foreground, C_TRANSPARENT, 0).height; + top += rg_gui_draw_text(NULL, 0, top, gui.width, buffer, gui.theme->foreground, C_TRANSPARENT, 0).height; } top += ((gui.height - top) - (lines * line_height)) / 2; @@ -506,7 +506,7 @@ void gui_draw_list(tab_t *tab) int idx = line_offset + i; int selected = idx == list->cursor; char *label = (idx >= 0 && idx < list->length) ? list->items[idx].text : ""; - top += rg_gui_draw_text(0, top, gui.width, label, fg[selected], bg[selected], 0).height; + top += rg_gui_draw_text(NULL, 0, top, gui.width, label, fg[selected], bg[selected], 0).height; } } diff --git a/retro-core/main/main_sms.c b/retro-core/main/main_sms.c index d24382f92..0a53ca2e3 100644 --- a/retro-core/main/main_sms.c +++ b/retro-core/main/main_sms.c @@ -219,7 +219,7 @@ void sms_main(void) if (joystick & RG_KEY_START) { - rg_gui_draw_text(RG_GUI_CENTER, RG_GUI_CENTER, 0, _("To start, try: 1 or * or #"), C_YELLOW, C_BLACK, RG_TEXT_BIGGER); + rg_gui_draw_text(NULL, RG_GUI_CENTER, RG_GUI_CENTER, 0, _("To start, try: 1 or * or #"), C_YELLOW, C_BLACK, RG_TEXT_BIGGER); rg_audio_set_mute(true); int key = rg_input_read_keyboard(&coleco_keyboard); rg_audio_set_mute(false);