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