* Sync up to trunk head (r64894).
[reactos.git] / win32ss / user / 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 = (USHORT)cbSize;
106 ustrDisplayDrivers.Length = (USHORT)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 = (USHORT)cbSize;
121 ustrDescription.Length = (USHORT)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 HARDWARE\\DEVICEMAP\\VIDEO registry key:0x%lx\n", Status);
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 = %lu\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 %lu 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, %lu)\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 /* If name is given only iDevNum==0 gives results */
386 if (pustrDevice && iDevNum != 0)
387 return FALSE;
388
389 /* Acquire global USER lock */
390 UserEnterShared();
391
392 /* Call the internal function */
393 Status = UserEnumDisplayDevices(pustrDevice, iDevNum, &dispdev, dwFlags);
394
395 /* Release lock */
396 UserLeave();
397
398 /* On success copy data to caller */
399 if (NT_SUCCESS(Status))
400 {
401 /* Enter SEH */
402 _SEH2_TRY
403 {
404 /* First probe the cb field */
405 ProbeForWrite(&pDisplayDevice->cb, sizeof(DWORD), 1);
406
407 /* Check the buffer size */
408 if (pDisplayDevice->cb)
409 {
410 /* Probe the output buffer */
411 pDisplayDevice->cb = min(pDisplayDevice->cb, sizeof(dispdev));
412 ProbeForWrite(pDisplayDevice, pDisplayDevice->cb, 1);
413
414 /* Copy as much as the given buffer allows */
415 RtlCopyMemory(pDisplayDevice, &dispdev, pDisplayDevice->cb);
416 }
417 }
418 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
419 {
420 Status = _SEH2_GetExceptionCode();
421 }
422 _SEH2_END
423 }
424
425 TRACE("Leave NtUserEnumDisplayDevices, Status = 0x%lx\n", Status);
426 /* Return the result */
427 // return Status;
428 return NT_SUCCESS(Status); // FIXME
429 }
430
431 NTSTATUS
432 NTAPI
433 UserEnumCurrentDisplaySettings(
434 PUNICODE_STRING pustrDevice,
435 PDEVMODEW *ppdm)
436 {
437 PPDEVOBJ ppdev;
438
439 /* Get the PDEV for the device */
440 ppdev = EngpGetPDEV(pustrDevice);
441 if (!ppdev)
442 {
443 /* No device found */
444 ERR("No PDEV found!\n");
445 return STATUS_UNSUCCESSFUL;
446 }
447
448 *ppdm = ppdev->pdmwDev;
449 PDEVOBJ_vRelease(ppdev);
450
451 return STATUS_SUCCESS;
452 }
453
454 NTSTATUS
455 NTAPI
456 UserEnumDisplaySettings(
457 PUNICODE_STRING pustrDevice,
458 DWORD iModeNum,
459 LPDEVMODEW *ppdm,
460 DWORD dwFlags)
461 {
462 PGRAPHICS_DEVICE pGraphicsDevice;
463 PDEVMODEENTRY pdmentry;
464 ULONG i, iFoundMode;
465
466 TRACE("Enter UserEnumDisplaySettings('%wZ', %lu)\n",
467 pustrDevice, iModeNum);
468
469 /* Ask GDI for the GRAPHICS_DEVICE */
470 pGraphicsDevice = EngpFindGraphicsDevice(pustrDevice, 0, 0);
471
472 if (!pGraphicsDevice)
473 {
474 /* No device found */
475 ERR("No device found!\n");
476 return STATUS_UNSUCCESSFUL;
477 }
478
479 if (iModeNum >= pGraphicsDevice->cDevModes)
480 return STATUS_NO_MORE_ENTRIES;
481
482 iFoundMode = 0;
483 for (i = 0; i < pGraphicsDevice->cDevModes; i++)
484 {
485 pdmentry = &pGraphicsDevice->pDevModeList[i];
486
487 /* FIXME: Consider EDS_RAWMODE */
488 #if 0
489 if ((!(dwFlags & EDS_RAWMODE) && (pdmentry->dwFlags & 1)) ||!
490 (dwFlags & EDS_RAWMODE))
491 #endif
492 {
493 /* Is this the one we want? */
494 if (iFoundMode == iModeNum)
495 {
496 *ppdm = pdmentry->pdm;
497 return STATUS_SUCCESS;
498 }
499
500 /* Increment number of found modes */
501 iFoundMode++;
502 }
503 }
504
505 /* Nothing was found */
506 return STATUS_INVALID_PARAMETER;
507 }
508
509 NTSTATUS
510 NTAPI
511 UserOpenDisplaySettingsKey(
512 OUT PHKEY phkey,
513 IN PUNICODE_STRING pustrDevice,
514 IN BOOL bGlobal)
515 {
516 HKEY hkey;
517 DISPLAY_DEVICEW dispdev;
518 NTSTATUS Status;
519
520 /* Get device info */
521 Status = UserEnumDisplayDevices(pustrDevice, 0, &dispdev, 0);
522 if (!NT_SUCCESS(Status))
523 return Status;
524
525 if (bGlobal)
526 {
527 // FIXME: Need to fix the registry key somehow
528 }
529
530 /* Open the registry key */
531 Status = RegOpenKey(dispdev.DeviceKey, &hkey);
532 if (!NT_SUCCESS(Status))
533 return Status;
534
535 *phkey = hkey;
536
537 return Status;
538 }
539
540 NTSTATUS
541 NTAPI
542 UserEnumRegistryDisplaySettings(
543 IN PUNICODE_STRING pustrDevice,
544 OUT LPDEVMODEW pdm)
545 {
546 HKEY hkey;
547 NTSTATUS Status = UserOpenDisplaySettingsKey(&hkey, pustrDevice, 0);
548 if(NT_SUCCESS(Status))
549 {
550 RegReadDisplaySettings(hkey, pdm);
551 ZwClose(hkey);
552 return STATUS_SUCCESS;
553 }
554 return Status ;
555 }
556
557 NTSTATUS
558 APIENTRY
559 NtUserEnumDisplaySettings(
560 IN PUNICODE_STRING pustrDevice,
561 IN DWORD iModeNum,
562 OUT LPDEVMODEW lpDevMode,
563 IN DWORD dwFlags)
564 {
565 UNICODE_STRING ustrDevice;
566 WCHAR awcDevice[CCHDEVICENAME];
567 NTSTATUS Status;
568 ULONG cbSize, cbExtra;
569 DEVMODEW dmReg, *pdm;
570
571 TRACE("Enter NtUserEnumDisplaySettings(%wZ, %lu, %p, 0x%lx)\n",
572 pustrDevice, iModeNum, lpDevMode, dwFlags);
573
574 if (pustrDevice)
575 {
576 /* Initialize destination string */
577 RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
578
579 _SEH2_TRY
580 {
581 /* Probe the UNICODE_STRING and the buffer */
582 ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
583 ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
584
585 /* Copy the string */
586 RtlCopyUnicodeString(&ustrDevice, pustrDevice);
587 }
588 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
589 {
590 _SEH2_YIELD(return _SEH2_GetExceptionCode());
591 }
592 _SEH2_END
593
594 pustrDevice = &ustrDevice;
595 }
596
597 /* Acquire global USER lock */
598 UserEnterShared();
599
600 if (iModeNum == ENUM_REGISTRY_SETTINGS)
601 {
602 /* Get the registry settings */
603 Status = UserEnumRegistryDisplaySettings(pustrDevice, &dmReg);
604 pdm = &dmReg;
605 }
606 else if (iModeNum == ENUM_CURRENT_SETTINGS)
607 {
608 /* Get the current settings */
609 Status = UserEnumCurrentDisplaySettings(pustrDevice, &pdm);
610 }
611 else
612 {
613 /* Get specified settings */
614 Status = UserEnumDisplaySettings(pustrDevice, iModeNum, &pdm, dwFlags);
615 }
616
617 /* Release lock */
618 UserLeave();
619
620 /* Did we succeed? */
621 if (NT_SUCCESS(Status))
622 {
623 /* Copy some information back */
624 _SEH2_TRY
625 {
626 ProbeForRead(lpDevMode, sizeof(DEVMODEW), 1);
627 cbSize = lpDevMode->dmSize;
628 cbExtra = lpDevMode->dmDriverExtra;
629
630 ProbeForWrite(lpDevMode, cbSize + cbExtra, 1);
631 /* Output what we got */
632 RtlCopyMemory(lpDevMode, pdm, min(cbSize, pdm->dmSize));
633
634 /* Output private/extra driver data */
635 if (cbExtra > 0 && pdm->dmDriverExtra > 0)
636 {
637 RtlCopyMemory((PCHAR)lpDevMode + cbSize,
638 (PCHAR)pdm + pdm->dmSize,
639 min(cbExtra, pdm->dmDriverExtra));
640 }
641 }
642 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
643 {
644 Status = _SEH2_GetExceptionCode();
645 }
646 _SEH2_END;
647 }
648
649 return Status;
650 }
651
652 LONG
653 APIENTRY
654 UserChangeDisplaySettings(
655 PUNICODE_STRING pustrDevice,
656 LPDEVMODEW pdm,
657 HWND hwnd,
658 DWORD flags,
659 LPVOID lParam)
660 {
661 DEVMODEW dm;
662 LONG lResult = DISP_CHANGE_SUCCESSFUL;
663 HKEY hkey;
664 NTSTATUS Status;
665 PPDEVOBJ ppdev;
666 //PDESKTOP pdesk;
667
668 /* If no DEVMODE is given, use registry settings */
669 if (!pdm)
670 {
671 /* Get the registry settings */
672 Status = UserEnumRegistryDisplaySettings(pustrDevice, &dm);
673 if (!NT_SUCCESS(Status))
674 {
675 ERR("Could not load registry settings\n");
676 return DISP_CHANGE_BADPARAM;
677 }
678 }
679 else if (pdm->dmSize < FIELD_OFFSET(DEVMODEW, dmFields))
680 return DISP_CHANGE_BADMODE; /* This is what winXP SP3 returns */
681 else
682 dm = *pdm;
683
684 /* Check params */
685 if ((dm.dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
686 {
687 ERR("Devmode doesn't specify the resolution.\n");
688 return DISP_CHANGE_BADMODE;
689 }
690
691 /* Get the PDEV */
692 ppdev = EngpGetPDEV(pustrDevice);
693 if (!ppdev)
694 {
695 ERR("Failed to get PDEV\n");
696 return DISP_CHANGE_BADPARAM;
697 }
698
699 /* Fixup values */
700 if(dm.dmBitsPerPel == 0 || !(dm.dmFields & DM_BITSPERPEL))
701 {
702 dm.dmBitsPerPel = ppdev->pdmwDev->dmBitsPerPel;
703 dm.dmFields |= DM_BITSPERPEL;
704 }
705
706 if((dm.dmFields & DM_DISPLAYFREQUENCY) && (dm.dmDisplayFrequency == 0))
707 dm.dmDisplayFrequency = ppdev->pdmwDev->dmDisplayFrequency;
708
709 /* Look for the requested DEVMODE */
710 pdm = PDEVOBJ_pdmMatchDevMode(ppdev, &dm);
711 if (!pdm)
712 {
713 ERR("Could not find a matching DEVMODE\n");
714 lResult = DISP_CHANGE_BADMODE;
715 goto leave;
716 }
717 else if (flags & CDS_TEST)
718 {
719 /* It's possible, go ahead! */
720 lResult = DISP_CHANGE_SUCCESSFUL;
721 goto leave;
722 }
723
724 /* Shall we update the registry? */
725 if (flags & CDS_UPDATEREGISTRY)
726 {
727 /* Open the local or global settings key */
728 Status = UserOpenDisplaySettingsKey(&hkey, pustrDevice, flags & CDS_GLOBAL);
729 if (NT_SUCCESS(Status))
730 {
731 /* Store the settings */
732 RegWriteDisplaySettings(hkey, pdm);
733
734 /* Close the registry key */
735 ZwClose(hkey);
736 }
737 else
738 {
739 ERR("Could not open registry key\n");
740 lResult = DISP_CHANGE_NOTUPDATED;
741 }
742 }
743
744 /* Check if DEVMODE matches the current mode */
745 if (pdm == ppdev->pdmwDev && !(flags & CDS_RESET))
746 {
747 ERR("DEVMODE matches, nothing to do\n");
748 goto leave;
749 }
750
751 /* Shall we apply the settings? */
752 if (!(flags & CDS_NORESET))
753 {
754 ULONG ulResult;
755 PVOID pvOldCursor;
756
757 /* Remove mouse pointer */
758 pvOldCursor = UserSetCursor(NULL, TRUE);
759
760 /* Do the mode switch */
761 ulResult = PDEVOBJ_bSwitchMode(ppdev, pdm);
762
763 /* Restore mouse pointer, no hooks called */
764 pvOldCursor = UserSetCursor(pvOldCursor, TRUE);
765 ASSERT(pvOldCursor == NULL);
766
767 /* Check for failure */
768 if (!ulResult)
769 {
770 ERR("Failed to set mode\n");
771 lResult = (lResult == DISP_CHANGE_NOTUPDATED) ?
772 DISP_CHANGE_FAILED : DISP_CHANGE_RESTART;
773
774 goto leave;
775 }
776
777 /* Update the system metrics */
778 InitMetrics();
779
780 //IntvGetDeviceCaps(&PrimarySurface, &GdiHandleTable->DevCaps);
781
782 /* Set new size of the monitor */
783 UserUpdateMonitorSize((HDEV)ppdev);
784
785 /* Remove all cursor clipping */
786 UserClipCursor(NULL);
787
788 //pdesk = IntGetActiveDesktop();
789 //IntHideDesktop(pdesk);
790
791 /* Send WM_DISPLAYCHANGE to all toplevel windows */
792 co_IntSendMessageTimeout(HWND_BROADCAST,
793 WM_DISPLAYCHANGE,
794 (WPARAM)ppdev->gdiinfo.cBitsPixel,
795 (LPARAM)(ppdev->gdiinfo.ulHorzRes + (ppdev->gdiinfo.ulVertRes << 16)),
796 SMTO_NORMAL,
797 100,
798 &ulResult);
799
800 //co_IntShowDesktop(pdesk, ppdev->gdiinfo.ulHorzRes, ppdev->gdiinfo.ulVertRes);
801
802 UserRedrawDesktop();
803 }
804
805 leave:
806 /* Release the PDEV */
807 PDEVOBJ_vRelease(ppdev);
808
809 return lResult;
810 }
811
812 LONG
813 APIENTRY
814 NtUserChangeDisplaySettings(
815 PUNICODE_STRING pustrDevice,
816 LPDEVMODEW lpDevMode,
817 HWND hwnd,
818 DWORD dwflags,
819 LPVOID lParam)
820 {
821 WCHAR awcDevice[CCHDEVICENAME];
822 UNICODE_STRING ustrDevice;
823 DEVMODEW dmLocal;
824 LONG lRet;
825
826 /* Check arguments */
827 if ((dwflags != CDS_VIDEOPARAMETERS && lParam != NULL) ||
828 (hwnd != NULL))
829 {
830 EngSetLastError(ERROR_INVALID_PARAMETER);
831 return DISP_CHANGE_BADPARAM;
832 }
833
834 /* Check flags */
835 if ((dwflags & (CDS_GLOBAL|CDS_NORESET)) && !(dwflags & CDS_UPDATEREGISTRY))
836 {
837 return DISP_CHANGE_BADFLAGS;
838 }
839
840 /* Copy the device name */
841 if (pustrDevice)
842 {
843 /* Initialize destination string */
844 RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
845
846 _SEH2_TRY
847 {
848 /* Probe the UNICODE_STRING and the buffer */
849 ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
850 ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
851
852 /* Copy the string */
853 RtlCopyUnicodeString(&ustrDevice, pustrDevice);
854 }
855 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
856 {
857 /* Set and return error */
858 SetLastNtError(_SEH2_GetExceptionCode());
859 _SEH2_YIELD(return DISP_CHANGE_BADPARAM);
860 }
861 _SEH2_END
862
863 pustrDevice = &ustrDevice;
864 }
865
866 /* Copy devmode */
867 if (lpDevMode)
868 {
869 _SEH2_TRY
870 {
871 /* Probe the size field of the structure */
872 ProbeForRead(lpDevMode, sizeof(dmLocal.dmSize), 1);
873
874 /* Calculate usable size */
875 dmLocal.dmSize = min(sizeof(dmLocal), lpDevMode->dmSize);
876
877 /* Probe and copy the full DEVMODE */
878 ProbeForRead(lpDevMode, dmLocal.dmSize, 1);
879 RtlCopyMemory(&dmLocal, lpDevMode, dmLocal.dmSize);
880 }
881 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
882 {
883 /* Set and return error */
884 SetLastNtError(_SEH2_GetExceptionCode());
885 _SEH2_YIELD(return DISP_CHANGE_BADPARAM);
886 }
887 _SEH2_END
888
889 /* Check for extra parameters */
890 if (dmLocal.dmDriverExtra > 0)
891 {
892 /* FIXME: TODO */
893 ERR("lpDevMode->dmDriverExtra is IGNORED!\n");
894 dmLocal.dmDriverExtra = 0;
895 }
896
897 /* Use the local structure */
898 lpDevMode = &dmLocal;
899 }
900
901 // FIXME: Copy videoparameters
902
903 /* Acquire global USER lock */
904 UserEnterExclusive();
905
906 /* Call internal function */
907 lRet = UserChangeDisplaySettings(pustrDevice, lpDevMode, hwnd, dwflags, NULL);
908
909 /* Release lock */
910 UserLeave();
911
912 return lRet;
913 }
914