From: Ivan Lyugaev <valy@etersoft.ru> --- dlls/user32/tests/msg.c | 74 +++++++++++++++++++++++++++++++++++++++ dlls/winex11.drv/window.c | 9 +++++ 2 files changed, 83 insertions(+) diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index 6fd76d28533..c8b679b4fa9 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -15410,6 +15410,79 @@ static const struct message WmSetWindowRgn_clear[] = { { 0 } }; +#define SET_WINDOW_RGN_FEEDBACK_LIMIT 10 +static unsigned int set_window_rgn_feedback_count; +static BOOL set_window_rgn_feedback_armed; + +static void set_window_rgn_feedback_update_region(HWND hwnd) +{ + RECT rect; + HRGN rgn; + int width, height; + BOOL ret; + + GetClientRect(hwnd, &rect); + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + rgn = CreateRectRgn(0, 0, width, height); + ok(rgn, "CreateRectRgn failed\n"); + ret = SetWindowRgn(hwnd, rgn, TRUE); + ok(ret, "SetWindowRgn failed\n"); + if (!ret) DeleteObject(rgn); +} + +static LRESULT CALLBACK set_window_rgn_feedback_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch(msg) + { + case WM_WINDOWPOSCHANGED: + { + WINDOWPOS *pos = (WINDOWPOS *)lparam; + if (set_window_rgn_feedback_armed && ((pos->flags & (SWP_NOSIZE | SWP_NOMOVE)) != (SWP_NOSIZE | SWP_NOMOVE))) + { + set_window_rgn_feedback_count++; + if (set_window_rgn_feedback_count < SET_WINDOW_RGN_FEEDBACK_LIMIT) + set_window_rgn_feedback_update_region(hwnd); + } + break; + } + } + return DefWindowProcA(hwnd, msg, wparam, lparam); +} + +static void test_SetWindowRgn_Feedback(void) +{ + HWND hwnd; + HINSTANCE instance = GetModuleHandleA(0); + WNDCLASSA cls = { + .lpfnWndProc = set_window_rgn_feedback_proc, + .hInstance = GetModuleHandleA(0), + .hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW), + .hbrBackground = GetStockObject(WHITE_BRUSH), + .lpszClassName = "TestSetWindowRgnFeedback" + }; + + register_class(&cls); + hwnd = CreateWindowExA(WS_EX_APPWINDOW, "TestSetWindowRgnFeedback", "TestSetWindowRgnFeedback", + WS_MAXIMIZE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, instance, NULL); + ok(hwnd != 0, "CreateWindowExA failed\n"); + ShowWindow(hwnd, SW_SHOWMAXIMIZED); + ok(IsZoomed(hwnd), "window should be maximized\n"); + + flush_events(); + set_window_rgn_feedback_count = 0; + set_window_rgn_feedback_armed = TRUE; + set_window_rgn_feedback_update_region(hwnd); + flush_events(); + + set_window_rgn_feedback_armed = FALSE; + DestroyWindow(hwnd) ; + flush_events(); + ok(!set_window_rgn_feedback_count, "got window region feedback loop, count %u (expected 0)\n", set_window_rgn_feedback_count); +} + static void test_SetWindowRgn(void) { HRGN hrgn; @@ -21536,6 +21609,7 @@ START_TEST(msg) test_TrackMouseEvent(); test_SetWindowRgn(); + test_SetWindowRgn_Feedback(); test_sys_menu(); test_dialog_messages(); test_EndDialog(); diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index d2d53a40be3..bc4b2665dbd 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1763,6 +1763,15 @@ static UINT window_update_client_config( struct x11drv_win_data *data ) return 0; } + /* Ignore shaped window config changes when a maximized window is still at the desired + * rect. This is needed because when calling SetWindowRgn for a maximized window in X11, + * the rect after adjustment is mistakenly taken as an actual window resize. Syncing back X11 rects + * that do not include the adjustment causes SetWindowRgn to be called again, leading to visual flickering + * of the application window.*/ + if (data->shaped && (data->current_state.net_wm_state & (1 << NET_WM_STATE_MAXIMIZED)) && + EqualRect( &data->current_state.rect, &data->desired_state.rect )) + return 0; + flags = SWP_NOACTIVATE | SWP_NOZORDER; rect = new_rect = window_rect_from_visible( &data->rects, data->current_state.rect ); if (new_rect.left == old_rect.left && new_rect.top == old_rect.top) flags |= SWP_NOMOVE; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10915