Page MenuHome

UI: Win32 - Restore Minimized App On WM_Close
AcceptedPublic

Authored by Harley Acheson (harley) on Jun 21 2019, 12:55 AM.

Details

Summary

On Windows, you can close Blender when it is minimized to the Tasktray. You can close the program by right-clicking and selecting "Close Window" of by clicking the "X" on the thumbnail while hovering.

Unfortunately this doesn't work if the "close save" dialog comes up to prompt for saving changes. The prompt pauses the quit process but you can't tell because it is just drawing on the minimized window. In a nutshell you can close Blender this way if there are no changes, otherwise it seems to just do nothing.

This patch alters the message handling on Win32 so that on message WM_CLOSE the application will restore if minimized. This has no negative effect if there are no changes as the program just exits normally. But if there are changes the program is restored and you see the "save close" dialog so you can answer the question.

Unfortunately I couldn't find a more general way to handle this, rather than for Windows only. Events are queued while minimized but they don't seem to be processed while in that state so couldn't find a way to restore the app at a higher level.

Diff Detail

Repository
rB Blender

Event Timeline

The only change is a word I missed in a comment. No functional changes.

Makes sense to me. This is what other apps do that rely on sheets. We should do this on MacOS and Linux too.

This revision is now accepted and ready to land.Jun 21 2019, 8:10 AM

Isn't it possible to call wm_window_raise on the window in which the popup is opened? Is the kEventWindowClose event not handled then?

Isn't it possible to call wm_window_raise on the window in which the popup is opened? Is the kEventWindowClose event not handled then?

I had assumed so too, but the timing of the messages don’t work out AFAICT. While the app is minimized I right-click and tell it to close. Windows sends the app a WM_close. Our win32 stuff gets that and puts kEventWindowClose into our queue. But nothing happens with that message while the app is minimized. It is only when I click on the task tray icon to restore the app that it then gets processed and then we get redrawn and our save_close dialog appears.

But having the w32 window restore when we get close, then it all gets processed immediately.

I don't have win10 to test on but this seems to work on win7

diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 7558468c3b5..150fcdb394e 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -391,6 +391,7 @@ void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win)
   /* 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);
+  wm_window_raise(win);

   if (U.uiflag & USER_SAVE_PROMPT) {
     if (wm_file_or_image_is_modified(C) && !G.background) {

If that works it would be a much better solution since this also occurs on Mac. I'll check with Windows 10 in the morning...

@LazyDodo (LazyDodo)

No, on my Windows 10 I am seeing the same behavior as before...

I make the exact edit as you show above. Afterward I can make a small change to the scene and minimize Blender. I right-click on the icon in the Tasktray and select "close window" from the popup menu. Nothing happens and the program remains minimized. When I do give up and then left-click click on the minimized icon the program will restore and confirm_quit will be waiting for me. If I put a breakpoint on that line it just isn't being hit until I restore it.

Yeah i did the same thing, edit the scene, minimize, right click on the button and select close, with this patch blender pops up, asks if i want to save, (and then proceeds not to quit, but thats a different bug)

Would be great if someone could test the patch on mac, cause if it works, we'll do that for mac/win7 and I'd be ok throwing in the ShowWindow for win10.

I gave win10 in a vm a spin and there's definitely different behavior between win7 and 10, before we land this i'd like to see if i cat atleast explain whats going on.

LazyDodo (LazyDodo) added a comment.EditedMon, Jun 24, 10:37 PM

3 problems here

  1. wm_quit_with_optional_confirmation_prompt didn't call wm_window_raise (I'm not entirely sure if we should check for win being null here?)
  1. PeekMessageW can and will dispatch our messages to our wndproc whenever it feels like it, msdn says it's allowed to do that, so we can't really rely on us dispatching a message alone for a signal that work was done. this->m_eventManager->getNumEvents() is a more reliable way to do this.
  1. GHOST_WindowWin32::setOrder didn't restore the window if it was being made topmost.

I'm unsure if we want to cut this up into 3 different commits or not (@Brecht Van Lommel (brecht) any opinion here?) but here's my final patch

 intern/ghost/intern/GHOST_SystemWin32.cpp       | 1 +
 intern/ghost/intern/GHOST_WindowWin32.cpp       | 3 +++
 source/blender/windowmanager/intern/wm_window.c | 1 +
 3 files changed, 5 insertions(+)

diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index 9f7b6f75e41..fe9777625dc 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -444,6 +444,7 @@ bool GHOST_SystemWin32::processEvents(bool waitForEvent)
       ::DispatchMessageW(&msg);
       hasEventHandled = true;
     }
+    hasEventHandled |= this->m_eventManager->getNumEvents() > 0;
   } while (waitForEvent && !hasEventHandled);
 
   return hasEventHandled;
diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp
index def075bd47c..69965ed4bcd 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.cpp
+++ b/intern/ghost/intern/GHOST_WindowWin32.cpp
@@ -621,6 +621,9 @@ GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order)
     hWndToRaise = ::GetWindow(m_hWnd, GW_HWNDNEXT); /* the window to raise */
   }
   else {
+    if (getState() == GHOST_kWindowStateMinimized) {
+      setState(GHOST_kWindowStateNormal);
+	}
     hWndInsertAfter = HWND_TOP;
     hWndToRaise = NULL;
   }
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 7ae572e5685..9f9d4df8424 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -387,6 +387,7 @@ void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win)
   /* 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);
+  wm_window_raise(win);
 
   if (U.uiflag & USER_SAVE_PROMPT) {
     if (wm_file_or_image_is_modified(C) && !G.background) {