From 39f59c326f6c748bc0661cb15d58685320742f28 Mon Sep 17 00:00:00 2001 From: KrutzOtrem Date: Sun, 31 May 2026 11:54:58 +0300 Subject: [PATCH 1/3] Add files via upload --- workspace/all/settings/settings.cpp | 54 ++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index 53fc6b7a6..b351a5164 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -92,6 +92,11 @@ struct FontEntry { std::string label; }; +struct ThemeEntry { + std::string folder; + std::string label; +}; + static std::vector enumerateFonts() { std::vector fonts; fonts.push_back({"font1.ttf", "Next"}); @@ -125,6 +130,32 @@ static std::vector enumerateFonts() { return fonts; } +static std::vector enumerateThemes() { + std::vector themes; + themes.push_back({"", "Off"}); + + // theme folders live at the sd root + DIR *dir = opendir(SDCARD_PATH "/Themes"); + if (dir) { + struct dirent *ent; + while ((ent = readdir(dir)) != NULL) { + if (ent->d_name[0] == '.') continue; + + char theme_path[MAX_PATH]; + snprintf(theme_path, sizeof(theme_path), SDCARD_PATH "/Themes/%s", ent->d_name); + DIR *theme_dir = opendir(theme_path); + if (!theme_dir) + continue; + closedir(theme_dir); + + themes.push_back({std::string(ent->d_name), std::string(ent->d_name)}); + } + closedir(dir); + } + + return themes; +} + static const std::vector screen_timeout_secs = {0U, 5U, 10U, 15U, 30U, 45U, 60U, 90U, 120U, 240U, 360U, 600U}; static const std::vector screen_timeout_labels = {"Never", "5s", "10s", "15s", "30s", "45s", "60s", "90s", "2m", "4m", "6m", "10m"}; @@ -403,6 +434,17 @@ int main(int argc, char *argv[]) []() -> std::any { return std::string(CFG_getFontFile()); }, [](const std::any &value) { CFG_setFontFile(std::any_cast(value).c_str()); }, []() { CFG_setFontFile(CFG_DEFAULT_FONT_FILE); }}); + auto themes = enumerateThemes(); + std::vector theme_values; + std::vector theme_labels; + for (const auto &t : themes) { + theme_values.push_back(t.folder); + theme_labels.push_back(t.label); + } + appearanceItems.push_back(new MenuItem{ListItemType::Generic, "Theme", "Theme folder for menu backgrounds.", theme_values, theme_labels, + []() -> std::any { return std::string(CFG_getThemeFolder()); }, + [](const std::any &value) { CFG_setThemeFolder(std::any_cast(value).c_str()); }, + []() { CFG_setThemeFolder(CFG_DEFAULT_THEME_FOLDER); }}); appearanceItems.push_back(new MenuItem{ListItemType::Generic, "Font style", "The style to render the UI font (e.g. bold)", std::vector{0, 1}, std::vector{"Normal", "Bold"}, []() -> std::any { return CFG_getFontStyle(); }, [](const std::any &value) { CFG_setFontStyle(std::any_cast(value)); }, @@ -1012,7 +1054,17 @@ int main(int argc, char *argv[]) ctx.menu = new MenuList(MenuItemType::List, "Main", mainItems); - SDL_Surface* bgbmp = IMG_Load(SDCARD_PATH "/bg.png"); + char bg_path[MAX_PATH]; + snprintf(bg_path, sizeof(bg_path), SDCARD_PATH "/bg.png"); + const char* theme = CFG_getThemeFolder(); + if (theme && theme[0]) { + char theme_bg_path[MAX_PATH]; + snprintf(theme_bg_path, sizeof(theme_bg_path), SDCARD_PATH "/Themes/%s/bg.png", theme); + if (exists(theme_bg_path)) { + snprintf(bg_path, sizeof(bg_path), "%s", theme_bg_path); + } + } + SDL_Surface* bgbmp = IMG_Load(bg_path); SDL_Surface* convertedbg = SDL_ConvertSurfaceFormat(bgbmp, SDL_PIXELFORMAT_RGB565, 0); if (convertedbg) { SDL_FreeSurface(bgbmp); From 350c5083b6f84d4a6b22f19ec5a0950347ec138a Mon Sep 17 00:00:00 2001 From: KrutzOtrem Date: Sun, 31 May 2026 11:55:31 +0300 Subject: [PATCH 2/3] Add files via upload --- workspace/all/common/config.c | 33 +++++++++++++++++++++++++++++++++ workspace/all/common/config.h | 4 ++++ 2 files changed, 37 insertions(+) diff --git a/workspace/all/common/config.c b/workspace/all/common/config.c index ff5af1a62..c425f0c00 100644 --- a/workspace/all/common/config.c +++ b/workspace/all/common/config.c @@ -31,6 +31,7 @@ void CFG_defaults(NextUISettings *cfg) NextUISettings defaults = { .fontFile = CFG_DEFAULT_FONT_FILE, + .themeFolder = CFG_DEFAULT_THEME_FOLDER, .fontStyle = CFG_DEFAULT_FONT_STYLE, .color1_255 = CFG_DEFAULT_COLOR1, .color2_255 = CFG_DEFAULT_COLOR2, @@ -134,6 +135,13 @@ void CFG_init(FontLoad_callback_t cb, ColorSet_callback_t ccb) fontLoaded = true; continue; } + if (strncmp(line, "theme=", 6) == 0) + { + char *value = line + 6; + value[strcspn(value, "\n")] = 0; + CFG_setThemeFolder(value); + continue; + } if (sscanf(line, "color1=%x", &temp_color) == 1) { char hexColor[7]; @@ -467,6 +475,23 @@ void CFG_setFontFile(const char* filename) settings.onFontChange(fontPath); } +const char* CFG_getThemeFolder(void) +{ + return settings.themeFolder; +} + +// empty folder means theme off +void CFG_setThemeFolder(const char* folder) +{ + if (!folder || !folder[0] || strchr(folder, '/')) { + strncpy(settings.themeFolder, CFG_DEFAULT_THEME_FOLDER, sizeof(settings.themeFolder) - 1); + settings.themeFolder[sizeof(settings.themeFolder) - 1] = '\0'; + } else { + strncpy(settings.themeFolder, folder, sizeof(settings.themeFolder) - 1); + settings.themeFolder[sizeof(settings.themeFolder) - 1] = '\0'; + } +} + uint32_t CFG_getColor(int color_id) { switch (color_id) @@ -1328,6 +1353,10 @@ void CFG_get(const char *key, char *value) { sprintf(value, "%i", CFG_getFontStyle()); } + else if (strcmp(key, "theme") == 0) + { + sprintf(value, "%s", CFG_getThemeFolder()); + } // meta, not a real setting else if (strcmp(key, "fontpath") == 0) @@ -1367,6 +1396,9 @@ void CFG_sync(void) fprintf(file, "font=0\n"); else fprintf(file, "font=%s\n", settings.fontFile); + // off stays out of the file so old installs stay the same + if (settings.themeFolder[0]) + fprintf(file, "theme=%s\n", settings.themeFolder); 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); @@ -1434,6 +1466,7 @@ void CFG_print(void) printf("\t\"font\": 0,\n"); else printf("\t\"font\": 1,\n"); + printf("\t\"theme\": \"%s\",\n", settings.themeFolder); 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); diff --git a/workspace/all/common/config.h b/workspace/all/common/config.h index d2c56b94b..1c4917d29 100644 --- a/workspace/all/common/config.h +++ b/workspace/all/common/config.h @@ -86,6 +86,7 @@ typedef struct { // Theme char fontFile[256]; + char themeFolder[256]; // empty means off int fontStyle; // 0x00 = TTF_STYLE_NORMAL, 0x01 = TTF_STYLE_BOLD, etc. uint32_t color1_255; // not screen mapped uint32_t color2_255; // not screen mapped @@ -172,6 +173,7 @@ typedef struct #define TRANSITION_COMFY 2 #define CFG_DEFAULT_FONT_FILE "font1.ttf" // Next +#define CFG_DEFAULT_THEME_FOLDER "" #define CFG_DEFAULT_FONT_STYLE 0x01 // TTF_STYLE_BOLD (MinUI default) #define CFG_DEFAULT_COLOR1 0xffffffU #define CFG_DEFAULT_COLOR2 0x9b2257U @@ -254,6 +256,8 @@ void CFG_get(const char *key, char * value); // Custom fonts go in RES_PATH alongside built-in fonts. const char* CFG_getFontFile(void); void CFG_setFontFile(const char* filename); +const char* CFG_getThemeFolder(void); +void CFG_setThemeFolder(const char* folder); // The font style to use for the UI font. int CFG_getFontStyle(void); void CFG_setFontStyle(int style); From 2637231e71cecea682210100704b30d3608e629a Mon Sep 17 00:00:00 2001 From: KrutzOtrem Date: Sun, 31 May 2026 11:55:50 +0300 Subject: [PATCH 3/3] Add files via upload --- workspace/all/nextui/nextui.c | 77 +++++++++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 7 deletions(-) diff --git a/workspace/all/nextui/nextui.c b/workspace/all/nextui/nextui.c index 69b66ba9b..fb5e0758f 100644 --- a/workspace/all/nextui/nextui.c +++ b/workspace/all/nextui/nextui.c @@ -136,6 +136,74 @@ typedef struct Entry { int alpha; // index in parent Directory's alphas Array, which points to the index of an Entry in its entries Array :sweat_smile: } Entry; +// theme bg.png replaces root bg.png when selected +static void getDefaultBackgroundPath(char* out_path, size_t out_size) { + const char* theme = CFG_getThemeFolder(); + if (theme && theme[0]) { + char theme_path[MAX_PATH]; + snprintf(theme_path, sizeof(theme_path), SDCARD_PATH "/Themes/%s/bg.png", theme); + if (exists(theme_path)) { + snprintf(out_path, out_size, "%s", theme_path); + return; + } + } + snprintf(out_path, out_size, SDCARD_PATH "/bg.png"); +} + +// these menu folders use plain theme names +static int getSpecialBackgroundName(char* folder_path, char* out_name) { + if (exactMatch(folder_path, COLLECTIONS_PATH)) + strcpy(out_name, "Collections"); + else if (exactMatch(folder_path, FAUX_RECENT_PATH)) + strcpy(out_name, "Recently Played"); + else if (exactMatch(folder_path, TOOLS_PATH)) + strcpy(out_name, "Tools"); + else + return 0; + return 1; +} + +// pick a background without breaking old .media folders +static int getBackgroundPath(char* out_path, size_t out_size, char* folder_path, int type, char* default_path) { + if (type == ENTRY_DIR) + snprintf(out_path, out_size, "%s/.media/bg.png", folder_path); + else if (type == ENTRY_ROM) + snprintf(out_path, out_size, "%s/.media/bglist.png", folder_path); + else { + snprintf(out_path, out_size, "%s", default_path); + return 0; + } + + // old media backgrounds stay first for compatibility and per-folder overrides + if (exists(out_path)) return 1; + + const char* theme = CFG_getThemeFolder(); + if (theme && theme[0]) { + char theme_name[MAX_PATH]; + char theme_path[MAX_PATH]; + theme_name[0] = '\0'; + + if (!exactMatch(folder_path, ROMS_PATH) && prefixMatch(ROMS_PATH, folder_path)) + getEmuName(folder_path, theme_name); + else + getSpecialBackgroundName(folder_path, theme_name); + + if (theme_name[0]) { + if (type == ENTRY_DIR) + snprintf(theme_path, sizeof(theme_path), SDCARD_PATH "/Themes/%s/%s.png", theme, theme_name); + else + snprintf(theme_path, sizeof(theme_path), SDCARD_PATH "/Themes/%s/%s-list.png", theme, theme_name); + if (exists(theme_path)) { + snprintf(out_path, out_size, "%s", theme_path); + return 1; + } + } + } + + snprintf(out_path, out_size, "%s", default_path); + return 0; +} + static Entry* Entry_new(char* path, int type) { char display_name[256]; getDisplayName(path, display_name); @@ -2950,7 +3018,7 @@ int main (int argc, char *argv[]) { // load folder background char defaultBgPath[512]; - snprintf(defaultBgPath, sizeof(defaultBgPath), SDCARD_PATH "/bg.png"); + getDefaultBackgroundPath(defaultBgPath, sizeof(defaultBgPath)); if(((entry->type == ENTRY_DIR || entry->type == ENTRY_ROM) && CFG_getRomsUseFolderBackground())) { char *newBg = entry->type == ENTRY_DIR ? entry->path:rompath; @@ -2958,14 +3026,9 @@ int main (int argc, char *argv[]) { lastType = entry->type; char tmppath[512]; strncpy(folderBgPath, newBg, sizeof(folderBgPath) - 1); - if (entry->type == ENTRY_DIR) - snprintf(tmppath, sizeof(tmppath), "%s/.media/bg.png", folderBgPath); - else if (entry->type == ENTRY_ROM) - snprintf(tmppath, sizeof(tmppath), "%s/.media/bglist.png", folderBgPath); - if(!exists(tmppath)) { + if(!getBackgroundPath(tmppath, sizeof(tmppath), folderBgPath, entry->type, defaultBgPath)) { // Safeguard: If no background is available, still render the text to leave the user a way out list_show_entry_names = true; - snprintf(tmppath, sizeof(tmppath), defaultBgPath, folderBgPath); } startLoadFolderBackground(tmppath, onBackgroundLoaded, NULL); }