I think the behaviour of SetWindowPos() in this case is partially explained by the fact that topmost state is not strictly applicable to child windows and so Windows denies such requests. Child window can still end up with WS_EX_TOPMOST style if it was set at window creation (doesn't work this way in Wine now) or, e. g., like in the test added in the patch.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=20190
Also fixes game load dialog in Indiana Jones and the Infernal Machine.
-- v2: win32u: Ignore SetWindowPos() if it is trying to change topmost state for child window.
From: Paul Gofman pgofman@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=20190 --- dlls/user32/tests/win.c | 56 +++++++++++++++++++++++++++++++++++++++-- dlls/win32u/window.c | 18 +++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-)
diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 4c2af09d0c6..82afff70887 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -3073,6 +3073,7 @@ static void test_SetWindowPos(HWND hwnd, HWND hwnd2) RECT orig_win_rc, rect; LONG_PTR old_proc; HWND hwnd_grandchild, hwnd_child, hwnd_child2; + DWORD style, ex_style; HWND hwnd_desktop; RECT rc_expected; RECT rc1, rc2; @@ -3170,7 +3171,7 @@ static void test_SetWindowPos(HWND hwnd, HWND hwnd2) ret = SetWindowPos(hwnd_child, HWND_NOTOPMOST, 1, 2, 3, 4, 0); ok(ret, "Got %d.\n", ret); GetWindowRect(hwnd_child, &rc2); - todo_wine ok(EqualRect(&rc1, &rc2), "%s != %s.\n", wine_dbgstr_rect(&rc1), wine_dbgstr_rect(&rc2)); + ok(EqualRect(&rc1, &rc2), "%s != %s.\n", wine_dbgstr_rect(&rc1), wine_dbgstr_rect(&rc2)); check_active_state(hwnd2, hwnd2, hwnd2); SetWindowPos(hwnd_child, HWND_NOTOPMOST, 0, 0, rc1.right - rc1.left, rc1.bottom - rc1.top, 0);
@@ -3178,7 +3179,58 @@ static void test_SetWindowPos(HWND hwnd, HWND hwnd2) ret = SetWindowPos(hwnd_child, HWND_TOPMOST, 1, 2, 3, 4, 0); ok(ret, "Got %d.\n", ret); GetWindowRect(hwnd_child, &rc2); - todo_wine ok(EqualRect(&rc1, &rc2), "%s != %s.\n", wine_dbgstr_rect(&rc1), wine_dbgstr_rect(&rc2)); + ok(EqualRect(&rc1, &rc2), "%s != %s.\n", wine_dbgstr_rect(&rc1), wine_dbgstr_rect(&rc2)); + check_active_state(hwnd2, hwnd2, hwnd2); + SetWindowPos(hwnd_child, HWND_TOPMOST, 0, 0, rc1.right - rc1.left, rc1.bottom - rc1.top, 0); + + /* Child windows with desktop window parent are not treated as child. */ + SetParent(hwnd_child, NULL); + ex_style = GetWindowLongA(hwnd_child, GWL_EXSTYLE); + ok(!(ex_style & WS_EX_TOPMOST), "got ex_style %#lx.\n", ex_style); + GetWindowRect(hwnd_child, &rc1); + rc_expected.left = rc1.left + 1; + rc_expected.top = rc1.top + 2; + rc_expected.right = rc1.left + 4; + rc_expected.bottom = rc1.top + 6; + ret = SetWindowPos(hwnd_child, HWND_TOPMOST, 1, 2, 3, 4, 0); + ok(ret, "Got %d.\n", ret); + GetWindowRect(hwnd_child, &rc2); + ok(EqualRect(&rc_expected, &rc2), "%s != %s.\n", + wine_dbgstr_rect(&rc_expected), wine_dbgstr_rect(&rc2)); + check_active_state(hwnd2, hwnd2, hwnd2); + SetWindowPos(hwnd_child, HWND_TOPMOST, 0, 0, rc1.right - rc1.left, rc1.bottom - rc1.top, 0); + + style = GetWindowLongA(hwnd_child, GWL_STYLE); + ok(style & WS_CHILD, "got style %#lx.\n", style); + ex_style = GetWindowLongA(hwnd_child, GWL_EXSTYLE); + ok(ex_style & WS_EX_TOPMOST, "got ex_style %#lx.\n", ex_style); + + SetParent(hwnd_child, hwnd); + ex_style = GetWindowLongA(hwnd_child, GWL_EXSTYLE); + ok(ex_style & WS_EX_TOPMOST, "got ex_style %#lx.\n", ex_style); + + SetParent(hwnd_child, NULL); + GetWindowRect(hwnd_child, &rc1); + rc_expected.left = rc1.left + 1; + rc_expected.top = rc1.top + 2; + rc_expected.right = rc1.left + 4; + rc_expected.bottom = rc1.top + 6; + ret = SetWindowPos(hwnd_child, HWND_NOTOPMOST, 1, 2, 3, 4, 0); + ok(ret, "Got %d.\n", ret); + GetWindowRect(hwnd_child, &rc2); + ok(EqualRect(&rc_expected, &rc2), "%s != %s.\n", + wine_dbgstr_rect(&rc_expected), wine_dbgstr_rect(&rc2)); + check_active_state(hwnd2, hwnd2, hwnd2); + SetWindowPos(hwnd_child, HWND_NOTOPMOST, 0, 0, rc1.right - rc1.left, rc1.bottom - rc1.top, 0); + SetParent(hwnd_child, hwnd); + ex_style = GetWindowLongA(hwnd_child, GWL_EXSTYLE); + ok(!(ex_style & WS_EX_TOPMOST), "got ex_style %#lx.\n", ex_style); + + GetWindowRect(hwnd_child, &rc1); + ret = SetWindowPos(hwnd_child, HWND_NOTOPMOST, 1, 2, 3, 4, 0); + ok(ret, "Got %d.\n", ret); + GetWindowRect(hwnd_child, &rc2); + ok(EqualRect(&rc1, &rc2), "%s != %s.\n", wine_dbgstr_rect(&rc1), wine_dbgstr_rect(&rc2)); check_active_state(hwnd2, hwnd2, hwnd2); SetWindowPos(hwnd_child, HWND_TOPMOST, 0, 0, rc1.right - rc1.left, rc1.bottom - rc1.top, 0);
diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c index d9c771e5d3d..a3a40248a53 100644 --- a/dlls/win32u/window.c +++ b/dlls/win32u/window.c @@ -3372,6 +3372,24 @@ BOOL set_window_pos( WINDOWPOS *winpos, int parent_x, int parent_y ) if (!insertafter_parent) return FALSE; if (insertafter_parent != parent) return TRUE; } + else if (winpos->hwndInsertAfter == HWND_TOPMOST || winpos->hwndInsertAfter == HWND_NOTOPMOST) + { + BOOL is_child_window; + HWND parent; + WND *win; + + if ((win = get_win_ptr( winpos->hwnd )) && win != WND_DESKTOP && win != WND_OTHER_PROCESS) + { + is_child_window = (win->dwStyle & (WS_POPUP | WS_CHILD)) == WS_CHILD; + release_win_ptr( win ); + if (is_child_window && (parent = NtUserGetAncestor( winpos->hwnd, GA_PARENT )) + && parent != get_desktop_window()) + { + TRACE( "Not changing topmost state for a child window.\n" ); + return TRUE; + } + } + } }
/* Make sure that coordinates are valid for WM_WINDOWPOSCHANGING */