From: Stefan Dösinger Subject: [PATCH 1/2] winex11: Don't pump messages in ChangeDisplaySettings. Message-Id: <1429179519-24915-1-git-send-email-stefan@codeweavers.com> Date: Thu, 16 Apr 2015 12:18:38 +0200 This is for bug 38394. During the handling of a focus loss on game exit, ChangeDisplaySettings calls SendMessage and causes a game-internal message to be delivered to the game's wndproc. The wndproc destroys the ddraw object, which causes a segfault in wined3d when control is returned to it. Moving the call that sends WM_DISPLAYCHANGE is necessary because otherwise the two SendMessageTimeout calls will deadlock. Replacing SMTO with PostMessage in the desktop thread is not an option because this message is delivered to the calling thread's windows before ChangeDisplaySettings returns. I cannot put the new test into test_WM_DISPLAYCHANGE because this test has a message loop running in a separate thread. --- dlls/user32/tests/sysparams.c | 75 +++++++++++++++++++++++++++++++++++++++++++ dlls/winex11.drv/desktop.c | 7 ++-- 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/dlls/user32/tests/sysparams.c b/dlls/user32/tests/sysparams.c index 31d1a8e..ac2359f 100644 --- a/dlls/user32/tests/sysparams.c +++ b/dlls/user32/tests/sysparams.c @@ -164,6 +164,7 @@ static int change_last_param; static int last_bpp; static BOOL displaychange_ok = FALSE, displaychange_test_active = FALSE; static HANDLE displaychange_sem = 0; +static BOOL wm_user_received; static LRESULT CALLBACK SysParamsTestWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) @@ -212,6 +213,10 @@ static LRESULT CALLBACK SysParamsTestWndProc( HWND hWnd, UINT msg, WPARAM wParam PostQuitMessage( 0 ); break; + case WM_USER: + wm_user_received = TRUE; + break; + /* drop through */ default: return( DefWindowProcA( hWnd, msg, wParam, lParam ) ); @@ -2898,6 +2903,74 @@ static void test_GetSysColorBrush(void) win_skip("COLOR_MENUBAR unsupported\n"); } +static DWORD WINAPI displaychange_test_thread(void *param) +{ + SendNotifyMessageA(ghTestWnd, WM_USER, 0, 0); + return 0; +} + +static void test_displaychange_message(void) +{ + DEVMODEA mode, startmode; + LONG change_ret; + DWORD tid, wait_ret; + HANDLE thread; + MSG msg; + unsigned int i = 0; + BOOL found; + + if (!pChangeDisplaySettingsExA) + { + win_skip("ChangeDisplaySettingsExA is not available\n"); + return; + } + + memset(&startmode, 0, sizeof(startmode)); + startmode.dmSize = sizeof(startmode); + EnumDisplaySettingsA(NULL, ENUM_CURRENT_SETTINGS, &startmode); + + memset(&mode, 0, sizeof(mode)); + mode.dmSize = sizeof(mode); + while (EnumDisplaySettingsA(NULL, i++, &mode)) + { + if (mode.dmPelsWidth != startmode.dmPelsWidth || mode.dmPelsHeight != startmode.dmPelsHeight) + { + found = TRUE; + break; + } + } + if (!found) + { + skip("Cound not find a mode different from the startup mode\n"); + return; + } + + displaychange_sem = CreateSemaphoreW(NULL, 0, 1, NULL); + + wm_user_received = FALSE; + thread = CreateThread(NULL, 0, displaychange_test_thread, NULL, 0, &tid); + ok(thread != NULL, "CreateThread failed\n"); + wait_ret = WaitForSingleObject(thread, INFINITE); + ok(wait_ret == WAIT_OBJECT_0, "Waiting for the thread to finish timed out\n"); + + displaychange_ok = TRUE; + change_ret = ChangeDisplaySettingsExA(NULL, &mode, NULL, CDS_FULLSCREEN, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsEx failed, %d\n", change_ret); + ok(!wm_user_received, "Received WM_USER, did not expect it\n"); + wait_ret = WaitForSingleObject(displaychange_sem, 0); + ok(wait_ret == WAIT_OBJECT_0, "Waiting for the WM_DISPLAYCHANGE message timed out\n"); + + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)); + ok(wm_user_received, "Expected WM_USER, did not receive it\n"); + + displaychange_ok = TRUE; + change_ret = pChangeDisplaySettingsExA(NULL, &startmode, NULL, 0, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsEx failed, %d\n", change_ret); + + CloseHandle(displaychange_sem); + displaychange_sem = 0; +} + START_TEST(sysparams) { int argc; @@ -2948,6 +3021,8 @@ START_TEST(sysparams) ghTestWnd = CreateWindowA( "SysParamsTestClass", "Test System Parameters Application", WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, 0, 0, hInstance, NULL ); + test_displaychange_message(); + hThread = CreateThread( NULL, 0, SysParamsThreadFunc, 0, 0, &dwThreadId ); assert( hThread ); CloseHandle( hThread ); diff --git a/dlls/winex11.drv/desktop.c b/dlls/winex11.drv/desktop.c index 603e19f..2eb673e 100644 --- a/dlls/winex11.drv/desktop.c +++ b/dlls/winex11.drv/desktop.c @@ -267,7 +267,10 @@ void X11DRV_resize_desktop( unsigned int width, unsigned int height ) if (GetWindowThreadProcessId( hwnd, NULL ) != GetCurrentThreadId()) { - SendMessageW( hwnd, WM_X11DRV_RESIZE_DESKTOP, 0, MAKELPARAM( width, height ) ); + SendMessageTimeoutW( hwnd, WM_X11DRV_RESIZE_DESKTOP, 0, + MAKELPARAM( width, height ), SMTO_BLOCK, ~0U, NULL ); + SendMessageTimeoutW( HWND_BROADCAST, WM_DISPLAYCHANGE, screen_bpp, MAKELPARAM( width, height ), + SMTO_BLOCK | SMTO_ABORTIFHUNG, 2000, NULL ); } else { @@ -278,8 +281,6 @@ void X11DRV_resize_desktop( unsigned int width, unsigned int height ) resize_data.new_virtual_rect.bottom - resize_data.new_virtual_rect.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_DEFERERASE ); ungrab_clipping_window(); - SendMessageTimeoutW( HWND_BROADCAST, WM_DISPLAYCHANGE, screen_bpp, - MAKELPARAM( width, height ), SMTO_ABORTIFHUNG, 2000, NULL ); } EnumWindows( update_windows_on_desktop_resize, (LPARAM)&resize_data ); -- 2.3.4