-- v10: explorer: Restore display settings on process exit. winex11.drv: Process RRNotify events in xrandr14_get_id. user32/tests: Test that display settings are restored on process exit.
From: Anton Baskanov baskanov@gmail.com
--- dlls/user32/tests/monitor.c | 308 +++++++++++++++++++++++++++++++++--- 1 file changed, 290 insertions(+), 18 deletions(-)
diff --git a/dlls/user32/tests/monitor.c b/dlls/user32/tests/monitor.c index e93a84242c4..63c1e4a1271 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) _expect_dm(__LINE__, a, b, c) -static void _expect_dm(INT line, const DEVMODEA *expected, const CHAR *device, DWORD test) +#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) { 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); - ok_(__FILE__, line)(!(expected->dmFields & DM_PELSWIDTH) || dm.dmPelsWidth == expected->dmPelsWidth, + todo_wine_if(todo) 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); - ok_(__FILE__, line)(!(expected->dmFields & DM_PELSHEIGHT) || dm.dmPelsHeight == expected->dmPelsHeight, + todo_wine_if(todo) 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,18 +325,79 @@ static void _expect_dm(INT line, const DEVMODEA *expected, const CHAR *device, D dm.dmDisplayOrientation); }
-static void test_ChangeDisplaySettingsEx(void) +#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) +{ + DEVMODEA dm; + BOOL ret; + int i; + + for (i = 0; i < 50; ++i) + { + memset(&dm, 0, sizeof(dm)); + dm.dmSize = sizeof(dm); + SetLastError(0xdeadbeef); + ret = EnumDisplaySettingsA(device, ENUM_CURRENT_SETTINGS, &dm); + ok_(__FILE__, line)(ret, "Device %s EnumDisplaySettingsA failed, error %#lx\n", device, GetLastError()); + + if (dm.dmPelsWidth == expected_width && dm.dmPelsHeight == expected_height) + break; + + Sleep(100); + } + + todo_wine_if(todo) 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, + "Device %s expect dmPelsHeight %lu, got %lu\n", device, expected_height, dm.dmPelsHeight); +} + +static HANDLE test_child_process_ChangeDisplaySettingsEx(const char *argv0, const char *device, DWORD flags, const char *exit_event_name) +{ + static const char *cds_event_name = "test_child_process_cds_event"; + PROCESS_INFORMATION info; + char buffer[MAX_PATH]; + STARTUPINFOA startup; + DWORD wait_result; + HANDLE cds_event; + LONG res; + + cds_event = CreateEventA(NULL, FALSE, FALSE, cds_event_name); + ok(!!cds_event, "CreateEventA failed, error %#lx\n", GetLastError()); + + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + + snprintf(buffer, sizeof(buffer), "%s monitor fullscreen %s %#lx %s %s", argv0, device, flags, + cds_event_name, exit_event_name); + res = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info); + ok(res, "CreateProcessA returned unexpected %ld\n", res); + wait_result = WaitForSingleObject(cds_event, 5000); + ok(wait_result == WAIT_OBJECT_0, "WaitForSingleObject returned %lx.\n", wait_result); + + CloseHandle(cds_event); + CloseHandle(info.hThread); + + return info.hProcess; +} + +static void test_ChangeDisplaySettingsEx(int argc, char **argv) { static const DWORD registry_fields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY | DM_POSITION; + static const char *exit_event0_name = "test_cds_exit_event0"; + static const char *exit_event1_name = "test_cds_exit_event1"; static const DWORD depths[] = {8, 16, 32}; DPI_AWARENESS_CONTEXT context = NULL; UINT primary, device, test, mode; + HANDLE exit_event0, exit_event1; UINT device_size, device_count; struct device_info *devices; + HANDLE process0, process1; DEVMODEA dm, dm2, dm3; INT count, old_count; DISPLAY_DEVICEA dd; + DWORD wait_result; POINTL position; DEVMODEW dmW; BOOL found; @@ -668,7 +729,7 @@ static void test_ChangeDisplaySettingsEx(void) continue; } flush_events(); - expect_dm(&dm3, devices[device].name, test); + expect_dm(&dm3, devices[device].name, test, FALSE);
/* Change the registry mode to the second mode */ res = ChangeDisplaySettingsExA(devices[device].name, &dm2, NULL, CDS_UPDATEREGISTRY | CDS_NORESET, NULL); @@ -802,7 +863,7 @@ static void test_ChangeDisplaySettingsEx(void) }
flush_events(); - expect_dm(&dm, devices[device].name, mode); + expect_dm(&dm, devices[device].name, mode, FALSE); }
/* Restore settings */ @@ -875,7 +936,7 @@ static void test_ChangeDisplaySettingsEx(void) }
flush_events(); - expect_dm(&dm, devices[device].name, 0); + expect_dm(&dm, devices[device].name, 0, FALSE);
/* Test specifying only position, width and height */ memset(&dm, 0, sizeof(dm)); @@ -920,7 +981,7 @@ static void test_ChangeDisplaySettingsEx(void) ok(dm.dmBitsPerPel, "Expected dmBitsPerPel not zero.\n"); ok(dm.dmDisplayFrequency, "Expected dmDisplayFrequency not zero.\n");
- expect_dm(&dm, devices[device].name, 0); + expect_dm(&dm, devices[device].name, 0, FALSE); }
/* Test dmPosition */ @@ -992,7 +1053,7 @@ static void test_ChangeDisplaySettingsEx(void) 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); + expect_dm(&dm2, devices[1].name, 0, FALSE);
/* Test placing the secondary adapter to all sides of the primary adapter */ for (test = 0; test < 8; ++test) @@ -1051,7 +1112,7 @@ static void test_ChangeDisplaySettingsEx(void) }
flush_events(); - expect_dm(&dm2, devices[1].name, test); + expect_dm(&dm2, devices[1].name, test, FALSE); }
/* Test automatic position update when other adapters change resolution */ @@ -1116,7 +1177,7 @@ static void test_ChangeDisplaySettingsEx(void) 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); + expect_dm(&dm2, devices[device].name, mode, FALSE);
/* EnumDisplaySettingsEx without EDS_ROTATEDMODE reports modes with current orientation */ memset(&dm3, 0, sizeof(dm3)); @@ -1148,6 +1209,170 @@ static void test_ChangeDisplaySettingsEx(void) ok(mode > 0, "Expected at least one display mode found.\n"); }
+ /* Restore all adapters to the current registry settings */ + res = ChangeDisplaySettingsExA(NULL, NULL, NULL, 0, NULL); + ok(res == DISP_CHANGE_SUCCESSFUL || + broken(res == DISP_CHANGE_FAILED), /* win8 TestBot */ + "ChangeDisplaySettingsExA returned unexpected %ld\n", res); + + if (res == DISP_CHANGE_FAILED) + { + win_skip("Failed to restore dispay mode.\n"); + } + else if (!device_count) + { + win_skip("No suitable devices found.\n"); + } + else + { + memset(&dm, 0, sizeof(dm)); + dm.dmSize = sizeof(dm); + dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; + + exit_event0 = CreateEventA(NULL, FALSE, FALSE, exit_event0_name); + ok(!!exit_event0, "CreateEventA failed, error %#lx\n", GetLastError()); + exit_event1 = CreateEventA(NULL, FALSE, FALSE, exit_event1_name); + ok(!!exit_event1, "CreateEventA failed, error %#lx\n", GetLastError()); + + /* Test that if the most recent ChangeDisplaySettingsEx call had + * CDS_FULLSCREEN set, then the settings are restored when the caller + * process exits */ + + process0 = test_child_process_ChangeDisplaySettingsEx(argv[0], devices[0].name, CDS_FULLSCREEN, exit_event0_name); + process1 = test_child_process_ChangeDisplaySettingsEx(argv[0], devices[0].name, CDS_FULLSCREEN, exit_event1_name); + + /* Verify that the settings are restored to the current registry settings */ + for (device = 0; device < device_count; ++device) + { + dm.dmPelsWidth = 800; + dm.dmPelsHeight = 600; + res = ChangeDisplaySettingsExA(devices[device].name, &dm, NULL, CDS_UPDATEREGISTRY | CDS_NORESET, NULL); + ok(res == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExA %s returned %ld.\n", devices[device].name, res); + } + + SetEvent(exit_event0); + wait_result = WaitForSingleObject(process0, 5000); + ok(wait_result == WAIT_OBJECT_0, "WaitForSingleObject returned %lx.\n", wait_result); + + Sleep(100); + + dm.dmPelsWidth = 640; + dm.dmPelsHeight = 480; + expect_dm(&dm, devices[0].name, 0, TRUE); + + 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); + + CloseHandle(process1); + CloseHandle(process0); + + /* Test processes exiting in reverse order */ + + process0 = test_child_process_ChangeDisplaySettingsEx(argv[0], devices[0].name, CDS_FULLSCREEN, exit_event0_name); + process1 = test_child_process_ChangeDisplaySettingsEx(argv[0], devices[0].name, CDS_FULLSCREEN, exit_event1_name); + + 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); + + SetEvent(exit_event0); + wait_result = WaitForSingleObject(process0, 5000); + ok(wait_result == WAIT_OBJECT_0, "WaitForSingleObject returned %lx.\n", wait_result); + + CloseHandle(process1); + CloseHandle(process0); + + /* Test that ChangeDisplaySettingsEx without CDS_FULLSCREEN cancels the restoration */ + + process0 = test_child_process_ChangeDisplaySettingsEx(argv[0], devices[0].name, CDS_FULLSCREEN, exit_event0_name); + process1 = test_child_process_ChangeDisplaySettingsEx(argv[0], devices[0].name, 0, exit_event1_name); + + SetEvent(exit_event0); + wait_result = WaitForSingleObject(process0, 5000); + ok(wait_result == WAIT_OBJECT_0, "WaitForSingleObject returned %lx.\n", wait_result); + + Sleep(100); + + dm.dmPelsWidth = 640; + dm.dmPelsHeight = 480; + expect_dm(&dm, devices[0].name, 0, TRUE); + + SetEvent(exit_event1); + wait_result = WaitForSingleObject(process1, 5000); + ok(wait_result == WAIT_OBJECT_0, "WaitForSingleObject returned %lx.\n", wait_result); + + Sleep(100); + + dm.dmPelsWidth = 640; + dm.dmPelsHeight = 480; + expect_dm(&dm, devices[0].name, 0, TRUE); + + CloseHandle(process1); + CloseHandle(process0); + + /* Test processes exiting in reverse order */ + + process0 = test_child_process_ChangeDisplaySettingsEx(argv[0], devices[0].name, CDS_FULLSCREEN, exit_event0_name); + process1 = test_child_process_ChangeDisplaySettingsEx(argv[0], devices[0].name, 0, exit_event1_name); + + SetEvent(exit_event1); + wait_result = WaitForSingleObject(process1, 5000); + ok(wait_result == WAIT_OBJECT_0, "WaitForSingleObject returned %lx.\n", wait_result); + + Sleep(100); + + dm.dmPelsWidth = 640; + dm.dmPelsHeight = 480; + expect_dm(&dm, devices[0].name, 0, TRUE); + + SetEvent(exit_event0); + wait_result = WaitForSingleObject(process0, 5000); + ok(wait_result == WAIT_OBJECT_0, "WaitForSingleObject returned %lx.\n", wait_result); + + Sleep(100); + + dm.dmPelsWidth = 640; + dm.dmPelsHeight = 480; + expect_dm(&dm, devices[0].name, 0, TRUE); + + CloseHandle(process1); + CloseHandle(process0); + + if (device_count < 2) + { + skip("Only one device found.\n"); + } + else + { + /* Test that the settings are restored for all devices, regardless of + * the process that changed them */ + + dm.dmPelsWidth = 640; + dm.dmPelsHeight = 480; + res = ChangeDisplaySettingsExA(devices[1].name, &dm, NULL, CDS_FULLSCREEN, NULL); + ok(res == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExA %s returned %ld.\n", devices[1].name, res); + + process0 = test_child_process_ChangeDisplaySettingsEx(argv[0], devices[0].name, CDS_FULLSCREEN, exit_event0_name); + + SetEvent(exit_event0); + 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); + + CloseHandle(process0); + } + + CloseHandle(exit_event1); + CloseHandle(exit_event0); + } + /* Restore all adapters to their original settings */ for (device = 0; device < device_count; ++device) { @@ -1162,7 +1387,7 @@ static void test_ChangeDisplaySettingsEx(void) 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); + expect_dm(&devices[device].original_mode, devices[device].name, 0, FALSE);
free(devices); } @@ -2663,6 +2888,45 @@ BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, return TRUE; }
+static void test_fullscreen(int argc, char **argv) +{ + HANDLE event0, event1; + DWORD wait_result; + DWORD flags = 0; + DEVMODEA dm; + LONG res; + + if (argc < 7) + { + ok(0, "too few arguments.\n"); + return; + } + + res = sscanf(argv[4], "%lx", &flags); + ok(res == 1, "sscanf returned unexpected %ld.\n", res); + + event0 = OpenEventA(EVENT_MODIFY_STATE, FALSE, argv[5]); + ok(!!event0, "OpenEventA failed, error %#lx\n", GetLastError()); + event1 = OpenEventA(SYNCHRONIZE, FALSE, argv[6]); + ok(!!event1, "OpenEventA failed, error %#lx\n", GetLastError()); + + memset(&dm, 0, sizeof(dm)); + dm.dmSize = sizeof(dm); + dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; + dm.dmPelsWidth = 640; + dm.dmPelsHeight = 480; + res = ChangeDisplaySettingsExA(argv[3], &dm, NULL, flags, NULL); + ok(res == DISP_CHANGE_SUCCESSFUL, + "ChangeDisplaySettingsExA %s returned unexpected %ld.\n", argv[3], res); + + SetEvent(event0); + CloseHandle(event0); + + wait_result = WaitForSingleObject(event1, 7000); + ok(wait_result == WAIT_OBJECT_0, "WaitForSingleObject returned %lx.\n", wait_result); + CloseHandle(event1); +} + START_TEST(monitor) { char** myARGV; @@ -2670,15 +2934,23 @@ START_TEST(monitor)
init_function_pointers();
- if (myARGC >= 3 && strcmp(myARGV[2], "info") == 0) + if (myARGC >= 3) { - printf("Monitor information:\n"); - EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, 0); - return; + if (strcmp(myARGV[2], "info") == 0) + { + printf("Monitor information:\n"); + EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, 0); + return; + } + else if (strcmp(myARGV[2], "fullscreen") == 0) + { + test_fullscreen(myARGC, myARGV); + return; + } }
test_enumdisplaydevices(); - test_ChangeDisplaySettingsEx(); + test_ChangeDisplaySettingsEx(myARGC, myARGV); test_DisplayConfigSetDeviceInfo(); test_EnumDisplayMonitors(); test_monitors();
From: Anton Baskanov baskanov@gmail.com
We have to invalidate the current mode cache if there are pending RRNotify events. The performance hit on EnumDisplaySettingsExW is around 7%.
Also call X11DRV_DisplayDevices_RegisterEventHandlers in x11drv_init. Otherwise, RRNotify events will only be handled in the explorer process. --- dlls/user32/tests/monitor.c | 10 +++++----- dlls/winex11.drv/event.c | 2 +- dlls/winex11.drv/window.c | 1 - dlls/winex11.drv/x11drv.h | 1 + dlls/winex11.drv/x11drv_main.c | 1 + dlls/winex11.drv/xrandr.c | 28 ++++++++++++++++++++++++++++ 6 files changed, 36 insertions(+), 7 deletions(-)
diff --git a/dlls/user32/tests/monitor.c b/dlls/user32/tests/monitor.c index 63c1e4a1271..c638e1cbfef 100644 --- a/dlls/user32/tests/monitor.c +++ b/dlls/user32/tests/monitor.c @@ -1258,7 +1258,7 @@ static void test_ChangeDisplaySettingsEx(int argc, char **argv)
dm.dmPelsWidth = 640; dm.dmPelsHeight = 480; - expect_dm(&dm, devices[0].name, 0, TRUE); + expect_dm(&dm, devices[0].name, 0, FALSE);
SetEvent(exit_event1); wait_result = WaitForSingleObject(process1, 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, TRUE); + expect_dm(&dm, devices[0].name, 0, FALSE);
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, TRUE); + expect_dm(&dm, devices[0].name, 0, FALSE);
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, TRUE); + expect_dm(&dm, devices[0].name, 0, FALSE);
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, TRUE); + expect_dm(&dm, devices[0].name, 0, FALSE);
CloseHandle(process1); CloseHandle(process0); diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index 379b27b3f70..2070d942056 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -405,7 +405,7 @@ static inline BOOL call_event_handler( Display *display, XEvent *event ) /*********************************************************************** * process_events */ -static BOOL process_events( Display *display, Bool (*filter)(Display*, XEvent*,XPointer), ULONG_PTR arg ) +BOOL process_events( Display *display, Bool (*filter)(Display*, XEvent*,XPointer), ULONG_PTR arg ) { XEvent event, prev_event; int count = 0; diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 5a014c9080d..19daac04203 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -2048,7 +2048,6 @@ BOOL X11DRV_CreateWindow( HWND hwnd ) CWOverrideRedirect | CWEventMask, &attr ); XFlush( data->display ); NtUserSetProp( hwnd, clip_window_prop, (HANDLE)data->clip_window ); - X11DRV_DisplayDevices_RegisterEventHandlers(); } return TRUE; } diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 8fdb46d98da..9aeb835f99e 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -679,6 +679,7 @@ extern void retry_grab_clipping_window(void); extern void ungrab_clipping_window(void); extern void move_resize_window( HWND hwnd, int dir ); extern void X11DRV_InitKeyboard( Display *display ); +extern BOOL process_events( Display *display, Bool (*filter)(Display*, XEvent*, XPointer), ULONG_PTR arg ); extern BOOL X11DRV_ProcessEvents( DWORD mask ); extern HWND *build_hwnd_list(void);
diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 4c8fd943ffd..f8e56702d8c 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -683,6 +683,7 @@ static NTSTATUS x11drv_init( void *arg )
init_user_driver(); X11DRV_DisplayDevices_Init(FALSE); + X11DRV_DisplayDevices_RegisterEventHandlers(); return STATUS_SUCCESS; }
diff --git a/dlls/winex11.drv/xrandr.c b/dlls/winex11.drv/xrandr.c index 0e7b9a07818..b8378561d4a 100644 --- a/dlls/winex11.drv/xrandr.c +++ b/dlls/winex11.drv/xrandr.c @@ -1220,6 +1220,32 @@ static void xrandr14_register_event_handlers(void) "XRandR ProviderChange" ); }
+static Bool filter_rrnotify_event( Display *display, XEvent *event, char *arg ) +{ + ULONG_PTR event_base = (ULONG_PTR)arg; + + if (event->type == event_base + RRNotify_CrtcChange + || event->type == event_base + RRNotify_OutputChange + || event->type == event_base + RRNotify_ProviderChange) + return 1; + + return 0; +} + +static void process_rrnotify_events(void) +{ + struct x11drv_thread_data *data = x11drv_thread_data(); + int event_base, error_base; + + if (!data) return; + if (data->current_event) return; /* don't process nested events */ + + if (!pXRRQueryExtension( data->display, &event_base, &error_base )) + return; + + process_events( data->display, filter_rrnotify_event, event_base ); +} + /* XRandR 1.4 display settings handler */ static BOOL xrandr14_get_id( const WCHAR *device_name, BOOL is_primary, x11drv_settings_id *id ) { @@ -1235,6 +1261,8 @@ static BOOL xrandr14_get_id( const WCHAR *device_name, BOOL is_primary, x11drv_s if (*end) return FALSE;
+ process_rrnotify_events(); + /* Update cache */ pthread_mutex_lock( &xrandr_mutex ); if (!current_modes)
From: Anton Baskanov baskanov@gmail.com
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 988da5a7641..566ab117c0f 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -3181,9 +3181,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; @@ -3232,6 +3236,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 25bba7cb7cb..ad00751537d 100644 --- a/programs/explorer/desktop.c +++ b/programs/explorer/desktop.c @@ -611,6 +611,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 * @@ -620,7 +632,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, @@ -630,12 +641,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; }
@@ -696,6 +702,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 )) + { + TRACE( "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; + + WARN( "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 */ @@ -968,6 +1055,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 )) @@ -1000,6 +1088,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 )
On Thu Mar 28 10:26:05 2024 +0000, Zhiyi Zhang wrote:
These new win11 test failures seem like they were introduced by this MR. So you will need to address them as well. Try submitting the MR to the TestBot first.
These should be fixed in v9.
v9: - Also skip the tests if no devices were found.
Zhiyi Zhang (@zhiyi) commented about programs/explorer/desktop.c:
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;
WARN( "restoring display settings on process exit\n" );
This should be a TRACE.
Zhiyi Zhang (@zhiyi) commented about programs/explorer/desktop.c:
- 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 ))
- {
TRACE( "failed to create display settings restorer window err %lu\n", GetLastError() );
This should be a WARN.