When transitioning a window from IconicState to NormalState and the window is managed, go through WithdrawnState. This is needed because Mutter doesn't unmap windows when making windows iconic/minimized as Mutter needs to support live preview for minimized windows. So on Mutter, a window can be both iconic and mapped. If the window is mapped, then XMapWindow() will have no effect according to the XMapWindow() documentation. Thus we have to transition to WithdrawnState first, then to NormalState.
Fix Mass Effect Legendary Edition (1328670) starts minimized.
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/user32/tests/win.c | 49 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+)
diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 8da4e11b635..bfe89539ba4 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -7453,6 +7453,11 @@ static void test_ShowWindow(void) LPARAM ret; MONITORINFO mon_info; unsigned int i; + HDC screen_dc, mem_dc; + WNDCLASSA cls = {0}; + HBITMAP mem_bitmap; + COLORREF color; + HBRUSH brush;
DWORD test_style[] = { @@ -7777,6 +7782,50 @@ static void test_ShowWindow(void)
flush_events(TRUE); } + + /* Test minimizing a normal window and then making it normal */ + brush = CreateSolidBrush(RGB(0xff, 0, 0xff)); + + cls.lpfnWndProc = DefWindowProcA; + cls.hInstance = GetModuleHandleA(0); + cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW); + cls.hbrBackground = brush; + cls.lpszClassName = "TestShowWindowClass"; + ok(RegisterClassA(&cls), "RegisterClass failed\n"); + + hwnd = CreateWindowExA(WS_EX_TOPMOST, cls.lpszClassName, NULL, WS_POPUP | WS_VISIBLE, + 100, 100, 100, 100, 0, 0, 0, NULL); + flush_events(TRUE); + + ret = ShowWindow(hwnd, SW_SHOWMINIMIZED); + ok(ret, "unexpected ret: %Iu\n", ret); + flush_events(TRUE); + + ret = ShowWindow(hwnd, SW_SHOWNORMAL); + ok(ret, "unexpected ret: %Iu\n", ret); + flush_events(TRUE); + + /* Wait until window manager animation finishes */ + Sleep(500); + + /* Test if window is really in normal state by testing the color on the screen because win32 + * states such as WS_MINIMIZE may not reflect the actual x11 window state */ + screen_dc = GetDC(GetDesktopWindow()); + mem_dc = CreateCompatibleDC(screen_dc); + mem_bitmap = CreateCompatibleBitmap(screen_dc, 200, 200); + SelectObject(mem_dc, mem_bitmap); + BitBlt(mem_dc, 0, 0, 200, 200, screen_dc, 0, 0, SRCCOPY); + + color = GetPixel(mem_dc, 150, 150); + todo_wine_if(color != RGB(0xff, 0, 0xff)) /* fails on Mutter */ + ok(color == RGB(0xff, 0, 0xff), "Got unexpected color %#lx.\n", color); + + DeleteObject(mem_bitmap); + DeleteDC(mem_dc); + ReleaseDC(GetDesktopWindow(), screen_dc); + DestroyWindow(hwnd); + DeleteObject(brush); + UnregisterClassA(cls.lpszClassName, cls.hInstance); }
static void test_ShowWindow_owned(HWND hwndMain)
From: Zhiyi Zhang zzhang@codeweavers.com
When transitioning a window from IconicState to NormalState and the window is managed, go through WithdrawnState. This is needed because Mutter doesn't unmap windows when making windows iconic/minimized as Mutter needs to support live preview for minimized windows. So on Mutter, a window can be both iconic and mapped. If the window is mapped, then XMapWindow() will have no effect according to the XMapWindow() documentation. Thus we have to transition to WithdrawnState first, then to NormalState.
Fix Mass Effect Legendary Edition (1328670) starts minimized. --- dlls/user32/tests/win.c | 1 - dlls/winex11.drv/window.c | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index bfe89539ba4..02a6793f352 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -7817,7 +7817,6 @@ static void test_ShowWindow(void) BitBlt(mem_dc, 0, 0, 200, 200, screen_dc, 0, 0, SRCCOPY);
color = GetPixel(mem_dc, 150, 150); - todo_wine_if(color != RGB(0xff, 0, 0xff)) /* fails on Mutter */ ok(color == RGB(0xff, 0, 0xff), "Got unexpected color %#lx.\n", color);
DeleteObject(mem_bitmap); diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index b033960fc26..7ae1d8ef66a 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1448,6 +1448,20 @@ static void window_set_wm_state( struct x11drv_win_data *data, UINT new_state ) return; /* another map/unmap WM_STATE update is pending, wait for it to complete */ if (old_state == new_state) return; /* states are the same, nothing to update */
+ /* When transitioning a window from IconicState to NormalState and the window is managed, go + * through WithdrawnState. This is needed because Mutter doesn't unmap windows when making + * windows iconic/minimized as Mutter needs to support live preview for minimized windows. So on + * Mutter, a window can be both iconic and mapped. If the window is mapped, then XMapWindow() + * will have no effect according to the XMapWindow() documentation. Thus we have to transition + * to WithdrawnState first, then to NormalState */ + if (data->managed && MAKELONG(old_state, new_state) == MAKELONG(IconicState, NormalState)) + { + WARN( "window %p/%lx is iconic, remapping to workaround Mutter issues.\n", data->hwnd, data->whole_window ); + window_set_wm_state( data, WithdrawnState ); + window_set_wm_state( data, NormalState ); + return; + } + switch (MAKELONG(old_state, new_state)) { case MAKELONG(WithdrawnState, IconicState):
Gamescope doesn't send WM_STATE change when handling WithdrawnState. So we have to find another way to do this for getting this upstream.
On Fri Feb 14 09:29:19 2025 +0000, Zhiyi Zhang wrote:
Gamescope doesn't send WM_STATE change when handling WithdrawnState. So we have to find another way to do this for getting this upstream.
It does since yesterday but otherwise I don't think it's something that we should worry about for Wine upstream, it's broken in many other aspects already.
Rémi Bernon (@rbernon) commented about dlls/user32/tests/win.c:
- flush_events(TRUE);
- ret = ShowWindow(hwnd, SW_SHOWNORMAL);
- ok(ret, "unexpected ret: %Iu\n", ret);
- flush_events(TRUE);
- /* Wait until window manager animation finishes */
- Sleep(500);
- /* Test if window is really in normal state by testing the color on the screen because win32
* states such as WS_MINIMIZE may not reflect the actual x11 window state */
- screen_dc = GetDC(GetDesktopWindow());
- mem_dc = CreateCompatibleDC(screen_dc);
- mem_bitmap = CreateCompatibleBitmap(screen_dc, 200, 200);
- SelectObject(mem_dc, mem_bitmap);
- BitBlt(mem_dc, 0, 0, 200, 200, screen_dc, 0, 0, SRCCOPY);
This doesn't look very reliable, I think we could do without tests in this case.