1 /*
2 * Unit tests for monitor APIs
3 *
4 * Copyright 2005 Huw Davies
5 * Copyright 2008 Dmitry Timoshkov
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #include "wine/test.h"
23 #include "winbase.h"
24 #include "wingdi.h"
25 #include "winuser.h"
26 #include "winreg.h"
27 #include <stdio.h>
28
29 static HMODULE hdll;
30 static LONG (WINAPI *pChangeDisplaySettingsExA)(LPCSTR, LPDEVMODEA, HWND, DWORD, LPVOID);
31 static LONG (WINAPI *pChangeDisplaySettingsExW)(LPCWSTR, LPDEVMODEW, HWND, DWORD, LPVOID);
32 static BOOL (WINAPI *pEnumDisplayDevicesA)(LPCSTR,DWORD,LPDISPLAY_DEVICEA,DWORD);
33 static BOOL (WINAPI *pEnumDisplayMonitors)(HDC,LPRECT,MONITORENUMPROC,LPARAM);
34 static BOOL (WINAPI *pGetMonitorInfoA)(HMONITOR,LPMONITORINFO);
35 static BOOL (WINAPI *pGetMonitorInfoW)(HMONITOR,LPMONITORINFO);
36 static HMONITOR (WINAPI *pMonitorFromPoint)(POINT,DWORD);
37 static HMONITOR (WINAPI *pMonitorFromRect)(LPCRECT,DWORD);
38 static HMONITOR (WINAPI *pMonitorFromWindow)(HWND,DWORD);
39 static LONG (WINAPI *pGetDisplayConfigBufferSizes)(UINT32,UINT32*,UINT32*);
40
init_function_pointers(void)41 static void init_function_pointers(void)
42 {
43 hdll = GetModuleHandleA("user32.dll");
44
45 #define GET_PROC(func) \
46 p ## func = (void*)GetProcAddress(hdll, #func); \
47 if(!p ## func) \
48 trace("GetProcAddress(%s) failed\n", #func);
49
50 GET_PROC(ChangeDisplaySettingsExA)
51 GET_PROC(ChangeDisplaySettingsExW)
52 GET_PROC(EnumDisplayDevicesA)
53 GET_PROC(EnumDisplayMonitors)
54 GET_PROC(GetDisplayConfigBufferSizes)
55 GET_PROC(GetMonitorInfoA)
56 GET_PROC(GetMonitorInfoW)
57 GET_PROC(MonitorFromPoint)
58 GET_PROC(MonitorFromRect)
59 GET_PROC(MonitorFromWindow)
60
61 #undef GET_PROC
62 }
63
monitor_enum_proc(HMONITOR hmon,HDC hdc,LPRECT lprc,LPARAM lparam)64 static BOOL CALLBACK monitor_enum_proc(HMONITOR hmon, HDC hdc, LPRECT lprc,
65 LPARAM lparam)
66 {
67 MONITORINFOEXA mi;
68 char *primary = (char *)lparam;
69
70 mi.cbSize = sizeof(mi);
71
72 ok(pGetMonitorInfoA(hmon, (MONITORINFO*)&mi), "GetMonitorInfo failed\n");
73 if (mi.dwFlags & MONITORINFOF_PRIMARY)
74 strcpy(primary, mi.szDevice);
75
76 return TRUE;
77 }
78
79 static int adapter_count = 0;
80 static int monitor_count = 0;
81
test_enumdisplaydevices_adapter(int index,const DISPLAY_DEVICEA * device,DWORD flags)82 static void test_enumdisplaydevices_adapter(int index, const DISPLAY_DEVICEA *device, DWORD flags)
83 {
84 char video_name[32];
85 char video_value[128];
86 char buffer[128];
87 int number;
88 int vendor_id;
89 int device_id;
90 int subsys_id;
91 int revision_id;
92 size_t length;
93 HKEY hkey;
94 HDC hdc;
95 DWORD size;
96 LSTATUS ls;
97
98 adapter_count++;
99
100 /* DeviceName */
101 ok(sscanf(device->DeviceName, "\\\\.\\DISPLAY%d", &number) == 1, "#%d: wrong DeviceName %s\n", index,
102 device->DeviceName);
103
104 /* DeviceKey */
105 /* win7 is the only OS version where \Device\Video? value in HLKM\HARDWARE\DEVICEMAP\VIDEO are not in order with adapter index. */
106 if (GetVersion() != 0x1db10106 || !strcmp(winetest_platform, "wine"))
107 {
108 sprintf(video_name, "\\Device\\Video%d", index);
109 ls = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\VIDEO", 0, KEY_READ, &hkey);
110 ok(!ls, "#%d: failed to open registry, error: %#x\n", index, ls);
111 if (!ls)
112 {
113 memset(video_value, 0, sizeof(video_value));
114 size = sizeof(video_value);
115 ls = RegQueryValueExA(hkey, video_name, NULL, NULL, (unsigned char *)video_value, &size);
116 ok(!ls, "#%d: failed to get registry value, error: %#x\n", index, ls);
117 RegCloseKey(hkey);
118 ok(!strcmp(video_value, device->DeviceKey), "#%d: wrong DeviceKey: %s\n", index, device->DeviceKey);
119 }
120 }
121 else
122 ok(sscanf(device->DeviceKey, "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Video\\%[^\\]\\%04d", buffer, &number) == 2,
123 "#%d: wrong DeviceKey %s\n", index, device->DeviceKey);
124
125 /* DeviceString */
126 length = strlen(device->DeviceString);
127 ok(broken(length == 0) || /* XP on Testbot will return an empty string, whereas XP on real machine doesn't. Probably a bug in virtual adapter driver */
128 length > 0, "#%d: expect DeviceString not empty\n", index);
129
130 /* StateFlags */
131 if (index == 0)
132 ok(device->StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE, "#%d: adapter should be primary\n", index);
133 else
134 ok(!(device->StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE), "#%d: adapter should not be primary\n", index);
135
136 if (device->StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
137 {
138 /* Test creating DC */
139 hdc = CreateDCA(device->DeviceName, NULL, NULL, NULL);
140 ok(hdc != NULL, "#%d: failed to CreateDC(\"%s\") err=%d\n", index, device->DeviceName, GetLastError());
141 DeleteDC(hdc);
142 }
143
144 /* DeviceID */
145 /* DeviceID should equal to the first string of HardwareID value data in PCI GPU instance. You can verify this
146 * by changing the data and rerun EnumDisplayDevices. But it's difficult to find corresponding PCI device on
147 * userland. So here we check the expected format instead. */
148 if (flags & EDD_GET_DEVICE_INTERFACE_NAME)
149 ok(strlen(device->DeviceID) == 0 || /* vista+ */
150 sscanf(device->DeviceID, "PCI\\VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X",
151 &vendor_id, &device_id, &subsys_id, &revision_id) == 4, /* XP/2003 ignores EDD_GET_DEVICE_INTERFACE_NAME */
152 "#%d: got %s\n", index, device->DeviceID);
153 else
154 {
155 ok(broken(strlen(device->DeviceID) == 0) || /* XP on Testbot returns an empty string, whereas real machine doesn't */
156 sscanf(device->DeviceID, "PCI\\VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X", &vendor_id, &device_id, &subsys_id,
157 &revision_id) == 4, "#%d: wrong DeviceID %s\n", index, device->DeviceID);
158 }
159 }
160
test_enumdisplaydevices_monitor(int adapter_index,int monitor_index,const char * adapter_name,const DISPLAY_DEVICEA * device,DWORD flags)161 static void test_enumdisplaydevices_monitor(int adapter_index, int monitor_index, const char *adapter_name,
162 const DISPLAY_DEVICEA *device, DWORD flags)
163 {
164 static const char device_id_prefix[] = "MONITOR\\Default_Monitor\\{4d36e96e-e325-11ce-bfc1-08002be10318}\\";
165 static const char device_key_prefix[] = "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class"
166 "\\{4d36e96e-e325-11ce-bfc1-08002be10318}\\";
167 char monitor_name[32];
168 char buffer[128];
169 int number;
170
171 monitor_count++;
172
173 /* DeviceName */
174 lstrcpyA(monitor_name, adapter_name);
175 sprintf(monitor_name + strlen(monitor_name), "\\Monitor%d", monitor_index);
176 ok(!strcmp(monitor_name, device->DeviceName), "#%d: expect %s, got %s\n", monitor_index, monitor_name, device->DeviceName);
177
178 /* DeviceString */
179 ok(strlen(device->DeviceString) > 0, "#%d: expect DeviceString not empty\n", monitor_index);
180
181 /* StateFlags */
182 if (adapter_index == 0 && monitor_index == 0)
183 ok(device->StateFlags & DISPLAY_DEVICE_ATTACHED, "#%d expect to have a primary monitor attached\n", monitor_index);
184 else
185 ok(device->StateFlags <= (DISPLAY_DEVICE_ATTACHED | DISPLAY_DEVICE_ACTIVE), "#%d wrong state %#x\n", monitor_index,
186 device->StateFlags);
187
188 /* DeviceID */
189 lstrcpynA(buffer, device->DeviceID, sizeof(device_id_prefix));
190 if (flags & EDD_GET_DEVICE_INTERFACE_NAME)
191 { /* HKLM\SYSTEM\CurrentControlSet\Enum\DISPLAY\Default_Monitor\4&2abfaa30&0&UID0 GUID_DEVINTERFACE_MONITOR
192 * ^ ^ ^
193 * Expect format \\?\DISPLAY#Default_Monitor#4&2abfaa30&0&UID0#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7} */
194 ok(strlen(device->DeviceID) == 0 || /* vista ~ win7 */
195 sscanf(device->DeviceID, "\\\\?\\DISPLAY#Default_Monitor#%[^#]#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}", buffer) == 1 || /* win8+ */
196 (!lstrcmpiA(buffer, device_id_prefix) &&
197 sscanf(device->DeviceID + sizeof(device_id_prefix) - 1, "%04d", &number) == 1), /* XP/2003 ignores EDD_GET_DEVICE_INTERFACE_NAME */
198 "#%d: wrong DeviceID : %s\n", monitor_index, device->DeviceID);
199 }
200 else
201 {
202 /* Expect HarewareID value data + Driver value data in HKLM\SYSTEM\CurrentControlSet\Enum\DISPLAY\Default_Monitor\{Instance} */
203 /* But we don't know which monitor instance this belongs to, so check format instead */
204 ok(!lstrcmpiA(buffer, device_id_prefix), "#%d wrong DeviceID : %s\n", monitor_index, device->DeviceID);
205 ok(sscanf(device->DeviceID + sizeof(device_id_prefix) - 1, "%04d", &number) == 1,
206 "#%d wrong DeviceID : %s\n", monitor_index, device->DeviceID);
207 }
208
209 /* DeviceKey */
210 lstrcpynA(buffer, device->DeviceKey, sizeof(device_key_prefix));
211 ok(!lstrcmpiA(buffer, device_key_prefix), "#%d: wrong DeviceKey : %s\n", monitor_index, device->DeviceKey);
212 ok(sscanf(device->DeviceKey + sizeof(device_key_prefix) - 1, "%04d", &number) == 1,
213 "#%d wrong DeviceKey : %s\n", monitor_index, device->DeviceKey);
214 }
215
test_enumdisplaydevices(void)216 static void test_enumdisplaydevices(void)
217 {
218 static const DWORD flags[] = {0, EDD_GET_DEVICE_INTERFACE_NAME};
219 DISPLAY_DEVICEA dd;
220 char primary_device_name[32];
221 char primary_monitor_device_name[32];
222 char adapter_name[32];
223 int number;
224 int flag_index;
225 int adapter_index;
226 int monitor_index;
227 BOOL ret;
228
229 if (!pEnumDisplayDevicesA)
230 {
231 win_skip("EnumDisplayDevicesA is not available\n");
232 return;
233 }
234
235 /* Doesn't accept \\.\DISPLAY */
236 dd.cb = sizeof(dd);
237 ret = pEnumDisplayDevicesA("\\\\.\\DISPLAY", 0, &dd, 0);
238 ok(!ret, "Expect failure\n");
239
240 /* Enumeration */
241 for (flag_index = 0; flag_index < ARRAY_SIZE(flags); flag_index++)
242 for (adapter_index = 0; pEnumDisplayDevicesA(NULL, adapter_index, &dd, flags[flag_index]); adapter_index++)
243 {
244 lstrcpyA(adapter_name, dd.DeviceName);
245
246 if (sscanf(adapter_name, "\\\\.\\DISPLAYV%d", &number) == 1)
247 {
248 skip("Skipping software devices %s:%s\n", adapter_name, dd.DeviceString);
249 continue;
250 }
251
252 test_enumdisplaydevices_adapter(adapter_index, &dd, flags[flag_index]);
253
254 for (monitor_index = 0; pEnumDisplayDevicesA(adapter_name, monitor_index, &dd, flags[flag_index]);
255 monitor_index++)
256 test_enumdisplaydevices_monitor(adapter_index, monitor_index, adapter_name, &dd, flags[flag_index]);
257 }
258
259 ok(adapter_count > 0, "Expect at least one adapter found\n");
260 /* XP on Testbot doesn't report a monitor, whereas XP on real machine does */
261 ok(broken(monitor_count == 0) || monitor_count > 0, "Expect at least one monitor found\n");
262
263 if (!pEnumDisplayMonitors || !pGetMonitorInfoA)
264 {
265 win_skip("EnumDisplayMonitors or GetMonitorInfoA are not available\n");
266 return;
267 }
268
269 ret = pEnumDisplayDevicesA(NULL, 0, &dd, 0);
270 ok(ret, "Expect success\n");
271 lstrcpyA(primary_device_name, dd.DeviceName);
272
273 primary_monitor_device_name[0] = 0;
274 ret = pEnumDisplayMonitors(NULL, NULL, monitor_enum_proc, (LPARAM)primary_monitor_device_name);
275 ok(ret, "EnumDisplayMonitors failed\n");
276 ok(!strcmp(primary_monitor_device_name, primary_device_name),
277 "monitor device name %s, device name %s\n", primary_monitor_device_name,
278 primary_device_name);
279 }
280
281 struct vid_mode
282 {
283 DWORD w, h, bpp, freq, fields;
284 BOOL must_succeed;
285 };
286
287 static const struct vid_mode vid_modes_test[] = {
288 {640, 480, 0, 0, DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY, 0},
289 {640, 480, 0, 0, DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY, 1},
290 {640, 480, 0, 0, DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL , 0},
291 {640, 480, 0, 0, DM_PELSWIDTH | DM_PELSHEIGHT , 1},
292 {640, 480, 0, 0, DM_BITSPERPEL , 0},
293 {640, 480, 0, 0, DM_DISPLAYFREQUENCY, 0},
294
295 {0, 0, 0, 0, DM_PELSWIDTH, 0},
296 {0, 0, 0, 0, DM_PELSHEIGHT, 0},
297
298 {640, 480, 0, 0, DM_PELSWIDTH, 0},
299 {640, 480, 0, 0, DM_PELSHEIGHT, 0},
300 { 0, 480, 0, 0, DM_PELSWIDTH | DM_PELSHEIGHT, 0},
301 {640, 0, 0, 0, DM_PELSWIDTH | DM_PELSHEIGHT, 0},
302
303 /* the following test succeeds under XP SP3
304 {0, 0, 0, 0, DM_DISPLAYFREQUENCY, 0}
305 */
306 };
307
test_ChangeDisplaySettingsEx(void)308 static void test_ChangeDisplaySettingsEx(void)
309 {
310 DEVMODEA dm;
311 DEVMODEW dmW;
312 DWORD width;
313 LONG res;
314 int i;
315
316 if (!pChangeDisplaySettingsExA)
317 {
318 win_skip("ChangeDisplaySettingsExA is not available\n");
319 return;
320 }
321
322 SetLastError(0xdeadbeef);
323 res = EnumDisplaySettingsA(NULL, ENUM_CURRENT_SETTINGS, &dm);
324 ok(res, "EnumDisplaySettings error %u\n", GetLastError());
325
326 width = dm.dmPelsWidth;
327
328 dm.dmDriverExtra = 1;
329 res = ChangeDisplaySettingsA(&dm, CDS_TEST);
330 ok(res == DISP_CHANGE_SUCCESSFUL,
331 "ChangeDisplaySettingsA returned %d, expected DISP_CHANGE_SUCCESSFUL\n", res);
332 ok(dm.dmDriverExtra == 0 || broken(dm.dmDriverExtra == 1) /* win9x */,
333 "ChangeDisplaySettingsA didn't reset dmDriverExtra to 0\n");
334
335 /* crashes under XP SP3 for large dmDriverExtra values */
336 dm.dmDriverExtra = 1;
337 res = pChangeDisplaySettingsExA(NULL, &dm, NULL, CDS_TEST, NULL);
338 ok(res == DISP_CHANGE_SUCCESSFUL,
339 "ChangeDisplaySettingsExW returned %d, expected DISP_CHANGE_SUCCESSFUL\n", res);
340 ok(dm.dmDriverExtra == 1, "ChangeDisplaySettingsExA shouldn't reset dmDriverExtra to 0\n");
341
342 memset(&dmW, 0, sizeof(dmW));
343 dmW.dmSize = sizeof(dmW);
344 dmW.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
345 dmW.dmPelsWidth = dm.dmPelsWidth;
346 dmW.dmPelsHeight = dm.dmPelsHeight;
347 dmW.dmDriverExtra = 1;
348 SetLastError(0xdeadbeef);
349 res = ChangeDisplaySettingsW(&dmW, CDS_TEST);
350 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
351 {
352 ok(res == DISP_CHANGE_SUCCESSFUL,
353 "ChangeDisplaySettingsW returned %d, expected DISP_CHANGE_SUCCESSFUL\n", res);
354 ok(dmW.dmDriverExtra == 0, "ChangeDisplaySettingsW didn't reset dmDriverExtra to 0\n");
355 }
356
357 /* Apparently XP treats dmDriverExtra being != 0 as an error */
358 dmW.dmDriverExtra = 1;
359 res = pChangeDisplaySettingsExW(NULL, &dmW, NULL, CDS_TEST, NULL);
360 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
361 {
362 ok(res == DISP_CHANGE_SUCCESSFUL,
363 "ChangeDisplaySettingsExW returned %d, expected DISP_CHANGE_SUCCESSFUL\n", res);
364 ok(dmW.dmDriverExtra == 1, "ChangeDisplaySettingsExW shouldn't reset dmDriverExtra to 0\n");
365 }
366
367 /* the following 2 tests show that dm.dmSize being 0 is invalid, but
368 * ChangeDisplaySettingsExA still reports success.
369 */
370 memset(&dm, 0, sizeof(dm));
371 dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
372 dm.dmPelsWidth = width;
373 res = pChangeDisplaySettingsExA(NULL, &dm, NULL, CDS_TEST, NULL);
374 ok(res == DISP_CHANGE_SUCCESSFUL ||
375 res == DISP_CHANGE_BADMODE || /* Win98, WinMe */
376 res == DISP_CHANGE_FAILED, /* NT4 */
377 "ChangeDisplaySettingsExA returned unexpected %d\n", res);
378
379 memset(&dmW, 0, sizeof(dmW));
380 dmW.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
381 dmW.dmPelsWidth = width;
382 SetLastError(0xdeadbeef);
383 res = pChangeDisplaySettingsExW(NULL, &dmW, NULL, CDS_TEST, NULL);
384 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
385 ok(res == DISP_CHANGE_FAILED ||
386 res == DISP_CHANGE_BADPARAM || /* NT4 */
387 res == DISP_CHANGE_BADMODE /* XP SP3 */,
388 "ChangeDisplaySettingsExW returned %d\n", res);
389
390 memset(&dm, 0, sizeof(dm));
391 dm.dmSize = sizeof(dm);
392
393 for (i = 0; i < ARRAY_SIZE(vid_modes_test); i++)
394 {
395 dm.dmPelsWidth = vid_modes_test[i].w;
396 dm.dmPelsHeight = vid_modes_test[i].h;
397 dm.dmBitsPerPel = vid_modes_test[i].bpp;
398 dm.dmDisplayFrequency = vid_modes_test[i].freq;
399 dm.dmFields = vid_modes_test[i].fields;
400 res = pChangeDisplaySettingsExA(NULL, &dm, NULL, CDS_TEST, NULL);
401 ok(vid_modes_test[i].must_succeed ?
402 (res == DISP_CHANGE_SUCCESSFUL || res == DISP_CHANGE_RESTART) :
403 (res == DISP_CHANGE_SUCCESSFUL || res == DISP_CHANGE_RESTART ||
404 res == DISP_CHANGE_BADMODE || res == DISP_CHANGE_BADPARAM),
405 "Unexpected ChangeDisplaySettingsEx() return code for resolution[%d]: %d\n", i, res);
406
407 if (res == DISP_CHANGE_SUCCESSFUL)
408 {
409 RECT r, r1, virt;
410
411 SetRect(&virt, 0, 0, GetSystemMetrics(SM_CXVIRTUALSCREEN), GetSystemMetrics(SM_CYVIRTUALSCREEN));
412 if (IsRectEmpty(&virt)) /* NT4 doesn't have SM_CX/YVIRTUALSCREEN */
413 SetRect(&virt, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
414 OffsetRect(&virt, GetSystemMetrics(SM_XVIRTUALSCREEN), GetSystemMetrics(SM_YVIRTUALSCREEN));
415
416 /* Resolution change resets clip rect */
417 ok(GetClipCursor(&r), "GetClipCursor() failed\n");
418 ok(EqualRect(&r, &virt), "Invalid clip rect: %s\n", wine_dbgstr_rect(&r));
419
420 if (!ClipCursor(NULL)) continue;
421 ok(GetClipCursor(&r), "GetClipCursor() failed\n");
422 ok(EqualRect(&r, &virt), "Invalid clip rect: %s\n", wine_dbgstr_rect(&r));
423
424 /* This should always work. Primary monitor is at (0,0) */
425 SetRect(&r1, 10, 10, 20, 20);
426 ok(ClipCursor(&r1), "ClipCursor() failed\n");
427 ok(GetClipCursor(&r), "GetClipCursor() failed\n");
428 ok(EqualRect(&r, &r1), "Invalid clip rect: %s\n", wine_dbgstr_rect(&r));
429 SetRect(&r1, 10, 10, 10, 10);
430 ok(ClipCursor(&r1), "ClipCursor() failed\n");
431 ok(GetClipCursor(&r), "GetClipCursor() failed\n");
432 ok(EqualRect(&r, &r1), "Invalid clip rect: %s\n", wine_dbgstr_rect(&r));
433 SetRect(&r1, 10, 10, 10, 9);
434 ok(!ClipCursor(&r1), "ClipCursor() succeeded\n");
435 /* Windows bug: further clipping fails once an empty rect is set, so we have to reset it */
436 ClipCursor(NULL);
437
438 SetRect(&r1, virt.left - 10, virt.top - 10, virt.right + 20, virt.bottom + 20);
439 ok(ClipCursor(&r1), "ClipCursor() failed\n");
440 ok(GetClipCursor(&r), "GetClipCursor() failed\n");
441 ok(EqualRect(&r, &virt) || broken(EqualRect(&r, &r1)) /* win9x */,
442 "Invalid clip rect: %s\n", wine_dbgstr_rect(&r));
443 ClipCursor(&virt);
444 }
445 }
446 res = pChangeDisplaySettingsExA(NULL, NULL, NULL, CDS_RESET, NULL);
447 ok(res == DISP_CHANGE_SUCCESSFUL, "Failed to reset default resolution: %d\n", res);
448 }
449
test_monitors(void)450 static void test_monitors(void)
451 {
452 HMONITOR monitor, primary, nearest;
453 POINT pt;
454 RECT rc;
455 MONITORINFO mi;
456 MONITORINFOEXA miexa;
457 MONITORINFOEXW miexw;
458 BOOL ret;
459 DWORD i;
460
461 static const struct
462 {
463 DWORD cbSize;
464 BOOL ret;
465 } testdatami[] = {
466 {0, FALSE},
467 {sizeof(MONITORINFO)+1, FALSE},
468 {sizeof(MONITORINFO)-1, FALSE},
469 {sizeof(MONITORINFO), TRUE},
470 {-1, FALSE},
471 {0xdeadbeef, FALSE},
472 },
473 testdatamiexa[] = {
474 {0, FALSE},
475 {sizeof(MONITORINFOEXA)+1, FALSE},
476 {sizeof(MONITORINFOEXA)-1, FALSE},
477 {sizeof(MONITORINFOEXA), TRUE},
478 {-1, FALSE},
479 {0xdeadbeef, FALSE},
480 },
481 testdatamiexw[] = {
482 {0, FALSE},
483 {sizeof(MONITORINFOEXW)+1, FALSE},
484 {sizeof(MONITORINFOEXW)-1, FALSE},
485 {sizeof(MONITORINFOEXW), TRUE},
486 {-1, FALSE},
487 {0xdeadbeef, FALSE},
488 };
489
490 if (!pMonitorFromPoint || !pMonitorFromWindow || !pMonitorFromRect)
491 {
492 win_skip("MonitorFromPoint, MonitorFromWindow, or MonitorFromRect is not available\n");
493 return;
494 }
495
496 pt.x = pt.y = 0;
497 primary = pMonitorFromPoint( pt, MONITOR_DEFAULTTOPRIMARY );
498 ok( primary != 0, "couldn't get primary monitor\n" );
499
500 monitor = pMonitorFromWindow( 0, MONITOR_DEFAULTTONULL );
501 ok( !monitor, "got %p, should not get a monitor for an invalid window\n", monitor );
502 monitor = pMonitorFromWindow( 0, MONITOR_DEFAULTTOPRIMARY );
503 ok( monitor == primary, "got %p, should get primary %p for MONITOR_DEFAULTTOPRIMARY\n", monitor, primary );
504 monitor = pMonitorFromWindow( 0, MONITOR_DEFAULTTONEAREST );
505 ok( monitor == primary, "got %p, should get primary %p for MONITOR_DEFAULTTONEAREST\n", monitor, primary );
506
507 SetRect( &rc, 0, 0, 1, 1 );
508 monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
509 ok( monitor == primary, "got %p, should get primary %p\n", monitor, primary );
510
511 monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTOPRIMARY );
512 ok( monitor == primary, "got %p, should get primary %p\n", monitor, primary );
513
514 monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTONEAREST );
515 ok( monitor == primary, "got %p, should get primary %p\n", monitor, primary );
516
517 /* Empty rect at 0,0 is considered inside the primary monitor */
518 SetRect( &rc, 0, 0, -1, -1 );
519 monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
520 ok( monitor == primary, "got %p, should get primary %p\n", monitor, primary );
521
522 /* Even if there is a monitor left of the primary, the primary will have the most overlapping area */
523 SetRect( &rc, -1, 0, 2, 1 );
524 monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
525 ok( monitor == primary, "got %p, should get primary %p\n", monitor, primary );
526
527 /* But the width of the rect doesn't matter if it's empty. */
528 SetRect( &rc, -1, 0, 2, -1 );
529 monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
530 ok( monitor != primary, "got primary %p\n", monitor );
531
532 if (!pGetMonitorInfoA)
533 {
534 win_skip("GetMonitorInfoA is not available\n");
535 return;
536 }
537
538 /* Search for a monitor that has no others equally near to (left, top-1) */
539 SetRect( &rc, -1, -2, 2, 0 );
540 monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
541 nearest = primary;
542 while (monitor != NULL)
543 {
544 ok( monitor != primary, "got primary %p\n", monitor );
545 nearest = monitor;
546 mi.cbSize = sizeof(mi);
547 ret = pGetMonitorInfoA( monitor, &mi );
548 ok( ret, "GetMonitorInfo failed\n" );
549 SetRect( &rc, mi.rcMonitor.left-1, mi.rcMonitor.top-2, mi.rcMonitor.left+2, mi.rcMonitor.top );
550 monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
551 }
552
553 /* tests for cbSize in MONITORINFO */
554 monitor = pMonitorFromWindow( 0, MONITOR_DEFAULTTOPRIMARY );
555 for (i = 0; i < ARRAY_SIZE(testdatami); i++)
556 {
557 memset( &mi, 0, sizeof(mi) );
558 mi.cbSize = testdatami[i].cbSize;
559 ret = pGetMonitorInfoA( monitor, &mi );
560 ok( ret == testdatami[i].ret, "GetMonitorInfo returned wrong value\n" );
561 if (ret)
562 ok( (mi.dwFlags & MONITORINFOF_PRIMARY), "MONITORINFOF_PRIMARY flag isn't set\n" );
563 else
564 ok( !(mi.dwFlags & MONITORINFOF_PRIMARY), "MONITORINFOF_PRIMARY flag is set\n" );
565
566 memset( &miexw, 0, sizeof(miexw) );
567 miexw.cbSize = testdatamiexw[i].cbSize;
568 ret = pGetMonitorInfoW( monitor, (LPMONITORINFO)&miexw );
569 ok( ret == testdatamiexw[i].ret, "GetMonitorInfo returned wrong value\n" );
570 if (ret)
571 ok( (miexw.dwFlags & MONITORINFOF_PRIMARY), "MONITORINFOF_PRIMARY flag isn't set\n" );
572 else
573 ok( !(miexw.dwFlags & MONITORINFOF_PRIMARY), "MONITORINFOF_PRIMARY flag is set\n" );
574 }
575
576 /* tests for cbSize in MONITORINFOEXA */
577 for (i = 0; i < ARRAY_SIZE(testdatamiexa); i++)
578 {
579 memset( &miexa, 0, sizeof(miexa) );
580 miexa.cbSize = testdatamiexa[i].cbSize;
581 ret = pGetMonitorInfoA( monitor, (LPMONITORINFO)&miexa );
582 ok( ret == testdatamiexa[i].ret, "GetMonitorInfo returned wrong value\n" );
583 if (ret)
584 ok( (miexa.dwFlags & MONITORINFOF_PRIMARY), "MONITORINFOF_PRIMARY flag isn't set\n" );
585 else
586 ok( !(miexa.dwFlags & MONITORINFOF_PRIMARY), "MONITORINFOF_PRIMARY flag is set\n" );
587 }
588
589 /* tests for cbSize in MONITORINFOEXW */
590 for (i = 0; i < ARRAY_SIZE(testdatamiexw); i++)
591 {
592 memset( &miexw, 0, sizeof(miexw) );
593 miexw.cbSize = testdatamiexw[i].cbSize;
594 ret = pGetMonitorInfoW( monitor, (LPMONITORINFO)&miexw );
595 ok( ret == testdatamiexw[i].ret, "GetMonitorInfo returned wrong value\n" );
596 if (ret)
597 ok( (miexw.dwFlags & MONITORINFOF_PRIMARY), "MONITORINFOF_PRIMARY flag isn't set\n" );
598 else
599 ok( !(miexw.dwFlags & MONITORINFOF_PRIMARY), "MONITORINFOF_PRIMARY flag is set\n" );
600 }
601
602 SetRect( &rc, rc.left+1, rc.top+1, rc.left+2, rc.top+2 );
603
604 monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTONULL );
605 ok( monitor == NULL, "got %p\n", monitor );
606
607 monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTOPRIMARY );
608 ok( monitor == primary, "got %p, should get primary %p\n", monitor, primary );
609
610 monitor = pMonitorFromRect( &rc, MONITOR_DEFAULTTONEAREST );
611 ok( monitor == nearest, "got %p, should get nearest %p\n", monitor, nearest );
612 }
613
find_primary_mon(HMONITOR hmon,HDC hdc,LPRECT rc,LPARAM lp)614 static BOOL CALLBACK find_primary_mon(HMONITOR hmon, HDC hdc, LPRECT rc, LPARAM lp)
615 {
616 MONITORINFO mi;
617 BOOL ret;
618
619 mi.cbSize = sizeof(mi);
620 ret = pGetMonitorInfoA(hmon, &mi);
621 ok(ret, "GetMonitorInfo failed\n");
622 if (mi.dwFlags & MONITORINFOF_PRIMARY)
623 {
624 *(HMONITOR *)lp = hmon;
625 return FALSE;
626 }
627 return TRUE;
628 }
629
test_work_area(void)630 static void test_work_area(void)
631 {
632 HMONITOR hmon;
633 MONITORINFO mi;
634 RECT rc_work, rc_normal;
635 HWND hwnd;
636 WINDOWPLACEMENT wp;
637 BOOL ret;
638
639 if (!pEnumDisplayMonitors || !pGetMonitorInfoA)
640 {
641 win_skip("EnumDisplayMonitors or GetMonitorInfoA are not available\n");
642 return;
643 }
644
645 hmon = 0;
646 ret = pEnumDisplayMonitors(NULL, NULL, find_primary_mon, (LPARAM)&hmon);
647 ok(!ret && hmon != 0, "Failed to find primary monitor\n");
648
649 mi.cbSize = sizeof(mi);
650 SetLastError(0xdeadbeef);
651 ret = pGetMonitorInfoA(hmon, &mi);
652 ok(ret, "GetMonitorInfo error %u\n", GetLastError());
653 ok(mi.dwFlags & MONITORINFOF_PRIMARY, "not a primary monitor\n");
654 trace("primary monitor %s\n", wine_dbgstr_rect(&mi.rcMonitor));
655
656 SetLastError(0xdeadbeef);
657 ret = SystemParametersInfoA(SPI_GETWORKAREA, 0, &rc_work, 0);
658 ok(ret, "SystemParametersInfo error %u\n", GetLastError());
659 trace("work area %s\n", wine_dbgstr_rect(&rc_work));
660 ok(EqualRect(&rc_work, &mi.rcWork), "work area is different\n");
661
662 hwnd = CreateWindowExA(0, "static", NULL, WS_OVERLAPPEDWINDOW|WS_VISIBLE,100,100,10,10,0,0,0,NULL);
663 ok(hwnd != 0, "CreateWindowEx failed\n");
664
665 ret = GetWindowRect(hwnd, &rc_normal);
666 ok(ret, "GetWindowRect failed\n");
667 trace("normal %s\n", wine_dbgstr_rect(&rc_normal));
668
669 wp.length = sizeof(wp);
670 ret = GetWindowPlacement(hwnd, &wp);
671 ok(ret, "GetWindowPlacement failed\n");
672 trace("min: %d,%d max %d,%d normal %s\n", wp.ptMinPosition.x, wp.ptMinPosition.y,
673 wp.ptMaxPosition.x, wp.ptMaxPosition.y, wine_dbgstr_rect(&wp.rcNormalPosition));
674 OffsetRect(&wp.rcNormalPosition, rc_work.left, rc_work.top);
675 todo_wine_if (mi.rcMonitor.left != mi.rcWork.left ||
676 mi.rcMonitor.top != mi.rcWork.top) /* FIXME: remove once Wine is fixed */
677 {
678 ok(EqualRect(&rc_normal, &wp.rcNormalPosition), "normal pos is different\n");
679 }
680
681 SetWindowLongA(hwnd, GWL_EXSTYLE, WS_EX_TOOLWINDOW);
682
683 wp.length = sizeof(wp);
684 ret = GetWindowPlacement(hwnd, &wp);
685 ok(ret, "GetWindowPlacement failed\n");
686 trace("min: %d,%d max %d,%d normal %s\n", wp.ptMinPosition.x, wp.ptMinPosition.y,
687 wp.ptMaxPosition.x, wp.ptMaxPosition.y, wine_dbgstr_rect(&wp.rcNormalPosition));
688 ok(EqualRect(&rc_normal, &wp.rcNormalPosition), "normal pos is different\n");
689
690 DestroyWindow(hwnd);
691 }
692
test_display_config(void)693 static void test_display_config(void)
694 {
695 UINT32 paths, modes;
696 LONG ret;
697
698 if (!pGetDisplayConfigBufferSizes)
699 {
700 win_skip("GetDisplayConfigBufferSizes is not supported\n");
701 return;
702 }
703
704 ret = pGetDisplayConfigBufferSizes(QDC_ALL_PATHS, NULL, NULL);
705 ok(ret == ERROR_INVALID_PARAMETER, "got %d\n", ret);
706
707 paths = 100;
708 ret = pGetDisplayConfigBufferSizes(QDC_ALL_PATHS, &paths, NULL);
709 ok(ret == ERROR_INVALID_PARAMETER, "got %d\n", ret);
710 ok(paths == 100, "got %u\n", paths);
711
712 modes = 100;
713 ret = pGetDisplayConfigBufferSizes(QDC_ALL_PATHS, NULL, &modes);
714 ok(ret == ERROR_INVALID_PARAMETER, "got %d\n", ret);
715 ok(modes == 100, "got %u\n", modes);
716
717 paths = modes = 0;
718 ret = pGetDisplayConfigBufferSizes(QDC_ALL_PATHS, &paths, &modes);
719 if (!ret)
720 ok(paths > 0 && modes > 0, "got %u, %u\n", paths, modes);
721 else
722 ok(ret == ERROR_NOT_SUPPORTED, "got %d\n", ret);
723
724 /* Invalid flags, non-zero invalid flags validation is version (or driver?) dependent,
725 it's unreliable to use in tests. */
726 ret = pGetDisplayConfigBufferSizes(0, NULL, NULL);
727 ok(ret == ERROR_INVALID_PARAMETER, "got %d\n", ret);
728
729 paths = 100;
730 ret = pGetDisplayConfigBufferSizes(0, &paths, NULL);
731 ok(ret == ERROR_INVALID_PARAMETER, "got %d\n", ret);
732 ok(paths == 100, "got %u\n", paths);
733
734 modes = 100;
735 ret = pGetDisplayConfigBufferSizes(0, NULL, &modes);
736 ok(ret == ERROR_INVALID_PARAMETER, "got %d\n", ret);
737 ok(modes == 100, "got %u\n", modes);
738
739 paths = modes = 100;
740 ret = pGetDisplayConfigBufferSizes(0, &paths, &modes);
741 ok(ret == ERROR_INVALID_PARAMETER || ret == ERROR_NOT_SUPPORTED, "got %d\n", ret);
742 ok((modes == 0 || modes == 100) && paths == 0, "got %u, %u\n", modes, paths);
743 }
744
START_TEST(monitor)745 START_TEST(monitor)
746 {
747 init_function_pointers();
748 test_enumdisplaydevices();
749 test_ChangeDisplaySettingsEx();
750 test_monitors();
751 test_work_area();
752 test_display_config();
753 }
754