-- v5: win32u: Don't move an owned window on top of its owner if it's not activated ever. user32/tests: Test the z-order for an owned window that's not activated ever. win32u: Skip windows that are not activated even once in activate_other_window(). user32/tests: Add window activation tests.
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/user32/tests/win.c | 62 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+)
diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 1b03e64181a..173c029a7b5 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -13064,6 +13064,67 @@ static void test_shell_tray(void) DestroyWindow(hwnd); }
+static BOOL hwnd3_got_wm_activate; + +static LRESULT WINAPI test_activate_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) +{ + if (msg == WM_ACTIVATE && wp) + hwnd3_got_wm_activate = TRUE; + + return DefWindowProcA(hwnd, msg, wp, lp); +} + +static void test_activate(void) +{ + HWND hwnd1, hwnd2, hwnd3, next_window, foreground_window; + WNDCLASSA cls; + + memset(&cls, 0, sizeof(cls)); + cls.hInstance = GetModuleHandleA(0); + cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW); + cls.hbrBackground = GetStockObject(WHITE_BRUSH); + cls.lpfnWndProc = test_activate_proc; + cls.lpszClassName = "test_activate_class"; + RegisterClassA(&cls); + + /* Test 1: a window that never gets activated is not a candidate for activation */ + hwnd1 = CreateWindowA("static", "1", WS_POPUP | WS_VISIBLE, 200, 200, 100, 100, 0, 0, 0, NULL); + ok(!!hwnd1, "CreateWindowA failed, error %lu.\n", GetLastError()); + hwnd2 = CreateWindowExA(WS_EX_TOPMOST, "static", "2", WS_POPUP | WS_VISIBLE, 300, 300, 100, 100, 0, 0, 0, NULL); + ok(!!hwnd2, "CreateWindowExA failed, error %lu.\n", GetLastError()); + + hwnd3 = CreateWindowA("test_activate_class", "3", WS_POPUP, 150, 150, 100, 100, 0, 0, 0, NULL); + ok(!!hwnd3, "CreateWindowA failed, error %lu.\n", GetLastError()); + ShowWindow(hwnd3, SW_SHOWNOACTIVATE); + /* Even a SetWindowPos() with HWND_TOP doesn't qualify it */ + SetWindowPos(hwnd3, HWND_TOP, 150, 150, 100, 100, SWP_NOACTIVATE); + flush_events(TRUE); + + foreground_window = GetForegroundWindow(); + todo_wine_if(foreground_window != hwnd2) + ok(foreground_window == hwnd2, "Got unexpected foreground window.\n"); + /* The following confirms that hwnd3 is indeed the next window in z-order */ + next_window = hwnd2; + do + { + next_window = GetWindow(next_window, GW_HWNDNEXT); + } while (next_window && next_window != hwnd1 && next_window != hwnd3); + ok(next_window == hwnd3, "Got unexpected next window.\n"); + + /* WM_ACTIVATE should not be sent to hwnd3 */ + DestroyWindow(hwnd2); + flush_events(TRUE); + foreground_window = GetForegroundWindow(); + todo_wine_if(foreground_window != hwnd1) + ok(GetForegroundWindow() == hwnd1, "Got unexpected foreground window.\n"); + todo_wine + ok(!hwnd3_got_wm_activate, "Expected WM_ACTIVATE not sent to hwnd3.\n"); + + DestroyWindow(hwnd3); + DestroyWindow(hwnd1); + UnregisterClassA(cls.lpszClassName, GetModuleHandleA(0)); +} + START_TEST(win) { char **argv; @@ -13143,6 +13204,7 @@ START_TEST(win) test_FindWindowEx(); test_FindWindow(); test_SetParent(); + test_activate();
hwndMain2 = CreateWindowExA(/*WS_EX_TOOLWINDOW*/ 0, "MainWindowClass", "Main window 2", WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/user32/tests/win.c | 1 - dlls/win32u/ntuser_private.h | 1 + dlls/win32u/window.c | 17 ++++++++++++++++- dlls/winex11.drv/event.c | 17 ++++++++++++++++- server/protocol.def | 1 + server/window.c | 5 +++++ 6 files changed, 39 insertions(+), 3 deletions(-)
diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 173c029a7b5..cc0b53e9822 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -13117,7 +13117,6 @@ static void test_activate(void) foreground_window = GetForegroundWindow(); todo_wine_if(foreground_window != hwnd1) ok(GetForegroundWindow() == hwnd1, "Got unexpected foreground window.\n"); - todo_wine ok(!hwnd3_got_wm_activate, "Expected WM_ACTIVATE not sent to hwnd3.\n");
DestroyWindow(hwnd3); diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 3b6cab5bdc9..48bd0dbffb7 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -259,5 +259,6 @@ static inline UINT win_get_flags( HWND hwnd ) WND *get_win_ptr( HWND hwnd ); BOOL is_child( HWND parent, HWND child ); BOOL is_window( HWND hwnd ); +BOOL is_window_ever_activated( HWND hwnd );
#endif /* __WINE_NTUSER_PRIVATE_H */ diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index 69dd8caba5d..c3f368c6de5 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -311,6 +311,20 @@ BOOL is_window( HWND hwnd ) return ret; }
+BOOL is_window_ever_activated( HWND hwnd ) +{ + BOOL ret = FALSE; + + SERVER_START_REQ( get_window_info ) + { + req->handle = wine_server_user_handle( hwnd ); + if (!wine_server_call_err( req )) + ret = reply->is_ever_activated; + } + SERVER_END_REQ; + return ret; +} + /* see GetWindowThreadProcessId */ DWORD get_window_thread( HWND hwnd, DWORD *process ) { @@ -3765,7 +3779,8 @@ static BOOL can_activate_window( HWND hwnd ) style = get_window_long( hwnd, GWL_STYLE ); if (!(style & WS_VISIBLE)) return FALSE; if ((style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE; - return !(style & WS_DISABLED); + if (style & WS_DISABLED) return FALSE; + return is_window_ever_activated( hwnd ); }
/******************************************************************* diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 8b02361aaff..ed7ab78702c 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -521,6 +521,20 @@ DWORD EVENT_x11_time_to_win32_time(Time time)
}
+static BOOL is_window_ever_activated( HWND hwnd ) +{ + BOOL ret = FALSE; + + SERVER_START_REQ( get_window_info ) + { + req->handle = wine_server_user_handle( hwnd ); + if (!wine_server_call_err( req )) + ret = reply->is_ever_activated; + } + SERVER_END_REQ; + return ret; +} + /******************************************************************* * can_activate_window * @@ -537,7 +551,8 @@ static inline BOOL can_activate_window( HWND hwnd ) if (NtUserGetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOACTIVATE) return FALSE; if (hwnd == NtUserGetDesktopWindow()) return FALSE; if (NtUserGetWindowRect( hwnd, &rect ) && IsRectEmpty( &rect )) return FALSE; - return !(style & WS_DISABLED); + if (style & WS_DISABLED) return FALSE; + return is_window_ever_activated( hwnd ); }
diff --git a/server/protocol.def b/server/protocol.def index 8b51618ebe0..a50b1190ad3 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2411,6 +2411,7 @@ enum message_type thread_id_t tid; /* thread owning the window */ atom_t atom; /* class atom */ int is_unicode; /* ANSI or unicode */ + int is_ever_activated; /* Ever activated */ int dpi; /* window DPI */ int awareness; /* DPI awareness */ @END diff --git a/server/window.c b/server/window.c index 242e93f303a..a6f65cdcd66 100644 --- a/server/window.c +++ b/server/window.c @@ -80,6 +80,7 @@ struct window unsigned int is_linked : 1; /* is it linked into the parent z-order list? */ unsigned int is_layered : 1; /* has layered info been set? */ unsigned int is_orphan : 1; /* is window orphaned */ + unsigned int is_ever_activated : 1; /* is window ever activated */ unsigned int color_key; /* color key for a layered window */ unsigned int alpha; /* alpha value for a layered window */ unsigned int layered_flags; /* flags for a layered window */ @@ -573,6 +574,7 @@ static struct window *create_window( struct window *parent, struct window *owner win->is_linked = 0; win->is_layered = 0; win->is_orphan = 0; + win->is_ever_activated = 0; win->dpi_awareness = DPI_AWARENESS_PER_MONITOR_AWARE; win->dpi = 0; win->user_data = 0; @@ -2220,6 +2222,7 @@ DECL_HANDLER(get_window_info) reply->full_handle = win->handle; reply->last_active = win->handle; reply->is_unicode = win->is_unicode; + reply->is_ever_activated = win->is_ever_activated; reply->awareness = win->dpi_awareness; reply->dpi = win->dpi ? win->dpi : get_monitor_dpi( win ); if (get_user_object( win->last_active, USER_WINDOW )) reply->last_active = win->last_active; @@ -2477,6 +2480,8 @@ DECL_HANDLER(set_window_pos) mirror_rect( &win->parent->client_rect, &valid_rect ); }
+ if (!win->is_ever_activated && !(flags & SWP_NOACTIVATE)) win->is_ever_activated = 1; + win->paint_flags = (win->paint_flags & ~PAINT_CLIENT_FLAGS) | (req->paint_flags & PAINT_CLIENT_FLAGS); if (win->paint_flags & PAINT_HAS_PIXEL_FORMAT) update_pixel_format_flags( win );
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/user32/tests/win.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+)
diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index cc0b53e9822..bf1123ba05b 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -13121,6 +13121,37 @@ static void test_activate(void)
DestroyWindow(hwnd3); DestroyWindow(hwnd1); + + /* Test 2: an owned window that never gets activated shouldn't get moved on top of its owner by SetWindowPos() */ + hwnd1 = CreateWindowA("test_activate_class", "1", WS_POPUP, 0, 0, 100, 100, 0, 0, 0, NULL); + ok(!!hwnd1, "CreateWindowExA failed, error %lu.\n", GetLastError()); + hwnd2 = CreateWindowA("test_activate_class", "2", WS_POPUP, 0, 0, 100, 100, hwnd1, 0, 0, NULL); + ok(!!hwnd2, "CreateWindowExA failed, error %lu.\n", GetLastError()); + ShowWindow(hwnd1, SW_SHOW); + flush_events(TRUE); + ShowWindow(hwnd2, SW_SHOWNA); + flush_events(TRUE); + + next_window = NULL; + do + { + next_window = next_window ? GetWindow(next_window, GW_HWNDNEXT) : GetTopWindow(0); + } while (next_window && next_window != hwnd1 && next_window != hwnd2); + ok(next_window == hwnd2, "Got unexpected next window.\n"); + + SetWindowPos(hwnd2, hwnd1, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + + next_window = NULL; + do + { + next_window = next_window ? GetWindow(next_window, GW_HWNDNEXT) : GetTopWindow(0); + } while (next_window && next_window != hwnd1 && next_window != hwnd2); + todo_wine + ok(next_window == hwnd1, "Got unexpected next window.\n"); + + DestroyWindow(hwnd2); + DestroyWindow(hwnd1); + UnregisterClassA(cls.lpszClassName, GetModuleHandleA(0)); }
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/user32/tests/win.c | 1 - dlls/win32u/window.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index bf1123ba05b..04e923e80b1 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -13146,7 +13146,6 @@ static void test_activate(void) { next_window = next_window ? GetWindow(next_window, GW_HWNDNEXT) : GetTopWindow(0); } while (next_window && next_window != hwnd1 && next_window != hwnd2); - todo_wine ok(next_window == hwnd1, "Got unexpected next window.\n");
DestroyWindow(hwnd2); diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index c3f368c6de5..966d5eefd28 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -3326,6 +3326,7 @@ static HWND swp_owner_popups( HWND hwnd, HWND after ) TRACE( "(%p) after = %p\n", hwnd, after );
if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) return after; + if (!is_window_ever_activated( hwnd )) return after;
if ((owner = get_window_relative( hwnd, GW_OWNER ))) {
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=143454
Your paranoid android.
=== w10pro64 (32 bit report) ===
user32: win.c:3818: Test failed: GetForegroundWindow returned 000202D6 win.c:3749: Test failed: SetForegroundWindow failed, error 0 win.c:3752: Test failed: GetForegroundWindow returned 000202D6 win.c:3789: Test failed: GetForegroundWindow returned 000202D6 win.c:3877: Test failed: GetActiveWindow() = 0002021C win.c:3881: Test failed: GetFocus() = 00000000 win.c:3884: Test failed: GetFocus() = 00000000
=== w10pro64 (64 bit report) ===
user32: win.c:3818: Test failed: GetForegroundWindow returned 00000000000202C6 win.c:3749: Test failed: SetForegroundWindow failed, error 0 win.c:3752: Test failed: GetForegroundWindow returned 00000000000202C6 win.c:3789: Test failed: GetForegroundWindow returned 00000000000202C6 win.c:3865: Test failed: GetActiveWindow() = 00000000000201A8 win.c:3868: Test failed: GetFocus() = 0000000000000000 win.c:3871: Test failed: GetFocus() = 0000000000000000 win.c:3874: Test failed: GetFocus() = 0000000000000000 win.c:3877: Test failed: GetActiveWindow() = 00000000000201A8 win.c:3881: Test failed: GetFocus() = 0000000000000000 win.c:3884: Test failed: GetFocus() = 0000000000000000
=== debian11 (32 bit report) ===
user32: win.c:3558: Test failed: 000A00D0: expected next 00090076, got 001C00DA win.c:3559: Test failed: 001C00DA: expected next 000A00D0, got 00090076 win.c:3560: Test failed: 001600D6: expected next 001C00DA, got 000A00D0 win.c:3558: Test failed: 000B00D0: expected next 00090076, got 001700D6 win.c:3559: Test failed: 001700D6: expected next 000B00D0, got 00090076 win.c:3560: Test failed: 002400D4: expected next 001700D6, got 000B00D0
=== debian11 (32 bit ar:MA report) ===
user32: win.c:3558: Test failed: 000A00D0: expected next 00090076, got 001C00DA win.c:3559: Test failed: 001C00DA: expected next 000A00D0, got 00090076 win.c:3560: Test failed: 001600D6: expected next 001C00DA, got 000A00D0 win.c:3558: Test failed: 000B00D0: expected next 00090076, got 001700D6 win.c:3559: Test failed: 001700D6: expected next 000B00D0, got 00090076 win.c:3560: Test failed: 002400D4: expected next 001700D6, got 000B00D0
=== debian11 (32 bit de report) ===
user32: win.c:3558: Test failed: 000A00D0: expected next 00090076, got 001C00DA win.c:3559: Test failed: 001C00DA: expected next 000A00D0, got 00090076 win.c:3560: Test failed: 001600D6: expected next 001C00DA, got 000A00D0 win.c:3558: Test failed: 000B00D0: expected next 00090076, got 001700D6 win.c:3559: Test failed: 001700D6: expected next 000B00D0, got 00090076 win.c:3560: Test failed: 002400D4: expected next 001700D6, got 000B00D0
=== debian11 (32 bit fr report) ===
user32: win.c:3558: Test failed: 000A00D0: expected next 00090076, got 001C00DA win.c:3559: Test failed: 001C00DA: expected next 000A00D0, got 00090076 win.c:3560: Test failed: 001600D6: expected next 001C00DA, got 000A00D0 win.c:3558: Test failed: 000B00D0: expected next 00090076, got 001700D6 win.c:3559: Test failed: 001700D6: expected next 000B00D0, got 00090076 win.c:3560: Test failed: 002400D4: expected next 001700D6, got 000B00D0
=== debian11 (32 bit he:IL report) ===
user32: win.c:3558: Test failed: 000A00D0: expected next 0009007E, got 001C00DA win.c:3559: Test failed: 001C00DA: expected next 000A00D0, got 0009007E win.c:3560: Test failed: 001600D6: expected next 001C00DA, got 000A00D0 win.c:3558: Test failed: 000B00D0: expected next 0009007E, got 001700D6 win.c:3559: Test failed: 001700D6: expected next 000B00D0, got 0009007E win.c:3560: Test failed: 002400D4: expected next 001700D6, got 000B00D0
=== debian11 (32 bit hi:IN report) ===
user32: win.c:3558: Test failed: 000A00D0: expected next 00090076, got 001C00DA win.c:3559: Test failed: 001C00DA: expected next 000A00D0, got 00090076 win.c:3560: Test failed: 001600D6: expected next 001C00DA, got 000A00D0 win.c:3558: Test failed: 000B00D0: expected next 00090076, got 001700D6 win.c:3559: Test failed: 001700D6: expected next 000B00D0, got 00090076 win.c:3560: Test failed: 002400D4: expected next 001700D6, got 000B00D0
=== debian11 (32 bit ja:JP report) ===
user32: win.c:3558: Test failed: 000A00D0: expected next 00090076, got 001C00DA win.c:3559: Test failed: 001C00DA: expected next 000A00D0, got 00090076 win.c:3560: Test failed: 001600D6: expected next 001C00DA, got 000A00D0 win.c:3558: Test failed: 000B00D0: expected next 00090076, got 001700D6 win.c:3559: Test failed: 001700D6: expected next 000B00D0, got 00090076 win.c:3560: Test failed: 002400D4: expected next 001700D6, got 000B00D0
=== debian11 (32 bit zh:CN report) ===
user32: win.c:3558: Test failed: 000A00D0: expected next 00090076, got 001C00DA win.c:3559: Test failed: 001C00DA: expected next 000A00D0, got 00090076 win.c:3560: Test failed: 001600D6: expected next 001C00DA, got 000A00D0 win.c:3558: Test failed: 000B00D0: expected next 00090076, got 001700D6 win.c:3559: Test failed: 001700D6: expected next 000B00D0, got 00090076 win.c:3560: Test failed: 002400D4: expected next 001700D6, got 000B00D0
=== debian11b (32 bit WoW report) ===
user32: win.c:3558: Test failed: 000A00D0: expected next 00090076, got 001C00DA win.c:3559: Test failed: 001C00DA: expected next 000A00D0, got 00090076 win.c:3560: Test failed: 001600D6: expected next 001C00DA, got 000A00D0 win.c:3558: Test failed: 000B00D0: expected next 00090076, got 001700D6 win.c:3559: Test failed: 001700D6: expected next 000B00D0, got 00090076 win.c:3560: Test failed: 002400D4: expected next 001700D6, got 000B00D0
=== debian11b (64 bit WoW report) ===
user32: win.c:3558: Test failed: 00000000003A00FE: expected next 0000000001CF0142, got 0000000001EA0148 win.c:3559: Test failed: 0000000001EA0148: expected next 00000000003A00FE, got 0000000001CF0142 win.c:3560: Test failed: 000000000142006A: expected next 0000000001EA0148, got 00000000003A00FE win.c:3558: Test failed: 00000000003B00FE: expected next 0000000001CF0142, got 000000000143006A win.c:3559: Test failed: 000000000143006A: expected next 00000000003B00FE, got 0000000001CF0142 win.c:3560: Test failed: 0000000001DB0128: expected next 000000000143006A, got 00000000003B00FE