diff --git a/workspace/all/battery/battery.c b/workspace/all/battery/battery.c index 990d63ac7..df031a1da 100644 --- a/workspace/all/battery/battery.c +++ b/workspace/all/battery/battery.c @@ -641,6 +641,7 @@ int main(int argc, char *argv[]) device_model = PLAT_getModel(); screen = GFX_init(MODE_MAIN); + bool firstFrame = true; PAD_init(); PWR_init(); @@ -766,6 +767,11 @@ int main(int argc, char *argv[]) GFX_blitButtonGroup((char *[]){"B", "BACK", NULL}, 1, screen, 1); + if (firstFrame) + { + GFX_animateBlack(LAYER_SCROLLTEXT, 255, 0, CFG_getAppStartExitAnimationDuration()); + firstFrame = false; + } GFX_flip(screen); dirty = 0; } @@ -776,6 +782,7 @@ int main(int argc, char *argv[]) QuitSettings(); PWR_quit(); PAD_quit(); + GFX_animateBlack(LAYER_SCROLLTEXT, 0, 255, CFG_getAppStartExitAnimationDuration()); GFX_quit(); return EXIT_SUCCESS; diff --git a/workspace/all/clock/clock.c b/workspace/all/clock/clock.c index 3734f6329..dc43e7b14 100644 --- a/workspace/all/clock/clock.c +++ b/workspace/all/clock/clock.c @@ -23,6 +23,7 @@ int main(int argc , char* argv[]) { PWR_setCPUSpeed(CPU_SPEED_AUTO); SDL_Surface* screen = GFX_init(MODE_MENU); + bool firstFrame = true; PAD_init(); PWR_init(); InitSettings(); @@ -309,6 +310,10 @@ int main(int argc , char* argv[]) { } blitBar(x,y, (select_cursor==CURSOR_YEAR ? SCALE1(40) : (select_cursor==CURSOR_AMPM ? ampm_w : SCALE1(20)))); + if (firstFrame) { + GFX_animateBlack(LAYER_SCROLLTEXT, 255, 0, CFG_getAppStartExitAnimationDuration()); + firstFrame = false; + } GFX_flip(screen); dirty = 0; } @@ -320,6 +325,7 @@ int main(int argc , char* argv[]) { QuitSettings(); PWR_quit(); PAD_quit(); + GFX_animateBlack(LAYER_SCROLLTEXT, 0, 255, CFG_getAppStartExitAnimationDuration()); GFX_quit(); if (save_changes) PLAT_setDateTime(year_selected, month_selected, day_selected, hour_selected, minute_selected, seconds_selected); diff --git a/workspace/all/common/api.c b/workspace/all/common/api.c index e107225da..f4ce3589a 100644 --- a/workspace/all/common/api.c +++ b/workspace/all/common/api.c @@ -428,11 +428,6 @@ SDL_Surface *GFX_init(int mode) CFG_init(GFX_loadSystemFont, GFX_updateColors); - // by default, we will clear with whatever background color the user prefers - // if MODE_MENU /e.g. minarch, clear with default black) - if(mode == MODE_MAIN) - GFX_setClearColor(mapUint(CFG_getColor(COLOR_BACKGROUND))); - // We always have to symlink, does not depend on NTP being enabled PLAT_initTimezones(); PLAT_setCurrentTimezone(PLAT_getCurrentTimezone()); @@ -570,8 +565,57 @@ SDL_Surface *GFX_init(int mode) PLAT_clearAll(); + if (mode == MODE_MENU) + { + // black-screen apps, e.g. minarch: keep the default black. + } + else + { + // regular apps: set background color from the user preferences. + // IMPORTANT: no screen flip here - the app may use fade-in, so the display + // stays black (from the clear call) until then app flips its own frame. + GFX_setClearColor(mapUint(CFG_getColor(COLOR_BACKGROUND))); + GFX_clearLayers(LAYER_ALL); + } + return gfx.screen; } + +void GFX_animateBlack(int layer, int start_opacity, int end_opacity, int duration_ms) +{ + int width = gfx.screen->w; + int height = gfx.screen->h; + Uint32 format = gfx.screen->format->format; + + // the frame is drawn into the screen surface but may not be composed yet. Compose it. + GFX_flipHidden(); + + SDL_Surface *screenshot = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, format); + SDL_Surface *black = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, format); + if (screenshot && black && PLAT_captureScreenshot(screenshot) == 0) + { + SDL_FillRect(black, NULL, SDL_MapRGBA(black->format, 0, 0, 0, 255)); + GFX_animateAndFadeSurface(screenshot, 0, 0, 0, 0, width, height, duration_ms, + black, 0, 0, 0, 0, width, height, + start_opacity, end_opacity, layer, + 0, 0, 1); + + if (end_opacity == 255) + { + // Fade-out: clear layer above main, draw black on the screen. + GFX_clearLayers(LAYER_ALL); + SDL_BlitSurface(black, NULL, gfx.screen, NULL); + GFX_flip(gfx.screen); + } + else + { + GFX_clearLayers(layer); + } + } + SDL_FreeSurface(black); + SDL_FreeSurface(screenshot); +} + void GFX_quit(void) { diff --git a/workspace/all/common/api.h b/workspace/all/common/api.h index e7b5d6808..034240a77 100644 --- a/workspace/all/common/api.h +++ b/workspace/all/common/api.h @@ -345,6 +345,8 @@ void GFX_flip_fixed_rate(SDL_Surface* screen, double target_fps); // if target_f void GFX_sync(void); // call this to maintain 60fps when not calling GFX_flip() this frame void GFX_delay(void); // gfx_sync() is only for everywhere where there is no audio buffer to rely on for delaying, stupid so doing gfx_delay() for like waiting for input loop in binding menu. Need to remove gfx_sync() everwhere eventually void GFX_quit(void); +// Performa an animation of an translucent black curtain over the screenshout on the layer. +void GFX_animateBlack(int layer, int start_opacity, int end_opacity, int duration_ms); enum { VSYNC_OFF = 0, diff --git a/workspace/all/common/config.c b/workspace/all/common/config.c index aea4cb496..f1430d1c9 100644 --- a/workspace/all/common/config.c +++ b/workspace/all/common/config.c @@ -793,6 +793,17 @@ void CFG_setMenuAnimations(bool show) CFG_sync(); } +int CFG_getAppStartExitAnimationDuration(void) +{ + int transition = CFG_getMenuTransitions(); + if (transition == TRANSITION_COMFY) + return TRANSITION_COMFY_DURATION; + else if (transition == TRANSITION_SNAPPY) + return TRANSITION_SNAPPY_DURATION; + else + return 20; +} + int CFG_getMenuTransitions(void) { return settings.showMenuTransitions; diff --git a/workspace/all/common/config.h b/workspace/all/common/config.h index bad3f5fc0..ae87693fa 100644 --- a/workspace/all/common/config.h +++ b/workspace/all/common/config.h @@ -346,6 +346,7 @@ void CFG_setMenuAnimations(bool show); // Menu transition mode: TRANSITION_OFF, TRANSITION_SNAPPY, TRANSITION_COMFY. int CFG_getMenuTransitions(void); void CFG_setMenuTransitions(int mode); +int CFG_getAppStartExitAnimationDuration(void); // Set thumbnail rounding radius. int CFG_getThumbnailRadius(void); void CFG_setThumbnailRadius(int radius); diff --git a/workspace/all/gametime/gametime.c b/workspace/all/gametime/gametime.c index a30f67c5c..8c03324f8 100644 --- a/workspace/all/gametime/gametime.c +++ b/workspace/all/gametime/gametime.c @@ -326,6 +326,7 @@ int main(int argc, char *argv[]) PWR_setCPUSpeed(CPU_SPEED_AUTO); screen = GFX_init(MODE_MAIN); + bool firstFrame = true; PAD_init(); PWR_init(); @@ -429,6 +430,11 @@ int main(int argc, char *argv[]) GFX_blitButtonGroup((char *[]){"B", "BACK", NULL}, 1, screen, 1); + if (firstFrame) + { + GFX_animateBlack(LAYER_SCROLLTEXT, 255, 0, CFG_getAppStartExitAnimationDuration()); + firstFrame = false; + } GFX_flip(screen); dirty = 0; } @@ -442,6 +448,7 @@ int main(int argc, char *argv[]) QuitSettings(); PWR_quit(); PAD_quit(); + GFX_animateBlack(LAYER_SCROLLTEXT, 0, 255, CFG_getAppStartExitAnimationDuration()); GFX_quit(); return EXIT_SUCCESS; diff --git a/workspace/all/ledcontrol/ledcontrol.c b/workspace/all/ledcontrol/ledcontrol.c index 8441d77fc..7602d1d65 100644 --- a/workspace/all/ledcontrol/ledcontrol.c +++ b/workspace/all/ledcontrol/ledcontrol.c @@ -2,6 +2,7 @@ #include #include "sdl.h" +#include "config.h" #include "defines.h" #include "api.h" #include "utils.h" @@ -251,9 +252,7 @@ int main(int argc, char *argv[]) PAD_init(); PWR_init(); - GFX_clearAll(); - GFX_clearLayers(0); - GFX_flip(screen); + bool firstFrame = true; // GFX_init() is what gets the platform far enough along to answer this // (the trimui platforms only learn their model in PLAT_initPlatform) @@ -273,6 +272,11 @@ int main(int argc, char *argv[]) GFX_blitMessage(font.large, "This device has no RGB lights.", screen, &(SDL_Rect){0, 0, screen->w, screen->h}); GFX_blitButtonGroup((char*[]){ "B","BACK", NULL }, 1, screen, 1); + if (firstFrame) { + GFX_animateBlack(LAYER_SCROLLTEXT, 255, 0, CFG_getAppStartExitAnimationDuration()); + firstFrame = false; + } + GFX_flip(screen); msg_dirty = 0; } @@ -280,6 +284,7 @@ int main(int argc, char *argv[]) } PWR_quit(); PAD_quit(); + GFX_animateBlack(LAYER_SCROLLTEXT, 0, 255, CFG_getAppStartExitAnimationDuration()); GFX_quit(); QuitSettings(); return 0; @@ -452,6 +457,11 @@ int main(int argc, char *argv[]) } } + if (firstFrame) { + GFX_animateBlack(LAYER_SCROLLTEXT, 255, 0, CFG_getAppStartExitAnimationDuration()); + firstFrame = false; + } + GFX_flip(screen); dirty = 0; } @@ -459,6 +469,7 @@ int main(int argc, char *argv[]) } PWR_quit(); PAD_quit(); + GFX_animateBlack(LAYER_SCROLLTEXT, 0, 255, CFG_getAppStartExitAnimationDuration()); GFX_quit(); QuitSettings(); diff --git a/workspace/all/minarch/minarch.c b/workspace/all/minarch/minarch.c index 82b5d4c7e..d44ccc657 100644 --- a/workspace/all/minarch/minarch.c +++ b/workspace/all/minarch/minarch.c @@ -347,7 +347,7 @@ int main(int argc , char* argv[]) { screen = converted; SDL_FreeSurface(rawSurface); free(pixels); - GFX_animateSurfaceOpacity(converted, 0, 0, cw, ch, 255, 0, CFG_getMenuTransitions() ? 200 : 20, 1); + GFX_animateSurfaceOpacity(converted, 0, 0, cw, ch, 255, 0, CFG_getAppStartExitAnimationDuration(), 1); SDL_FreeSurface(converted); Video_cleanup(); diff --git a/workspace/all/minput/minput.c b/workspace/all/minput/minput.c index 5656a8111..e2e2c3330 100644 --- a/workspace/all/minput/minput.c +++ b/workspace/all/minput/minput.c @@ -63,6 +63,7 @@ int main(int argc , char* argv[]) { PWR_setCPUSpeed(CPU_SPEED_AUTO); SDL_Surface* screen = GFX_init(MODE_MAIN); + bool firstFrame = true; PAD_init(); PWR_init(); InitSettings(); @@ -307,6 +308,11 @@ int main(int argc , char* argv[]) { blitButton("R3", screen, PAD_isPressed(BTN_R3), x+o+dx, y+o+dy,0); } + if (firstFrame) + { + GFX_animateBlack(LAYER_SCROLLTEXT, 255, 0, CFG_getAppStartExitAnimationDuration()); + firstFrame = false; + } GFX_flip(screen); dirty = 0; } @@ -316,6 +322,7 @@ int main(int argc , char* argv[]) { QuitSettings(); PWR_quit(); PAD_quit(); + GFX_animateBlack(LAYER_SCROLLTEXT, 0, 255, CFG_getAppStartExitAnimationDuration()); GFX_quit(); return EXIT_SUCCESS; diff --git a/workspace/all/nextui/nextui.c b/workspace/all/nextui/nextui.c index 90c567390..315f8ad3c 100644 --- a/workspace/all/nextui/nextui.c +++ b/workspace/all/nextui/nextui.c @@ -2286,9 +2286,12 @@ int main (int argc, char *argv[]) { currentScreen = SCREEN_GAMESWITCHER; } - // add a nice fade into the game switcher - if(currentScreen == SCREEN_GAMESWITCHER) + bool fadeInFromBlack = true; + if (currentScreen == SCREEN_GAMESWITCHER) { lastScreen = SCREEN_GAME; + // Game switcher animates from screenshot on opening + fadeInFromBlack = false; + } // make sure we have no running games logged as active anymore (we might be launching back into the UI here) system("gametimectl.elf stop_all"); @@ -2637,7 +2640,10 @@ int main (int argc, char *argv[]) { } } - if(dirty) { + // When a game or pak starts, keep the last frame as it is: the exit fade-out + // captures the frame the user saw. A redraw here would show the screen the + // launcher returns to instead (e.g. the game list behind the quick menu). + if(dirty && !startgame) { SDL_Surface *tmpOldScreen = NULL; SDL_Surface * switcherSur = NULL; // NOTE:22 This causes slowdown when CFG_getMenuTransitions is set to false because animationdirection turns > 0 somewhere but is never set back to 0 and so this code runs on every action, will fix later @@ -2820,14 +2826,6 @@ int main (int argc, char *argv[]) { } lastScreen = SCREEN_QUICKMENU; } - else if(startgame) { - //pilltargetY = +screen->w; - //animationdirection = ANIM_NONE; - GFX_clearLayers(LAYER_ALL); - GFX_clear(screen); - GFX_flipHidden(); - GFX_animateSurfaceOpacity(tmpOldScreen,0,0,screen->w,screen->h,255,0,CFG_getMenuTransitions() ? 150:20,LAYER_BACKGROUND); - } else if(currentScreen == SCREEN_GAMESWITCHER) { GFX_clearLayers(LAYER_ALL); ox = 0; @@ -3157,9 +3155,6 @@ int main (int argc, char *argv[]) { animationdirection = ANIM_NONE; } } - if(lastScreen==SCREEN_OFF) { - GFX_animateSurfaceOpacity(blackBG,0,0,screen->w,screen->h,255,0,CFG_getMenuTransitions() ? 200:20,LAYER_THUMBNAIL); - } previous_row = selected_row; previous_depth = stack->count; @@ -3271,11 +3266,17 @@ int main (int argc, char *argv[]) { } SDL_UnlockMutex(animMutex); } - if(!startgame) // dont flip if game gonna start + if (fadeInFromBlack) { + // The fade uses the scroll-text layer (the top one in use) for the picture + // and its cover, and clears it after. The next pass draws it again. + GFX_animateBlack(LAYER_SCROLLTEXT, 255, 0, CFG_getAppStartExitAnimationDuration()); + fadeInFromBlack = 0; + dirty = 1; + } else { GFX_flip(screen); - - dirty = 0; - } else if(getAnimationDraw() || folderbgchanged || thumbchanged || is_scrolling) { + dirty = 0; + } + } else if(!startgame && (getAnimationDraw() || folderbgchanged || thumbchanged || is_scrolling)) { // honestly this whole thing is here only for the scrolling text, I set it now to run this at 30fps which is enough for scrolling text, should move this to seperate animation function eventually Uint32 now = SDL_GetTicks(); Uint32 frame_start = now; @@ -3424,6 +3425,7 @@ int main (int argc, char *argv[]) { // Cleanup worker threads and their synchronization primitives cleanupImageLoaderPool(); + GFX_animateBlack(LAYER_SCROLLTEXT, 0, 255, CFG_getAppStartExitAnimationDuration()); GFX_quit(); // Cleanup video subsystem first to stop GPU threads // Now safe to free surfaces after GPU threads are stopped diff --git a/workspace/all/settings/settings.cpp b/workspace/all/settings/settings.cpp index f9f740470..60b3b3d8d 100644 --- a/workspace/all/settings/settings.cpp +++ b/workspace/all/settings/settings.cpp @@ -411,6 +411,7 @@ int main(int argc, char *argv[]) ctx.dirty = 1; ctx.show_setting = 0; ctx.screen = GFX_init(MODE_MAIN); + bool firstFrame = true; PAD_init(); PWR_init(); TIME_init(); @@ -1258,6 +1259,11 @@ int main(int argc, char *argv[]) ctx.menu->draw(ctx.screen, listRect, titleRect); // present + if (firstFrame) + { + GFX_animateBlack(LAYER_SCROLLTEXT, 255, 0, CFG_getAppStartExitAnimationDuration()); + firstFrame = false; + } GFX_flip(ctx.screen); ctx.dirty = false; } @@ -1281,6 +1287,7 @@ int main(int argc, char *argv[]) PWR_quit(); PAD_quit(); BT_quit(); + GFX_animateBlack(LAYER_SCROLLTEXT, 0, 255, CFG_getAppStartExitAnimationDuration()); GFX_quit(); return EXIT_SUCCESS;