4bb036d9c5bbebee10959012d3d75ce6a7e12113
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / display.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Video initialization and display settings
5 * FILE: subsystems/win32/win32k/ntuser/display.c
6 * PROGRAMER: Timo Kreuzer (timo.kreuzer@reactos.org)
7 */
8
9 #include <win32k.h>
10 DBG_DEFAULT_CHANNEL(UserDisplay);
11
12 BOOL gbBaseVideo = 0;
13
14 static const PWCHAR KEY_VIDEO = L"\\Registry\\Machine\\HARDWARE\\DEVICEMAP\\VIDEO";
15
16 VOID
17 RegWriteDisplaySettings(HKEY hkey, PDEVMODEW pdm)
18 {
19 RegWriteDWORD(hkey, L"DefaultSettings.BitsPerPel", pdm->dmBitsPerPel);
20 RegWriteDWORD(hkey, L"DefaultSettings.XResolution", pdm->dmPelsWidth);
21 RegWriteDWORD(hkey, L"DefaultSettings.YResolution", pdm->dmPelsHeight);
22 RegWriteDWORD(hkey, L"DefaultSettings.Flags", pdm->dmDisplayFlags);
23 RegWriteDWORD(hkey, L"DefaultSettings.VRefresh", pdm->dmDisplayFrequency);
24 RegWriteDWORD(hkey, L"DefaultSettings.XPanning", pdm->dmPanningWidth);
25 RegWriteDWORD(hkey, L"DefaultSettings.YPanning", pdm->dmPanningHeight);
26 RegWriteDWORD(hkey, L"DefaultSettings.Orientation", pdm->dmDisplayOrientation);
27 RegWriteDWORD(hkey, L"DefaultSettings.FixedOutput", pdm->dmDisplayFixedOutput);
28 RegWriteDWORD(hkey, L"Attach.RelativeX", pdm->dmPosition.x);
29 RegWriteDWORD(hkey, L"Attach.RelativeY", pdm->dmPosition.y);
30 // RegWriteDWORD(hkey, L"Attach.ToDesktop, pdm->dmBitsPerPel", pdm->);
31 }
32
33 VOID
34 RegReadDisplaySettings(HKEY hkey, PDEVMODEW pdm)
35 {
36 DWORD dwValue;
37
38 /* Zero out the structure */
39 RtlZeroMemory(pdm, sizeof(DEVMODEW));
40
41 /* Helper macro */
42 #define READ(field, str, flag) \
43 if (RegReadDWORD(hkey, L##str, &dwValue)) \
44 { \
45 pdm->field = dwValue; \
46 pdm->dmFields |= flag; \
47 }
48
49 /* Read all present settings */
50 READ(dmBitsPerPel, "DefaultSettings.BitsPerPel", DM_BITSPERPEL);
51 READ(dmPelsWidth, "DefaultSettings.XResolution", DM_PELSWIDTH);
52 READ(dmPelsHeight, "DefaultSettings.YResolution", DM_PELSHEIGHT);
53 READ(dmDisplayFlags, "DefaultSettings.Flags", DM_DISPLAYFLAGS);
54 READ(dmDisplayFrequency, "DefaultSettings.VRefresh", DM_DISPLAYFREQUENCY);
55 READ(dmPanningWidth, "DefaultSettings.XPanning", DM_PANNINGWIDTH);
56 READ(dmPanningHeight, "DefaultSettings.YPanning", DM_PANNINGHEIGHT);
57 READ(dmDisplayOrientation, "DefaultSettings.Orientation", DM_DISPLAYORIENTATION);
58 READ(dmDisplayFixedOutput, "DefaultSettings.FixedOutput", DM_DISPLAYFIXEDOUTPUT);
59 READ(dmPosition.x, "Attach.RelativeX", DM_POSITION);
60 READ(dmPosition.y, "Attach.RelativeY", DM_POSITION);
61 }
62
63 PGRAPHICS_DEVICE
64 NTAPI
65 InitDisplayDriver(
66 IN PWSTR pwszDeviceName,
67 IN PWSTR pwszRegKey)
68 {
69 PGRAPHICS_DEVICE pGraphicsDevice;
70 UNICODE_STRING ustrDeviceName, ustrDisplayDrivers, ustrDescription;
71 NTSTATUS Status;
72 WCHAR awcBuffer[128];
73 ULONG cbSize;
74 HKEY hkey;
75 DEVMODEW dmDefault;
76 DWORD dwVga;
77
78 ERR("InitDisplayDriver(%S, %S);\n",
79 pwszDeviceName, pwszRegKey);
80
81 /* Open the driver's registry key */
82 Status = RegOpenKey(pwszRegKey, &hkey);
83 if (!NT_SUCCESS(Status))
84 {
85 ERR("Failed to open registry key: %ls\n", pwszRegKey);
86 return NULL;
87 }
88
89 /* Query the diplay drivers */
90 cbSize = sizeof(awcBuffer) - 10;
91 Status = RegQueryValue(hkey,
92 L"InstalledDisplayDrivers",
93 REG_MULTI_SZ,
94 awcBuffer,
95 &cbSize);
96 if (!NT_SUCCESS(Status))
97 {
98 ERR("Didn't find 'InstalledDisplayDrivers', status = 0x%lx\n", Status);
99 ZwClose(hkey);
100 return NULL;
101 }
102
103 /* Initialize the UNICODE_STRING */
104 ustrDisplayDrivers.Buffer = awcBuffer;
105 ustrDisplayDrivers.MaximumLength = cbSize;
106 ustrDisplayDrivers.Length = cbSize;
107
108 /* Set Buffer for description and size of remaining buffer */
109 ustrDescription.Buffer = awcBuffer + (cbSize / sizeof(WCHAR));
110 cbSize = sizeof(awcBuffer) - cbSize;
111
112 /* Query the device string */
113 Status = RegQueryValue(hkey,
114 L"Device Description",
115 REG_SZ,
116 ustrDescription.Buffer,
117 &cbSize);
118 if (NT_SUCCESS(Status))
119 {
120 ustrDescription.MaximumLength = cbSize;
121 ustrDescription.Length = cbSize;
122 }
123 else
124 {
125 RtlInitUnicodeString(&ustrDescription, L"<unknown>");
126 }
127
128 /* Query the default settings */
129 RegReadDisplaySettings(hkey, &dmDefault);
130
131 /* Query if this is a VGA compatible driver */
132 cbSize = sizeof(DWORD);
133 Status = RegQueryValue(hkey, L"VgaCompatible", REG_DWORD, &dwVga, &cbSize);
134 if (!NT_SUCCESS(Status)) dwVga = 0;
135
136 /* Close the registry key */
137 ZwClose(hkey);
138
139 /* Register the device with GDI */
140 RtlInitUnicodeString(&ustrDeviceName, pwszDeviceName);
141 pGraphicsDevice = EngpRegisterGraphicsDevice(&ustrDeviceName,
142 &ustrDisplayDrivers,
143 &ustrDescription,
144 &dmDefault);
145 if (pGraphicsDevice && dwVga)
146 {
147 pGraphicsDevice->StateFlags |= DISPLAY_DEVICE_VGA_COMPATIBLE;
148 }
149
150 return pGraphicsDevice;
151 }
152
153 NTSTATUS
154 NTAPI
155 InitVideo()
156 {
157 ULONG iDevNum, iVGACompatible = -1, ulMaxObjectNumber = 0;
158 WCHAR awcDeviceName[20];
159 WCHAR awcBuffer[256];
160 NTSTATUS Status;
161 PGRAPHICS_DEVICE pGraphicsDevice;
162 ULONG cbValue;
163 HKEY hkey;
164
165 TRACE("----------------------------- InitVideo() -------------------------------\n");
166
167 /* Open the key for the boot command line */
168 Status = RegOpenKey(L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control", &hkey);
169 if (NT_SUCCESS(Status))
170 {
171 cbValue = 256;
172 Status = RegQueryValue(hkey, L"SystemStartOptions", REG_SZ, awcBuffer, &cbValue);
173 if (NT_SUCCESS(Status))
174 {
175 /* Check if VGA mode is requested. */
176 if (wcsstr(awcBuffer, L"/BASEVIDEO") != 0)
177 {
178 ERR("VGA mode requested.\n");
179 gbBaseVideo = TRUE;
180 }
181 }
182
183 ZwClose(hkey);
184 }
185
186 /* Open the key for the adapters */
187 Status = RegOpenKey(KEY_VIDEO, &hkey);
188 if (!NT_SUCCESS(Status))
189 {
190 ERR("Could not open device registry key!\n");
191 return Status;
192 }
193
194 /* Read the name of the VGA adapter */
195 cbValue = 20;
196 Status = RegQueryValue(hkey, L"VgaCompatible", REG_SZ, awcDeviceName, &cbValue);
197 if (NT_SUCCESS(Status))
198 {
199 iVGACompatible = _wtoi(&awcDeviceName[13]);
200 ERR("VGA adapter = %ld\n", iVGACompatible);
201 }
202
203 /* Get the maximum mumber of adapters */
204 if (!RegReadDWORD(hkey, L"MaxObjectNumber", &ulMaxObjectNumber))
205 {
206 ERR("Could not read MaxObjectNumber, defaulting to 0.\n");
207 }
208
209 TRACE("Found %ld devices\n", ulMaxObjectNumber + 1);
210
211 /* Loop through all adapters */
212 for (iDevNum = 0; iDevNum <= ulMaxObjectNumber; iDevNum++)
213 {
214 /* Create the adapter's key name */
215 swprintf(awcDeviceName, L"\\Device\\Video%lu", iDevNum);
216
217 /* Read the reg key name */
218 cbValue = sizeof(awcBuffer);
219 Status = RegQueryValue(hkey, awcDeviceName, REG_SZ, awcBuffer, &cbValue);
220 if (!NT_SUCCESS(Status))
221 {
222 ERR("failed to query the registry path:0x%lx\n", Status);
223 continue;
224 }
225
226 /* Initialize the driver for this device */
227 pGraphicsDevice = InitDisplayDriver(awcDeviceName, awcBuffer);
228 if (!pGraphicsDevice) continue;
229
230 /* Check if this is a VGA compatible adapter */
231 if (pGraphicsDevice->StateFlags & DISPLAY_DEVICE_VGA_COMPATIBLE)
232 {
233 /* Save this as the VGA adapter */
234 if (!gpVgaGraphicsDevice)
235 gpVgaGraphicsDevice = pGraphicsDevice;
236 TRACE("gpVgaGraphicsDevice = %p\n", gpVgaGraphicsDevice);
237 }
238 else
239 {
240 /* Set the first one as primary device */
241 if (!gpPrimaryGraphicsDevice)
242 gpPrimaryGraphicsDevice = pGraphicsDevice;
243 TRACE("gpPrimaryGraphicsDevice = %p\n", gpPrimaryGraphicsDevice);
244 }
245 }
246
247 /* Close the device map registry key */
248 ZwClose(hkey);
249
250 /* Was VGA mode requested? */
251 if (gbBaseVideo)
252 {
253 /* Check if we found a VGA compatible device */
254 if (gpVgaGraphicsDevice)
255 {
256 /* Set the VgaAdapter as primary */
257 gpPrimaryGraphicsDevice = gpVgaGraphicsDevice;
258 // FIXME: DEVMODE
259 }
260 else
261 {
262 ERR("Could not find VGA compatible driver. Trying normal.\n");
263 }
264 }
265
266 /* Check if we had any success */
267 if (!gpPrimaryGraphicsDevice)
268 {
269 /* Check if there is a VGA device we skipped */
270 if (gpVgaGraphicsDevice)
271 {
272 /* There is, use the VGA device */
273 gpPrimaryGraphicsDevice = gpVgaGraphicsDevice;
274 }
275 else
276 {
277 ERR("No usable display driver was found.\n");
278 return STATUS_UNSUCCESSFUL;
279 }
280 }
281
282 InitSysParams();
283
284 return 1;
285 }
286
287 NTSTATUS
288 NTAPI
289 UserEnumDisplayDevices(
290 PUNICODE_STRING pustrDevice,
291 DWORD iDevNum,
292 PDISPLAY_DEVICEW pdispdev,
293 DWORD dwFlags)
294 {
295 PGRAPHICS_DEVICE pGraphicsDevice;
296 ULONG cbSize;
297 HKEY hkey;
298 NTSTATUS Status;
299
300 /* Ask gdi for the GRAPHICS_DEVICE */
301 pGraphicsDevice = EngpFindGraphicsDevice(pustrDevice, iDevNum, 0);
302 if (!pGraphicsDevice)
303 {
304 /* No device found */
305 ERR("No GRAPHICS_DEVICE found\n");
306 return STATUS_UNSUCCESSFUL;
307 }
308
309 /* Open the device map registry key */
310 Status = RegOpenKey(KEY_VIDEO, &hkey);
311 if (!NT_SUCCESS(Status))
312 {
313 /* No device found */
314 ERR("Could not open reg key\n");
315 return STATUS_UNSUCCESSFUL;
316 }
317
318 /* Query the registry path */
319 cbSize = sizeof(pdispdev->DeviceKey);
320 RegQueryValue(hkey,
321 pGraphicsDevice->szNtDeviceName,
322 REG_SZ,
323 pdispdev->DeviceKey,
324 &cbSize);
325
326 /* Close registry key */
327 ZwClose(hkey);
328
329 /* Copy device name, device string and StateFlags */
330 RtlStringCbCopyW(pdispdev->DeviceName, sizeof(pdispdev->DeviceName), pGraphicsDevice->szWinDeviceName);
331 RtlStringCbCopyW(pdispdev->DeviceString, sizeof(pdispdev->DeviceString), pGraphicsDevice->pwszDescription);
332 pdispdev->StateFlags = pGraphicsDevice->StateFlags;
333 // FIXME: fill in DEVICE ID
334 pdispdev->DeviceID[0] = UNICODE_NULL;
335
336 return STATUS_SUCCESS;
337 }
338
339 //NTSTATUS
340 BOOL
341 NTAPI
342 NtUserEnumDisplayDevices(
343 PUNICODE_STRING pustrDevice,
344 DWORD iDevNum,
345 PDISPLAY_DEVICEW pDisplayDevice,
346 DWORD dwFlags)
347 {
348 UNICODE_STRING ustrDevice;
349 WCHAR awcDevice[CCHDEVICENAME];
350 DISPLAY_DEVICEW dispdev;
351 NTSTATUS Status;
352
353 TRACE("Enter NtUserEnumDisplayDevices(%wZ, %ld)\n",
354 pustrDevice, iDevNum);
355
356 dispdev.cb = sizeof(dispdev);
357
358 if (pustrDevice)
359 {
360 /* Initialize destination string */
361 RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
362
363 _SEH2_TRY
364 {
365 /* Probe the UNICODE_STRING and the buffer */
366 ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
367 ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
368
369 /* Copy the string */
370 RtlCopyUnicodeString(&ustrDevice, pustrDevice);
371 }
372 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
373 {
374 // _SEH2_YIELD(return _SEH2_GetExceptionCode());
375 _SEH2_YIELD(return NT_SUCCESS(_SEH2_GetExceptionCode()));
376 }
377 _SEH2_END
378
379 if (ustrDevice.Length > 0)
380 pustrDevice = &ustrDevice;
381 else
382 pustrDevice = NULL;
383 }
384
385 /* Acquire global USER lock */
386 UserEnterShared();
387
388 /* Call the internal function */
389 Status = UserEnumDisplayDevices(pustrDevice, iDevNum, &dispdev, dwFlags);
390
391 /* Release lock */
392 UserLeave();
393
394 /* On success copy data to caller */
395 if (NT_SUCCESS(Status))
396 {
397 /* Enter SEH */
398 _SEH2_TRY
399 {
400 /* First probe the cb field */
401 ProbeForWrite(&pDisplayDevice->cb, sizeof(DWORD), 1);
402
403 /* Check the buffer size */
404 if (pDisplayDevice->cb)
405 {
406 /* Probe the output buffer */
407 pDisplayDevice->cb = min(pDisplayDevice->cb, sizeof(dispdev));
408 ProbeForWrite(pDisplayDevice, pDisplayDevice->cb, 1);
409
410 /* Copy as much as the given buffer allows */
411 RtlCopyMemory(pDisplayDevice, &dispdev, pDisplayDevice->cb);
412 }
413 }
414 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
415 {
416 Status = _SEH2_GetExceptionCode();
417 }
418 _SEH2_END
419 }
420
421 TRACE("Leave NtUserEnumDisplayDevices, Status = 0x%lx\n", Status);
422 /* Return the result */
423 // return Status;
424 return NT_SUCCESS(Status); // FIXME
425 }
426
427 NTSTATUS
428 NTAPI
429 UserEnumCurrentDisplaySettings(
430 PUNICODE_STRING pustrDevice,
431 PDEVMODEW *ppdm)
432 {
433 PPDEVOBJ ppdev;
434
435 /* Get the PDEV for the device */
436 ppdev = EngpGetPDEV(pustrDevice);
437 if (!ppdev)
438 {
439 /* No device found */
440 ERR("No PDEV found!\n");
441 return STATUS_UNSUCCESSFUL;
442 }
443
444 *ppdm = ppdev->pdmwDev;
445 PDEVOBJ_vRelease(ppdev);
446
447 return STATUS_SUCCESS;
448 }
449
450 NTSTATUS
451 NTAPI
452 UserEnumDisplaySettings(
453 PUNICODE_STRING pustrDevice,
454 DWORD iModeNum,
455 LPDEVMODEW *ppdm,
456 DWORD dwFlags)
457 {
458 PGRAPHICS_DEVICE pGraphicsDevice;
459 PDEVMODEENTRY pdmentry;
460 ULONG i, iFoundMode;
461
462 TRACE("Enter UserEnumDisplaySettings('%wZ', %ld)\n",
463 pustrDevice, iModeNum);
464
465 /* Ask gdi for the GRAPHICS_DEVICE */
466 pGraphicsDevice = EngpFindGraphicsDevice(pustrDevice, 0, 0);
467
468 if (!pGraphicsDevice)
469 {
470 /* No device found */
471 ERR("No device found!\n");
472 return STATUS_UNSUCCESSFUL;
473 }
474
475 if (iModeNum >= pGraphicsDevice->cDevModes)
476 return STATUS_NO_MORE_ENTRIES;
477
478 iFoundMode = 0;
479 for (i = 0; i < pGraphicsDevice->cDevModes; i++)
480 {
481 pdmentry = &pGraphicsDevice->pDevModeList[i];
482
483 /* FIXME: consider EDS_RAWMODE */
484 #if 0
485 if ((!(dwFlags & EDS_RAWMODE) && (pdmentry->dwFlags & 1)) ||!
486 (dwFlags & EDS_RAWMODE))
487 #endif
488 {
489 /* Is this the one we want? */
490 if (iFoundMode == iModeNum)
491 {
492 *ppdm = pdmentry->pdm;
493 return STATUS_SUCCESS;
494 }
495
496 /* Increment number of found modes */
497 iFoundMode++;
498 }
499 }
500
501 /* Nothing was found */
502 return STATUS_INVALID_PARAMETER;
503 }
504
505 NTSTATUS
506 NTAPI
507 UserOpenDisplaySettingsKey(
508 OUT PHKEY phkey,
509 IN PUNICODE_STRING pustrDevice,
510 IN BOOL bGlobal)
511 {
512 HKEY hkey;
513 DISPLAY_DEVICEW dispdev;
514 NTSTATUS Status;
515
516 /* Get device info */
517 Status = UserEnumDisplayDevices(pustrDevice, 0, &dispdev, 0);
518 if (!NT_SUCCESS(Status))
519 return Status;
520
521 if (bGlobal)
522 {
523 // FIXME: need to fix the registry key somehow
524 }
525
526 /* Open the registry key */
527 Status = RegOpenKey(dispdev.DeviceKey, &hkey);
528 if (!NT_SUCCESS(Status))
529 return Status;
530
531 *phkey = hkey;
532
533 return Status;
534 }
535
536 NTSTATUS
537 NTAPI
538 UserEnumRegistryDisplaySettings(
539 IN PUNICODE_STRING pustrDevice,
540 OUT LPDEVMODEW pdm)
541 {
542 HKEY hkey;
543 NTSTATUS Status = UserOpenDisplaySettingsKey(&hkey, pustrDevice, 0);
544 if(NT_SUCCESS(Status))
545 {
546 RegReadDisplaySettings(hkey, pdm);
547 ZwClose(hkey);
548 return STATUS_SUCCESS;
549 }
550 return Status ;
551 }
552
553 NTSTATUS
554 APIENTRY
555 NtUserEnumDisplaySettings(
556 IN PUNICODE_STRING pustrDevice,
557 IN DWORD iModeNum,
558 OUT LPDEVMODEW lpDevMode,
559 IN DWORD dwFlags)
560 {
561 UNICODE_STRING ustrDevice;
562 WCHAR awcDevice[CCHDEVICENAME];
563 NTSTATUS Status;
564 ULONG cbSize, cbExtra;
565 DEVMODEW dmReg, *pdm;
566
567 TRACE("Enter NtUserEnumDisplaySettings(%wZ, %ld, %p, 0x%lx)\n",
568 pustrDevice, iModeNum, lpDevMode, dwFlags);
569
570 if (pustrDevice)
571 {
572 /* Initialize destination string */
573 RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
574
575 _SEH2_TRY
576 {
577 /* Probe the UNICODE_STRING and the buffer */
578 ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
579 ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
580
581 /* Copy the string */
582 RtlCopyUnicodeString(&ustrDevice, pustrDevice);
583 }
584 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
585 {
586 _SEH2_YIELD(return _SEH2_GetExceptionCode());
587 }
588 _SEH2_END
589
590 pustrDevice = &ustrDevice;
591 }
592
593 /* Acquire global USER lock */
594 UserEnterShared();
595
596 if (iModeNum == ENUM_REGISTRY_SETTINGS)
597 {
598 /* Get the registry settings */
599 Status = UserEnumRegistryDisplaySettings(pustrDevice, &dmReg);
600 pdm = &dmReg;
601 }
602 else if (iModeNum == ENUM_CURRENT_SETTINGS)
603 {
604 /* Get the current settings */
605 Status = UserEnumCurrentDisplaySettings(pustrDevice, &pdm);
606 }
607 else
608 {
609 /* Get specified settings */
610 Status = UserEnumDisplaySettings(pustrDevice, iModeNum, &pdm, dwFlags);
611 }
612
613 /* Release lock */
614 UserLeave();
615
616 /* Did we succeed? */
617 if (NT_SUCCESS(Status))
618 {
619 /* Copy some information back */
620 _SEH2_TRY
621 {
622 ProbeForRead(lpDevMode, sizeof(DEVMODEW), 1);
623 cbSize = lpDevMode->dmSize;
624 cbExtra = lpDevMode->dmDriverExtra;
625
626 ProbeForWrite(lpDevMode, cbSize + cbExtra, 1);
627 /* Output what we got */
628 RtlCopyMemory(lpDevMode, pdm, min(cbSize, pdm->dmSize));
629
630 /* output private/extra driver data */
631 if (cbExtra > 0 && pdm->dmDriverExtra > 0)
632 {
633 RtlCopyMemory((PCHAR)lpDevMode + cbSize,
634 (PCHAR)pdm + pdm->dmSize,
635 min(cbExtra, pdm->dmDriverExtra));
636 }
637 }
638 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
639 {
640 Status = _SEH2_GetExceptionCode();
641 }
642 _SEH2_END;
643 }
644
645 return Status;
646 }
647
648 LONG
649 APIENTRY
650 UserChangeDisplaySettings(
651 PUNICODE_STRING pustrDevice,
652 LPDEVMODEW pdm,
653 HWND hwnd,
654 DWORD flags,
655 LPVOID lParam)
656 {
657 DEVMODEW dm;
658 LONG lResult = DISP_CHANGE_SUCCESSFUL;
659 HKEY hkey;
660 NTSTATUS Status;
661 PPDEVOBJ ppdev;
662 //PDESKTOP pdesk;
663
664 /* If no DEVMODE is given, use registry settings */
665 if (!pdm)
666 {
667 /* Get the registry settings */
668 Status = UserEnumRegistryDisplaySettings(pustrDevice, &dm);
669 if (!NT_SUCCESS(Status))
670 {
671 ERR("Could not load registry settings\n");
672 return DISP_CHANGE_BADPARAM;
673 }
674 }
675 else if (pdm->dmSize < FIELD_OFFSET(DEVMODEW, dmFields))
676 return DISP_CHANGE_BADMODE; /* This is what winXP SP3 returns */
677 else
678 dm = *pdm;
679
680 /* Check params */
681 if ((dm.dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
682 {
683 ERR("devmode doesn't specify the resolution.\n");
684 return DISP_CHANGE_BADMODE;
685 }
686
687 /* Get the PDEV */
688 ppdev = EngpGetPDEV(pustrDevice);
689 if (!ppdev)
690 {
691 ERR("failed to get PDEV\n");
692 return DISP_CHANGE_BADPARAM;
693 }
694
695 /* Fixup values */
696 if(dm.dmBitsPerPel == 0 || !(dm.dmFields & DM_BITSPERPEL))
697 {
698 dm.dmBitsPerPel = ppdev->pdmwDev->dmBitsPerPel;
699 dm.dmFields |= DM_BITSPERPEL;
700 }
701
702 if((dm.dmFields & DM_DISPLAYFREQUENCY) && (dm.dmDisplayFrequency == 0))
703 dm.dmDisplayFrequency = ppdev->pdmwDev->dmDisplayFrequency;
704
705 /* Look for the requested DEVMODE */
706 pdm = PDEVOBJ_pdmMatchDevMode(ppdev, &dm);
707 if (!pdm)
708 {
709 ERR("Could not find a matching DEVMODE\n");
710 lResult = DISP_CHANGE_BADMODE;
711 goto leave;
712 }
713 else if (flags & CDS_TEST)
714 {
715 /* It's possible, go ahead! */
716 lResult = DISP_CHANGE_SUCCESSFUL;
717 goto leave;
718 }
719
720 /* Shall we update the registry? */
721 if (flags & CDS_UPDATEREGISTRY)
722 {
723 /* Open the local or global settings key */
724 Status = UserOpenDisplaySettingsKey(&hkey, pustrDevice, flags & CDS_GLOBAL);
725 if (NT_SUCCESS(Status))
726 {
727 /* Store the settings */
728 RegWriteDisplaySettings(hkey, pdm);
729
730 /* Close the registry key */
731 ZwClose(hkey);
732 }
733 else
734 {
735 ERR("Could not open registry key\n");
736 lResult = DISP_CHANGE_NOTUPDATED;
737 }
738 }
739
740 /* Check if DEVMODE matches the current mode */
741 if (pdm == ppdev->pdmwDev && !(flags & CDS_RESET))
742 {
743 ERR("DEVMODE matches, nothing to do\n");
744 goto leave;
745 }
746
747 /* Shall we apply the settings? */
748 if (!(flags & CDS_NORESET))
749 {
750 ULONG ulResult;
751 PVOID pvOldCursor;
752
753 /* Remove mouse pointer */
754 pvOldCursor = UserSetCursor(NULL, TRUE);
755
756 /* Do the mode switch */
757 ulResult = PDEVOBJ_bSwitchMode(ppdev, pdm);
758
759 /* Restore mouse pointer, no hooks called */
760 UserSetCursor(pvOldCursor, TRUE);
761
762 /* Check for failure */
763 if (!ulResult)
764 {
765 ERR("failed to set mode\n");
766 lResult = (lResult == DISP_CHANGE_NOTUPDATED) ?
767 DISP_CHANGE_FAILED : DISP_CHANGE_RESTART;
768
769 goto leave;
770 }
771
772 /* Update the system metrics */
773 InitMetrics();
774
775 //IntvGetDeviceCaps(&PrimarySurface, &GdiHandleTable->DevCaps);
776
777 /* Set new size of the monitor */
778 IntUpdateMonitorSize(ppdev);
779
780 /* Remove all cursor clipping */
781 UserClipCursor(NULL);
782
783 //pdesk = IntGetActiveDesktop();
784 //IntHideDesktop(pdesk);
785
786 /* Send WM_DISPLAYCHANGE to all toplevel windows */
787 co_IntSendMessageTimeout(HWND_BROADCAST,
788 WM_DISPLAYCHANGE,
789 (WPARAM)ppdev->gdiinfo.cBitsPixel,
790 (LPARAM)(ppdev->gdiinfo.ulHorzRes + (ppdev->gdiinfo.ulVertRes << 16)),
791 SMTO_NORMAL,
792 100,
793 &ulResult);
794
795 //co_IntShowDesktop(pdesk, ppdev->gdiinfo.ulHorzRes, ppdev->gdiinfo.ulVertRes);
796
797 UserRedrawDesktop();
798 }
799
800 leave:
801 /* Release the PDEV */
802 PDEVOBJ_vRelease(ppdev);
803
804 return lResult;
805 }
806
807 LONG
808 APIENTRY
809 NtUserChangeDisplaySettings(
810 PUNICODE_STRING pustrDevice,
811 LPDEVMODEW lpDevMode,
812 HWND hwnd,
813 DWORD dwflags,
814 LPVOID lParam)
815 {
816 WCHAR awcDevice[CCHDEVICENAME];
817 UNICODE_STRING ustrDevice;
818 DEVMODEW dmLocal;
819 LONG lRet;
820
821 /* Check arguments */
822 if ((dwflags != CDS_VIDEOPARAMETERS && lParam != NULL) ||
823 (hwnd != NULL))
824 {
825 EngSetLastError(ERROR_INVALID_PARAMETER);
826 return DISP_CHANGE_BADPARAM;
827 }
828
829 /* Check flags */
830 if ((dwflags & (CDS_GLOBAL|CDS_NORESET)) && !(dwflags & CDS_UPDATEREGISTRY))
831 {
832 return DISP_CHANGE_BADFLAGS;
833 }
834
835 /* Copy the device name */
836 if (pustrDevice)
837 {
838 /* Initialize destination string */
839 RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
840
841 _SEH2_TRY
842 {
843 /* Probe the UNICODE_STRING and the buffer */
844 ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
845 ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
846
847 /* Copy the string */
848 RtlCopyUnicodeString(&ustrDevice, pustrDevice);
849 }
850 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
851 {
852 /* Set and return error */
853 SetLastNtError(_SEH2_GetExceptionCode());
854 _SEH2_YIELD(return DISP_CHANGE_BADPARAM);
855 }
856 _SEH2_END
857
858 pustrDevice = &ustrDevice;
859 }
860
861 /* Copy devmode */
862 if (lpDevMode)
863 {
864 _SEH2_TRY
865 {
866 /* Probe the size field of the structure */
867 ProbeForRead(lpDevMode, sizeof(dmLocal.dmSize), 1);
868
869 /* Calculate usable size */
870 dmLocal.dmSize = min(sizeof(dmLocal), lpDevMode->dmSize);
871
872 /* Probe and copy the full DEVMODE */
873 ProbeForRead(lpDevMode, dmLocal.dmSize, 1);
874 RtlCopyMemory(&dmLocal, lpDevMode, dmLocal.dmSize);
875 }
876 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
877 {
878 /* Set and return error */
879 SetLastNtError(_SEH2_GetExceptionCode());
880 _SEH2_YIELD(return DISP_CHANGE_BADPARAM);
881 }
882 _SEH2_END
883
884 /* Check for extra parameters */
885 if (dmLocal.dmDriverExtra > 0)
886 {
887 /* FIXME: TODO */
888 ERR("lpDevMode->dmDriverExtra is IGNORED!\n");
889 dmLocal.dmDriverExtra = 0;
890 }
891
892 /* Use the local structure */
893 lpDevMode = &dmLocal;
894 }
895
896 // FIXME: Copy videoparameters
897
898 /* Acquire global USER lock */
899 UserEnterExclusive();
900
901 /* Call internal function */
902 lRet = UserChangeDisplaySettings(pustrDevice, lpDevMode, hwnd, dwflags, NULL);
903
904 /* Release lock */
905 UserLeave();
906
907 return lRet;
908 }
909