#include #include "common.h" #include "netloader.h" #include "invalid_icon_bin.h" #include "folder_icon_bin.h" #include "theme_icon_dark_bin.h" #include "theme_icon_light_bin.h" #include "charging_icon_bin.h" #include "battery_icon_bin.h" char rootPathBase[PATH_MAX]; char rootPath[PATH_MAX+8]; void computeFrontGradient(color_t baseColor, int height); char *menuGetRootPath(void) { return rootPath; } char *menuGetRootBasePath(void) { return rootPathBase; } void launchMenuEntryTask(menuEntry_s* arg) { menuEntry_s* me = arg; if (me->type == ENTRY_TYPE_FOLDER) menuScan(me->path); //changeDirTask(me->path); else if(me->type == ENTRY_TYPE_THEME) launchApplyThemeTask(me); else launchMenuEntry(me); } static enum { HBMENU_DEFAULT, HBMENU_NETLOADER_ACTIVE, HBMENU_NETLOADER_ERROR, HBMENU_NETLOADER_SUCCESS, HBMENU_THEME_MENU, } hbmenu_state = HBMENU_DEFAULT; void launchMenuNetloaderTask() { if(hbmenu_state == HBMENU_DEFAULT) workerSchedule(netloaderTask, NULL); } void launchMenuBackTask() { if(hbmenu_state == HBMENU_NETLOADER_ACTIVE) { netloaderSignalExit(); } else if(hbmenu_state == HBMENU_THEME_MENU) { hbmenu_state = HBMENU_DEFAULT; menuScan(rootPath); } else { menuScan(".."); } } void menuHandleAButton(void) { menu_s* menu = menuGetCurrent(); if (menuIsMsgBoxOpen()) { menuCloseMsgBox(); } else if (menu->nEntries > 0 && (hbmenu_state == HBMENU_DEFAULT || hbmenu_state == HBMENU_THEME_MENU)) { int i; menuEntry_s* me; for (i = 0, me = menu->firstEntry; i != menu->curEntry; i ++, me = me->next); launchMenuEntryTask(me); //workerSchedule(launchMenuEntryTask, me); } } void launchApplyThemeTask(menuEntry_s* arg) { const char* themePath = arg->path; SetThemePathToConfig(themePath); themeStartup(themeGlobalPreset); computeFrontGradient(themeCurrent.frontWaveColor, 280); } //Draws an RGB888 or RGBA8888 image. static void drawImage(int x, int y, int width, int height, const uint8_t *image, ImageMode mode) { int tmpx, tmpy; int pos; color_t current_color; for (tmpy=0; tmpy= border_start_x + shadow_inset && x < border_end_x - shadow_inset) { for (j=0; j<4; j++) DrawPixel(x+j, end_y +shadow_y, shadow_color); } } } for (y=border_start_y; yicon_gfx_small && me->icon_gfx) { smallimg = me->icon_gfx_small; largeimg = me->icon_gfx; } else if (me->type == ENTRY_TYPE_FOLDER) { smallimg = folder_icon_small; largeimg = folder_icon_bin; } else if (me->type == ENTRY_TYPE_THEME){ smallimg = theme_icon_small; if(themeGlobalPreset == THEME_PRESET_DARK) largeimg = theme_icon_dark_bin; else largeimg = theme_icon_light_bin; } else { smallimg = invalid_icon_small; largeimg = invalid_icon_bin; } if (smallimg) { drawImage(start_x, start_y + 32, 140, 140, smallimg, IMAGE_MODE_RGB24); } if (is_active && largeimg) { drawImage(117, 100, 256, 256, largeimg, IMAGE_MODE_RGB24); shadow_start_y = 100+256; border_start_x = 117; border_end_x = 117+256; for (shadow_y=shadow_start_y; shadow_y = border_start_x + shadow_inset && x <= border_end_x - shadow_inset) { DrawPixel(x, shadow_y, shadow_color); } } } } DrawTextTruncate(interuiregular14, start_x + 4, start_y + 4 + 18, themeCurrent.borderTextColor, me->name, 140 - 32, "..."); if (is_active) { start_x = 1280 - 790; start_y = 135; DrawTextTruncate(interuimedium30, start_x, start_y + 39, themeCurrent.textColor, me->name, 1280 - start_x - 120 ,"..."); if (me->type != ENTRY_TYPE_FOLDER) { memset(tmpstr, 0, sizeof(tmpstr)); snprintf(tmpstr, sizeof(tmpstr)-1, "%s: %s", textGetString(StrId_AppInfo_Author), me->author); DrawText(interuiregular14, start_x, start_y + 28 + 30 + 18, themeCurrent.textColor, tmpstr); memset(tmpstr, 0, sizeof(tmpstr)); snprintf(tmpstr, sizeof(tmpstr)-1, "%s: %s", textGetString(StrId_AppInfo_Version), me->version); DrawText(interuiregular14, start_x, start_y + 28 + 30 + 18 + 6 + 18, themeCurrent.textColor, tmpstr); } } } color_t frontWaveGradient[720]; void computeFrontGradient(color_t baseColor, int height) { int y; int alpha; float dark_mult, dark_sub = 75; color_t color; for (y=0; y<720; y++) { alpha = y - (720 - height); if (alpha < 0) color = baseColor; else { dark_mult = clamp((float)(alpha - 50) / (float)height, 0.0, 1.0); color = MakeColor(baseColor.r - dark_sub * dark_mult, baseColor.g - dark_sub * dark_mult, baseColor.b - dark_sub * dark_mult, 255); } frontWaveGradient[y] = color; } } void menuStartupPath(void) { char tmp_path[PATH_MAX+28]; #ifdef __SWITCH__ strncpy(rootPathBase, "sdmc:", sizeof(rootPathBase)-1); #else getcwd(rootPathBase, sizeof(rootPathBase)); #endif snprintf(rootPath, sizeof(rootPath)-1, "%s%s%s", rootPathBase, DIRECTORY_SEPARATOR, "switch"); struct stat st = {0}; if (stat(rootPath, &st) == -1) { mkdir(rootPath, 0755); } snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu/themes", rootPathBase); if (stat(tmp_path, &st) == -1) { snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config", rootPathBase); mkdir(tmp_path, 0755); snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu", rootPathBase); mkdir(tmp_path, 0755); snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu/themes", rootPathBase); mkdir(tmp_path, 0755); } snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu/fileassoc", rootPathBase); if (stat(tmp_path, &st) == -1) { mkdir(tmp_path, 0755); } } void menuStartup(void) { char tmp_path[PATH_MAX+28]; snprintf(tmp_path, sizeof(tmp_path)-1, "%s/config/nx-hbmenu/fileassoc", rootPathBase); menuFileassocScan(tmp_path); menuScan(rootPath); folder_icon_small = downscaleImg(folder_icon_bin, 256, 256, 140, 140, IMAGE_MODE_RGB24); invalid_icon_small = downscaleImg(invalid_icon_bin, 256, 256, 140, 140, IMAGE_MODE_RGB24); if(themeGlobalPreset == THEME_PRESET_DARK) theme_icon_small = downscaleImg(theme_icon_dark_bin, 256, 256, 140, 140, IMAGE_MODE_RGB24); else theme_icon_small = downscaleImg(theme_icon_light_bin, 256, 256, 140, 140, IMAGE_MODE_RGB24); computeFrontGradient(themeCurrent.frontWaveColor, 280); //menuCreateMsgBox(780, 300, "This is a test"); } void themeMenuStartup(void) { if(hbmenu_state != HBMENU_DEFAULT) return; hbmenu_state = HBMENU_THEME_MENU; char tmp_path[PATH_MAX+25]; snprintf(tmp_path, sizeof(tmp_path)-1, "%s%s%s%s%s%s%s", rootPathBase, DIRECTORY_SEPARATOR, "config", DIRECTORY_SEPARATOR, "nx-hbmenu" , DIRECTORY_SEPARATOR, "themes"); themeMenuScan(tmp_path); } color_t waveBlendAdd(color_t a, color_t b, float alpha) { return MakeColor(a.r*(1.0f-alpha) + b.r*alpha, a.g*(1.0f-alpha) + b.g*alpha, a.b*(1.0f-alpha) + b.b*alpha, 255); } void drawWave(int id, float timer, color_t color, int height, float phase, float speed) { int x, y; float wave_top_y, alpha, one_minus_alpha; color_t existing_color, new_color; height = 720 - height; for (x=0; x<1280; x++) { wave_top_y = approxSin(x*speed/1280.0+timer+phase) * 10.0 + height; for (y=wave_top_y; y<720; y++) { if (id != 2 && y > wave_top_y + 30) break; alpha = y-wave_top_y; if (themeCurrent.enableWaveBlending) { existing_color = FetchPixelColor(x, y); new_color = waveBlendAdd(existing_color, color, clamp(alpha, 0.0, 1.0) * 0.3); } else if (alpha >= 0.3) { if (id == 2) { new_color = frontWaveGradient[y]; } else { // no anti-aliasing new_color = color; } } else { // anti-aliasing existing_color = FetchPixelColor(x, y); alpha = fabs(alpha); one_minus_alpha = (1.0 - alpha); new_color = MakeColor(color.r * one_minus_alpha + existing_color.r * alpha, color.g * one_minus_alpha + existing_color.g * alpha, color.b * one_minus_alpha + existing_color.b * alpha, 255); } DrawPixelRaw(x, y, new_color); } } } void drawTime() { char timeString[9]; time_t unixTime = time(NULL); struct tm* timeStruct = localtime((const time_t *)&unixTime); int hours = timeStruct->tm_hour; int minutes = timeStruct->tm_min; int seconds = timeStruct->tm_sec; sprintf(timeString, "%02d:%02d:%02d", hours, minutes, seconds); int tmpX = GetTextXCoordinate(interuimedium20, 1180, timeString, 'r'); DrawText(interuimedium20, tmpX, 0 + 47 + 10, themeCurrent.textColor, timeString); } void drawCharge() { char chargeString[5]; uint32_t batteryCharge; bool isCharging; bool validPower; validPower = powerGetDetails(&batteryCharge, &isCharging); if (validPower) { batteryCharge = (batteryCharge > 100) ? 100 : batteryCharge; sprintf(chargeString, "%d%%", batteryCharge); int tmpX = GetTextXCoordinate(interuiregular14, 1180, chargeString, 'r'); DrawText(interuiregular14, tmpX - 15, 0 + 47 + 10 + 21, themeCurrent.textColor, chargeString); drawIcon(1180 - 11, 0 + 47 + 10 + 6, 10, 15, battery_icon_bin, themeCurrent.textColor); if (isCharging) drawIcon(tmpX - 32, 0 + 47 + 10 + 6, 9, 15, charging_icon_bin, themeCurrent.textColor); } } void drawButtons(menu_s* menu, bool emptyDir, int *x_image_out) { int x_image = 1280 - 252 - 30 - 32; int x_text = 1280 - 216 - 30 - 32; if(emptyDir) { x_image = 1280 - 126 - 30 - 32; x_text = 1280 - 90 - 30 - 32; } #ifdef __SWITCH__ if (strcmp( menu->dirname, "sdmc:/") != 0) #else if (strcmp( menu->dirname, "/") != 0) #endif { //drawImage(x_image, 720 - 48, 32, 32, themeCurrent.buttonBImage, IMAGE_MODE_RGBA32); DrawText(fontscale7, x_image, 720 - 47 + 26, themeCurrent.textColor, themeCurrent.buttonBText);//Display the 'B' button from SharedFont. DrawText(interuimedium20, x_text, 720 - 47 + 26, themeCurrent.textColor, textGetString(StrId_Actions_Back)); } if(hbmenu_state == HBMENU_DEFAULT) { x_text = GetTextXCoordinate(interuiregular18, x_image - 32, textGetString(StrId_NetLoader), 'r'); x_image = x_text - 36; *x_image_out = x_image - 40; DrawText(fontscale7, x_image, 720 - 47 + 26, themeCurrent.textColor, themeCurrent.buttonYText); DrawText(interuiregular18, x_text, 720 - 47 + 26, themeCurrent.textColor, textGetString(StrId_NetLoader)); x_text = GetTextXCoordinate(interuiregular18, x_image - 32, textGetString(StrId_ThemeMenu), 'r'); x_image = x_text - 36; *x_image_out = x_image - 40; DrawText(fontscale7, x_image, 720 - 47 + 26, themeCurrent.textColor, themeCurrent.buttonMText); DrawText(interuiregular18, x_text, 720 - 47 + 26, themeCurrent.textColor, textGetString(StrId_ThemeMenu)); } } void menuLoop(void) { menuEntry_s* me; menuEntry_s* netloader_me = NULL; menu_s* menu = NULL; int i; int x, y; int menupath_x_endpos = 918 + 40; bool netloader_activated = 0, netloader_launch_app = 0; char netloader_errormsg[1024]; for (y=0; y<450; y++) { for (x=0; x<1280; x+=4) {// don't draw bottom pixels as they are covered by the waves Draw4PixelsRaw(x, y, themeCurrent.backgroundColor); } } drawWave(0, menuTimer, themeCurrent.backWaveColor, 295, 0.0, 3.0); drawWave(1, menuTimer, themeCurrent.middleWaveColor, 290, 2.0, 3.5); drawWave(2, menuTimer, themeCurrent.frontWaveColor, 280, 4.0, -2.5); menuTimer += 0.05; drawImage(40, 20, 140, 60, themeCurrent.hbmenuLogoImage, IMAGE_MODE_RGBA32); DrawText(interuiregular14, 180, 46 + 18, themeCurrent.textColor, VERSION); #ifdef PERF_LOG_DRAW//Seperate from the PERF_LOG define since this might affect perf. extern u64 g_tickdiff_vsync; extern u64 g_tickdiff_frame; char tmpstr[64]; snprintf(tmpstr, sizeof(tmpstr)-1, "%lu", g_tickdiff_vsync); DrawText(interuiregular14, 180 + 256, 46 + 18, themeCurrent.textColor, tmpstr); snprintf(tmpstr, sizeof(tmpstr)-1, "%lu", g_tickdiff_frame); DrawText(interuiregular14, 180 + 256, 46 + 16 + 18, themeCurrent.textColor, tmpstr); #endif drawTime(); drawCharge(); netloaderGetState(&netloader_activated, &netloader_launch_app, &netloader_me, netloader_errormsg, sizeof(netloader_errormsg)); if(hbmenu_state == HBMENU_DEFAULT && netloader_activated) { hbmenu_state = HBMENU_NETLOADER_ACTIVE; } else if(hbmenu_state == HBMENU_NETLOADER_ACTIVE && !netloader_activated && !netloader_launch_app) { hbmenu_state = HBMENU_DEFAULT; menuScan(".");//Reload the menu since netloader may have deleted the NRO if the transfer aborted. } menu = menuGetCurrent(); if (netloader_errormsg[0]) menuCreateMsgBox(780,300, netloader_errormsg); if (menu->nEntries==0 || hbmenu_state == HBMENU_NETLOADER_ACTIVE) { if (hbmenu_state == HBMENU_NETLOADER_ACTIVE) { if (netloader_launch_app) { hbmenu_state = HBMENU_DEFAULT; menuCreateMsgBox(240,240, textGetString(StrId_Loading)); launchMenuEntryTask(netloader_me); } } else { DrawText(interuiregular14, 64, 128 + 18, themeCurrent.textColor, textGetString(StrId_NoAppsFound_Msg)); } drawButtons(menu, true, &menupath_x_endpos); } else { static int v = 0; if (menu->nEntries > 7) { int wanted_x = clamp(-menu->curEntry * (140 + 30), -(menu->nEntries - 7) * (140 + 30), 0); menu->xPos += v; v += (wanted_x - menu->xPos) / 3; v /= 2; } else { menu->xPos = v = 0; } menuEntry_s *active_entry = NULL; // Draw menu entries for (me = menu->firstEntry, i = 0; me; me = me->next, i ++) { int entry_start_x = 29 + i * (140 + 30); int screen_width = 1280; if (entry_start_x >= (screen_width - menu->xPos)) break; int is_active = i==menu->curEntry; if (is_active) active_entry = me; drawEntry(me, entry_start_x + menu->xPos, is_active); } int getX = GetTextXCoordinate(interuiregular18, 1180, textGetString(StrId_ThemeMenu), 'r'); if(hbmenu_state == HBMENU_THEME_MENU) { DrawText(interuiregular18, getX, 30 + 26 + 32 + 10, themeCurrent.textColor, textGetString(StrId_ThemeMenu)); } else { //DrawText(interuiregular18, getX, 30 + 26 + 32 + 10, themeCurrent.textColor, textGetString(StrId_ThemeMenu)); //DrawText(fontscale7, getX - 40, 30 + 26 + 32 + 10, themeCurrent.textColor, themeCurrent.buttonMText); } if(active_entry != NULL) { if (active_entry->type == ENTRY_TYPE_THEME) { DrawText(fontscale7, 1280 - 126 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonAText); DrawText(interuiregular18, 1280 - 90 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_Actions_Apply)); } else if (active_entry->type != ENTRY_TYPE_FOLDER) { DrawText(fontscale7, 1280 - 126 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonAText);//Display the 'A' button from SharedFont. DrawText(interuiregular18, 1280 - 90 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_Actions_Launch)); } else { DrawText(fontscale7, 1280 - 126 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, themeCurrent.buttonAText); DrawText(interuiregular18, 1280 - 90 - 30 - 32, 720 - 47 + 24, themeCurrent.textColor, textGetString(StrId_Actions_Open)); } } drawButtons(menu, false, &menupath_x_endpos); } DrawTextTruncate(interuiregular18, 40, 720 - 47 + 24, themeCurrent.textColor, menu->dirname, menupath_x_endpos - 40, "..."); menuDrawMsgBox(); }