/* only read the userdef from a .blend */ int BKE_read_file_userdef(const char *filepath, ReportList *reports) { BlendFileData *bfd; int retval = 0; bfd = BLO_read_from_file(filepath, reports); if (bfd->user) { retval = BKE_READ_FILE_OK_USERPREFS; /* only here free userdef themes... */ BKE_userdef_free(); U = *bfd->user; MEM_freeN(bfd->user); } free_main(bfd->main); MEM_freeN(bfd); return retval; }
static void setup_app_data(bContext *C, BlendFileData *bfd, const char *filepath) { bScreen *curscreen = NULL; Scene *curscene = NULL; int recover; enum { LOAD_UI = 1, LOAD_UI_OFF, LOAD_UNDO, } mode; if (BLI_listbase_is_empty(&bfd->main->screen)) { mode = LOAD_UNDO; } else if (G.fileflags & G_FILE_NO_UI) { mode = LOAD_UI_OFF; } else { mode = LOAD_UI; } recover = (G.fileflags & G_FILE_RECOVER); /* Free all render results, without this stale data gets displayed after loading files */ if (mode != LOAD_UNDO) { RE_FreeAllRenderResults(); } /* Only make filepaths compatible when loading for real (not undo) */ if (mode != LOAD_UNDO) { clean_paths(bfd->main); } /* XXX here the complex windowmanager matching */ /* no load screens? */ if (mode != LOAD_UI) { /* Logic for 'track_undo_scene' is to keep using the scene which the active screen has, * as long as the scene associated with the undo operation is visible in one of the open windows. * * - 'curscreen->scene' - scene the user is currently looking at. * - 'bfd->curscene' - scene undo-step was created in. * * This means users can have 2+ windows open and undo in both without screens switching. * But if they close one of the screens, * undo will ensure that the scene being operated on will be activated * (otherwise we'd be undoing on an off-screen scene which isn't acceptable). * see: T43424 */ bool track_undo_scene; /* comes from readfile.c */ SWAP(ListBase, G.main->wm, bfd->main->wm); SWAP(ListBase, G.main->screen, bfd->main->screen); SWAP(ListBase, G.main->script, bfd->main->script); /* we re-use current screen */ curscreen = CTX_wm_screen(C); /* but use new Scene pointer */ curscene = bfd->curscene; track_undo_scene = (mode == LOAD_UNDO && curscreen && bfd->main->wm.first); if (curscene == NULL) curscene = bfd->main->scene.first; /* empty file, we add a scene to make Blender work */ if (curscene == NULL) curscene = BKE_scene_add(bfd->main, "Empty"); if (track_undo_scene) { /* keep the old (free'd) scene, let 'blo_lib_link_screen_restore' * replace it with 'curscene' if its needed */ } else { /* and we enforce curscene to be in current screen */ if (curscreen) { /* can run in bgmode */ curscreen->scene = curscene; } } /* clear_global will free G.main, here we can still restore pointers */ blo_lib_link_screen_restore(bfd->main, curscreen, curscene); curscene = curscreen->scene; if (track_undo_scene) { wmWindowManager *wm = bfd->main->wm.first; if (wm_scene_is_visible(wm, bfd->curscene) == false) { curscene = bfd->curscene; curscreen->scene = curscene; } } } /* free G.main Main database */ // CTX_wm_manager_set(C, NULL); clear_global(); /* clear old property update cache, in case some old references are left dangling */ RNA_property_update_cache_free(); G.main = bfd->main; CTX_data_main_set(C, G.main); sound_init_main(G.main); if (bfd->user) { /* only here free userdef themes... */ BKE_userdef_free(); U = *bfd->user; /* Security issue: any blend file could include a USER block. * * Currently we load prefs from BLENDER_STARTUP_FILE and later on load BLENDER_USERPREF_FILE, * to load the preferences defined in the users home dir. * * This means we will never accidentally (or maliciously) * enable scripts auto-execution by loading a '.blend' file. */ U.flag |= USER_SCRIPT_AUTOEXEC_DISABLE; MEM_freeN(bfd->user); } /* case G_FILE_NO_UI or no screens in file */ if (mode != LOAD_UI) { /* leave entire context further unaltered? */ CTX_data_scene_set(C, curscene); } else { G.fileflags = bfd->fileflags; CTX_wm_manager_set(C, G.main->wm.first); CTX_wm_screen_set(C, bfd->curscreen); CTX_data_scene_set(C, bfd->curscene); CTX_wm_area_set(C, NULL); CTX_wm_region_set(C, NULL); CTX_wm_menu_set(C, NULL); curscene = bfd->curscene; } /* this can happen when active scene was lib-linked, and doesn't exist anymore */ if (CTX_data_scene(C) == NULL) { /* in case we don't even have a local scene, add one */ if (!G.main->scene.first) BKE_scene_add(G.main, "Scene"); CTX_data_scene_set(C, G.main->scene.first); CTX_wm_screen(C)->scene = CTX_data_scene(C); curscene = CTX_data_scene(C); } BLI_assert(curscene == CTX_data_scene(C)); /* special cases, override loaded flags: */ if (G.f != bfd->globalf) { const int flags_keep = (G_SWAP_EXCHANGE | G_SCRIPT_AUTOEXEC | G_SCRIPT_OVERRIDE_PREF); bfd->globalf = (bfd->globalf & ~flags_keep) | (G.f & flags_keep); } G.f = bfd->globalf; #ifdef WITH_PYTHON /* let python know about new main */ BPY_context_update(C); #endif if (!G.background) { //setscreen(G.curscreen); } /* FIXME: this version patching should really be part of the file-reading code, * but we still get too many unrelated data-corruption crashes otherwise... */ if (G.main->versionfile < 250) do_versions_ipos_to_animato(G.main); G.main->recovered = 0; /* startup.blend or recovered startup */ if (bfd->filename[0] == 0) { G.main->name[0] = 0; } else if (recover && G.relbase_valid) { /* in case of autosave or quit.blend, use original filename instead * use relbase_valid to make sure the file is saved, else we get <memory2> in the filename */ filepath = bfd->filename; G.main->recovered = 1; /* these are the same at times, should never copy to the same location */ if (G.main->name != filepath) BLI_strncpy(G.main->name, filepath, FILE_MAX); } /* baseflags, groups, make depsgraph, etc */ /* first handle case if other windows have different scenes visible */ if (mode == LOAD_UI) { wmWindowManager *wm = G.main->wm.first; if (wm) { wmWindow *win; for (win = wm->windows.first; win; win = win->next) { if (win->screen && win->screen->scene) /* zealous check... */ if (win->screen->scene != curscene) BKE_scene_set_background(G.main, win->screen->scene); } } } BKE_scene_set_background(G.main, curscene); if (mode != LOAD_UNDO) { IMB_colormanagement_check_file_config(G.main); } MEM_freeN(bfd); }
/* note, doesnt run exit() call WM_exit() for that */ void WM_exit_ext(bContext *C, const bool do_python) { wmWindowManager *wm = C ? CTX_wm_manager(C) : NULL; BKE_sound_exit(); /* first wrap up running stuff, we assume only the active WM is running */ /* modal handlers are on window level freed, others too? */ /* note; same code copied in wm_files.c */ if (C && wm) { wmWindow *win; if (!G.background) { if ((U.uiflag2 & USER_KEEP_SESSION) || BKE_undo_is_valid(NULL)) { /* save the undo state as quit.blend */ char filename[FILE_MAX]; bool has_edited; int fileflags = G.fileflags & ~(G_FILE_COMPRESS | G_FILE_AUTOPLAY | G_FILE_HISTORY); BLI_make_file_string("/", filename, BKE_tempdir_base(), BLENDER_QUIT_FILE); has_edited = ED_editors_flush_edits(C, false); if ((has_edited && BLO_write_file(CTX_data_main(C), filename, fileflags, NULL, NULL)) || BKE_undo_save_file(filename)) { printf("Saved session recovery to '%s'\n", filename); } } } WM_jobs_kill_all(wm); for (win = wm->windows.first; win; win = win->next) { CTX_wm_window_set(C, win); /* needed by operator close callbacks */ WM_event_remove_handlers(C, &win->handlers); WM_event_remove_handlers(C, &win->modalhandlers); ED_screen_exit(C, win, win->screen); } } BKE_addon_pref_type_free(); wm_operatortype_free(); wm_dropbox_free(); WM_menutype_free(); WM_uilisttype_free(); /* all non-screen and non-space stuff editors did, like editmode */ if (C) ED_editors_exit(C); // XXX // BIF_GlobalReebFree(); // BIF_freeRetarget(); BIF_freeTemplates(C); free_openrecent(); BKE_mball_cubeTable_free(); /* render code might still access databases */ RE_FreeAllRender(); RE_engines_exit(); ED_preview_free_dbase(); /* frees a Main dbase, before free_blender! */ if (C && wm) wm_free_reports(C); /* before free_blender! - since the ListBases get freed there */ BKE_sequencer_free_clipboard(); /* sequencer.c */ BKE_tracking_clipboard_free(); BKE_mask_clipboard_free(); #ifdef WITH_COMPOSITOR COM_deinitialize(); #endif free_blender(); /* blender.c, does entire library and spacetypes */ // free_matcopybuf(); free_anim_copybuf(); free_anim_drivers_copybuf(); free_fmodifiers_copybuf(); ED_gpencil_strokes_copybuf_free(); ED_clipboard_posebuf_free(); BKE_node_clipboard_clear(); BLF_exit(); #ifdef WITH_INTERNATIONAL BLF_free_unifont(); BLF_free_unifont_mono(); BLT_lang_free(); #endif ANIM_keyingset_infos_exit(); // free_txt_data(); #ifdef WITH_PYTHON /* option not to close python so we can use 'atexit' */ if (do_python) { /* XXX - old note */ /* before free_blender so py's gc happens while library still exists */ /* needed at least for a rare sigsegv that can happen in pydrivers */ /* Update for blender 2.5, move after free_blender because blender now holds references to PyObject's * so decref'ing them after python ends causes bad problems every time * the pyDriver bug can be fixed if it happens again we can deal with it then */ BPY_python_end(); } #else (void)do_python; #endif #ifdef WITH_OPENSUBDIV openSubdiv_cleanup(); #endif if (!G.background) { GPU_global_buffer_pool_free(); GPU_free_unused_buffers(); GPU_exit(); } BKE_undo_reset(); ED_file_exit(); /* for fsmenu */ UI_exit(); BKE_userdef_free(); RNA_exit(); /* should be after BPY_python_end so struct python slots are cleared */ wm_ghost_exit(); CTX_free(C); #ifdef WITH_GAMEENGINE SYS_DeleteSystem(SYS_GetSystem()); #endif GHOST_DisposeSystemPaths(); BLI_threadapi_exit(); if (MEM_get_memory_blocks_in_use() != 0) { size_t mem_in_use = MEM_get_memory_in_use() + MEM_get_memory_in_use(); printf("Error: Not freed memory blocks: %u, total unfreed memory %f MB\n", MEM_get_memory_blocks_in_use(), (double)mem_in_use / 1024 / 1024); MEM_printmemlist(); } wm_autosave_delete(); BKE_tempdir_session_purge(); }
/* called in creator.c even... tsk, split this! */ void WM_exit(bContext *C) { wmWindow *win; sound_exit(); /* first wrap up running stuff, we assume only the active WM is running */ /* modal handlers are on window level freed, others too? */ /* note; same code copied in wm_files.c */ if(C && CTX_wm_manager(C)) { WM_jobs_stop_all(CTX_wm_manager(C)); for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) { CTX_wm_window_set(C, win); /* needed by operator close callbacks */ WM_event_remove_handlers(C, &win->handlers); WM_event_remove_handlers(C, &win->modalhandlers); ED_screen_exit(C, win, win->screen); } } wm_operatortype_free(); WM_menutype_free(); /* all non-screen and non-space stuff editors did, like editmode */ if(C) ED_editors_exit(C); // XXX // BIF_GlobalReebFree(); // BIF_freeRetarget(); BIF_freeTemplates(C); free_ttfont(); /* bke_font.h */ free_openrecent(); BKE_freecubetable(); fastshade_free_render(); /* shaded view */ ED_preview_free_dbase(); /* frees a Main dbase, before free_blender! */ if(C && CTX_wm_manager(C)) wm_free_reports(C); /* before free_blender! - since the ListBases get freed there */ free_blender(); /* blender.c, does entire library and spacetypes */ // free_matcopybuf(); free_anim_copybuf(); free_anim_drivers_copybuf(); free_posebuf(); // free_vertexpaint(); // free_imagepaint(); // fsmenu_free(); BLF_exit(); RE_FreeAllRender(); RE_engines_exit(); // free_txt_data(); #ifndef DISABLE_PYTHON /* XXX - old note */ /* before free_blender so py's gc happens while library still exists */ /* needed at least for a rare sigsegv that can happen in pydrivers */ /* Update for blender 2.5, move after free_blender because blender now holds references to PyObject's * so decref'ing them after python ends causes bad problems every time * the pyDriver bug can be fixed if it happens again we can deal with it then */ BPY_end_python(); #endif libtiff_exit(); #ifdef WITH_QUICKTIME quicktime_exit(); #endif if (!G.background) { // XXX UI_filelist_free_icons(); } GPU_buffer_pool_free(0); GPU_extensions_exit(); // if (copybuf) MEM_freeN(copybuf); // if (copybufinfo) MEM_freeN(copybufinfo); BKE_undo_save_quit(); // saves quit.blend if global undo is on BKE_reset_undo(); ED_file_exit(); /* for fsmenu */ UI_exit(); BKE_userdef_free(); RNA_exit(); /* should be after BPY_end_python so struct python slots are cleared */ wm_ghost_exit(); CTX_free(C); SYS_DeleteSystem(SYS_GetSystem()); if(MEM_get_memory_blocks_in_use()!=0) { printf("Error Totblock: %d\n", MEM_get_memory_blocks_in_use()); MEM_printmemlist(); } wm_autosave_delete(); printf("\nBlender quit\n"); #ifdef WIN32 /* ask user to press enter when in debug mode */ if(G.f & G_DEBUG) { printf("press enter key to exit...\n\n"); getchar(); } #endif exit(G.afbreek==1); }
static void setup_app_data(bContext *C, BlendFileData *bfd, const char *filepath) { bScreen *curscreen = NULL; Scene *curscene = NULL; int recover; char mode; /* 'u' = undo save, 'n' = no UI load */ if (bfd->main->screen.first == NULL) mode = 'u'; else if (G.fileflags & G_FILE_NO_UI) mode = 'n'; else mode = 0; recover = (G.fileflags & G_FILE_RECOVER); /* Free all render results, without this stale data gets displayed after loading files */ if (mode != 'u') { RE_FreeAllRenderResults(); } /* Only make filepaths compatible when loading for real (not undo) */ if (mode != 'u') { clean_paths(bfd->main); } /* XXX here the complex windowmanager matching */ /* no load screens? */ if (mode) { /* comes from readfile.c */ SWAP(ListBase, G.main->wm, bfd->main->wm); SWAP(ListBase, G.main->screen, bfd->main->screen); SWAP(ListBase, G.main->script, bfd->main->script); /* we re-use current screen */ curscreen = CTX_wm_screen(C); /* but use new Scene pointer */ curscene = bfd->curscene; if (curscene == NULL) curscene = bfd->main->scene.first; /* empty file, we add a scene to make Blender work */ if (curscene == NULL) curscene = BKE_scene_add(bfd->main, "Empty"); /* and we enforce curscene to be in current screen */ if (curscreen) curscreen->scene = curscene; /* can run in bgmode */ /* clear_global will free G.main, here we can still restore pointers */ blo_lib_link_screen_restore(bfd->main, curscreen, curscene); } /* free G.main Main database */ // CTX_wm_manager_set(C, NULL); clear_global(); /* clear old property update cache, in case some old references are left dangling */ RNA_property_update_cache_free(); G.main = bfd->main; CTX_data_main_set(C, G.main); sound_init_main(G.main); if (bfd->user) { /* only here free userdef themes... */ BKE_userdef_free(); U = *bfd->user; MEM_freeN(bfd->user); } /* case G_FILE_NO_UI or no screens in file */ if (mode) { /* leave entire context further unaltered? */ CTX_data_scene_set(C, curscene); } else { G.winpos = bfd->winpos; G.displaymode = bfd->displaymode; G.fileflags = bfd->fileflags; CTX_wm_manager_set(C, G.main->wm.first); CTX_wm_screen_set(C, bfd->curscreen); CTX_data_scene_set(C, bfd->curscene); CTX_wm_area_set(C, NULL); CTX_wm_region_set(C, NULL); CTX_wm_menu_set(C, NULL); } /* this can happen when active scene was lib-linked, and doesn't exist anymore */ if (CTX_data_scene(C) == NULL) { /* in case we don't even have a local scene, add one */ if (!G.main->scene.first) BKE_scene_add(G.main, "Scene"); CTX_data_scene_set(C, G.main->scene.first); CTX_wm_screen(C)->scene = CTX_data_scene(C); curscene = CTX_data_scene(C); } /* special cases, override loaded flags: */ if (G.f != bfd->globalf) { const int flags_keep = (G_SWAP_EXCHANGE | G_SCRIPT_AUTOEXEC | G_SCRIPT_OVERRIDE_PREF); bfd->globalf = (bfd->globalf & ~flags_keep) | (G.f & flags_keep); } G.f = bfd->globalf; #ifdef WITH_PYTHON /* let python know about new main */ BPY_context_update(C); #endif if (!G.background) { //setscreen(G.curscreen); } /* FIXME: this version patching should really be part of the file-reading code, * but we still get too many unrelated data-corruption crashes otherwise... */ if (G.main->versionfile < 250) do_versions_ipos_to_animato(G.main); G.main->recovered = 0; /* startup.blend or recovered startup */ if (bfd->filename[0] == 0) { G.main->name[0] = 0; } else if (recover && G.relbase_valid) { /* in case of autosave or quit.blend, use original filename instead * use relbase_valid to make sure the file is saved, else we get <memory2> in the filename */ filepath = bfd->filename; G.main->recovered = 1; /* these are the same at times, should never copy to the same location */ if (G.main->name != filepath) BLI_strncpy(G.main->name, filepath, FILE_MAX); } /* baseflags, groups, make depsgraph, etc */ /* first handle case if other windows have different scenes visible */ if (mode == 0) { wmWindowManager *wm = G.main->wm.first; if (wm) { wmWindow *win; for (win = wm->windows.first; win; win = win->next) { if (win->screen && win->screen->scene) /* zealous check... */ if (win->screen->scene != CTX_data_scene(C)) BKE_scene_set_background(G.main, win->screen->scene); } } } BKE_scene_set_background(G.main, CTX_data_scene(C)); if (mode != 'u') { IMB_colormanagement_check_file_config(G.main); } MEM_freeN(bfd); }
static void setup_app_data(bContext *C, BlendFileData *bfd, const char *filepath) { bScreen *curscreen= NULL; Scene *curscene= NULL; int recover; char mode; /* 'u' = undo save, 'n' = no UI load */ if(bfd->main->screen.first==NULL) mode= 'u'; else if(G.fileflags & G_FILE_NO_UI) mode= 'n'; else mode= 0; recover= (G.fileflags & G_FILE_RECOVER); /* Only make filepaths compatible when loading for real (not undo) */ if(mode != 'u') { clean_paths(bfd->main); } /* XXX here the complex windowmanager matching */ /* no load screens? */ if(mode) { /* comes from readfile.c */ extern void lib_link_screen_restore(Main *, bScreen *, Scene *); SWAP(ListBase, G.main->wm, bfd->main->wm); SWAP(ListBase, G.main->screen, bfd->main->screen); SWAP(ListBase, G.main->script, bfd->main->script); /* we re-use current screen */ curscreen= CTX_wm_screen(C); /* but use new Scene pointer */ curscene= bfd->curscene; if(curscene==NULL) curscene= bfd->main->scene.first; /* and we enforce curscene to be in current screen */ if(curscreen) curscreen->scene= curscene; /* can run in bgmode */ /* clear_global will free G.main, here we can still restore pointers */ lib_link_screen_restore(bfd->main, curscreen, curscene); } /* free G.main Main database */ // CTX_wm_manager_set(C, NULL); clear_global(); /* clear old property update cache, in case some old references are left dangling */ RNA_property_update_cache_free(); G.main= bfd->main; CTX_data_main_set(C, G.main); sound_init_main(G.main); if (bfd->user) { /* only here free userdef themes... */ BKE_userdef_free(); U= *bfd->user; MEM_freeN(bfd->user); } /* case G_FILE_NO_UI or no screens in file */ if(mode) { /* leave entire context further unaltered? */ CTX_data_scene_set(C, curscene); } else { G.winpos= bfd->winpos; G.displaymode= bfd->displaymode; G.fileflags= bfd->fileflags; CTX_wm_manager_set(C, bfd->main->wm.first); CTX_wm_screen_set(C, bfd->curscreen); CTX_data_scene_set(C, bfd->curscreen->scene); CTX_wm_area_set(C, NULL); CTX_wm_region_set(C, NULL); CTX_wm_menu_set(C, NULL); } /* this can happen when active scene was lib-linked, and doesnt exist anymore */ if(CTX_data_scene(C)==NULL) { CTX_data_scene_set(C, bfd->main->scene.first); CTX_wm_screen(C)->scene= CTX_data_scene(C); curscene= CTX_data_scene(C); } /* special cases, override loaded flags: */ if(G.f != bfd->globalf) { const int flags_keep= (G_DEBUG | G_SWAP_EXCHANGE | G_SCRIPT_AUTOEXEC | G_SCRIPT_OVERRIDE_PREF); bfd->globalf= (bfd->globalf & ~flags_keep) | (G.f & flags_keep); } G.f= bfd->globalf; if (!G.background) { //setscreen(G.curscreen); } // FIXME: this version patching should really be part of the file-reading code, // but we still get too many unrelated data-corruption crashes otherwise... if (G.main->versionfile < 250) do_versions_ipos_to_animato(G.main); if(recover && bfd->filename[0] && G.relbase_valid) { /* in case of autosave or quit.blend, use original filename instead * use relbase_valid to make sure the file is saved, else we get <memory2> in the filename */ filepath= bfd->filename; } #if 0 else if (!G.relbase_valid) { /* otherwise, use an empty string as filename, rather than <memory2> */ filepath=""; } #endif /* these are the same at times, should never copy to the same location */ if(G.main->name != filepath) BLI_strncpy(G.main->name, filepath, FILE_MAX); /* baseflags, groups, make depsgraph, etc */ set_scene_bg(G.main, CTX_data_scene(C)); MEM_freeN(bfd); (void)curscene; /* quiet warning */ }
/* note, doesnt run exit() call WM_exit() for that */ void WM_exit_ext(bContext *C, const short do_python) { wmWindow *win; sound_exit(); /* first wrap up running stuff, we assume only the active WM is running */ /* modal handlers are on window level freed, others too? */ /* note; same code copied in wm_files.c */ if (C && CTX_wm_manager(C)) { WM_jobs_stop_all(CTX_wm_manager(C)); for (win = CTX_wm_manager(C)->windows.first; win; win = win->next) { CTX_wm_window_set(C, win); /* needed by operator close callbacks */ WM_event_remove_handlers(C, &win->handlers); WM_event_remove_handlers(C, &win->modalhandlers); ED_screen_exit(C, win, win->screen); } } wm_operatortype_free(); wm_dropbox_free(); WM_menutype_free(); /* all non-screen and non-space stuff editors did, like editmode */ if (C) ED_editors_exit(C); // XXX // BIF_GlobalReebFree(); // BIF_freeRetarget(); BIF_freeTemplates(C); BKE_vfont_free_global_ttf(); /* bke_font.h */ free_openrecent(); BKE_mball_cubeTable_free(); ED_preview_free_dbase(); /* frees a Main dbase, before free_blender! */ if (C && CTX_wm_manager(C)) wm_free_reports(C); /* before free_blender! - since the ListBases get freed there */ seq_free_clipboard(); /* sequencer.c */ BKE_tracking_clipboard_free(); free_blender(); /* blender.c, does entire library and spacetypes */ // free_matcopybuf(); free_anim_copybuf(); free_anim_drivers_copybuf(); free_fmodifiers_copybuf(); free_posebuf(); BLF_exit(); #ifdef WITH_INTERNATIONAL BLF_free_unifont(); #endif ANIM_keyingset_infos_exit(); RE_FreeAllRender(); RE_engines_exit(); // free_txt_data(); #ifdef WITH_PYTHON /* option not to close python so we can use 'atexit' */ if (do_python) { /* XXX - old note */ /* before free_blender so py's gc happens while library still exists */ /* needed at least for a rare sigsegv that can happen in pydrivers */ /* Update for blender 2.5, move after free_blender because blender now holds references to PyObject's * so decref'ing them after python ends causes bad problems every time * the pyDriver bug can be fixed if it happens again we can deal with it then */ BPY_python_end(); } #else (void)do_python; #endif GPU_global_buffer_pool_free(); GPU_free_unused_buffers(); GPU_extensions_exit(); if (!G.background) { BKE_undo_save_quit(); /* saves quit.blend if global undo is on */ } BKE_reset_undo(); ED_file_exit(); /* for fsmenu */ UI_exit(); BKE_userdef_free(); RNA_exit(); /* should be after BPY_python_end so struct python slots are cleared */ wm_ghost_exit(); CTX_free(C); #ifdef WITH_GAMEENGINE SYS_DeleteSystem(SYS_GetSystem()); #endif GHOST_DisposeSystemPaths(); if (MEM_get_memory_blocks_in_use() != 0) { printf("Error: Not freed memory blocks: %d\n", MEM_get_memory_blocks_in_use()); MEM_printmemlist(); } wm_autosave_delete(); printf("\nBlender quit\n"); #ifdef WIN32 /* ask user to press a key when in debug mode */ if (G.debug & G_DEBUG) { printf("Press any key to exit . . .\n\n"); wait_for_console_key(); } #endif }