This prevents a window from getting reactivated when calling `SetWindowPos` inside the WM_ACTIVATE handler. For now I only was able to reproduce this by triggering a change in the active window from X11 with an alt-tab or by clicking another window, I was not able to write a wine test for this.
This test program works for reproducing it. ```c #include <windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_ACTIVATE) { SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); }
done: return DefWindowProc(hwnd, uMsg, wParam, lParam); }
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nCmdShow) { HWND hwnd; const char CLASS_NAME[] = "class"; WNDCLASSA wc = {}; wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
hwnd = CreateWindowEx(0, CLASS_NAME, "test", 0, 4, 4, 1024, 768, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, SW_SHOW);
MSG msg; while (TRUE) { GetMessage(&msg, NULL, 0, 0); TranslateMessage(&msg); DispatchMessage(&msg); } } ```
-- v2: win32u: Don't activate a window that is being deactivated. win32u: Set WIN_IS_DEACTIVATING for previous window.
From: Santino Mazza smazza@codeweavers.com
--- dlls/win32u/input.c | 10 ++++++---- dlls/win32u/ntuser_private.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 084ee561f30..d5ecbb33958 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1991,7 +1991,7 @@ static HWND set_focus_window( HWND hwnd ) BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus, DWORD new_active_thread_id ) { HWND previous = get_active_window(); - BOOL ret; + BOOL ret = TRUE; DWORD old_thread, new_thread; CBTACTIVATESTRUCT cbt;
@@ -2005,6 +2005,7 @@ BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus, DWORD new cbt.fMouse = mouse; cbt.hWndActive = previous; if (call_hooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, sizeof(cbt) )) return FALSE; + if (previous) win_set_flags(previous, WIN_IS_DEACTIVATING, 0);
if (is_window( previous )) { @@ -2020,7 +2021,7 @@ BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus, DWORD new previous = wine_server_ptr_handle( reply->previous ); } SERVER_END_REQ; - if (!ret) return FALSE; + if (!ret) goto done; if (prev) *prev = previous; if (previous == hwnd) goto done;
@@ -2032,7 +2033,7 @@ BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus, DWORD new if (send_message( hwnd, WM_QUERYNEWPALETTE, 0, 0 )) send_message_timeout( HWND_BROADCAST, WM_PALETTEISCHANGING, (WPARAM)hwnd, 0, SMTO_ABORTIFHUNG, 2000, FALSE ); - if (!is_window(hwnd)) return FALSE; + if (!(ret = is_window(hwnd))) goto done; }
old_thread = previous ? get_window_thread( previous, NULL ) : 0; @@ -2091,12 +2092,13 @@ BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus, DWORD new }
done: + if (previous) win_set_flags(previous, 0, WIN_IS_DEACTIVATING); if (hwnd) { if (hwnd == NtUserGetForegroundWindow()) user_driver->pActivateWindow( hwnd, previous ); clip_fullscreen_window( hwnd, FALSE ); } - return TRUE; + return ret; }
/********************************************************************** diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h index 1d0131e5886..0be90de5663 100644 --- a/dlls/win32u/ntuser_private.h +++ b/dlls/win32u/ntuser_private.h @@ -87,6 +87,7 @@ typedef struct tagWND #define WIN_NEEDS_SHOW_OWNEDPOPUP 0x0020 /* WM_SHOWWINDOW:SC_SHOW must be sent in the next ShowOwnedPopup call */ #define WIN_CHILDREN_MOVED 0x0040 /* children may have moved, ignore stored positions */ #define WIN_HAS_IME_WIN 0x0080 /* the window has been registered with imm32 */ +#define WIN_IS_DEACTIVATING 0x0100 /* window is being deactivated */
#define WND_OTHER_PROCESS ((WND *)1) /* returned by get_win_ptr on unknown window handles */ #define WND_DESKTOP ((WND *)2) /* returned by get_win_ptr on the desktop window */
From: Santino Mazza smazza@codeweavers.com
This fixes a difference in behavior between Wine and Windows, where in Wine it's possible to immediatly reactivate a window by calling SetWindowPos or SetForegroundWindow inside the WM_ACTIVATE handler. In Windows this isn't possible. --- dlls/win32u/input.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index d5ecbb33958..0b7d745d30d 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -2207,6 +2207,7 @@ BOOL set_foreground_window( HWND hwnd, BOOL mouse ) DWORD new_thread_id; HWND previous = 0;
+ if (win_get_flags(hwnd) & WIN_IS_DEACTIVATING) return TRUE; if (mouse) hwnd = get_full_window_handle( hwnd ); new_thread_id = get_window_thread( hwnd, NULL );
Rémi Bernon (@rbernon) commented about dlls/win32u/input.c:
previous = wine_server_ptr_handle( reply->previous );
Seems to me that `previous` might change here.
Rémi Bernon (@rbernon) commented about dlls/win32u/input.c:
send_message( previous, WM_ACTIVATE, MAKEWPARAM( WA_INACTIVE, is_iconic(previous) ), (LPARAM)hwnd ); }
Should we then maybe reduce the flag scope to this?
```suggestion:-8+0 if (is_window( previous )) { win_set_flags(previous, WIN_IS_DEACTIVATING, 0); send_message( previous, WM_NCACTIVATE, FALSE, (LPARAM)hwnd ); send_message( previous, WM_ACTIVATE, MAKEWPARAM( WA_INACTIVE, is_iconic(previous) ), (LPARAM)hwnd ); win_set_flags(previous, 0, WIN_IS_DEACTIVATING); }
```
Unrelated to the comments above and unfortunately, after looking at https://bugs.winehq.org/show_bug.cgi?id=58167 which seems like a similar issue, I think there's more to it that just preventing reactivation *during* the WM_ACTIVATE message. It's maybe a slightly different issue but I think it's related and I'll see if we can fix both at once.