From 89ad36b1d96fdd64cb2e59bd5743716b5874dac9 Mon Sep 17 00:00:00 2001 From: Anton Baskanov Date: Tue, 6 Feb 2024 20:59:57 +0700 Subject: [PATCH] explorer: Restore display settings on process exit. Restore display settings to the ones in the registry when CDS_FULLSCREEN is used in ChangeDisplaySettings(). Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=49674 --- dlls/user32/tests/monitor.c | 50 ++++++++--------- dlls/win32u/sysparams.c | 13 +++++ programs/explorer/desktop.c | 105 +++++++++++++++++++++++++++++++++--- 3 files changed, 136 insertions(+), 32 deletions(-) diff --git a/dlls/user32/tests/monitor.c b/dlls/user32/tests/monitor.c index c638e1cbfef..e4fe79d6882 100644 --- a/dlls/user32/tests/monitor.c +++ b/dlls/user32/tests/monitor.c @@ -291,8 +291,8 @@ struct device_info DEVMODEA original_mode; }; -#define expect_dm(a, b, c, d) _expect_dm(__LINE__, a, b, c, d) -static void _expect_dm(INT line, const DEVMODEA *expected, const CHAR *device, DWORD test, BOOL todo) +#define expect_dm(a, b, c) _expect_dm(__LINE__, a, b, c) +static void _expect_dm(INT line, const DEVMODEA *expected, const CHAR *device, DWORD test) { DEVMODEA dm; BOOL ret; @@ -307,9 +307,9 @@ static void _expect_dm(INT line, const DEVMODEA *expected, const CHAR *device, D "Device %s test %ld expect dmFields to contain %#lx, got %#lx\n", device, test, expected->dmFields, dm.dmFields); ok_(__FILE__, line)(!(expected->dmFields & DM_BITSPERPEL) || dm.dmBitsPerPel == expected->dmBitsPerPel, "Device %s test %ld expect dmBitsPerPel %lu, got %lu\n", device, test, expected->dmBitsPerPel, dm.dmBitsPerPel); - todo_wine_if(todo) ok_(__FILE__, line)(!(expected->dmFields & DM_PELSWIDTH) || dm.dmPelsWidth == expected->dmPelsWidth, + ok_(__FILE__, line)(!(expected->dmFields & DM_PELSWIDTH) || dm.dmPelsWidth == expected->dmPelsWidth, "Device %s test %ld expect dmPelsWidth %lu, got %lu\n", device, test, expected->dmPelsWidth, dm.dmPelsWidth); - todo_wine_if(todo) ok_(__FILE__, line)(!(expected->dmFields & DM_PELSHEIGHT) || dm.dmPelsHeight == expected->dmPelsHeight, + ok_(__FILE__, line)(!(expected->dmFields & DM_PELSHEIGHT) || dm.dmPelsHeight == expected->dmPelsHeight, "Device %s test %ld expect dmPelsHeight %lu, got %lu\n", device, test, expected->dmPelsHeight, dm.dmPelsHeight); ok_(__FILE__, line)(!(expected->dmFields & DM_POSITION) || dm.dmPosition.x == expected->dmPosition.x, "Device %s test %ld expect dmPosition.x %ld, got %ld\n", device, test, expected->dmPosition.x, dm.dmPosition.x); @@ -325,8 +325,8 @@ static void _expect_dm(INT line, const DEVMODEA *expected, const CHAR *device, D dm.dmDisplayOrientation); } -#define wait_for_dm(a, b, c, d) wait_for_dm_(__LINE__, a, b, c, d) -static void wait_for_dm_(int line, const char *device, DWORD expected_width, DWORD expected_height, BOOL todo) +#define wait_for_dm(a, b, c) wait_for_dm_(__LINE__, a, b, c) +static void wait_for_dm_(int line, const char *device, DWORD expected_width, DWORD expected_height) { DEVMODEA dm; BOOL ret; @@ -346,9 +346,9 @@ static void wait_for_dm_(int line, const char *device, DWORD expected_width, DWO Sleep(100); } - todo_wine_if(todo) ok_(__FILE__, line)(dm.dmPelsWidth == expected_width, + ok_(__FILE__, line)(dm.dmPelsWidth == expected_width, "Device %s expect dmPelsWidth %lu, got %lu\n", device, expected_width, dm.dmPelsWidth); - todo_wine_if(todo) ok_(__FILE__, line)(dm.dmPelsHeight == expected_height, + ok_(__FILE__, line)(dm.dmPelsHeight == expected_height, "Device %s expect dmPelsHeight %lu, got %lu\n", device, expected_height, dm.dmPelsHeight); } @@ -729,7 +729,7 @@ static void test_ChangeDisplaySettingsEx(int argc, char **argv) continue; } flush_events(); - expect_dm(&dm3, devices[device].name, test, FALSE); + expect_dm(&dm3, devices[device].name, test); /* Change the registry mode to the second mode */ res = ChangeDisplaySettingsExA(devices[device].name, &dm2, NULL, CDS_UPDATEREGISTRY | CDS_NORESET, NULL); @@ -863,7 +863,7 @@ static void test_ChangeDisplaySettingsEx(int argc, char **argv) } flush_events(); - expect_dm(&dm, devices[device].name, mode, FALSE); + expect_dm(&dm, devices[device].name, mode); } /* Restore settings */ @@ -936,7 +936,7 @@ static void test_ChangeDisplaySettingsEx(int argc, char **argv) } flush_events(); - expect_dm(&dm, devices[device].name, 0, FALSE); + expect_dm(&dm, devices[device].name, 0); /* Test specifying only position, width and height */ memset(&dm, 0, sizeof(dm)); @@ -981,7 +981,7 @@ static void test_ChangeDisplaySettingsEx(int argc, char **argv) ok(dm.dmBitsPerPel, "Expected dmBitsPerPel not zero.\n"); ok(dm.dmDisplayFrequency, "Expected dmDisplayFrequency not zero.\n"); - expect_dm(&dm, devices[device].name, 0, FALSE); + expect_dm(&dm, devices[device].name, 0); } /* Test dmPosition */ @@ -1053,7 +1053,7 @@ static void test_ChangeDisplaySettingsEx(int argc, char **argv) ok(res == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExA %s returned unexpected %ld\n", devices[1].name, res); dm2.dmPosition.x = dm.dmPosition.x + dm.dmPelsWidth; - expect_dm(&dm2, devices[1].name, 0, FALSE); + expect_dm(&dm2, devices[1].name, 0); /* Test placing the secondary adapter to all sides of the primary adapter */ for (test = 0; test < 8; ++test) @@ -1112,7 +1112,7 @@ static void test_ChangeDisplaySettingsEx(int argc, char **argv) } flush_events(); - expect_dm(&dm2, devices[1].name, test, FALSE); + expect_dm(&dm2, devices[1].name, test); } /* Test automatic position update when other adapters change resolution */ @@ -1177,7 +1177,7 @@ static void test_ChangeDisplaySettingsEx(int argc, char **argv) ok(res == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExA %s mode %d returned unexpected %ld.\n", devices[device].name, mode, res); flush_events(); - expect_dm(&dm2, devices[device].name, mode, FALSE); + expect_dm(&dm2, devices[device].name, mode); /* EnumDisplaySettingsEx without EDS_ROTATEDMODE reports modes with current orientation */ memset(&dm3, 0, sizeof(dm3)); @@ -1258,13 +1258,13 @@ static void test_ChangeDisplaySettingsEx(int argc, char **argv) dm.dmPelsWidth = 640; dm.dmPelsHeight = 480; - expect_dm(&dm, devices[0].name, 0, FALSE); + expect_dm(&dm, devices[0].name, 0); SetEvent(exit_event1); wait_result = WaitForSingleObject(process1, 5000); ok(wait_result == WAIT_OBJECT_0, "WaitForSingleObject returned %lx.\n", wait_result); - wait_for_dm(devices[0].name, 800, 600, TRUE); + wait_for_dm(devices[0].name, 800, 600); CloseHandle(process1); CloseHandle(process0); @@ -1278,7 +1278,7 @@ static void test_ChangeDisplaySettingsEx(int argc, char **argv) wait_result = WaitForSingleObject(process1, 5000); ok(wait_result == WAIT_OBJECT_0, "WaitForSingleObject returned %lx.\n", wait_result); - wait_for_dm(devices[0].name, 800, 600, TRUE); + wait_for_dm(devices[0].name, 800, 600); SetEvent(exit_event0); wait_result = WaitForSingleObject(process0, 5000); @@ -1300,7 +1300,7 @@ static void test_ChangeDisplaySettingsEx(int argc, char **argv) dm.dmPelsWidth = 640; dm.dmPelsHeight = 480; - expect_dm(&dm, devices[0].name, 0, FALSE); + expect_dm(&dm, devices[0].name, 0); SetEvent(exit_event1); wait_result = WaitForSingleObject(process1, 5000); @@ -1310,7 +1310,7 @@ static void test_ChangeDisplaySettingsEx(int argc, char **argv) dm.dmPelsWidth = 640; dm.dmPelsHeight = 480; - expect_dm(&dm, devices[0].name, 0, FALSE); + expect_dm(&dm, devices[0].name, 0); CloseHandle(process1); CloseHandle(process0); @@ -1328,7 +1328,7 @@ static void test_ChangeDisplaySettingsEx(int argc, char **argv) dm.dmPelsWidth = 640; dm.dmPelsHeight = 480; - expect_dm(&dm, devices[0].name, 0, FALSE); + expect_dm(&dm, devices[0].name, 0); SetEvent(exit_event0); wait_result = WaitForSingleObject(process0, 5000); @@ -1338,7 +1338,7 @@ static void test_ChangeDisplaySettingsEx(int argc, char **argv) dm.dmPelsWidth = 640; dm.dmPelsHeight = 480; - expect_dm(&dm, devices[0].name, 0, FALSE); + expect_dm(&dm, devices[0].name, 0); CloseHandle(process1); CloseHandle(process0); @@ -1363,8 +1363,8 @@ static void test_ChangeDisplaySettingsEx(int argc, char **argv) wait_result = WaitForSingleObject(process0, 5000); ok(wait_result == WAIT_OBJECT_0, "WaitForSingleObject returned %lx.\n", wait_result); - wait_for_dm(devices[0].name, 800, 600, TRUE); - wait_for_dm(devices[1].name, 800, 600, TRUE); + wait_for_dm(devices[0].name, 800, 600); + wait_for_dm(devices[1].name, 800, 600); CloseHandle(process0); } @@ -1387,7 +1387,7 @@ static void test_ChangeDisplaySettingsEx(int argc, char **argv) broken(res == DISP_CHANGE_FAILED), /* win8 TestBot */ "ChangeDisplaySettingsExA returned unexpected %ld\n", res); for (device = 0; device < device_count; ++device) - expect_dm(&devices[device].original_mode, devices[device].name, 0, FALSE); + expect_dm(&devices[device].original_mode, devices[device].name, 0); free(devices); } diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 6993373ca54..e66ad488486 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -3149,9 +3149,13 @@ static BOOL all_detached_settings( const DEVMODEW *displays ) static LONG apply_display_settings( struct source *target, const DEVMODEW *devmode, HWND hwnd, DWORD flags, void *lparam ) { + static const WCHAR restorerW[] = {'_','_','w','i','n','e','_','d','i','s','p','l','a','y','_', + 's','e','t','t','i','n','g','s','_','r','e','s','t','o','r','e','r',0}; + UNICODE_STRING restoter_str = RTL_CONSTANT_STRING( restorerW ); WCHAR primary_name[CCHDEVICENAME]; struct source *primary, *source; DEVMODEW *mode, *displays; + HWND restorer_window; LONG ret; if (!lock_display_devices()) return DISP_CHANGE_FAILED; @@ -3200,6 +3204,15 @@ static LONG apply_display_settings( struct source *target, const DEVMODEW *devmo free( displays ); if (ret) return ret; + if ((restorer_window = NtUserFindWindowEx( NULL, NULL, &restoter_str, NULL, 0 ))) + { + if (NtUserGetWindowThread( restorer_window, NULL ) != GetCurrentThreadId()) + { + DWORD fullscreen_process_id = (flags & CDS_FULLSCREEN) ? GetCurrentProcessId() : 0; + send_message( restorer_window, WM_USER + 0, 0, fullscreen_process_id ); + } + } + if (!update_display_cache( TRUE )) WARN( "Failed to update display cache after mode change.\n" ); diff --git a/programs/explorer/desktop.c b/programs/explorer/desktop.c index a18c9806d42..fb59258ad36 100644 --- a/programs/explorer/desktop.c +++ b/programs/explorer/desktop.c @@ -613,6 +613,18 @@ static void initialize_launchers( HWND hwnd ) } } +static void wait_named_mutex( const WCHAR *name ) +{ + HANDLE mutex; + + mutex = CreateMutexW( NULL, TRUE, name ); + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + TRACE( "waiting for mutex %s\n", debugstr_w( name )); + WaitForSingleObject( mutex, INFINITE ); + } +} + /************************************************************************** * wait_clipboard_mutex * @@ -622,7 +634,6 @@ static BOOL wait_clipboard_mutex(void) { static const WCHAR prefix[] = L"__wine_clipboard_"; WCHAR buffer[MAX_PATH + ARRAY_SIZE( prefix )]; - HANDLE mutex; memcpy( buffer, prefix, sizeof(prefix) ); if (!GetUserObjectInformationW( GetProcessWindowStation(), UOI_NAME, @@ -632,12 +643,7 @@ static BOOL wait_clipboard_mutex(void) ERR( "failed to get winstation name\n" ); return FALSE; } - mutex = CreateMutexW( NULL, TRUE, buffer ); - if (GetLastError() == ERROR_ALREADY_EXISTS) - { - TRACE( "waiting for mutex %s\n", debugstr_w( buffer )); - WaitForSingleObject( mutex, INFINITE ); - } + wait_named_mutex( buffer ); return TRUE; } @@ -698,6 +704,87 @@ static DWORD WINAPI clipboard_thread( void *arg ) return 0; } +static HANDLE fullscreen_process; + +static LRESULT WINAPI display_settings_restorer_wndproc( HWND hwnd, UINT message, WPARAM wp, LPARAM lp ) +{ + TRACE( "got msg %04x wp %Ix lp %Ix\n", message, wp, lp ); + + switch(message) + { + case WM_USER + 0: + TRACE( "fullscreen process id %Iu.\n", lp ); + + if (fullscreen_process) + { + CloseHandle( fullscreen_process ); + fullscreen_process = NULL; + } + + if (lp) + fullscreen_process = OpenProcess( SYNCHRONIZE, FALSE, lp ); + + return 0; + } + + return DefWindowProcW( hwnd, message, wp, lp ); +} + +static DWORD WINAPI display_settings_restorer_thread( void *param ) +{ + static const WCHAR *display_settings_restorer_classname = L"__wine_display_settings_restorer"; + DWORD wait_result; + WNDCLASSW class; + MSG msg; + + SetThreadDescription( GetCurrentThread(), L"wine_explorer_display_settings_restorer" ); + + wait_named_mutex( L"__wine_display_settings_restorer_mutex" ); + + memset( &class, 0, sizeof(class) ); + class.lpfnWndProc = display_settings_restorer_wndproc; + class.lpszClassName = display_settings_restorer_classname; + + if (!RegisterClassW( &class ) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) + { + ERR( "could not register display settings restorer window class err %lu\n", GetLastError() ); + return 0; + } + if (!CreateWindowW( display_settings_restorer_classname, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, NULL )) + { + WARN( "failed to create display settings restorer window err %lu\n", GetLastError() ); + UnregisterClassW( display_settings_restorer_classname, NULL ); + return 0; + } + + for (;;) + { + if (PeekMessageW( &msg, NULL, 0, 0, PM_REMOVE )) + { + if (msg.message == WM_QUIT) + break; + DispatchMessageW( &msg ); + continue; + } + + wait_result = MsgWaitForMultipleObjects( fullscreen_process ? 1 : 0, &fullscreen_process, + FALSE, INFINITE, QS_ALLINPUT ); + if (wait_result == WAIT_FAILED) + break; + if (!fullscreen_process || wait_result != WAIT_OBJECT_0) + continue; + + TRACE( "restoring display settings on process exit\n" ); + + ChangeDisplaySettingsExW( NULL, NULL, NULL, 0, NULL ); + + CloseHandle( fullscreen_process ); + fullscreen_process = NULL; + } + + return 0; +} + static WNDPROC desktop_orig_wndproc; /* window procedure for the desktop window */ @@ -970,6 +1057,7 @@ static void initialize_display_settings( unsigned int width, unsigned int height { DISPLAY_DEVICEW device = {.cb = sizeof(DISPLAY_DEVICEW)}; DWORD i = 0, flags = CDS_GLOBAL | CDS_UPDATEREGISTRY; + HANDLE thread; /* Store current display mode in the registry */ while (EnumDisplayDevicesW( NULL, i++, &device, 0 )) @@ -1002,6 +1090,9 @@ static void initialize_display_settings( unsigned int width, unsigned int height if (ChangeDisplaySettingsExW( NULL, &devmode, 0, flags, NULL )) ERR( "Failed to set primary display settings.\n" ); } + + thread = CreateThread( NULL, 0, display_settings_restorer_thread, NULL, 0, NULL ); + if (thread) CloseHandle( thread ); } static void set_desktop_window_title( HWND hwnd, const WCHAR *name )