From 1a4f2e23c00eaed3e9a4a5d77941b89f972cda89 Mon Sep 17 00:00:00 2001 From: Zhiyi Zhang Date: Fri, 30 Oct 2020 14:45:26 +0800 Subject: [PATCH] ddraw/tests: Test display mode restoration. Mostly to test that when doing mode restoration, all display outputs get restored to their display settings in the registry, not just one output gets restored. Signed-off-by: Zhiyi Zhang Signed-off-by: Henri Verbeet Signed-off-by: Alexandre Julliard --- dlls/ddraw/tests/ddraw1.c | 396 ++++++++++++++++++++++++++++++++++++- dlls/ddraw/tests/ddraw2.c | 397 +++++++++++++++++++++++++++++++++++++- dlls/ddraw/tests/ddraw4.c | 396 ++++++++++++++++++++++++++++++++++++- dlls/ddraw/tests/ddraw7.c | 396 ++++++++++++++++++++++++++++++++++++- 4 files changed, 1581 insertions(+), 4 deletions(-) diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c index 63791c5b606..783c1b961a0 100644 --- a/dlls/ddraw/tests/ddraw1.c +++ b/dlls/ddraw/tests/ddraw1.c @@ -265,6 +265,69 @@ static IDirectDrawSurface *get_depth_stencil(IDirect3DDevice *device) return ret; } +/* Free original_modes after finished using it */ +static BOOL save_display_modes(DEVMODEW **original_modes, unsigned int *display_count) +{ + unsigned int number, size = 2, count = 0, index = 0; + DISPLAY_DEVICEW display_device; + DEVMODEW *modes, *tmp; + + if (!(modes = heap_alloc(size * sizeof(*modes)))) + return FALSE; + + display_device.cb = sizeof(display_device); + while (EnumDisplayDevicesW(NULL, index++, &display_device, 0)) + { + /* Skip software devices */ + if (swscanf(display_device.DeviceName, L"\\\\.\\DISPLAY%u", &number) != 1) + continue; + + if (!(display_device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) + continue; + + if (count >= size) + { + size *= 2; + if (!(tmp = heap_realloc(modes, size * sizeof(*modes)))) + { + heap_free(modes); + return FALSE; + } + modes = tmp; + } + + memset(&modes[count], 0, sizeof(modes[count])); + modes[count].dmSize = sizeof(modes[count]); + if (!EnumDisplaySettingsW(display_device.DeviceName, ENUM_CURRENT_SETTINGS, &modes[count])) + { + heap_free(modes); + return FALSE; + } + + lstrcpyW(modes[count++].dmDeviceName, display_device.DeviceName); + } + + *original_modes = modes; + *display_count = count; + return TRUE; +} + +static BOOL restore_display_modes(DEVMODEW *modes, unsigned int count) +{ + unsigned int index; + LONG ret; + + for (index = 0; index < count; ++index) + { + ret = ChangeDisplaySettingsExW(modes[index].dmDeviceName, &modes[index], NULL, + CDS_UPDATEREGISTRY | CDS_NORESET, NULL); + if (ret != DISP_CHANGE_SUCCESSFUL) + return FALSE; + } + ret = ChangeDisplaySettingsExW(NULL, NULL, NULL, 0, NULL); + return ret == DISP_CHANGE_SUCCESSFUL; +} + static HRESULT set_display_mode(IDirectDraw *ddraw, DWORD width, DWORD height) { if (SUCCEEDED(IDirectDraw_SetDisplayMode(ddraw, width, height, 32))) @@ -1301,6 +1364,14 @@ static void test_coop_level_threaded(void) IDirectDraw_Release(ddraw); } +static BOOL compare_mode_rect(const DEVMODEW *mode1, const DEVMODEW *mode2) +{ + return mode1->dmPosition.x == mode2->dmPosition.x + && mode1->dmPosition.y == mode2->dmPosition.y + && mode1->dmPelsWidth == mode2->dmPelsWidth + && mode1->dmPelsHeight == mode2->dmPelsHeight; +} + static ULONG get_refcount(IUnknown *test_iface) { IUnknown_AddRef(test_iface); @@ -2630,6 +2701,8 @@ static HRESULT CALLBACK test_coop_level_mode_set_enum_cb(DDSURFACEDESC *surface_ static void test_coop_level_mode_set(void) { + DEVMODEW *original_modes = NULL, devmode, devmode2; + unsigned int display_count = 0; IDirectDrawSurface *primary; RECT registry_rect, ddraw_rect, user32_rect, r; IDirectDraw *ddraw; @@ -2640,7 +2713,6 @@ static void test_coop_level_mode_set(void) ULONG ref; MSG msg; struct test_coop_level_mode_set_enum_param param; - DEVMODEW devmode; BOOL ret; LONG change_ret; @@ -2716,6 +2788,18 @@ static void test_coop_level_mode_set(void) {0, FALSE, 0}, }; + memset(&devmode, 0, sizeof(devmode)); + devmode.dmSize = sizeof(devmode); + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode, ®istry_mode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode, ®istry_mode), "Got a different mode.\n"); + + ret = save_display_modes(&original_modes, &display_count); + ok(ret, "Failed to save original display modes.\n"); + ddraw = create_ddraw(); ok(!!ddraw, "Failed to create a ddraw object.\n"); @@ -2728,6 +2812,7 @@ static void test_coop_level_mode_set(void) if (!param.user32_height) { skip("Fewer than 3 different modes supported, skipping mode restore test.\n"); + heap_free(original_modes); return; } @@ -3315,19 +3400,125 @@ static void test_coop_level_mode_set(void) ok(EqualRect(&r, &ddraw_rect), "Expected %s, got %s.\n", wine_dbgstr_rect(&ddraw_rect), wine_dbgstr_rect(&r)); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that no mode restorations if no mode changes happened */ + devmode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; + devmode.dmPelsWidth = param.user32_width; + devmode.dmPelsHeight = param.user32_height; + change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret); + + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + ref = IDirectDraw_Release(ddraw); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + memset(&devmode2, 0, sizeof(devmode2)); + devmode2.dmSize = sizeof(devmode2); + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, ®istry_mode), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that no mode restorations if no mode changes happened with fullscreen ddraw objects */ + change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret); + + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + hr = IDirectDraw_SetCooperativeLevel(ddraw, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + ok(hr == DD_OK, "SetCooperativeLevel failed, hr %#x.\n", hr); + hr = IDirectDraw_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL); + ok(hr == DD_OK, "SetCooperativeLevel failed, hr %#x.\n", hr); + ref = IDirectDraw_Release(ddraw); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, ®istry_mode), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations use display settings in the registry after ddraw object releases + * if SetDisplayMode() was called */ + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw, registry_mode.dmPelsWidth, registry_mode.dmPelsHeight); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret); + + ref = IDirectDraw_Release(ddraw); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &devmode), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations use display settings in the registry after RestoreDisplayMode() */ + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw, param.ddraw_width, param.ddraw_height); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret); + + hr = IDirectDraw_RestoreDisplayMode(ddraw); + ok(hr == DD_OK, "RestoreDisplayMode failed, hr %#x.\n", hr); + + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &devmode), "Got a different mode.\n"); + + ref = IDirectDraw_Release(ddraw); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + done: expect_messages = NULL; DestroyWindow(window); UnregisterClassA("ddraw_test_wndproc_wc", GetModuleHandleA(NULL)); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + heap_free(original_modes); } static void test_coop_level_mode_set_multi(void) { + DEVMODEW old_devmode, devmode, devmode2, devmode3, *original_modes = NULL; + unsigned int mode_idx = 0, display_idx, display_count = 0; + WCHAR second_monitor_name[CCHDEVICENAME]; IDirectDraw *ddraw1, *ddraw2; + LONG change_ret; UINT w, h; HWND window; HRESULT hr; ULONG ref; + BOOL ret; + + memset(&devmode, 0, sizeof(devmode)); + devmode.dmSize = sizeof(devmode); + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode, ®istry_mode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode, ®istry_mode), "Got a different mode.\n"); + + ret = save_display_modes(&original_modes, &display_count); + ok(ret, "Failed to save original display modes.\n"); window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, 0, 0, 0, 0); @@ -3509,7 +3700,210 @@ static void test_coop_level_mode_set_multi(void) h = GetSystemMetrics(SM_CYSCREEN); ok(h == registry_mode.dmPelsHeight, "Got unexpected screen height %u.\n", h); + if (display_count < 2) + { + skip("Following tests require two monitors.\n"); + goto done; + } + + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + second_monitor_name[0] = '\0'; + for (display_idx = 0; display_idx < display_count; ++display_idx) + { + if (original_modes[display_idx].dmPosition.x || original_modes[display_idx].dmPosition.y) + { + lstrcpyW(second_monitor_name, original_modes[display_idx].dmDeviceName); + break; + } + } + ok(lstrlenW(second_monitor_name), "Got an empty second monitor name.\n"); + memset(&old_devmode, 0, sizeof(old_devmode)); + old_devmode.dmSize = sizeof(old_devmode); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &old_devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + + devmode = old_devmode; + while (EnumDisplaySettingsW(second_monitor_name, mode_idx++, &devmode)) + { + if (devmode.dmPelsWidth != old_devmode.dmPelsWidth + || devmode.dmPelsHeight != old_devmode.dmPelsHeight) + break; + } + ok(devmode.dmPelsWidth != old_devmode.dmPelsWidth + || devmode.dmPelsHeight != old_devmode.dmPelsHeight, + "Failed to find a different mode for the second monitor.\n"); + + /* Test that no mode restorations for non-primary monitors if SetDisplayMode() was not called */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + hr = IDirectDraw_SetCooperativeLevel(ddraw1, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + ok(hr == DD_OK, "SetCooperativeLevel failed, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + memset(&devmode2, 0, sizeof(devmode2)); + devmode2.dmSize = sizeof(devmode2); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + if (compare_mode_rect(&devmode2, &old_devmode)) + { + skip("Failed to change display settings of the second monitor.\n"); + ref = IDirectDraw_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + goto done; + } + + hr = IDirectDraw_SetCooperativeLevel(ddraw1, window, DDSCL_NORMAL); + ok(hr == DD_OK, "SetCooperativeLevel failed, hr %#x.\n", hr); + ref = IDirectDraw_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + memset(&devmode3, 0, sizeof(devmode3)); + devmode3.dmSize = sizeof(devmode3); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode3); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode3, &devmode2), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations happen for non-primary monitors on ddraw releases if + * SetDisplayMode() was called */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + ref = IDirectDraw_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations happen for non-primary monitors as well */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + hr = IDirectDraw_RestoreDisplayMode(ddraw1); + ok(hr == DD_OK, "RestoreDisplayMode failed, hr %#x.\n", hr); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + + ref = IDirectDraw_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations for non-primary monitors use display settings in the registry */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, + CDS_UPDATEREGISTRY | CDS_NORESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + ref = IDirectDraw_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(devmode2.dmPelsWidth == devmode.dmPelsWidth && devmode2.dmPelsHeight == devmode.dmPelsHeight, + "Expected resolution %ux%u, got %ux%u.\n", devmode.dmPelsWidth, devmode.dmPelsHeight, + devmode2.dmPelsWidth, devmode2.dmPelsHeight); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(devmode2.dmPelsWidth == devmode.dmPelsWidth && devmode2.dmPelsHeight == devmode.dmPelsHeight, + "Expected resolution %ux%u, got %ux%u.\n", devmode.dmPelsWidth, devmode.dmPelsHeight, + devmode2.dmPelsWidth, devmode2.dmPelsHeight); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test mode restorations for non-primary monitors when there are multiple fullscreen ddraw + * objects and one of them restores display mode */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + ddraw2 = create_ddraw(); + ok(!!ddraw2, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + hr = set_display_mode(ddraw2, 640, 480); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + hr = IDirectDraw_RestoreDisplayMode(ddraw2); + ok(hr == DD_OK, "RestoreDisplayMode failed, hr %#x.\n", hr); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + + ref = IDirectDraw_Release(ddraw2); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + ref = IDirectDraw_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test mode restorations for non-primary monitors when there are multiple fullscreen ddraw + * objects and one of them got released */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + ddraw2 = create_ddraw(); + ok(!!ddraw2, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + hr = set_display_mode(ddraw2, 640, 480); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + ref = IDirectDraw_Release(ddraw2); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + + ref = IDirectDraw_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + +done: DestroyWindow(window); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + heap_free(original_modes); } static void test_initialize(void) diff --git a/dlls/ddraw/tests/ddraw2.c b/dlls/ddraw/tests/ddraw2.c index 4b83230de1f..23d3e0c1f02 100644 --- a/dlls/ddraw/tests/ddraw2.c +++ b/dlls/ddraw/tests/ddraw2.c @@ -22,6 +22,7 @@ #define COBJMACROS #include "wine/test.h" +#include "wine/heap.h" #include #include #include "ddrawi.h" @@ -261,6 +262,69 @@ static IDirectDrawSurface *get_depth_stencil(IDirect3DDevice2 *device) return ret; } +/* Free original_modes after finished using it */ +static BOOL save_display_modes(DEVMODEW **original_modes, unsigned int *display_count) +{ + unsigned int number, size = 2, count = 0, index = 0; + DISPLAY_DEVICEW display_device; + DEVMODEW *modes, *tmp; + + if (!(modes = heap_alloc(size * sizeof(*modes)))) + return FALSE; + + display_device.cb = sizeof(display_device); + while (EnumDisplayDevicesW(NULL, index++, &display_device, 0)) + { + /* Skip software devices */ + if (swscanf(display_device.DeviceName, L"\\\\.\\DISPLAY%u", &number) != 1) + continue; + + if (!(display_device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) + continue; + + if (count >= size) + { + size *= 2; + if (!(tmp = heap_realloc(modes, size * sizeof(*modes)))) + { + heap_free(modes); + return FALSE; + } + modes = tmp; + } + + memset(&modes[count], 0, sizeof(modes[count])); + modes[count].dmSize = sizeof(modes[count]); + if (!EnumDisplaySettingsW(display_device.DeviceName, ENUM_CURRENT_SETTINGS, &modes[count])) + { + heap_free(modes); + return FALSE; + } + + lstrcpyW(modes[count++].dmDeviceName, display_device.DeviceName); + } + + *original_modes = modes; + *display_count = count; + return TRUE; +} + +static BOOL restore_display_modes(DEVMODEW *modes, unsigned int count) +{ + unsigned int index; + LONG ret; + + for (index = 0; index < count; ++index) + { + ret = ChangeDisplaySettingsExW(modes[index].dmDeviceName, &modes[index], NULL, + CDS_UPDATEREGISTRY | CDS_NORESET, NULL); + if (ret != DISP_CHANGE_SUCCESSFUL) + return FALSE; + } + ret = ChangeDisplaySettingsExW(NULL, NULL, NULL, 0, NULL); + return ret == DISP_CHANGE_SUCCESSFUL; +} + static HRESULT set_display_mode(IDirectDraw2 *ddraw, DWORD width, DWORD height) { if (SUCCEEDED(IDirectDraw2_SetDisplayMode(ddraw, width, height, 32, 0, 0))) @@ -1458,6 +1522,14 @@ done: if (ddraw) IDirectDraw2_Release(ddraw); } +static BOOL compare_mode_rect(const DEVMODEW *mode1, const DEVMODEW *mode2) +{ + return mode1->dmPosition.x == mode2->dmPosition.x + && mode1->dmPosition.y == mode2->dmPosition.y + && mode1->dmPelsWidth == mode2->dmPelsWidth + && mode1->dmPelsHeight == mode2->dmPelsHeight; +} + static ULONG get_refcount(IUnknown *test_iface) { IUnknown_AddRef(test_iface); @@ -2694,6 +2766,8 @@ static HRESULT CALLBACK test_coop_level_mode_set_enum_cb(DDSURFACEDESC *surface_ static void test_coop_level_mode_set(void) { + DEVMODEW *original_modes = NULL, devmode, devmode2; + unsigned int display_count = 0; IDirectDrawSurface *primary; RECT registry_rect, ddraw_rect, user32_rect, r; IDirectDraw2 *ddraw; @@ -2704,7 +2778,6 @@ static void test_coop_level_mode_set(void) ULONG ref; MSG msg; struct test_coop_level_mode_set_enum_param param; - DEVMODEW devmode; BOOL ret; LONG change_ret; @@ -2780,6 +2853,18 @@ static void test_coop_level_mode_set(void) {0, FALSE, 0}, }; + memset(&devmode, 0, sizeof(devmode)); + devmode.dmSize = sizeof(devmode); + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode, ®istry_mode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode, ®istry_mode), "Got a different mode.\n"); + + ret = save_display_modes(&original_modes, &display_count); + ok(ret, "Failed to save original display modes.\n"); + ddraw = create_ddraw(); ok(!!ddraw, "Failed to create a ddraw object.\n"); @@ -2792,6 +2877,7 @@ static void test_coop_level_mode_set(void) if (!param.user32_height) { skip("Fewer than 3 different modes supported, skipping mode restore test.\n"); + heap_free(original_modes); return; } @@ -3472,21 +3558,127 @@ static void test_coop_level_mode_set(void) ok(EqualRect(&r, &ddraw_rect), "Expected %s, got %s.\n", wine_dbgstr_rect(&ddraw_rect), wine_dbgstr_rect(&r)); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that no mode restorations if no mode changes happened */ + devmode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; + devmode.dmPelsWidth = param.user32_width; + devmode.dmPelsHeight = param.user32_height; + change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret); + + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + ref = IDirectDraw2_Release(ddraw); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + memset(&devmode2, 0, sizeof(devmode2)); + devmode2.dmSize = sizeof(devmode2); + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, ®istry_mode), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that no mode restorations if no mode changes happened with fullscreen ddraw objects */ + change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret); + + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + hr = IDirectDraw2_SetCooperativeLevel(ddraw, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + ok(hr == DD_OK, "SetCooperativeLevel failed, hr %#x.\n", hr); + hr = IDirectDraw2_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL); + ok(hr == DD_OK, "SetCooperativeLevel failed, hr %#x.\n", hr); + ref = IDirectDraw2_Release(ddraw); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, ®istry_mode), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations use display settings in the registry after ddraw object releases + * if SetDisplayMode() was called */ + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw, registry_mode.dmPelsWidth, registry_mode.dmPelsHeight); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret); + + ref = IDirectDraw2_Release(ddraw); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &devmode), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations use display settings in the registry after RestoreDisplayMode() */ + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw, param.ddraw_width, param.ddraw_height); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret); + + hr = IDirectDraw2_RestoreDisplayMode(ddraw); + ok(hr == DD_OK, "RestoreDisplayMode failed, hr %#x.\n", hr); + + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &devmode), "Got a different mode.\n"); + + ref = IDirectDraw2_Release(ddraw); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + done: expect_messages = NULL; DestroyWindow(window); DestroyWindow(window2); UnregisterClassA("ddraw_test_wndproc_wc", GetModuleHandleA(NULL)); UnregisterClassA("ddraw_test_wndproc_wc2", GetModuleHandleA(NULL)); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + heap_free(original_modes); } static void test_coop_level_mode_set_multi(void) { + DEVMODEW old_devmode, devmode, devmode2, devmode3, *original_modes = NULL; + unsigned int mode_idx = 0, display_idx, display_count = 0; + WCHAR second_monitor_name[CCHDEVICENAME]; IDirectDraw2 *ddraw1, *ddraw2; + LONG change_ret; UINT w, h; HWND window; HRESULT hr; ULONG ref; + BOOL ret; + + memset(&devmode, 0, sizeof(devmode)); + devmode.dmSize = sizeof(devmode); + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode, ®istry_mode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode, ®istry_mode), "Got a different mode.\n"); + + ret = save_display_modes(&original_modes, &display_count); + ok(ret, "Failed to save original display modes.\n"); window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, 0, 0, 0, 0); @@ -3668,7 +3860,210 @@ static void test_coop_level_mode_set_multi(void) h = GetSystemMetrics(SM_CYSCREEN); ok(h == registry_mode.dmPelsHeight, "Got unexpected screen height %u.\n", h); + if (display_count < 2) + { + skip("Following tests require two monitors.\n"); + goto done; + } + + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + second_monitor_name[0] = '\0'; + for (display_idx = 0; display_idx < display_count; ++display_idx) + { + if (original_modes[display_idx].dmPosition.x || original_modes[display_idx].dmPosition.y) + { + lstrcpyW(second_monitor_name, original_modes[display_idx].dmDeviceName); + break; + } + } + ok(lstrlenW(second_monitor_name), "Got an empty second monitor name.\n"); + memset(&old_devmode, 0, sizeof(old_devmode)); + old_devmode.dmSize = sizeof(old_devmode); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &old_devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + + devmode = old_devmode; + while (EnumDisplaySettingsW(second_monitor_name, mode_idx++, &devmode)) + { + if (devmode.dmPelsWidth != old_devmode.dmPelsWidth + || devmode.dmPelsHeight != old_devmode.dmPelsHeight) + break; + } + ok(devmode.dmPelsWidth != old_devmode.dmPelsWidth + || devmode.dmPelsHeight != old_devmode.dmPelsHeight, + "Failed to find a different mode for the second monitor.\n"); + + /* Test that no mode restorations for non-primary monitors if SetDisplayMode() was not called */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + hr = IDirectDraw2_SetCooperativeLevel(ddraw1, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + ok(hr == DD_OK, "SetCooperativeLevel failed, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + memset(&devmode2, 0, sizeof(devmode2)); + devmode2.dmSize = sizeof(devmode2); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + if (compare_mode_rect(&devmode2, &old_devmode)) + { + skip("Failed to change display settings of the second monitor.\n"); + ref = IDirectDraw2_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + goto done; + } + + hr = IDirectDraw2_SetCooperativeLevel(ddraw1, window, DDSCL_NORMAL); + ok(hr == DD_OK, "SetCooperativeLevel failed, hr %#x.\n", hr); + ref = IDirectDraw2_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + memset(&devmode3, 0, sizeof(devmode3)); + devmode3.dmSize = sizeof(devmode3); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode3); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode3, &devmode2), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations happen for non-primary monitors on ddraw releases if + * SetDisplayMode() was called */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + ref = IDirectDraw2_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations happen for non-primary monitors as well */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + hr = IDirectDraw2_RestoreDisplayMode(ddraw1); + ok(hr == DD_OK, "RestoreDisplayMode failed, hr %#x.\n", hr); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + + ref = IDirectDraw2_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations for non-primary monitors use display settings in the registry */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, + CDS_UPDATEREGISTRY | CDS_NORESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + ref = IDirectDraw2_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(devmode2.dmPelsWidth == devmode.dmPelsWidth && devmode2.dmPelsHeight == devmode.dmPelsHeight, + "Expected resolution %ux%u, got %ux%u.\n", devmode.dmPelsWidth, devmode.dmPelsHeight, + devmode2.dmPelsWidth, devmode2.dmPelsHeight); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(devmode2.dmPelsWidth == devmode.dmPelsWidth && devmode2.dmPelsHeight == devmode.dmPelsHeight, + "Expected resolution %ux%u, got %ux%u.\n", devmode.dmPelsWidth, devmode.dmPelsHeight, + devmode2.dmPelsWidth, devmode2.dmPelsHeight); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test mode restorations for non-primary monitors when there are multiple fullscreen ddraw + * objects and one of them restores display mode */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + ddraw2 = create_ddraw(); + ok(!!ddraw2, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + hr = set_display_mode(ddraw2, 640, 480); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + hr = IDirectDraw2_RestoreDisplayMode(ddraw2); + ok(hr == DD_OK, "RestoreDisplayMode failed, hr %#x.\n", hr); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + + ref = IDirectDraw2_Release(ddraw2); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + ref = IDirectDraw2_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test mode restorations for non-primary monitors when there are multiple fullscreen ddraw + * objects and one of them got released */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + ddraw2 = create_ddraw(); + ok(!!ddraw2, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + hr = set_display_mode(ddraw2, 640, 480); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + ref = IDirectDraw2_Release(ddraw2); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + + ref = IDirectDraw2_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + +done: DestroyWindow(window); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + heap_free(original_modes); } static void test_initialize(void) diff --git a/dlls/ddraw/tests/ddraw4.c b/dlls/ddraw/tests/ddraw4.c index 72aad35b1e1..12cfbc3784f 100644 --- a/dlls/ddraw/tests/ddraw4.c +++ b/dlls/ddraw/tests/ddraw4.c @@ -263,6 +263,69 @@ static IDirectDrawSurface4 *get_depth_stencil(IDirect3DDevice3 *device) return ret; } +/* Free original_modes after finished using it */ +static BOOL save_display_modes(DEVMODEW **original_modes, unsigned int *display_count) +{ + unsigned int number, size = 2, count = 0, index = 0; + DISPLAY_DEVICEW display_device; + DEVMODEW *modes, *tmp; + + if (!(modes = heap_alloc(size * sizeof(*modes)))) + return FALSE; + + display_device.cb = sizeof(display_device); + while (EnumDisplayDevicesW(NULL, index++, &display_device, 0)) + { + /* Skip software devices */ + if (swscanf(display_device.DeviceName, L"\\\\.\\DISPLAY%u", &number) != 1) + continue; + + if (!(display_device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) + continue; + + if (count >= size) + { + size *= 2; + if (!(tmp = heap_realloc(modes, size * sizeof(*modes)))) + { + heap_free(modes); + return FALSE; + } + modes = tmp; + } + + memset(&modes[count], 0, sizeof(modes[count])); + modes[count].dmSize = sizeof(modes[count]); + if (!EnumDisplaySettingsW(display_device.DeviceName, ENUM_CURRENT_SETTINGS, &modes[count])) + { + heap_free(modes); + return FALSE; + } + + lstrcpyW(modes[count++].dmDeviceName, display_device.DeviceName); + } + + *original_modes = modes; + *display_count = count; + return TRUE; +} + +static BOOL restore_display_modes(DEVMODEW *modes, unsigned int count) +{ + unsigned int index; + LONG ret; + + for (index = 0; index < count; ++index) + { + ret = ChangeDisplaySettingsExW(modes[index].dmDeviceName, &modes[index], NULL, + CDS_UPDATEREGISTRY | CDS_NORESET, NULL); + if (ret != DISP_CHANGE_SUCCESSFUL) + return FALSE; + } + ret = ChangeDisplaySettingsExW(NULL, NULL, NULL, 0, NULL); + return ret == DISP_CHANGE_SUCCESSFUL; +} + static HRESULT set_display_mode(IDirectDraw4 *ddraw, DWORD width, DWORD height) { if (SUCCEEDED(IDirectDraw4_SetDisplayMode(ddraw, width, height, 32, 0, 0))) @@ -1628,6 +1691,14 @@ static void test_texture_load_ckey(void) IDirectDraw4_Release(ddraw); } +static BOOL compare_mode_rect(const DEVMODEW *mode1, const DEVMODEW *mode2) +{ + return mode1->dmPosition.x == mode2->dmPosition.x + && mode1->dmPosition.y == mode2->dmPosition.y + && mode1->dmPelsWidth == mode2->dmPelsWidth + && mode1->dmPelsHeight == mode2->dmPelsHeight; +} + static ULONG get_refcount(IUnknown *test_iface) { IUnknown_AddRef(test_iface); @@ -2935,6 +3006,8 @@ static HRESULT CALLBACK test_coop_level_mode_set_enum_cb(DDSURFACEDESC2 *surface static void test_coop_level_mode_set(void) { + DEVMODEW *original_modes = NULL, devmode, devmode2; + unsigned int display_count = 0; IDirectDrawSurface4 *primary; RECT registry_rect, ddraw_rect, user32_rect, r; IDirectDraw4 *ddraw; @@ -2945,7 +3018,6 @@ static void test_coop_level_mode_set(void) ULONG ref; MSG msg; struct test_coop_level_mode_set_enum_param param; - DEVMODEW devmode; BOOL ret; LONG change_ret; @@ -3021,6 +3093,18 @@ static void test_coop_level_mode_set(void) {0, FALSE, 0}, }; + memset(&devmode, 0, sizeof(devmode)); + devmode.dmSize = sizeof(devmode); + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode, ®istry_mode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode, ®istry_mode), "Got a different mode.\n"); + + ret = save_display_modes(&original_modes, &display_count); + ok(ret, "Failed to save original display modes.\n"); + ddraw = create_ddraw(); ok(!!ddraw, "Failed to create a ddraw object.\n"); @@ -3033,6 +3117,7 @@ static void test_coop_level_mode_set(void) if (!param.user32_height) { skip("Fewer than 3 different modes supported, skipping mode restore test.\n"); + heap_free(original_modes); return; } @@ -3709,20 +3794,126 @@ static void test_coop_level_mode_set(void) ok(EqualRect(&r, &ddraw_rect), "Expected %s, got %s.\n", wine_dbgstr_rect(&ddraw_rect), wine_dbgstr_rect(&r)); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that no mode restorations if no mode changes happened */ + devmode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; + devmode.dmPelsWidth = param.user32_width; + devmode.dmPelsHeight = param.user32_height; + change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret); + + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + ref = IDirectDraw4_Release(ddraw); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + memset(&devmode2, 0, sizeof(devmode2)); + devmode2.dmSize = sizeof(devmode2); + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, ®istry_mode), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that no mode restorations if no mode changes happened with fullscreen ddraw objects */ + change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret); + + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + hr = IDirectDraw4_SetCooperativeLevel(ddraw, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + ok(hr == DD_OK, "SetCooperativeLevel failed, hr %#x.\n", hr); + hr = IDirectDraw4_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL); + ok(hr == DD_OK, "SetCooperativeLevel failed, hr %#x.\n", hr); + ref = IDirectDraw4_Release(ddraw); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, ®istry_mode), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations use display settings in the registry after ddraw object releases + * if SetDisplayMode() was called */ + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw, registry_mode.dmPelsWidth, registry_mode.dmPelsHeight); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret); + + ref = IDirectDraw4_Release(ddraw); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &devmode), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations use display settings in the registry after RestoreDisplayMode() */ + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw, param.ddraw_width, param.ddraw_height); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret); + + hr = IDirectDraw4_RestoreDisplayMode(ddraw); + ok(hr == DD_OK, "RestoreDisplayMode failed, hr %#x.\n", hr); + + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &devmode), "Got a different mode.\n"); + + ref = IDirectDraw4_Release(ddraw); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + expect_messages = NULL; DestroyWindow(window); DestroyWindow(window2); UnregisterClassA("ddraw_test_wndproc_wc", GetModuleHandleA(NULL)); UnregisterClassA("ddraw_test_wndproc_wc2", GetModuleHandleA(NULL)); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + heap_free(original_modes); } static void test_coop_level_mode_set_multi(void) { + DEVMODEW old_devmode, devmode, devmode2, devmode3, *original_modes = NULL; + unsigned int mode_idx = 0, display_idx, display_count = 0; + WCHAR second_monitor_name[CCHDEVICENAME]; IDirectDraw4 *ddraw1, *ddraw2; + LONG change_ret; UINT w, h; HWND window; HRESULT hr; ULONG ref; + BOOL ret; + + memset(&devmode, 0, sizeof(devmode)); + devmode.dmSize = sizeof(devmode); + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode, ®istry_mode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode, ®istry_mode), "Got a different mode.\n"); + + ret = save_display_modes(&original_modes, &display_count); + ok(ret, "Failed to save original display modes.\n"); window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, 0, 0, 0, 0); @@ -3897,7 +4088,210 @@ static void test_coop_level_mode_set_multi(void) h = GetSystemMetrics(SM_CYSCREEN); ok(h == registry_mode.dmPelsHeight, "Got unexpected screen height %u.\n", h); + if (display_count < 2) + { + skip("Following tests require two monitors.\n"); + goto done; + } + + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + second_monitor_name[0] = '\0'; + for (display_idx = 0; display_idx < display_count; ++display_idx) + { + if (original_modes[display_idx].dmPosition.x || original_modes[display_idx].dmPosition.y) + { + lstrcpyW(second_monitor_name, original_modes[display_idx].dmDeviceName); + break; + } + } + ok(lstrlenW(second_monitor_name), "Got an empty second monitor name.\n"); + memset(&old_devmode, 0, sizeof(old_devmode)); + old_devmode.dmSize = sizeof(old_devmode); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &old_devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + + devmode = old_devmode; + while (EnumDisplaySettingsW(second_monitor_name, mode_idx++, &devmode)) + { + if (devmode.dmPelsWidth != old_devmode.dmPelsWidth + || devmode.dmPelsHeight != old_devmode.dmPelsHeight) + break; + } + ok(devmode.dmPelsWidth != old_devmode.dmPelsWidth + || devmode.dmPelsHeight != old_devmode.dmPelsHeight, + "Failed to find a different mode for the second monitor.\n"); + + /* Test that no mode restorations for non-primary monitors if SetDisplayMode() was not called */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + hr = IDirectDraw4_SetCooperativeLevel(ddraw1, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + ok(hr == DD_OK, "SetCooperativeLevel failed, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + memset(&devmode2, 0, sizeof(devmode2)); + devmode2.dmSize = sizeof(devmode2); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + if (compare_mode_rect(&devmode2, &old_devmode)) + { + skip("Failed to change display settings of the second monitor.\n"); + ref = IDirectDraw4_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + goto done; + } + + hr = IDirectDraw4_SetCooperativeLevel(ddraw1, window, DDSCL_NORMAL); + ok(hr == DD_OK, "SetCooperativeLevel failed, hr %#x.\n", hr); + ref = IDirectDraw4_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + memset(&devmode3, 0, sizeof(devmode3)); + devmode3.dmSize = sizeof(devmode3); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode3); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode3, &devmode2), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations happen for non-primary monitors on ddraw releases if + * SetDisplayMode() was called */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + ref = IDirectDraw4_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations happen for non-primary monitors as well */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + hr = IDirectDraw4_RestoreDisplayMode(ddraw1); + ok(hr == DD_OK, "RestoreDisplayMode failed, hr %#x.\n", hr); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + + ref = IDirectDraw4_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations for non-primary monitors use display settings in the registry */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, + CDS_UPDATEREGISTRY | CDS_NORESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + ref = IDirectDraw4_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(devmode2.dmPelsWidth == devmode.dmPelsWidth && devmode2.dmPelsHeight == devmode.dmPelsHeight, + "Expected resolution %ux%u, got %ux%u.\n", devmode.dmPelsWidth, devmode.dmPelsHeight, + devmode2.dmPelsWidth, devmode2.dmPelsHeight); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(devmode2.dmPelsWidth == devmode.dmPelsWidth && devmode2.dmPelsHeight == devmode.dmPelsHeight, + "Expected resolution %ux%u, got %ux%u.\n", devmode.dmPelsWidth, devmode.dmPelsHeight, + devmode2.dmPelsWidth, devmode2.dmPelsHeight); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test mode restorations for non-primary monitors when there are multiple fullscreen ddraw + * objects and one of them restores display mode */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + ddraw2 = create_ddraw(); + ok(!!ddraw2, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + hr = set_display_mode(ddraw2, 640, 480); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + hr = IDirectDraw4_RestoreDisplayMode(ddraw2); + ok(hr == DD_OK, "RestoreDisplayMode failed, hr %#x.\n", hr); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + + ref = IDirectDraw4_Release(ddraw2); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + ref = IDirectDraw4_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test mode restorations for non-primary monitors when there are multiple fullscreen ddraw + * objects and one of them got released */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + ddraw2 = create_ddraw(); + ok(!!ddraw2, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + hr = set_display_mode(ddraw2, 640, 480); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + ref = IDirectDraw4_Release(ddraw2); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + + ref = IDirectDraw4_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + +done: DestroyWindow(window); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + heap_free(original_modes); } static void test_initialize(void) diff --git a/dlls/ddraw/tests/ddraw7.c b/dlls/ddraw/tests/ddraw7.c index e85b7d76369..c214cbc221e 100644 --- a/dlls/ddraw/tests/ddraw7.c +++ b/dlls/ddraw/tests/ddraw7.c @@ -102,6 +102,14 @@ static BOOL compare_color(D3DCOLOR c1, D3DCOLOR c2, BYTE max_diff) && compare_uint((c1 >> 24) & 0xff, (c2 >> 24) & 0xff, max_diff); } +static BOOL compare_mode_rect(const DEVMODEW *mode1, const DEVMODEW *mode2) +{ + return mode1->dmPosition.x == mode2->dmPosition.x + && mode1->dmPosition.y == mode2->dmPosition.y + && mode1->dmPelsWidth == mode2->dmPelsWidth + && mode1->dmPelsHeight == mode2->dmPelsHeight; +} + static ULONG get_refcount(IUnknown *iface) { IUnknown_AddRef(iface); @@ -281,6 +289,69 @@ static IDirectDrawSurface7 *get_depth_stencil(IDirect3DDevice7 *device) return ret; } +/* Free original_modes after finished using it */ +static BOOL save_display_modes(DEVMODEW **original_modes, unsigned int *display_count) +{ + unsigned int number, size = 2, count = 0, index = 0; + DISPLAY_DEVICEW display_device; + DEVMODEW *modes, *tmp; + + if (!(modes = heap_alloc(size * sizeof(*modes)))) + return FALSE; + + display_device.cb = sizeof(display_device); + while (EnumDisplayDevicesW(NULL, index++, &display_device, 0)) + { + /* Skip software devices */ + if (swscanf(display_device.DeviceName, L"\\\\.\\DISPLAY%u", &number) != 1) + continue; + + if (!(display_device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) + continue; + + if (count >= size) + { + size *= 2; + if (!(tmp = heap_realloc(modes, size * sizeof(*modes)))) + { + heap_free(modes); + return FALSE; + } + modes = tmp; + } + + memset(&modes[count], 0, sizeof(modes[count])); + modes[count].dmSize = sizeof(modes[count]); + if (!EnumDisplaySettingsW(display_device.DeviceName, ENUM_CURRENT_SETTINGS, &modes[count])) + { + heap_free(modes); + return FALSE; + } + + lstrcpyW(modes[count++].dmDeviceName, display_device.DeviceName); + } + + *original_modes = modes; + *display_count = count; + return TRUE; +} + +static BOOL restore_display_modes(DEVMODEW *modes, unsigned int count) +{ + unsigned int index; + LONG ret; + + for (index = 0; index < count; ++index) + { + ret = ChangeDisplaySettingsExW(modes[index].dmDeviceName, &modes[index], NULL, + CDS_UPDATEREGISTRY | CDS_NORESET, NULL); + if (ret != DISP_CHANGE_SUCCESSFUL) + return FALSE; + } + ret = ChangeDisplaySettingsExW(NULL, NULL, NULL, 0, NULL); + return ret == DISP_CHANGE_SUCCESSFUL; +} + static HRESULT set_display_mode(IDirectDraw7 *ddraw, DWORD width, DWORD height) { if (SUCCEEDED(IDirectDraw7_SetDisplayMode(ddraw, width, height, 32, 0, 0))) @@ -2582,6 +2653,8 @@ static HRESULT CALLBACK test_coop_level_mode_set_enum_cb(DDSURFACEDESC2 *surface static void test_coop_level_mode_set(void) { + DEVMODEW *original_modes = NULL, devmode, devmode2; + unsigned int display_count = 0; IDirectDrawSurface7 *primary; RECT registry_rect, ddraw_rect, user32_rect, r; IDirectDraw7 *ddraw; @@ -2592,7 +2665,6 @@ static void test_coop_level_mode_set(void) ULONG ref; MSG msg; struct test_coop_level_mode_set_enum_param param; - DEVMODEW devmode; BOOL ret; LONG change_ret; @@ -2668,6 +2740,18 @@ static void test_coop_level_mode_set(void) {0, FALSE, 0}, }; + memset(&devmode, 0, sizeof(devmode)); + devmode.dmSize = sizeof(devmode); + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode, ®istry_mode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode, ®istry_mode), "Got a different mode.\n"); + + ret = save_display_modes(&original_modes, &display_count); + ok(ret, "Failed to save original display modes.\n"); + ddraw = create_ddraw(); ok(!!ddraw, "Failed to create a ddraw object.\n"); @@ -2680,6 +2764,7 @@ static void test_coop_level_mode_set(void) if (!param.user32_height) { skip("Fewer than 3 different modes supported, skipping mode restore test.\n"); + heap_free(original_modes); return; } @@ -3356,20 +3441,126 @@ static void test_coop_level_mode_set(void) ok(EqualRect(&r, &ddraw_rect), "Expected %s, got %s.\n", wine_dbgstr_rect(&ddraw_rect), wine_dbgstr_rect(&r)); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that no mode restorations if no mode changes happened */ + devmode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; + devmode.dmPelsWidth = param.user32_width; + devmode.dmPelsHeight = param.user32_height; + change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret); + + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + ref = IDirectDraw7_Release(ddraw); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + memset(&devmode2, 0, sizeof(devmode2)); + devmode2.dmSize = sizeof(devmode2); + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, ®istry_mode), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that no mode restorations if no mode changes happened with fullscreen ddraw objects */ + change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret); + + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + hr = IDirectDraw7_SetCooperativeLevel(ddraw, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + ok(hr == DD_OK, "SetCooperativeLevel failed, hr %#x.\n", hr); + hr = IDirectDraw7_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL); + ok(hr == DD_OK, "SetCooperativeLevel failed, hr %#x.\n", hr); + ref = IDirectDraw7_Release(ddraw); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, ®istry_mode), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations use display settings in the registry after ddraw object releases + * if SetDisplayMode() was called */ + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw, registry_mode.dmPelsWidth, registry_mode.dmPelsHeight); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret); + + ref = IDirectDraw7_Release(ddraw); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &devmode), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations use display settings in the registry after RestoreDisplayMode() */ + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw, param.ddraw_width, param.ddraw_height); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret); + + hr = IDirectDraw7_RestoreDisplayMode(ddraw); + ok(hr == DD_OK, "RestoreDisplayMode failed, hr %#x.\n", hr); + + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &devmode), "Got a different mode.\n"); + + ref = IDirectDraw7_Release(ddraw); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + expect_messages = NULL; DestroyWindow(window); DestroyWindow(window2); UnregisterClassA("ddraw_test_wndproc_wc", GetModuleHandleA(NULL)); UnregisterClassA("ddraw_test_wndproc_wc2", GetModuleHandleA(NULL)); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + heap_free(original_modes); } static void test_coop_level_mode_set_multi(void) { + DEVMODEW old_devmode, devmode, devmode2, devmode3, *original_modes = NULL; + unsigned int mode_idx = 0, display_idx, display_count = 0; + WCHAR second_monitor_name[CCHDEVICENAME]; IDirectDraw7 *ddraw1, *ddraw2; + LONG change_ret; UINT w, h; HWND window; HRESULT hr; ULONG ref; + BOOL ret; + + memset(&devmode, 0, sizeof(devmode)); + devmode.dmSize = sizeof(devmode); + ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode, ®istry_mode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode, ®istry_mode), "Got a different mode.\n"); + + ret = save_display_modes(&original_modes, &display_count); + ok(ret, "Failed to save original display modes.\n"); window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, 0, 0, 0, 0); @@ -3544,7 +3735,210 @@ static void test_coop_level_mode_set_multi(void) h = GetSystemMetrics(SM_CYSCREEN); ok(h == registry_mode.dmPelsHeight, "Got unexpected screen height %u.\n", h); + if (display_count < 2) + { + skip("Following tests require two monitors.\n"); + goto done; + } + + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + second_monitor_name[0] = '\0'; + for (display_idx = 0; display_idx < display_count; ++display_idx) + { + if (original_modes[display_idx].dmPosition.x || original_modes[display_idx].dmPosition.y) + { + lstrcpyW(second_monitor_name, original_modes[display_idx].dmDeviceName); + break; + } + } + ok(lstrlenW(second_monitor_name), "Got an empty second monitor name.\n"); + memset(&old_devmode, 0, sizeof(old_devmode)); + old_devmode.dmSize = sizeof(old_devmode); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &old_devmode); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + + devmode = old_devmode; + while (EnumDisplaySettingsW(second_monitor_name, mode_idx++, &devmode)) + { + if (devmode.dmPelsWidth != old_devmode.dmPelsWidth + || devmode.dmPelsHeight != old_devmode.dmPelsHeight) + break; + } + ok(devmode.dmPelsWidth != old_devmode.dmPelsWidth + || devmode.dmPelsHeight != old_devmode.dmPelsHeight, + "Failed to find a different mode for the second monitor.\n"); + + /* Test that no mode restorations for non-primary monitors if SetDisplayMode() was not called */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + hr = IDirectDraw7_SetCooperativeLevel(ddraw1, window, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + ok(hr == DD_OK, "SetCooperativeLevel failed, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + memset(&devmode2, 0, sizeof(devmode2)); + devmode2.dmSize = sizeof(devmode2); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + if (compare_mode_rect(&devmode2, &old_devmode)) + { + skip("Failed to change display settings of the second monitor.\n"); + ref = IDirectDraw7_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + goto done; + } + + hr = IDirectDraw7_SetCooperativeLevel(ddraw1, window, DDSCL_NORMAL); + ok(hr == DD_OK, "SetCooperativeLevel failed, hr %#x.\n", hr); + ref = IDirectDraw7_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + memset(&devmode3, 0, sizeof(devmode3)); + devmode3.dmSize = sizeof(devmode3); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode3); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode3, &devmode2), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations happen for non-primary monitors on ddraw releases if + * SetDisplayMode() was called */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + ref = IDirectDraw7_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations happen for non-primary monitors as well */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + hr = IDirectDraw7_RestoreDisplayMode(ddraw1); + ok(hr == DD_OK, "RestoreDisplayMode failed, hr %#x.\n", hr); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + + ref = IDirectDraw7_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test that mode restorations for non-primary monitors use display settings in the registry */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, + CDS_UPDATEREGISTRY | CDS_NORESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + ref = IDirectDraw7_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(devmode2.dmPelsWidth == devmode.dmPelsWidth && devmode2.dmPelsHeight == devmode.dmPelsHeight, + "Expected resolution %ux%u, got %ux%u.\n", devmode.dmPelsWidth, devmode.dmPelsHeight, + devmode2.dmPelsWidth, devmode2.dmPelsHeight); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(devmode2.dmPelsWidth == devmode.dmPelsWidth && devmode2.dmPelsHeight == devmode.dmPelsHeight, + "Expected resolution %ux%u, got %ux%u.\n", devmode.dmPelsWidth, devmode.dmPelsHeight, + devmode2.dmPelsWidth, devmode2.dmPelsHeight); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test mode restorations for non-primary monitors when there are multiple fullscreen ddraw + * objects and one of them restores display mode */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + ddraw2 = create_ddraw(); + ok(!!ddraw2, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + hr = set_display_mode(ddraw2, 640, 480); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + hr = IDirectDraw7_RestoreDisplayMode(ddraw2); + ok(hr == DD_OK, "RestoreDisplayMode failed, hr %#x.\n", hr); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + + ref = IDirectDraw7_Release(ddraw2); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + ref = IDirectDraw7_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + + /* Test mode restorations for non-primary monitors when there are multiple fullscreen ddraw + * objects and one of them got released */ + ddraw1 = create_ddraw(); + ok(!!ddraw1, "Failed to create a ddraw object.\n"); + ddraw2 = create_ddraw(); + ok(!!ddraw2, "Failed to create a ddraw object.\n"); + hr = set_display_mode(ddraw1, 800, 600); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + hr = set_display_mode(ddraw2, 640, 480); + ok(hr == DD_OK, "Failed to set display mode, hr %#x.\n", hr); + + change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL); + ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret); + + ref = IDirectDraw7_Release(ddraw2); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + todo_wine ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2); + ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError()); + ok(compare_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n"); + + ref = IDirectDraw7_Release(ddraw1); + ok(ref == 0, "The ddraw object was not properly freed: refcount %u.\n", ref); + +done: DestroyWindow(window); + ret = restore_display_modes(original_modes, display_count); + ok(ret, "Failed to restore display modes.\n"); + heap_free(original_modes); } static void test_initialize(void)