diff --git a/workspace/all/battery/battery.c b/workspace/all/battery/battery.c index 68fae04d5..990d63ac7 100644 --- a/workspace/all/battery/battery.c +++ b/workspace/all/battery/battery.c @@ -487,9 +487,9 @@ void renderPage() // monochrome is more MinUI, but some colours are nice const bool ilikeitcolourful = true; if(ilikeitcolourful) { - white_pixel_color = SDL_MapRGB(screen->format, 255, 255, 255); - red_pixel_color = SDL_MapRGB(screen->format, 255, 170, 170); - blue_pixel_color = SDL_MapRGB(screen->format, 89, 167, 255); + white_pixel_color = SDL_MapRGBA(screen->format, 255, 255, 255, 255); + red_pixel_color = SDL_MapRGBA(screen->format, 255, 170, 170, 255); + blue_pixel_color = SDL_MapRGBA(screen->format, 89, 167, 255, 255); pixel_color = white_pixel_color; } diff --git a/workspace/all/common/api.c b/workspace/all/common/api.c index 25b4998e9..3084abc4a 100644 --- a/workspace/all/common/api.c +++ b/workspace/all/common/api.c @@ -110,47 +110,57 @@ SDL_Color ALT_BUTTON_TEXT_COLOR; // Function to convert hex color code to RGB and set the values static inline uint32_t HexToUint(const char *hexColor) { - int r, g, b; - sscanf(hexColor, "%02x%02x%02x", &r, &g, &b); - return SDL_MapRGB(gfx.screen->format, r, g, b); + int r, g, b, a = 255; // Default alpha value to 255 (fully opaque) + // hex color code may or may not have alpha channel, so we need to handle both cases + if (strlen(hexColor) == 8) // If the hex color code has alpha channel + { + sscanf(hexColor, "%02x%02x%02x%02x", &r, &g, &b, &a); + } + else // If the hex color code does not have alpha channel + { + sscanf(hexColor, "%02x%02x%02x", &r, &g, &b); + } + return SDL_MapRGBA(gfx.screen->format, r, g, b, a); } static inline uint32_t HexToUint32_unmapped(const char *hexColor) { - // Convert the hex string to an unsigned long - uint32_t value = (uint32_t)strtoul(hexColor, NULL, 16); - return value; + // Convert the hex string to an unsigned long + uint32_t value = (uint32_t)strtoul(hexColor, NULL, 16); + return value; } -static inline void rgb_unpack(uint32_t col, int *r, int *g, int *b) +static inline void rgba_unpack(uint32_t col, int *r, int *g, int *b, int *a) { - *r = (col >> 16) & 0xff; - *g = (col >> 8) & 0xff; - *b = col & 0xff; + *r = (col >> 24) & 0xff; + *g = (col >> 16) & 0xff; + *b = (col >> 8) & 0xff; + *a = col & 0xff; } -static inline uint32_t rgb_pack(int r, int g, int b) +static inline uint32_t rgba_pack(int r, int g, int b, int a) { - return (r << 16) + (g << 8) + b; + return (r << 24) + (g << 16) + (b << 8) + a; } static inline uint32_t mapUint(uint32_t col) { - int r, g, b; - rgb_unpack(col, &r, &g, &b); - return SDL_MapRGB(gfx.screen->format, r, g, b); + int r, g, b, a; + rgba_unpack(col, &r, &g, &b, &a); + return SDL_MapRGBA(gfx.screen->format, r, g, b, a); } -static inline uint32_t UintMult(uint32_t color, uint32_t modulate_rgb) +static inline uint32_t UintMult(uint32_t color, uint32_t modulate_rgba) { SDL_Color dest = uintToColour(color); - SDL_Color modulate = uintToColour(modulate_rgb); + SDL_Color modulate = uintToColour(modulate_rgba); dest.r = (int)dest.r * modulate.r / 255; dest.g = (int)dest.g * modulate.g / 255; dest.b = (int)dest.b * modulate.b / 255; + dest.a = (int)dest.a * modulate.a / 255; - return (dest.r << 16) | (dest.g << 8) | dest.b; + return (dest.r << 24) | (dest.g << 16) | (dest.b << 8) | dest.a; } /////////////////////////////// @@ -370,10 +380,15 @@ SDL_Surface *GFX_init(int mode) RGB_DARK_GRAY = SDL_MapRGB(gfx.screen->format, TRIAD_DARK_GRAY); asset_rgbs[ASSET_WHITE_PILL] = RGB_WHITE; + asset_rgbs[ASSET_WHITE_RECT] = RGB_WHITE; asset_rgbs[ASSET_BLACK_PILL] = RGB_BLACK; + asset_rgbs[ASSET_BLACK_RECT] = RGB_BLACK; asset_rgbs[ASSET_DARK_GRAY_PILL] = RGB_DARK_GRAY; + asset_rgbs[ASSET_DARK_GRAY_RECT] = RGB_DARK_GRAY; asset_rgbs[ASSET_OPTION] = RGB_DARK_GRAY; + asset_rgbs[ASSET_OPTION_RECT] = RGB_DARK_GRAY; asset_rgbs[ASSET_BUTTON] = RGB_WHITE; + asset_rgbs[ASSET_BUTTON_RECT] = RGB_WHITE; asset_rgbs[ASSET_PAGE_BG] = RGB_WHITE; asset_rgbs[ASSET_STATE_BG] = RGB_WHITE; asset_rgbs[ASSET_PAGE] = RGB_BLACK; @@ -387,8 +402,13 @@ SDL_Surface *GFX_init(int mode) asset_rects[ASSET_WHITE_PILL] = (SDL_Rect){SCALE4(1, 1, 30, 30)}; asset_rects[ASSET_BLACK_PILL] = (SDL_Rect){SCALE4(33, 1, 30, 30)}; asset_rects[ASSET_DARK_GRAY_PILL] = (SDL_Rect){SCALE4(65, 1, 30, 30)}; + asset_rects[ASSET_WHITE_RECT] = (SDL_Rect){SCALE4(1+14, 1, 2, 30)}; // two pixels wide, middle of the asset + asset_rects[ASSET_BLACK_RECT] = (SDL_Rect){SCALE4(33+14, 1, 2, 30)}; // two pixels wide, middle of the asset + asset_rects[ASSET_DARK_GRAY_RECT] = (SDL_Rect){SCALE4(65+14, 1, 2, 30)}; // two pixels wide, middle of the asset asset_rects[ASSET_OPTION] = (SDL_Rect){SCALE4(97, 1, 20, 20)}; + asset_rects[ASSET_OPTION_RECT] = (SDL_Rect){SCALE4(97+9, 1, 2, 20)}; // two pixels wide, middle of the asset asset_rects[ASSET_BUTTON] = (SDL_Rect){SCALE4(1, 33, 20, 20)}; + asset_rects[ASSET_BUTTON_RECT] = (SDL_Rect){SCALE4(1+9, 33, 2, 20)}; // two pixels wide, middle of the asset asset_rects[ASSET_PAGE_BG] = (SDL_Rect){SCALE4(64, 33, 15, 15)}; asset_rects[ASSET_STATE_BG] = (SDL_Rect){SCALE4(23, 54, 8, 8)}; asset_rects[ASSET_PAGE] = (SDL_Rect){SCALE4(39, 54, 6, 6)}; @@ -1347,13 +1367,13 @@ void GFX_freeAAScaler(void) /////////////////////////////// -SDL_Color /*GFX_*/ uintToColour(uint32_t colour) +SDL_Color /*GFX_*/ uintToColour(uint32_t rgba) { SDL_Color tempcol; - tempcol.a = 255; - tempcol.r = (colour >> 16) & 0xFF; - tempcol.g = (colour >> 8) & 0xFF; - tempcol.b = colour & 0xFF; + tempcol.r = (rgba >> 24) & 0xFF; + tempcol.g = (rgba >> 16) & 0xFF; + tempcol.b = (rgba >> 8) & 0xFF; + tempcol.a = rgba & 0xFF; return tempcol; } @@ -1646,14 +1666,22 @@ void GFX_blitSurfaceColor(SDL_Surface *src, SDL_Rect *src_rect, SDL_Surface *dst else if (asset_color == THEME_COLOR7) asset_color = THEME_COLOR7_255; + SDL_Color tint = uintToColour(asset_color); + SDL_Color restore; + Uint8 restore_a; + SDL_BlendMode restore_blend; SDL_GetSurfaceColorMod(src, &restore.r, &restore.g, &restore.b); - SDL_SetSurfaceColorMod(src, - (asset_color >> 16) & 0xFF, - (asset_color >> 8) & 0xFF, - asset_color & 0xFF); + SDL_GetSurfaceAlphaMod(src, &restore_a); + SDL_GetSurfaceBlendMode(src, &restore_blend); + + SDL_SetSurfaceColorMod(src, tint.r, tint.g, tint.b); + SDL_SetSurfaceAlphaMod(src, tint.a); + SDL_SetSurfaceBlendMode(src, SDL_BLENDMODE_BLEND); SDL_BlitSurface(src, src_rect, dst, dst_rect); SDL_SetSurfaceColorMod(src, restore.r, restore.g, restore.b); + SDL_SetSurfaceAlphaMod(src, restore_a); + SDL_SetSurfaceBlendMode(src, restore_blend); } else { @@ -1661,6 +1689,48 @@ void GFX_blitSurfaceColor(SDL_Surface *src, SDL_Rect *src_rect, SDL_Surface *dst } } +static void GFX_blitSurfaceColorScaled(SDL_Surface *src, SDL_Rect *src_rect, SDL_Surface *dst, SDL_Rect *dst_rect, uint32_t asset_color) +{ + if (asset_color != RGB_WHITE) + { + if (asset_color == THEME_COLOR1) + asset_color = THEME_COLOR1_255; + else if (asset_color == THEME_COLOR2) + asset_color = THEME_COLOR2_255; + else if (asset_color == THEME_COLOR3) + asset_color = THEME_COLOR3_255; + else if (asset_color == THEME_COLOR4) + asset_color = THEME_COLOR4_255; + else if (asset_color == THEME_COLOR5) + asset_color = THEME_COLOR5_255; + else if (asset_color == THEME_COLOR6) + asset_color = THEME_COLOR6_255; + else if (asset_color == THEME_COLOR7) + asset_color = THEME_COLOR7_255; + + SDL_Color tint = uintToColour(asset_color); + + SDL_Color restore; + Uint8 restore_a; + SDL_BlendMode restore_blend; + SDL_GetSurfaceColorMod(src, &restore.r, &restore.g, &restore.b); + SDL_GetSurfaceAlphaMod(src, &restore_a); + SDL_GetSurfaceBlendMode(src, &restore_blend); + + SDL_SetSurfaceColorMod(src, tint.r, tint.g, tint.b); + SDL_SetSurfaceAlphaMod(src, tint.a); + SDL_SetSurfaceBlendMode(src, SDL_BLENDMODE_BLEND); + SDL_BlitScaled(src, src_rect, dst, dst_rect); + SDL_SetSurfaceColorMod(src, restore.r, restore.g, restore.b); + SDL_SetSurfaceAlphaMod(src, restore_a); + SDL_SetSurfaceBlendMode(src, restore_blend); + } + else + { + SDL_BlitScaled(src, src_rect, dst, dst_rect); + } +} + void GFX_blitAssetColor(int asset, SDL_Rect *src_rect, SDL_Surface *dst, SDL_Rect *dst_rect, uint32_t asset_color) { @@ -1685,6 +1755,47 @@ void GFX_blitAsset(int asset, SDL_Rect *src_rect, SDL_Surface *dst, SDL_Rect *ds { GFX_blitAssetColor(asset, src_rect, dst, dst_rect, RGB_WHITE); } + +static void GFX_fillRectBlend(SDL_Surface *dst, const SDL_Rect *rect, uint32_t mapped_color) +{ + if (!dst || !rect || rect->w <= 0 || rect->h <= 0) + return; + + Uint8 r, g, b, a; + SDL_GetRGBA(mapped_color, dst->format, &r, &g, &b, &a); + + SDL_Surface *tmp = SDL_CreateRGBSurfaceWithFormat(0, rect->w, rect->h, + 32, SDL_PIXELFORMAT_ARGB8888); + if (!tmp) + return; + + SDL_SetSurfaceBlendMode(tmp, SDL_BLENDMODE_BLEND); + SDL_FillRect(tmp, NULL, SDL_MapRGBA(tmp->format, r, g, b, a)); + + SDL_Rect dst_rect = *rect; + SDL_BlitSurface(tmp, NULL, dst, &dst_rect); + SDL_FreeSurface(tmp); +} + +static int GFX_pillRectAsset(int asset) +{ + switch (asset) + { + case ASSET_WHITE_PILL: + return ASSET_WHITE_RECT; + case ASSET_BLACK_PILL: + return ASSET_BLACK_RECT; + case ASSET_DARK_GRAY_PILL: + return ASSET_DARK_GRAY_RECT; + case ASSET_BUTTON: + return ASSET_BUTTON_RECT; + case ASSET_OPTION: + return ASSET_OPTION_RECT; + default: + return -1; + } +} + void GFX_blitPillColor(int asset, SDL_Surface *dst, SDL_Rect *dst_rect, uint32_t asset_color, uint32_t fill_color) { int x = dst_rect->x; @@ -1704,8 +1815,19 @@ void GFX_blitPillColor(int asset, SDL_Surface *dst, SDL_Rect *dst_rect, uint32_t x += r; if (w > 0) { - // SDL_FillRect(dst, &(SDL_Rect){x,y,w,h}, UintMult(fill_color, asset_color)); - SDL_FillRect(dst, &(SDL_Rect){x, y, w, h}, asset_color); + int rect_asset = GFX_pillRectAsset(asset); + if (rect_asset >= 0) + { + uint32_t center_color = (fill_color != RGB_WHITE) ? fill_color : asset_color; + SDL_Rect src_rect = asset_rects[rect_asset]; + SDL_Rect center_dst = {x, y, w, h}; + GFX_blitSurfaceColorScaled(gfx.assets, &src_rect, dst, ¢er_dst, center_color); + } + else + { + // Fallback for non-pill assets that still use this helper. + GFX_fillRectBlend(dst, &(SDL_Rect){x, y, w, h}, asset_color); + } x += w; } GFX_blitAssetColor(asset, &(SDL_Rect){r, 0, r, h}, dst, &(SDL_Rect){x, y}, asset_color); @@ -2216,6 +2338,26 @@ int GFX_blitButtonGroup(char **pairs, int primary, SDL_Surface *dst, int align_r return ow; } +void GFX_blitTopCurtain(SDL_Surface* dst) +{ + // blit a top curtain to the screen, which is a semi-transparent black rectangle at the top of the screen + int opacity = CFG_getGameSwitcherCurtain() * 255 / 100; // convert percentage to 0-255 range + if(opacity <= 0) + return; // no need to draw if fully transparent + SDL_Rect rect = {0, 0, dst->w, SCALE1(PADDING+PILL_SIZE+PADDING)}; + GFX_fillRectBlend(dst, &rect, SDL_MapRGBA(dst->format, 0, 0, 0, opacity)); +} + +void GFX_blitBottomCurtain(SDL_Surface* dst) +{ + // blit a bottom curtain to the screen, which is a semi-transparent black rectangle at the bottom of the screen + int opacity = CFG_getGameSwitcherCurtain() * 255 / 100; // convert percentage to 0-255 range + if(opacity <= 0) + return; // no need to draw if fully transparent + SDL_Rect rect = {0, dst->h - SCALE1(PADDING+PILL_SIZE+PADDING), dst->w, SCALE1(PADDING+PILL_SIZE+PADDING)}; + GFX_fillRectBlend(dst, &rect, SDL_MapRGBA(dst->format, 0, 0, 0, opacity)); +} + #define MAX_TEXT_LINES 16 void GFX_sizeText(TTF_Font *font, const char *str, int leading, int *w, int *h) { diff --git a/workspace/all/common/api.h b/workspace/all/common/api.h index da8e14534..9488c09ca 100644 --- a/workspace/all/common/api.h +++ b/workspace/all/common/api.h @@ -117,8 +117,13 @@ enum { ASSET_WHITE_PILL, ASSET_BLACK_PILL, ASSET_DARK_GRAY_PILL, + ASSET_WHITE_RECT, + ASSET_BLACK_RECT, + ASSET_DARK_GRAY_RECT, ASSET_OPTION, + ASSET_OPTION_RECT, ASSET_BUTTON, + ASSET_BUTTON_RECT, ASSET_PAGE_BG, ASSET_STATE_BG, ASSET_PAGE, @@ -281,7 +286,7 @@ SDL_Surface* GFX_init(int mode); void GFX_setMode(int mode); int GFX_hdmiChanged(void); -SDL_Color /*GFX_*/ uintToColour(uint32_t colour); +SDL_Color /*GFX_*/ uintToColour(uint32_t rgba); #define GFX_clear PLAT_clearVideo // (SDL_Surface* screen) #define GFX_clearAll PLAT_clearAll // (void) @@ -352,6 +357,8 @@ void GFX_blitMessage(TTF_Font* font, char* msg, SDL_Surface* dst, SDL_Rect* dst_ int GFX_blitHardwareGroup(SDL_Surface* dst, int show_setting); void GFX_blitHardwareHints(SDL_Surface* dst, int show_setting); +void GFX_blitTopCurtain(SDL_Surface* dst); +void GFX_blitBottomCurtain(SDL_Surface* dst); typedef enum { INDICATOR_BRIGHTNESS = 1, diff --git a/workspace/all/common/config.c b/workspace/all/common/config.c index ff5af1a62..03dbd303a 100644 --- a/workspace/all/common/config.c +++ b/workspace/all/common/config.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "defines.h" #include "utils.h" @@ -55,6 +56,7 @@ void CFG_defaults(NextUISettings *cfg) .gameSwitcherScaling = CFG_DEFAULT_GAMESWITCHERSCALING, .defaultView = CFG_DEFAULT_VIEW, .showQuickSwitcherUi = CFG_DEFAULT_SHOWQUICKWITCHERUI, + .gameSwitcherCurtain = CFG_DEFAULT_GAMESWITCHER_CURTAIN, .muteLeds = CFG_DEFAULT_MUTELEDS, @@ -93,12 +95,34 @@ void CFG_defaults(NextUISettings *cfg) .raNotificationDuration = CFG_DEFAULT_RA_NOTIFICATION_DURATION, .raProgressNotificationDuration = CFG_DEFAULT_RA_PROGRESS_NOTIFICATION_DURATION, .raAchievementSortOrder = CFG_DEFAULT_RA_ACHIEVEMENT_SORT_ORDER, - -}; + }; *cfg = defaults; } +static inline uint32_t parseHexColor(const char *hexColor) { + // Accept both legacy RGB (RRGGBB) and RGBA (RRGGBBAA) text formats. + // Decide by input hex digit count, not numeric value. + uint32_t value = HexToUint32_unmapped(hexColor); + + const char *p = hexColor; + while (*p == ' ' || *p == '\t') p++; + if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) + p += 2; + + size_t digits = 0; + while (isxdigit((unsigned char)p[digits])) + digits++; + + if (digits <= 6) { + // Legacy RGB -> packed RGBA with opaque alpha. + return (value << 8) | 0xFF; + } + + // 8+ hex digits are treated as explicit packed RGBA. + return value; +} + void CFG_init(FontLoad_callback_t cb, ColorSet_callback_t ccb) { CFG_defaults(&settings); @@ -134,41 +158,53 @@ void CFG_init(FontLoad_callback_t cb, ColorSet_callback_t ccb) fontLoaded = true; continue; } - if (sscanf(line, "color1=%x", &temp_color) == 1) + if(strncmp(line, "color1=", 7) == 0) { - char hexColor[7]; - snprintf(hexColor, sizeof(hexColor), "%06x", temp_color); - CFG_setColor(1, HexToUint32_unmapped(hexColor)); + char *value = line + 7; + value[strcspn(value, "\n")] = 0; + CFG_setColor(1, parseHexColor(value)); continue; } - if (sscanf(line, "color2=%x", &temp_color) == 1) + if (strncmp(line, "color2=", 7) == 0) { - CFG_setColor(2, temp_color); + char *value = line + 7; + value[strcspn(value, "\n")] = 0; + CFG_setColor(2, parseHexColor(value)); continue; } - if (sscanf(line, "color3=%x", &temp_color) == 1) + if (strncmp(line, "color3=", 7) == 0) { - CFG_setColor(3, temp_color); + char *value = line + 7; + value[strcspn(value, "\n")] = 0; + CFG_setColor(3, parseHexColor(value)); continue; } - if (sscanf(line, "color4=%x", &temp_color) == 1) + if (strncmp(line, "color4=", 7) == 0) { - CFG_setColor(4, temp_color); + char *value = line + 7; + value[strcspn(value, "\n")] = 0; + CFG_setColor(4, parseHexColor(value)); continue; } - if (sscanf(line, "color5=%x", &temp_color) == 1) + if (strncmp(line, "color5=", 7) == 0) { - CFG_setColor(5, temp_color); + char *value = line + 7; + value[strcspn(value, "\n")] = 0; + CFG_setColor(5, parseHexColor(value)); continue; } - if (sscanf(line, "color6=%x", &temp_color) == 1) + if (strncmp(line, "color6=", 7) == 0) { - CFG_setColor(6, temp_color); + char *value = line + 7; + value[strcspn(value, "\n")] = 0; + CFG_setColor(6, parseHexColor(value)); continue; } - if (sscanf(line, "color7=%x", &temp_color) == 1) + if (strncmp(line, "color7=", 7) == 0) { - CFG_setColor(7, temp_color); + char *value = line + 7; + value[strcspn(value, "\n")] = 0; + CFG_setColor(7, parseHexColor(value)); continue; } if (sscanf(line, "radius=%i", &temp_value) == 1) @@ -420,6 +456,11 @@ void CFG_init(FontLoad_callback_t cb, ColorSet_callback_t ccb) CFG_setFontStyle(temp_value); continue; } + if (sscanf(line, "gameSwitcherCurtain=%i", &temp_value) == 1) + { + CFG_setGameSwitcherCurtain(temp_value); + continue; + } } fclose(file); } @@ -1105,6 +1146,17 @@ void CFG_setFontStyle(int style) CFG_setFontFile(CFG_getFontFile()); } +int CFG_getGameSwitcherCurtain(void) +{ + return settings.gameSwitcherCurtain; +} + +void CFG_setGameSwitcherCurtain(int opacity) +{ + settings.gameSwitcherCurtain = clamp(opacity, 0, 100); + CFG_sync(); +} + void CFG_get(const char *key, char *value) { if (strcmp(key, "font") == 0) @@ -1118,31 +1170,31 @@ void CFG_get(const char *key, char *value) } else if (strcmp(key, "color1") == 0) { - sprintf(value, "\"0x%06X\"", CFG_getColor(COLOR_MAIN)); + sprintf(value, "\"0x%08X\"", CFG_getColor(COLOR_MAIN)); } else if (strcmp(key, "color2") == 0) { - sprintf(value, "\"0x%06X\"", CFG_getColor(COLOR_ACCENT)); + sprintf(value, "\"0x%08X\"", CFG_getColor(COLOR_ACCENT)); } else if (strcmp(key, "color3") == 0) { - sprintf(value, "\"0x%06X\"", CFG_getColor(COLOR_ACCENT2)); + sprintf(value, "\"0x%08X\"", CFG_getColor(COLOR_ACCENT2)); } else if (strcmp(key, "color4") == 0) { - sprintf(value, "\"0x%06X\"", CFG_getColor(COLOR_LIST_TEXT)); + sprintf(value, "\"0x%08X\"", CFG_getColor(COLOR_LIST_TEXT)); } else if (strcmp(key, "color5") == 0) { - sprintf(value, "\"0x%06X\"", CFG_getColor(COLOR_LIST_TEXT_SELECTED)); + sprintf(value, "\"0x%08X\"", CFG_getColor(COLOR_LIST_TEXT_SELECTED)); } else if (strcmp(key, "color6") == 0) { - sprintf(value, "\"0x%06X\"", CFG_getColor(COLOR_HINT)); + sprintf(value, "\"0x%08X\"", CFG_getColor(COLOR_HINT)); } else if (strcmp(key, "color7") == 0) { - sprintf(value, "\"0x%06X\"", CFG_getColor(COLOR_BACKGROUND)); + sprintf(value, "\"0x%08X\"", CFG_getColor(COLOR_BACKGROUND)); } else if (strcmp(key, "radius") == 0) { @@ -1328,6 +1380,10 @@ void CFG_get(const char *key, char *value) { sprintf(value, "%i", CFG_getFontStyle()); } + else if (strcmp(key, "gameSwitcherCurtain") == 0) + { + sprintf(value, "%i", CFG_getGameSwitcherCurtain()); + } // meta, not a real setting else if (strcmp(key, "fontpath") == 0) @@ -1367,13 +1423,13 @@ void CFG_sync(void) fprintf(file, "font=0\n"); else fprintf(file, "font=%s\n", settings.fontFile); - fprintf(file, "color1=0x%06X\n", settings.color1_255); - fprintf(file, "color2=0x%06X\n", settings.color2_255); - fprintf(file, "color3=0x%06X\n", settings.color3_255); - fprintf(file, "color4=0x%06X\n", settings.color4_255); - fprintf(file, "color5=0x%06X\n", settings.color5_255); - fprintf(file, "color6=0x%06X\n", settings.color6_255); - fprintf(file, "color7=0x%06X\n", settings.color7_255); + fprintf(file, "color1=0x%08X\n", settings.color1_255); + fprintf(file, "color2=0x%08X\n", settings.color2_255); + fprintf(file, "color3=0x%08X\n", settings.color3_255); + fprintf(file, "color4=0x%08X\n", settings.color4_255); + fprintf(file, "color5=0x%08X\n", settings.color5_255); + fprintf(file, "color6=0x%08X\n", settings.color6_255); + fprintf(file, "color7=0x%08X\n", settings.color7_255); fprintf(file, "radius=%i\n", settings.thumbRadius); fprintf(file, "showclock=%i\n", settings.showClock); fprintf(file, "clock24h=%i\n", settings.clock24h); @@ -1422,6 +1478,7 @@ void CFG_sync(void) fprintf(file, "raProgressNotificationDuration=%i\n", settings.raProgressNotificationDuration); fprintf(file, "raAchievementSortOrder=%i\n", settings.raAchievementSortOrder); fprintf(file, "fontStyle=%i\n", settings.fontStyle); + fprintf(file, "gameSwitcherCurtain=%i\n", settings.gameSwitcherCurtain); fclose(file); } @@ -1434,13 +1491,13 @@ void CFG_print(void) printf("\t\"font\": 0,\n"); else printf("\t\"font\": 1,\n"); - printf("\t\"color1\": \"0x%06X\",\n", settings.color1_255); - printf("\t\"color2\": \"0x%06X\",\n", settings.color2_255); - printf("\t\"color3\": \"0x%06X\",\n", settings.color3_255); - printf("\t\"color4\": \"0x%06X\",\n", settings.color4_255); - printf("\t\"color5\": \"0x%06X\",\n", settings.color5_255); - printf("\t\"color6\": \"0x%06X\",\n", settings.color6_255); - printf("\t\"color7\": \"0x%06X\",\n", settings.color7_255); + printf("\t\"color1\": \"0x%08X\",\n", settings.color1_255); + printf("\t\"color2\": \"0x%08X\",\n", settings.color2_255); + printf("\t\"color3\": \"0x%08X\",\n", settings.color3_255); + printf("\t\"color4\": \"0x%08X\",\n", settings.color4_255); + printf("\t\"color5\": \"0x%08X\",\n", settings.color5_255); + printf("\t\"color6\": \"0x%08X\",\n", settings.color6_255); + printf("\t\"color7\": \"0x%08X\",\n", settings.color7_255); printf("\t\"radius\": %i,\n", settings.thumbRadius); printf("\t\"showclock\": %i,\n", settings.showClock); printf("\t\"clock24h\": %i,\n", settings.clock24h); @@ -1488,6 +1545,7 @@ void CFG_print(void) printf("\t\"raProgressNotificationDuration\": %i,\n", settings.raProgressNotificationDuration); printf("\t\"raAchievementSortOrder\": %i,\n", settings.raAchievementSortOrder); printf("\t\"fontStyle\": %i,\n", settings.fontStyle); + printf("\t\"gameSwitcherCurtain\": %i,\n", settings.gameSwitcherCurtain); // meta, not a real setting printf("\t\"fontpath\": \"%s/%s\"\n", RES_PATH, CFG_getFontFile()); diff --git a/workspace/all/common/config.h b/workspace/all/common/config.h index d2c56b94b..24708756d 100644 --- a/workspace/all/common/config.h +++ b/workspace/all/common/config.h @@ -96,6 +96,7 @@ typedef struct uint32_t color7_255; // not screen mapped int thumbRadius; int gameSwitcherScaling; // enum + int gameSwitcherCurtain; double gameArtWidth; // [0,1] -> 0-100% of screen width // font loading/unloading callback @@ -173,13 +174,13 @@ typedef struct #define CFG_DEFAULT_FONT_FILE "font1.ttf" // Next #define CFG_DEFAULT_FONT_STYLE 0x01 // TTF_STYLE_BOLD (MinUI default) -#define CFG_DEFAULT_COLOR1 0xffffffU -#define CFG_DEFAULT_COLOR2 0x9b2257U -#define CFG_DEFAULT_COLOR3 0x1e2329U -#define CFG_DEFAULT_COLOR4 0xffffffU -#define CFG_DEFAULT_COLOR5 0x000000U -#define CFG_DEFAULT_COLOR6 0xffffffU -#define CFG_DEFAULT_COLOR7 0x000000U +#define CFG_DEFAULT_COLOR1 0xffffffffU +#define CFG_DEFAULT_COLOR2 0x9b2257ffU +#define CFG_DEFAULT_COLOR3 0x1e2329ffU +#define CFG_DEFAULT_COLOR4 0xffffffffU +#define CFG_DEFAULT_COLOR5 0x000000ffU +#define CFG_DEFAULT_COLOR6 0xffffffffU +#define CFG_DEFAULT_COLOR7 0x000000ffU #define CFG_DEFAULT_COLOR_MAIN CFG_DEFAULT_COLOR1 #define CFG_DEFAULT_COLOR_ACCENT CFG_DEFAULT_COLOR2 #define CFG_DEFAULT_COLOR_ACCENT2 CFG_DEFAULT_COLOR3 @@ -218,6 +219,7 @@ typedef struct #define CFG_DEFAULT_BLUETOOTH_MAXRATE 48000 #define CFG_DEFAULT_NTP false #define CFG_DEFAULT_TIMEZONE 320 // Europe/Berlin +#define CFG_DEFAULT_GAMESWITCHER_CURTAIN 0 // Notification defaults #define CFG_DEFAULT_NOTIFY_MANUAL_SAVE true @@ -257,7 +259,7 @@ void CFG_setFontFile(const char* filename); // The font style to use for the UI font. int CFG_getFontStyle(void); void CFG_setFontStyle(int style); -// The colors to use for the UI. These are 0xRRGGBB values. +// The colors to use for the UI. These are packed 0xRRGGBBAA values. // 0 - Color1 (primary hint/asset colour) // 1 - Color2 (accent colour) // 2 - Color3 (secondary accent colour @@ -362,6 +364,9 @@ void CFG_setNTP(bool on); // Current timezone index in tz database int CFG_getCurrentTimezone(void); void CFG_setCurrentTimezone(int index); +// Show/hide curtain on the game switcher screen (0-100% opacity) +int CFG_getGameSwitcherCurtain(void); +void CFG_setGameSwitcherCurtain(int opacity); // Notification settings bool CFG_getNotifyManualSave(void); diff --git a/workspace/all/nextui/nextui.c b/workspace/all/nextui/nextui.c index 69b66ba9b..23d9e7323 100644 --- a/workspace/all/nextui/nextui.c +++ b/workspace/all/nextui/nextui.c @@ -2616,6 +2616,11 @@ int main (int argc, char *argv[]) { } GFX_clear(screen); + if(CFG_getGameSwitcherCurtain() > 0 && currentScreen == SCREEN_GAMESWITCHER) { + GFX_blitTopCurtain(screen); + GFX_blitBottomCurtain(screen); + } + int ow = GFX_blitHardwareGroup(screen, show_setting); if (currentScreen == SCREEN_QUICKMENU) { if(lastScreen != SCREEN_QUICKMENU) { diff --git a/workspace/all/settings/colorpickermenu.cpp b/workspace/all/settings/colorpickermenu.cpp index 8d5e7f1ec..33646a79f 100644 --- a/workspace/all/settings/colorpickermenu.cpp +++ b/workspace/all/settings/colorpickermenu.cpp @@ -2,13 +2,15 @@ #include -static constexpr int NUM_SLIDERS = 3; +static constexpr int NUM_SLIDERS = 4; -static void decodeColor(uint32_t c, int &r, int &g, int &b) +static void decodeColor(uint32_t c, int &r, int &g, int &b, int &a) { - r = (c >> 16) & 0xFF; - g = (c >> 8) & 0xFF; - b = c & 0xFF; + auto col = uintToColour(c); + a = col.a; + r = col.r; + g = col.g; + b = col.b; } /////////////////////////////////////////////////////////// @@ -20,12 +22,12 @@ ColorPickerMenu::ColorPickerMenu(uint32_t initialColor, ValueSetCallback on_set, presets(std::move(presets)), label_(std::move(label)), originalColor_(initialColor), selected(0) { - decodeColor(initialColor, r, g, b); + decodeColor(initialColor, r, g, b, a); } void ColorPickerMenu::reset(uint32_t color, std::vector newPresets, std::string label) { - decodeColor(color, r, g, b); + decodeColor(color, r, g, b, a); originalColor_ = color; selected = 0; presets = std::move(newPresets); @@ -34,7 +36,8 @@ void ColorPickerMenu::reset(uint32_t color, std::vector newPresets, uint32_t ColorPickerMenu::currentColor() const { - return ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b; + // in rgba format + return ((r & 0xFF) << 24) | ((g & 0xFF) << 16) | ((b & 0xFF) << 8) | (a & 0xFF); } void ColorPickerMenu::applyColor() @@ -67,7 +70,7 @@ InputReactionHint ColorPickerMenu::handleInput(int &dirty, int &quit) } else if (PAD_justPressed(BTN_B)) { - decodeColor(originalColor_, r, g, b); + decodeColor(originalColor_, r, g, b, a); applyColor(); dirty = 1; quit = 1; @@ -83,7 +86,7 @@ InputReactionHint ColorPickerMenu::handleInput(int &dirty, int &quit) if (selected < NUM_SLIDERS) { - int *channels[] = {&r, &g, &b}; + int *channels[] = {&r, &g, &b, &a}; int *channel = channels[selected]; if (PAD_justRepeated(BTN_LEFT)) { @@ -120,7 +123,7 @@ InputReactionHint ColorPickerMenu::handleInput(int &dirty, int &quit) if (PAD_justPressed(BTN_X) && presetIdx < (int)presets.size()) { const auto &preset = presets[presetIdx]; - decodeColor(preset.color, r, g, b); + decodeColor(preset.color, r, g, b, a); applyColor(); selected = 0; dirty = 1; @@ -167,19 +170,64 @@ static void drawRoundedRect(SDL_Surface *surface, const SDL_Rect &rect, int radi } } +static void drawRoundedAlphaRect(SDL_Surface *surface, const SDL_Rect &rect, int radius, + uint32_t outer, uint32_t middle, uint32_t fill) +{ + if (rect.w <= 0 || rect.h <= 0) return; + + SDL_Surface *tmp = SDL_CreateRGBSurfaceWithFormat(0, rect.w, rect.h, + 32, SDL_PIXELFORMAT_ARGB8888); + if (!tmp) return; + + SDL_SetSurfaceBlendMode(tmp, SDL_BLENDMODE_BLEND); + SDL_FillRect(tmp, nullptr, SDL_MapRGBA(tmp->format, 0, 0, 0, 0)); + + auto remap = [&](uint32_t c) -> uint32_t { + uint8_t rr, gg, bb, aa; + SDL_GetRGBA(c, surface->format, &rr, &gg, &bb, &aa); + return SDL_MapRGBA(tmp->format, rr, gg, bb, aa); + }; + + SDL_Rect r0 = {0, 0, rect.w, rect.h}; + drawSolidRoundedRect(tmp, r0, radius, remap(outer)); + if (rect.w > 2 && rect.h > 2) + { + SDL_Rect r1 = {1, 1, rect.w - 2, rect.h - 2}; + drawSolidRoundedRect(tmp, r1, std::max(0, radius - 1), remap(middle)); + } + if (rect.w > 4 && rect.h > 4) + { + SDL_Rect r2 = {2, 2, rect.w - 4, rect.h - 4}; + drawSolidRoundedRect(tmp, r2, std::max(0, radius - 2), remap(fill)); + } + + SDL_Rect dst = rect; + SDL_BlitSurface(tmp, nullptr, surface, &dst); + SDL_FreeSurface(tmp); +} + static void drawGradientCapsule(SDL_Surface *surface, const SDL_Rect &bar, - int r, int g, int b, int channel) + int r, int g, int b, int a, int channel) { if (bar.w <= 0 || bar.h <= 0) return; const int R = bar.h / 2; + // SDL_FillRect does not alpha blend into destination surfaces. Build the + // gradient into an ARGB temp surface, then alpha-blit it onto destination. + SDL_Surface *grad = SDL_CreateRGBSurfaceWithFormat(0, bar.w, bar.h, + 32, SDL_PIXELFORMAT_ARGB8888); + if (!grad) return; + SDL_SetSurfaceBlendMode(grad, SDL_BLENDMODE_BLEND); + SDL_FillRect(grad, nullptr, SDL_MapRGBA(grad->format, 0, 0, 0, 0)); + for (int x = 0; x < bar.w; x++) { int ch_val = (bar.w > 1) ? (int)((float)x / (bar.w - 1) * 255.0f) : 128; uint8_t cr = (channel == 0) ? (uint8_t)ch_val : (uint8_t)r; uint8_t cg = (channel == 1) ? (uint8_t)ch_val : (uint8_t)g; uint8_t cb = (channel == 2) ? (uint8_t)ch_val : (uint8_t)b; - uint32_t col = SDL_MapRGB(surface->format, cr, cg, cb); + uint8_t ca = (channel == 3) ? (uint8_t)ch_val : (uint8_t)a; + uint32_t col = SDL_MapRGBA(grad->format, cr, cg, cb, ca); int y_inset = 0; if (R > 0) { @@ -197,9 +245,13 @@ static void drawGradientCapsule(SDL_Surface *surface, const SDL_Rect &bar, int col_h = bar.h - 2 * y_inset; if (col_h <= 0) continue; - SDL_Rect col_rect = {bar.x + x, bar.y + y_inset, 1, col_h}; - SDL_FillRect(surface, &col_rect, col); + SDL_Rect col_rect = {x, y_inset, 1, col_h}; + SDL_FillRect(grad, &col_rect, col); } + + SDL_Rect dst = bar; + SDL_BlitSurface(grad, nullptr, surface, &dst); + SDL_FreeSurface(grad); } void ColorPickerMenu::drawSlider(SDL_Surface *surface, const SDL_Rect &row, @@ -213,16 +265,17 @@ void ColorPickerMenu::drawSlider(SDL_Surface *surface, const SDL_Rect &row, // Fixed-width slots so all sliders align regardless of rendered glyph widths int label_fixed_w; { - int wr, wg, wb; + int wr, wg, wb, wa; TTF_SizeUTF8(font.small, "R", &wr, nullptr); TTF_SizeUTF8(font.small, "G", &wg, nullptr); TTF_SizeUTF8(font.small, "B", &wb, nullptr); - label_fixed_w = std::max({wr, wg, wb}); + TTF_SizeUTF8(font.small, "A", &wa, nullptr); + label_fixed_w = std::max({wr, wg, wb, wa}); } int hex_fixed_w; TTF_SizeUTF8(font.tiny, "00", &hex_fixed_w, nullptr); - // Channel label ("R", "G", "B") — centered in fixed-width slot + // Channel label ("R", "G", "B", "A") — centered in fixed-width slot SDL_Surface *label_surf = TTF_RenderUTF8_Blended(font.small, label, text_color); int label_x = row.x + SCALE1(OPTION_PADDING) + (label_fixed_w - label_surf->w) / 2; SDL_BlitSurfaceCPP(label_surf, {}, surface, @@ -230,7 +283,7 @@ void ColorPickerMenu::drawSlider(SDL_Surface *surface, const SDL_Rect &row, SDL_FreeSurface(label_surf); // Hex value right-aligned in fixed-width slot - char hex_str[3]; + char hex_str[4]; snprintf(hex_str, sizeof(hex_str), "%02X", value); SDL_Surface *hex_surf = TTF_RenderUTF8_Blended(font.tiny, hex_str, text_color); int hex_slot_x = row.x + row.w - SCALE1(OPTION_PADDING) - hex_fixed_w; @@ -247,17 +300,21 @@ void ColorPickerMenu::drawSlider(SDL_Surface *surface, const SDL_Rect &row, if (bar_w > 0) { - uint32_t white = SDL_MapRGB(surface->format, 255, 255, 255); - uint32_t black = SDL_MapRGB(surface->format, 0, 0, 0); + uint32_t white = SDL_MapRGBA(surface->format, 255, 255, 255, 255); + uint32_t black = SDL_MapRGBA(surface->format, 0, 0, 0, 255); // Ring inset within bar bounds: 1px black outer, 1px white inner, gradient fill drawSolidRoundedRect(surface, {bar_x, bar_y, bar_w, bar_h}, bar_h / 2, black); if (bar_w > 2 && bar_h > 2) drawSolidRoundedRect(surface, {bar_x + 1, bar_y + 1, bar_w - 2, bar_h - 2}, (bar_h - 2) / 2, white); - if (bar_w > 4 && bar_h > 4) + if (bar_w > 4 && bar_h > 4) { + // inner section is filled with background color, so we can blend + drawSolidRoundedRect(surface, {bar_x + 2, bar_y + 2, bar_w - 4, bar_h - 4}, + (bar_h - 4) / 2, THEME_COLOR7); drawGradientCapsule(surface, {bar_x + 2, bar_y + 2, bar_w - 4, bar_h - 4}, - this->r, this->g, this->b, channel); + this->r, this->g, this->b, this->a, channel); + } // Circle thumb indicator const int circ_d = bar_h + SCALE1(2); @@ -305,9 +362,9 @@ void ColorPickerMenu::drawPreset(SDL_Surface *surface, const SDL_Rect &row, SDL_Color text_color = is_selected ? uintToColour(THEME_COLOR5_255) : uintToColour(THEME_COLOR4_255); - // Hex value "#RRGGBB" - char hex_str[8]; - snprintf(hex_str, sizeof(hex_str), "#%06X", preset.color); + // Hex value "#RRGGBBAA" + char hex_str[9]; + snprintf(hex_str, sizeof(hex_str), "#%08X", preset.color); SDL_Surface *hex_surf = TTF_RenderUTF8_Blended(font.tiny, hex_str, text_color); int hex_x = sq_rect.x + sq + SCALE1(OPTION_PADDING / 2 + 2); SDL_BlitSurfaceCPP(hex_surf, {}, surface, @@ -359,13 +416,13 @@ void ColorPickerMenu::drawCustom(SDL_Surface *surface, const SDL_Rect &dst, cons const int SLIDER_AREA_W = dst.w - PREVIEW_SIZE - SCALE1(PADDING); const int PREVIEW_RADIUS = SCALE1(4); - uint32_t white = SDL_MapRGB(surface->format, 255, 255, 255); - uint32_t black = SDL_MapRGB(surface->format, 0, 0, 0); - uint32_t col_mapped = SDL_MapRGB(surface->format, r, g, b); + uint32_t white = SDL_MapRGBA(surface->format, 255, 255, 255, 255); + uint32_t black = SDL_MapRGBA(surface->format, 0, 0, 0, 255); + uint32_t col_mapped = SDL_MapRGBA(surface->format, r, g, b, a); // R, G, B slider rows - const char *channel_labels[] = {"R", "G", "B"}; - int channel_values[] = {r, g, b}; + const char *channel_labels[] = {"R", "G", "B", "A"}; + int channel_values[] = {r, g, b, a}; for (int i = 0; i < NUM_SLIDERS; i++) { SDL_Rect row = {dst.x, dst.y + TOP_OFFSET + SCALE1(i * BUTTON_SIZE), SLIDER_AREA_W, SCALE1(BUTTON_SIZE)}; @@ -379,7 +436,7 @@ void ColorPickerMenu::drawCustom(SDL_Surface *surface, const SDL_Rect &dst, cons PREVIEW_SIZE, PREVIEW_SIZE }; - drawRoundedRect(surface, preview, PREVIEW_RADIUS, black, white, col_mapped); + drawRoundedAlphaRect(surface, preview, PREVIEW_RADIUS, black, white, col_mapped); // Preset rows below the sliders for (int i = 0; i < (int)presets.size(); i++) @@ -390,6 +447,8 @@ void ColorPickerMenu::drawCustom(SDL_Surface *surface, const SDL_Rect &dst, cons dst.w, SCALE1(BUTTON_SIZE) }; + if(row.y + row.h > dst.y + dst.h - SCALE1(BUTTON_SIZE)) + break; // don't draw outside the menu area drawPreset(surface, row, presets[i], selected == NUM_SLIDERS + i); } } diff --git a/workspace/all/settings/colorpickermenu.hpp b/workspace/all/settings/colorpickermenu.hpp index e11d388ca..5e183cd11 100644 --- a/workspace/all/settings/colorpickermenu.hpp +++ b/workspace/all/settings/colorpickermenu.hpp @@ -9,7 +9,7 @@ struct ColorPreset { class ColorPickerMenu : public MenuList { - int r, g, b; + int r, g, b, a; int selected; ValueSetCallback on_set; std::vector presets; diff --git a/workspace/all/settings/menu.cpp b/workspace/all/settings/menu.cpp index ab5915b03..b17a0cabb 100644 --- a/workspace/all/settings/menu.cpp +++ b/workspace/all/settings/menu.cpp @@ -684,18 +684,19 @@ void MenuList::drawFixed(SDL_Surface *surface, const SDL_Rect &dst, const SDL_Re // TODO: expose API functions that do the same namespace { - static inline void rgb_unpack(uint32_t col, int *r, int *g, int *b) + static inline void rgba_unpack(uint32_t col, int *r, int *g, int *b, int *a) { - *r = (col >> 16) & 0xff; - *g = (col >> 8) & 0xff; - *b = col & 0xff; + *r = (col >> 24) & 0xff; + *g = (col >> 16) & 0xff; + *b = (col >> 8) & 0xff; + *a = col & 0xff; } static inline uint32_t mapUint(SDL_Surface *surface, uint32_t col) { - int r, g, b; - rgb_unpack(col, &r, &g, &b); - return SDL_MapRGB(surface->format, r, g, b); + int r, g, b, a; + rgba_unpack(col, &r, &g, &b, &a); + return SDL_MapRGBA(surface->format, r, g, b, a); } } @@ -740,7 +741,7 @@ void MenuList::drawFixedItem(SDL_Surface *surface, const SDL_Rect &dst, const Ab // Rerender the label from the live hex value SDL_FreeSurface(text); char hexLabel[12]; - snprintf(hexLabel, sizeof(hexLabel), "0x%06X", rawColor); + snprintf(hexLabel, sizeof(hexLabel), "0x%08X", rawColor); text = TTF_RenderUTF8_Blended(font.tiny, hexLabel, text_color_value); SDL_BlitSurfaceCPP(text, {}, surface, {dst.x + mw - text->w - SCALE1(OPTION_PADDING + COLOR_PADDING + FONT_TINY), dst.y + ((dst.h - text->h) / 2)}); } diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index f40153e1c..c443e5c0c 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -150,6 +150,10 @@ static const std::vector progress_duration_labels = {"Off", "1s", " static const std::vector transition_mode_values = {(int)TRANSITION_OFF, (int)TRANSITION_SNAPPY, (int)TRANSITION_COMFY}; static const std::vector transition_mode_labels = {"Off", "Snappy", "Comfy"}; +// Game switcher curtain opacity options (0-100) +static const std::vector curtain_opacity_values = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; +static const std::vector curtain_opacity_labels = {"Off", "10%", "20%", "30%", "40%", "50%", "60%", "70%", "80%", "90%", "100%"}; + // RetroAchievements sort order options static const std::vector ra_sort_values = { (int)RA_SORT_UNLOCKED_FIRST, @@ -181,7 +185,7 @@ static const std::vector ra_sort_labels = { namespace { struct ColorDef { int id; const char *name; const char *desc; uint32_t defaultColor; }; static const ColorDef g_colorDefs[] = { - {1, "Main Color", "The color used to render main UI elements.", CFG_DEFAULT_COLOR1}, + {1, "Main Color", "The color used to render main UI elements.", CFG_DEFAULT_COLOR1}, {2, "Primary Accent Color", "The color used to highlight important things in the user interface.", CFG_DEFAULT_COLOR2}, {3, "Secondary Accent Color", "A secondary highlight color.", CFG_DEFAULT_COLOR3}, {6, "Hint info Color", "Color for button hints and info", CFG_DEFAULT_COLOR6}, @@ -375,7 +379,7 @@ int main(int argc, char *argv[]) return [id, defaultColor]() { CFG_setColor(id, defaultColor); }; }; - // Pre-create one RGB picker per color setting (reused across opens) + // Pre-create one RGBA picker per color setting (reused across opens) std::vector> pickers; pickers.reserve(std::size(g_colorDefs)); for (const auto &def : g_colorDefs) @@ -460,6 +464,10 @@ int main(int argc, char *argv[]) []() -> std::any{ return CFG_getShowQuickswitcherUI(); }, [](const std::any &value){ CFG_setShowQuickswitcherUI(std::any_cast(value)); }, []() { CFG_setShowQuickswitcherUI(CFG_DEFAULT_SHOWQUICKWITCHERUI);}}); + appearanceItems.push_back(new MenuItem{ListItemType::Generic, "Game switcher curtain opacity", "Show/hide curtain overlay. Helps UI elements to \nstand out when using transparent backgrounds.", curtain_opacity_values, curtain_opacity_labels, + []() -> std::any{ return CFG_getGameSwitcherCurtain(); }, + [](const std::any &value){ CFG_setGameSwitcherCurtain(std::any_cast(value)); }, + []() { CFG_setGameSwitcherCurtain(CFG_DEFAULT_GAMESWITCHER_CURTAIN);}}); // not needed anymore // new MenuItem{ListItemType::Generic, "Game switcher scaling", "The scaling algorithm used to display the savegame image.", scaling, scaling_strings, []() -> std::any // { return CFG_getGameSwitcherScaling(); }, @@ -1092,7 +1100,7 @@ int main(int argc, char *argv[]) SDL_BlitSurface(bgbmp, NULL, ctx.screen, &image_rect); } else { uint32_t bgc = CFG_getColor(COLOR_BACKGROUND); - SDL_FillRect(ctx.screen, NULL, SDL_MapRGB(ctx.screen->format, (bgc >> 16) & 0xFF, (bgc >> 8) & 0xFF, bgc & 0xFF)); + SDL_FillRect(ctx.screen, NULL, SDL_MapRGBA(ctx.screen->format, (bgc >> 24) & 0xFF, (bgc >> 16) & 0xFF, (bgc >> 8) & 0xFF, bgc & 0xFF)); } int ow = 0;