[PATCH] user32: Always send WM_NCACTIVATE when changing the foreground window.

Alex Henrie alexhenrie24 at gmail.com
Sun Mar 5 23:23:19 CST 2017


Fixes https://bugs.winehq.org/show_bug.cgi?id=7198

The problem was that WM_NCACTIVATE was not being sent if the new
foreground window was already an active window.

I added a few tests for this:
- Test to show that calling SetActiveWindow on an already active window
  does NOT send WM_NCACTIVATE
- Test to show that calling SetForegroundWindow on an active background
  window DOES send WM_NCACTIVATE
- Test to show that calling SetForegroundWindow on the foreground window
  (which has to be an active window) does NOT send WM_NCACTIVATE

Based on these tests, the right place and time to send the message is in
set_foreground_window, but only if the foreground window is actually
changing.

It's not necessary to call set_active_window on the active window (or
send WM_WINE_SETACTIVEWINDOW to the active window) if the active window
is not changing because SetActiveWindow and set_active_window are no-ops
in that case.

Signed-off-by: Alex Henrie <alexhenrie24 at gmail.com>
---
 dlls/user32/focus.c     | 27 +++++++++++++++++----------
 dlls/user32/tests/msg.c | 30 +++++++++++++++++++++++++++---
 2 files changed, 44 insertions(+), 13 deletions(-)

diff --git a/dlls/user32/focus.c b/dlls/user32/focus.c
index f1c883167e..f97b64f835 100644
--- a/dlls/user32/focus.c
+++ b/dlls/user32/focus.c
@@ -197,18 +197,25 @@ static BOOL set_foreground_window( HWND hwnd, BOOL mouse )
     }
     SERVER_END_REQ;
 
-    if (ret && previous != hwnd)
+    if (!ret || previous == hwnd)
+        return ret;
+
+    if (send_msg_old)  /* old window belongs to other thread */
+        SendNotifyMessageW( previous, WM_WINE_SETACTIVEWINDOW, 0, 0 );
+    else if (send_msg_new)  /* old window belongs to us but new one to other thread */
+        ret = set_active_window( 0, NULL, mouse, TRUE );
+
+    if (GetActiveWindow() == hwnd)
     {
-        if (send_msg_old)  /* old window belongs to other thread */
-            SendNotifyMessageW( previous, WM_WINE_SETACTIVEWINDOW, 0, 0 );
-        else if (send_msg_new)  /* old window belongs to us but new one to other thread */
-            ret = set_active_window( 0, NULL, mouse, TRUE );
-
-        if (send_msg_new)  /* new window belongs to other thread */
-            SendNotifyMessageW( hwnd, WM_WINE_SETACTIVEWINDOW, (WPARAM)hwnd, 0 );
-        else  /* new window belongs to us */
-            ret = set_active_window( hwnd, NULL, mouse, TRUE );
+        SendNotifyMessageW( hwnd, WM_NCACTIVATE, 1, (LPARAM)hwnd );
+        return ret;
     }
+
+    if (send_msg_new)  /* new window belongs to other thread */
+        SendNotifyMessageW( hwnd, WM_WINE_SETACTIVEWINDOW, (WPARAM)hwnd, 0 );
+    else  /* new window belongs to us */
+        ret = set_active_window( hwnd, NULL, mouse, TRUE );
+
     return ret;
 }
 
diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c
index 23c261e2ac..3b3ca5c62b 100644
--- a/dlls/user32/tests/msg.c
+++ b/dlls/user32/tests/msg.c
@@ -13294,6 +13294,11 @@ static void test_SetActiveWindow(void)
     if (ret == hwnd) ok_sequence(SetActiveWindowSeq1, "SetActiveWindow(hwnd), hwnd visible", TRUE);
     flush_sequence();
 
+    trace("SetActiveWindow(hwnd), hwnd visible and already active\n");
+    ret = SetActiveWindow(hwnd);
+    if (ret == hwnd) ok_sequence(WmEmptySeq, "SetActiveWindow(hwnd), hwnd visible and already active", FALSE);
+    flush_sequence();
+
     trace("SetActiveWindow(popup), hwnd visible, popup visible\n");
     ret = SetActiveWindow(popup);
     ok( ret == hwnd, "Failed to SetActiveWindow(popup), popup visible\n");
@@ -13321,7 +13326,7 @@ static void test_SetActiveWindow(void)
     DestroyWindow(hwnd);
 }
 
-static const struct message SetForegroundWindowSeq[] =
+static const struct message SetForegroundWindowSeq0[] =
 {
     { WM_NCACTIVATE, sent|wparam, 0 },
     { WM_GETTEXT, sent|defwinproc|optional },
@@ -13333,6 +13338,14 @@ static const struct message SetForegroundWindowSeq[] =
     { 0 }
 };
 
+static const struct message SetForegroundWindowSeq1[] =
+{
+    { WM_NCACTIVATE, sent|wparam, 1 },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_WINDOWPOSCHANGING, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE }, /* FIXME: Missing in Wine */
+    { 0 }
+};
+
 static void test_SetForegroundWindow(void)
 {
     HWND hwnd;
@@ -13347,10 +13360,21 @@ static void test_SetForegroundWindow(void)
     trace("SetForegroundWindow( 0 )\n");
     SetForegroundWindow( 0 );
     ok_sequence(WmEmptySeq, "SetForegroundWindow( 0 ) away from foreground top level window", FALSE);
+
     trace("SetForegroundWindow( GetDesktopWindow() )\n");
     SetForegroundWindow( GetDesktopWindow() );
-    ok_sequence(SetForegroundWindowSeq, "SetForegroundWindow( desktop ) away from "
-                                        "foreground top level window", FALSE);
+    ok_sequence(SetForegroundWindowSeq0, "SetForegroundWindow( desktop ) away from foreground top level window", FALSE);
+
+    trace("SetForegroundWindow to a background window that is already active\n");
+    SetActiveWindow( hwnd );
+    flush_sequence();
+    SetForegroundWindow( hwnd );
+    ok_sequence(SetForegroundWindowSeq1, "SetForegroundWindow to a background window that is already active", FALSE);
+
+    trace("SetForegroundWindow to the current foreground window\n");
+    SetForegroundWindow( hwnd );
+    ok_sequence(WmEmptySeq, "SetForegroundWindow to the current foreground window", FALSE);
+
     trace("done\n");
 
     DestroyWindow(hwnd);
-- 
2.12.0




More information about the wine-patches mailing list