b5ea068167a1f65d57717f84f99e983dbe7bf4f8
[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 RtlStringCbCopyW(pdispdev->DeviceName, sizeof(pdispdev->DeviceName), pGraphicsDevice->szWinDeviceName);
313 RtlStringCbCopyW(pdispdev->DeviceString, sizeof(pdispdev->DeviceString), pGraphicsDevice->pwszDescription);
314 pdispdev->StateFlags = pGraphicsDevice->StateFlags;
315 // FIXME: fill in DEVICE ID
316 pdispdev->DeviceID[0] = UNICODE_NULL;
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(dispdev);
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 DPRINT("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
638 LONG
639 APIENTRY
640 UserChangeDisplaySettings(
641 PUNICODE_STRING pustrDevice,
642 LPDEVMODEW pdm,
643 HWND hwnd,
644 DWORD flags,
645 LPVOID lParam)
646 {
647 DEVMODEW dm;
648 LONG lResult = DISP_CHANGE_SUCCESSFUL;
649 HKEY hkey;
650 NTSTATUS Status;
651 PPDEVOBJ ppdev;
652 PDESKTOP pdesk;
653
654 /* If no DEVMODE is given, use registry settings */
655 if (!pdm)
656 {
657 /* Get the registry settings */
658 Status = UserEnumRegistryDisplaySettings(pustrDevice, &dm);
659 if (!NT_SUCCESS(Status))
660 {
661 DPRINT1("Could not load registry settings\n");
662 return DISP_CHANGE_BADPARAM;
663 }
664 }
665 else if (pdm->dmSize < FIELD_OFFSET(DEVMODEW, dmFields))
666 return DISP_CHANGE_BADMODE; /* This is what winXP SP3 returns */
667 else
668 dm = *pdm;
669
670 /* Check params */
671 if ((dm.dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
672 {
673 DPRINT1("devmode doesn't specify the resolution.\n");
674 return DISP_CHANGE_BADMODE;
675 }
676
677 /* Get the PDEV */
678 ppdev = EngpGetPDEV(pustrDevice);
679 if (!ppdev)
680 {
681 DPRINT1("failed to get PDEV\n");
682 return DISP_CHANGE_BADPARAM;
683 }
684
685 /* Fixup values */
686 if(dm.dmBitsPerPel == 0 || !(dm.dmFields & DM_BITSPERPEL))
687 {
688 dm.dmBitsPerPel = ppdev->pdmwDev->dmBitsPerPel;
689 dm.dmFields |= DM_BITSPERPEL;
690 }
691
692 if((dm.dmFields & DM_DISPLAYFREQUENCY) && (dm.dmDisplayFrequency == 0))
693 dm.dmDisplayFrequency = ppdev->pdmwDev->dmDisplayFrequency;
694
695 /* Look for the requested DEVMODE */
696 pdm = PDEVOBJ_pdmMatchDevMode(ppdev, &dm);
697 if (!pdm)
698 {
699 DPRINT1("Could not find a matching DEVMODE\n");
700 lResult = DISP_CHANGE_BADMODE;
701 goto leave;
702 }
703 else if (flags & CDS_TEST)
704 {
705 /* It's possible, go ahead! */
706 lResult = DISP_CHANGE_SUCCESSFUL;
707 goto leave;
708 }
709
710 /* Shall we update the registry? */
711 if (flags & CDS_UPDATEREGISTRY)
712 {
713 /* Open the local or global settings key */
714 Status = UserOpenDisplaySettingsKey(&hkey, pustrDevice, flags & CDS_GLOBAL);
715 if (NT_SUCCESS(Status))
716 {
717 /* Store the settings */
718 RegWriteDisplaySettings(hkey, pdm);
719
720 /* Close the registry key */
721 ZwClose(hkey);
722 }
723 else
724 {
725 DPRINT1("Could not open registry key\n");
726 lResult = DISP_CHANGE_NOTUPDATED;
727 }
728 }
729
730 /* Check if DEVMODE matches the current mode */
731 if (pdm == ppdev->pdmwDev && !(flags & CDS_RESET))
732 {
733 DPRINT1("DEVMODE matches, nothing to do\n");
734 goto leave;
735 }
736
737 /* Shall we apply the settings? */
738 if (!(flags & CDS_NORESET))
739 {
740 ULONG ulResult;
741 PVOID pvOldCursor;
742
743 /* Remove mouse pointer */
744 pvOldCursor = UserSetCursor(NULL, TRUE);
745
746 /* Do the mode switch */
747 ulResult = PDEVOBJ_bSwitchMode(ppdev, pdm);
748
749 /* Restore mouse pointer, no hooks called */
750 UserSetCursor(pvOldCursor, TRUE);
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 /* Set new size of the monitor */
768 IntUpdateMonitorSize(ppdev);
769
770 /* Remove all cursor clipping */
771 UserClipCursor(NULL);
772
773 pdesk = IntGetActiveDesktop();
774 //IntHideDesktop(pdesk);
775
776 /* Send WM_DISPLAYCHANGE to all toplevel windows */
777 co_IntSendMessageTimeout(HWND_BROADCAST,
778 WM_DISPLAYCHANGE,
779 (WPARAM)ppdev->gdiinfo.cBitsPixel,
780 (LPARAM)(ppdev->gdiinfo.ulHorzRes + (ppdev->gdiinfo.ulVertRes << 16)),
781 SMTO_NORMAL,
782 100,
783 &ulResult);
784
785 //co_IntShowDesktop(pdesk, ppdev->gdiinfo.ulHorzRes, ppdev->gdiinfo.ulVertRes);
786
787 UserRedrawDesktop();
788 }
789
790 leave:
791 /* Release the PDEV */
792 PDEVOBJ_vRelease(ppdev);
793
794 return lResult;
795 }
796
797 LONG
798 APIENTRY
799 NtUserChangeDisplaySettings(
800 PUNICODE_STRING pustrDevice,
801 LPDEVMODEW lpDevMode,
802 HWND hwnd,
803 DWORD dwflags,
804 LPVOID lParam)
805 {
806 WCHAR awcDevice[CCHDEVICENAME];
807 UNICODE_STRING ustrDevice;
808 DEVMODEW dmLocal;
809 LONG lRet;
810
811 /* Check arguments */
812 if ((dwflags != CDS_VIDEOPARAMETERS && lParam != NULL) ||
813 (hwnd != NULL))
814 {
815 EngSetLastError(ERROR_INVALID_PARAMETER);
816 return DISP_CHANGE_BADPARAM;
817 }
818
819 /* Check flags */
820 if ((dwflags & (CDS_GLOBAL|CDS_NORESET)) && !(dwflags & CDS_UPDATEREGISTRY))
821 {
822 return DISP_CHANGE_BADFLAGS;
823 }
824
825 /* Copy the device name */
826 if (pustrDevice)
827 {
828 /* Initialize destination string */
829 RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
830
831 _SEH2_TRY
832 {
833 /* Probe the UNICODE_STRING and the buffer */
834 ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
835 ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
836
837 /* Copy the string */
838 RtlCopyUnicodeString(&ustrDevice, pustrDevice);
839 }
840 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
841 {
842 /* Set and return error */
843 SetLastNtError(_SEH2_GetExceptionCode());
844 _SEH2_YIELD(return DISP_CHANGE_BADPARAM);
845 }
846 _SEH2_END
847
848 pustrDevice = &ustrDevice;
849 }
850
851 /* Copy devmode */
852 if (lpDevMode)
853 {
854 _SEH2_TRY
855 {
856 /* Probe the size field of the structure */
857 ProbeForRead(lpDevMode, sizeof(dmLocal.dmSize), 1);
858
859 /* Calculate usable size */
860 dmLocal.dmSize = min(sizeof(dmLocal), lpDevMode->dmSize);
861
862 /* Probe and copy the full DEVMODE */
863 ProbeForRead(lpDevMode, dmLocal.dmSize, 1);
864 RtlCopyMemory(&dmLocal, lpDevMode, dmLocal.dmSize);
865 }
866 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
867 {
868 /* Set and return error */
869 SetLastNtError(_SEH2_GetExceptionCode());
870 _SEH2_YIELD(return DISP_CHANGE_BADPARAM);
871 }
872 _SEH2_END
873
874 /* Check for extra parameters */
875 if (dmLocal.dmDriverExtra > 0)
876 {
877 /* FIXME: TODO */
878 DPRINT1("lpDevMode->dmDriverExtra is IGNORED!\n");
879 dmLocal.dmDriverExtra = 0;
880 }
881
882 /* Use the local structure */
883 lpDevMode = &dmLocal;
884 }
885
886 // FIXME: Copy videoparameters
887
888 /* Acquire global USER lock */
889 UserEnterExclusive();
890
891 /* Call internal function */
892 lRet = UserChangeDisplaySettings(pustrDevice, lpDevMode, hwnd, dwflags, NULL);
893
894 /* Release lock */
895 UserLeave();
896
897 return lRet;
898 }
899