From 4cb4556fa5ba78fff69cf2f6cb1d156d1705a5c5 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 22 Mar 2018 23:09:19 +0100 Subject: Fix T48027: Memory leaks when exiting Blender from menu You only had to close Blender through File -> Quit. Leaks happened because WM_exit() was called from within operator, UI wasn't able to free some of it's heap data then. This data was the handler added in uiTemplateRunningJobs() and the IDProperty group added in uiItemFullO_ptr_ex(). There was obviously a general design issue which only became visible in this specific case. We now delay the WM_exit call by wrapping it into a handler that gets registered as usual. I didn't see a better way to do this, all tricks done in ui_apply_but_funcs_after() to prevent leaks didn't work here. In fact they may be redundant now, but am not brave enough to try ;) --- source/blender/windowmanager/intern/wm_init_exit.c | 28 ++++++++++++++++++++++ source/blender/windowmanager/intern/wm_operators.c | 5 ++-- source/blender/windowmanager/wm.h | 3 +++ 3 files changed, 33 insertions(+), 3 deletions(-) (limited to 'source/blender/windowmanager') diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 7608b015f49..4fcbff6bf98 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -434,6 +434,30 @@ static void wait_for_console_key(void) } #endif +static int wm_exit_handler(bContext *C, const wmEvent *event, void *userdata) +{ + WM_exit(C); + + UNUSED_VARS(event, userdata); + return WM_UI_HANDLER_BREAK; +} + +/** + * Cause a delayed WM_exit() call to avoid leaking memory when trying to exit from within operators. + */ +void wm_exit_schedule_delayed(const bContext *C) +{ + /* What we do here is a little bit hacky, but quite simple and doesn't require bigger + * changes: Add a handler wrapping WM_exit() to cause a delayed call of it. */ + + wmWindowManager *wm = CTX_wm_manager(C); + /* Doesn't matter which window we use. */ + wmWindow *win = wm->windows.first; + + /* Use modal UI handler for now. Could add separate WM handlers or so, but probably not worth it. */ + WM_event_add_ui_handler(C, &win->modalhandlers, wm_exit_handler, NULL, NULL, 0); +} + /** * \note doesn't run exit() call #WM_exit() for that. */ @@ -604,6 +628,10 @@ void WM_exit_ext(bContext *C, const bool do_python) BKE_tempdir_session_purge(); } +/** + * \brief Main exit function to close Blender ordinarily. + * \note Use #wm_exit_schedule_delayed() to close Blender from an operator. Might leak memory otherwise. + */ void WM_exit(bContext *C) { WM_exit_ext(C, 1); diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 363b8e61763..bf2f00e9460 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -2160,7 +2160,7 @@ static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot) ot->poll = WM_operator_winactive; } -static int wm_exit_blender_exec(bContext *C, wmOperator *op) +static int wm_exit_blender_exec(bContext *C, wmOperator *UNUSED(op)) { wmWindowManager *wm = CTX_wm_manager(C); @@ -2168,8 +2168,7 @@ static int wm_exit_blender_exec(bContext *C, wmOperator *op) wm_confirm_quit(C); } else { - WM_operator_free(op); - WM_exit(C); + wm_exit_schedule_delayed(C); } return OPERATOR_FINISHED; diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h index a417a719b8d..5ced36a104f 100644 --- a/source/blender/windowmanager/wm.h +++ b/source/blender/windowmanager/wm.h @@ -43,6 +43,9 @@ typedef struct wmPaintCursor { void (*draw)(bContext *C, int, int, void *customdata); } wmPaintCursor; + +void wm_exit_schedule_delayed(const bContext *C); + extern void wm_close_and_free(bContext *C, wmWindowManager *); extern void wm_close_and_free_all(bContext *C, ListBase *); -- cgit v1.2.3 From a4ea46ffc5570499078c0caeba55121f709e5ee7 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 22 Mar 2018 23:29:48 +0100 Subject: Fix memory leaks when doing "Save & Quit" Steps to reproduce were: * Open Blender (no need for factory settings, "Promt Quit" needs to be enabled) * Edit the file (e.g. translate some object) * Quit Blender but don't skip quit promt * Press "Save & Quit" * Save the file Not sure if Windows supports the "Save & Quit" behavior, so this may not have applied to Windows. --- source/blender/windowmanager/intern/wm_files.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source/blender/windowmanager') diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 551b17302fe..7df6deaecf6 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -2076,7 +2076,7 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_WM | ND_FILESAVE, NULL); if (RNA_boolean_get(op->ptr, "exit")) { - WM_exit(C); + wm_exit_schedule_delayed(C); } return OPERATOR_FINISHED; -- cgit v1.2.3 From aeec19d2e76fc292d21d00090d02c672c3997647 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Thu, 22 Mar 2018 23:52:38 +0100 Subject: Fix own mistake from 4cb4556fa5ba78ff Trying to close Blender from a second window wouldn't work, the first window would have to be hovered first. Ouch! --- source/blender/windowmanager/WM_api.h | 2 +- source/blender/windowmanager/intern/wm_event_system.c | 2 +- source/blender/windowmanager/intern/wm_init_exit.c | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) (limited to 'source/blender/windowmanager') diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index a910e1bce2e..bf26d512589 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -186,7 +186,7 @@ enum { struct wmEventHandler *WM_event_add_dropbox_handler(ListBase *handlers, ListBase *dropboxes); /* mouse */ -void WM_event_add_mousemove(struct bContext *C); +void WM_event_add_mousemove(const struct bContext *C); bool WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event); bool WM_event_is_last_mousemove(const struct wmEvent *event); diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index d62327a83a9..b18e9f050c2 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -2919,7 +2919,7 @@ static void WM_event_remove_handler(ListBase *handlers, wmEventHandler *handler) } #endif -void WM_event_add_mousemove(bContext *C) +void WM_event_add_mousemove(const bContext *C) { wmWindow *window = CTX_wm_window(C); diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 4fcbff6bf98..9b4868523dc 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -450,12 +450,11 @@ void wm_exit_schedule_delayed(const bContext *C) /* What we do here is a little bit hacky, but quite simple and doesn't require bigger * changes: Add a handler wrapping WM_exit() to cause a delayed call of it. */ - wmWindowManager *wm = CTX_wm_manager(C); - /* Doesn't matter which window we use. */ - wmWindow *win = wm->windows.first; + wmWindow *win = CTX_wm_window(C); /* Use modal UI handler for now. Could add separate WM handlers or so, but probably not worth it. */ WM_event_add_ui_handler(C, &win->modalhandlers, wm_exit_handler, NULL, NULL, 0); + WM_event_add_mousemove(C); /* ensure handler actually gets called */ } /** -- cgit v1.2.3 From 529c21acc2e6e0830844a610d4f8d6a9b72a9902 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 23 Mar 2018 02:00:14 +0100 Subject: Fix issues with confirmation prompt on Windows * Pressing "OK" wouldn't close Blender anymore * Using File -> Quit would use popup version, not OS native window Cleaned up code a bit to avoid duplicated logic. --- source/blender/windowmanager/intern/wm_operators.c | 10 +--- source/blender/windowmanager/intern/wm_window.c | 62 +++++++++++++--------- source/blender/windowmanager/wm_window.h | 2 +- 3 files changed, 39 insertions(+), 35 deletions(-) (limited to 'source/blender/windowmanager') diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index bf2f00e9460..1c21069879a 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -2162,15 +2162,7 @@ static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot) static int wm_exit_blender_exec(bContext *C, wmOperator *UNUSED(op)) { - wmWindowManager *wm = CTX_wm_manager(C); - - if ((U.uiflag & USER_QUIT_PROMPT) && !wm->file_saved) { - wm_confirm_quit(C); - } - else { - wm_exit_schedule_delayed(C); - } - + wm_quit_with_optional_confirmation_prompt(C, CTX_wm_window(C)); return OPERATOR_FINISHED; } diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 6573ae0f4a1..408229e501c 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -400,18 +400,46 @@ static uiBlock *block_create_confirm_quit(struct bContext *C, struct ARegion *ar } -/** Call the confirm dialog on quitting. */ -void wm_confirm_quit(bContext *C) +/** + * Call the confirm dialog on quitting. It's displayed in the context window so + * caller should set it as desired. + */ +static void wm_confirm_quit(bContext *C) +{ + wmWindow *win = CTX_wm_window(C); + + if (GHOST_SupportsNativeDialogs() == 0) { + UI_popup_block_invoke(C, block_create_confirm_quit, NULL); + } + else if (GHOST_confirmQuit(win->ghostwin)) { + wm_exit_schedule_delayed(C); + } +} + +/** + * Call the quit confirmation prompt or exit directly if needed. The use can + * still cancel via the confirmation popup. Also, this may not quit Blender + * immediately, but rather schedule the closing. + * + * \param win The window to show the confirmation popup/window in. + */ +void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win) { wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win_ctx = CTX_wm_window(C); + + /* The popup will be displayed in the context window which may not be set + * here (this function gets called outside of normal event handling loop). */ + CTX_wm_window_set(C, win); - /* The popup needs to have a window set in context to show up since - * it's being called outside the normal operator event handling loop */ - if (wm->winactive) { - CTX_wm_window_set(C, wm->winactive); + if ((U.uiflag & USER_QUIT_PROMPT) && !wm->file_saved && !G.background) { + wm_confirm_quit(C); + } + else { + wm_exit_schedule_delayed(C); } - UI_popup_block_invoke(C, block_create_confirm_quit, NULL); + CTX_wm_window_set(C, win_ctx); } /** \} */ @@ -420,7 +448,6 @@ void wm_confirm_quit(bContext *C) void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win) { wmWindow *tmpwin; - bool do_exit = false; /* first check if we have to quit (there are non-temp remaining windows) */ for (tmpwin = wm->windows.first; tmpwin; tmpwin = tmpwin->next) { @@ -430,23 +457,8 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win) break; } - if (tmpwin == NULL) - do_exit = 1; - - if ((U.uiflag & USER_QUIT_PROMPT) && !wm->file_saved && !G.background && do_exit) { - /* We have unsaved changes and we're quitting */ - if(GHOST_SupportsNativeDialogs() == 0) { - wm_confirm_quit(C); - } - else { - if (!GHOST_confirmQuit(win->ghostwin)) - return; - } - } - else if (do_exit) { - /* No changes but we're quitting */ - /* let WM_exit do all freeing, for correct quit.blend save */ - WM_exit(C); + if (tmpwin == NULL) { + wm_quit_with_optional_confirmation_prompt(C, win); } else { /* We're just closing a window */ diff --git a/source/blender/windowmanager/wm_window.h b/source/blender/windowmanager/wm_window.h index 4313c978ef4..5209af87960 100644 --- a/source/blender/windowmanager/wm_window.h +++ b/source/blender/windowmanager/wm_window.h @@ -78,7 +78,7 @@ void wm_window_IME_end (wmWindow *win); int wm_window_close_exec(bContext *C, struct wmOperator *op); int wm_window_duplicate_exec(bContext *C, struct wmOperator *op); int wm_window_fullscreen_toggle_exec(bContext *C, struct wmOperator *op); -void wm_confirm_quit(bContext *C); +void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win) ATTR_NONNULL(); /* Initial (unmaximized) size to start with for * systems that can't find it for themselves (X11). -- cgit v1.2.3