diff --git a/source/blender/windowmanager/WM_api.hh b/source/blender/windowmanager/WM_api.hh index 7f21cad4167..987677b4972 100644 --- a/source/blender/windowmanager/WM_api.hh +++ b/source/blender/windowmanager/WM_api.hh @@ -1909,7 +1909,7 @@ void WM_draw_region_free(ARegion *region); GPUViewport *WM_draw_region_get_viewport(ARegion *region); GPUViewport *WM_draw_region_get_bound_viewport(ARegion *region); -void WM_main_playanim(int argc, const char **argv); +int WM_main_playanim(int argc, const char **argv); /** * Debugging only, convenience function to write on crash. diff --git a/source/blender/windowmanager/intern/wm_playanim.cc b/source/blender/windowmanager/intern/wm_playanim.cc index 66806c13861..9ad8615e57c 100644 --- a/source/blender/windowmanager/intern/wm_playanim.cc +++ b/source/blender/windowmanager/intern/wm_playanim.cc @@ -93,6 +93,9 @@ static struct { static CLG_LogRef LOG = {"wm.playanim"}; +/** Used in user viable messages. */ +static const char *message_prefix = "Animation Player"; + struct PlayState; static void playanim_window_zoom(PlayState &ps, const float zoom_offset); static bool playanim_window_font_scale_from_dpi(PlayState &ps); @@ -1690,7 +1693,7 @@ static bool playanim_window_font_scale_from_dpi(PlayState &ps) * \return True when `args_next` is filled with arguments used to re-run this function * (used for drag & drop). */ -static bool wm_main_playanim_intern(int argc, const char **argv, PlayArgs *args_next) +static std::optional wm_main_playanim_intern(int argc, const char **argv, PlayArgs *args_next) { ImBuf *ibuf = nullptr; blender::int2 window_pos = {0, 0}; @@ -1805,67 +1808,90 @@ static bool wm_main_playanim_intern(int argc, const char **argv, PlayArgs *args_ argv++; } - if (argc == 0) { - printf("%s: no filepath argument given\n", __func__); - exit(EXIT_FAILURE); - } - - const char *filepath = argv[0]; - - if (MOV_is_movie_file(filepath)) { - /* OCIO_TODO: support different input color spaces. */ - MovieReader *anim = MOV_open_file(filepath, IB_byte_data, 0, nullptr); - if (anim) { - ibuf = MOV_decode_frame(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE); - MOV_close(anim); - anim = nullptr; - } - } - else if (!IMB_test_image(filepath)) { - printf("%s: '%s' not an image file\n", __func__, filepath); - exit(EXIT_FAILURE); - } - - if (ibuf == nullptr) { - /* OCIO_TODO: support different input color space. */ - ibuf = IMB_load_image_from_filepath(filepath, IB_byte_data); - } - - if (ibuf == nullptr) { - printf("%s: '%s' couldn't open\n", __func__, filepath); - exit(EXIT_FAILURE); - } - - /* Select GPU backend. */ - GPU_backend_type_selection_detect(); - - /* Init GHOST and open window. */ + const char *filepath = nullptr; GHOST_EventConsumerHandle ghost_event_consumer = nullptr; + { - ghost_event_consumer = GHOST_CreateEventConsumer(ghost_event_proc, &ps); + std::optional exit_code = [&]() -> std::optional { + if (argc == 0) { + fprintf(stderr, "%s: no filepath argument given\n", message_prefix); + return EXIT_FAILURE; + } - GHOST_SetBacktraceHandler((GHOST_TBacktraceFn)BLI_system_backtrace); + filepath = argv[0]; + if (MOV_is_movie_file(filepath)) { + /* OCIO_TODO: support different input color spaces. */ + MovieReader *anim = MOV_open_file(filepath, IB_byte_data, 0, nullptr); + if (anim) { + ibuf = MOV_decode_frame(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE); + MOV_close(anim); + anim = nullptr; + } + } + else if (IMB_test_image(filepath)) { + /* Pass. */ + } + else { + fprintf(stderr, "%s: '%s' not an image file\n", message_prefix, filepath); + return EXIT_FAILURE; + } - ps.ghost_data.system = GHOST_CreateSystem(); - GPU_backend_ghost_system_set(ps.ghost_data.system); + if (ibuf == nullptr) { + /* OCIO_TODO: support different input color space. */ + ibuf = IMB_load_image_from_filepath(filepath, IB_byte_data); + } - if (UNLIKELY(ps.ghost_data.system == nullptr)) { - /* GHOST will have reported the back-ends that failed to load. */ - CLOG_WARN(&LOG, "GHOST: unable to initialize, exiting!"); - /* This will leak memory, it's preferable to crashing. */ - exit(EXIT_FAILURE); + if (ibuf == nullptr) { + fprintf(stderr, "%s: '%s' couldn't open\n", message_prefix, filepath); + return EXIT_FAILURE; + } + + /* Select GPU backend. */ + GPU_backend_type_selection_detect(); + + /* Init GHOST and open window. */ + GHOST_SetBacktraceHandler((GHOST_TBacktraceFn)BLI_system_backtrace); + + ps.ghost_data.system = GHOST_CreateSystem(); + if (UNLIKELY(ps.ghost_data.system == nullptr)) { + /* GHOST will have reported the back-ends that failed to load. */ + fprintf(stderr, "%s: unable to initialize GHOST, exiting!\n", message_prefix); + return EXIT_FAILURE; + } + + GPU_backend_ghost_system_set(ps.ghost_data.system); + + GHOST_UseNativePixels(); + + ps.ghost_data.window = playanim_window_open(ps.ghost_data.system, + "Blender Animation Player", + window_pos[0], + window_pos[1], + ibuf->x, + ibuf->y); + + if (UNLIKELY(ps.ghost_data.window == nullptr)) { + fprintf(stderr, "%s: unable to create window, exiting!\n", message_prefix); + return EXIT_FAILURE; + } + + ghost_event_consumer = GHOST_CreateEventConsumer(ghost_event_proc, &ps); + GHOST_AddEventConsumer(ps.ghost_data.system, ghost_event_consumer); + + return std::nullopt; + }(); + + if (exit_code) { + if (ps.ghost_data.system) { + GHOST_DisposeSystem(ps.ghost_data.system); + } + if (ibuf) { + IMB_freeImBuf(ibuf); + } + IMB_exit(); + MOV_exit(); + return exit_code; } - - GHOST_AddEventConsumer(ps.ghost_data.system, ghost_event_consumer); - - GHOST_UseNativePixels(); - - ps.ghost_data.window = playanim_window_open(ps.ghost_data.system, - "Blender Animation Player", - window_pos[0], - window_pos[1], - ibuf->x, - ibuf->y); } // GHOST_ActivateWindowDrawingContext(ps.ghost_data.window); @@ -2165,24 +2191,16 @@ static bool wm_main_playanim_intern(int argc, const char **argv, PlayArgs *args_ if (ps.argv_next) { args_next->argc = ps.argc_next; args_next->argv = ps.argv_next; - return true; + /* No exit code, keep running. */ + return std::nullopt; } GHOST_DisposeSystem(ps.ghost_data.system); -#if 0 - const int totblock = MEM_get_memory_blocks_in_use(); - if (totblock != 0) { - /* Prints many `bAKey`, `bArgument` messages which are tricky to fix. */ - printf("Error Totblock: %d\n", totblock); - MEM_printmemlist(); - } -#endif - - return false; + return EXIT_SUCCESS; } -void WM_main_playanim(int argc, const char **argv) +int WM_main_playanim(int argc, const char **argv) { #ifdef WITH_AUDASPACE { @@ -2200,19 +2218,20 @@ void WM_main_playanim(int argc, const char **argv) } #endif + std::optional exit_code = std::nullopt; PlayArgs args_next = {0}; do { PlayArgs args_free = args_next; args_next = {0}; - if (wm_main_playanim_intern(argc, argv, &args_next)) { - argc = args_next.argc; - argv = const_cast(args_next.argv); - } - else { + if ((exit_code = wm_main_playanim_intern(argc, argv, &args_next))) { argc = 0; argv = nullptr; } + else { + argc = args_next.argc; + argv = const_cast(args_next.argv); + } if (args_free.argv) { for (int i = 0; i < args_free.argc; i++) { @@ -2221,9 +2240,23 @@ void WM_main_playanim(int argc, const char **argv) MEM_freeN(args_free.argv); } } while (argv != nullptr); + /* Set in the loop. */ + BLI_assert(exit_code.has_value()); #ifdef WITH_AUDASPACE AUD_exit(g_audaspace.audio_device); AUD_exitOnce(); #endif + + /* NOTE(@ideasman42): Not useful unless all subsystems are properly shutdown. */ + if (false) { + const int totblock = MEM_get_memory_blocks_in_use(); + if (totblock != 0) { + /* Prints many `bAKey`, `bArgument` messages which are tricky to fix. */ + printf("Error Totblock: %d\n", totblock); + MEM_printmemlist(); + } + } + + return exit_code.value(); } diff --git a/source/creator/creator_args.cc b/source/creator/creator_args.cc index df5637dccc3..c2b30748656 100644 --- a/source/creator/creator_args.cc +++ b/source/creator/creator_args.cc @@ -1699,9 +1699,9 @@ static int arg_handle_playback_mode(int argc, const char **argv, void * /*data*/ /* Ignore the animation player if `-b` was given first. */ if (G.background == 0) { /* Skip this argument (`-a`). */ - WM_main_playanim(argc - 1, argv + 1); + const int exit_code = WM_main_playanim(argc - 1, argv + 1); - exit(EXIT_SUCCESS); + exit(exit_code); } return -2;