xref: /reactos/win32ss/user/ntuser/display.c (revision 08d10098)
1 /*
2  * COPYRIGHT:        See COPYING in the top level directory
3  * PROJECT:          ReactOS kernel
4  * PURPOSE:          Video initialization and display settings
5  * FILE:             win32ss/user/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 = FALSE;
13 static PPROCESSINFO gpFullscreen = NULL;
14 
15 static const PWCHAR KEY_VIDEO = L"\\Registry\\Machine\\HARDWARE\\DEVICEMAP\\VIDEO";
16 
17 VOID
18 RegWriteDisplaySettings(HKEY hkey, PDEVMODEW pdm)
19 {
20     RegWriteDWORD(hkey, L"DefaultSettings.BitsPerPel", pdm->dmBitsPerPel);
21     RegWriteDWORD(hkey, L"DefaultSettings.XResolution", pdm->dmPelsWidth);
22     RegWriteDWORD(hkey, L"DefaultSettings.YResolution", pdm->dmPelsHeight);
23     RegWriteDWORD(hkey, L"DefaultSettings.Flags", pdm->dmDisplayFlags);
24     RegWriteDWORD(hkey, L"DefaultSettings.VRefresh", pdm->dmDisplayFrequency);
25     RegWriteDWORD(hkey, L"DefaultSettings.XPanning", pdm->dmPanningWidth);
26     RegWriteDWORD(hkey, L"DefaultSettings.YPanning", pdm->dmPanningHeight);
27     RegWriteDWORD(hkey, L"DefaultSettings.Orientation", pdm->dmDisplayOrientation);
28     RegWriteDWORD(hkey, L"DefaultSettings.FixedOutput", pdm->dmDisplayFixedOutput);
29     RegWriteDWORD(hkey, L"Attach.RelativeX", pdm->dmPosition.x);
30     RegWriteDWORD(hkey, L"Attach.RelativeY", pdm->dmPosition.y);
31 //    RegWriteDWORD(hkey, L"Attach.ToDesktop, pdm->dmBitsPerPel", pdm->);
32 }
33 
34 VOID
35 RegReadDisplaySettings(HKEY hkey, PDEVMODEW pdm)
36 {
37     DWORD dwValue;
38 
39     /* Zero out the structure */
40     RtlZeroMemory(pdm, sizeof(DEVMODEW));
41 
42 /* Helper macro */
43 #define READ(field, str, flag) \
44     if (RegReadDWORD(hkey, L##str, &dwValue)) \
45     { \
46         pdm->field = dwValue; \
47         pdm->dmFields |= flag; \
48     }
49 
50     /* Read all present settings */
51     READ(dmBitsPerPel, "DefaultSettings.BitsPerPel", DM_BITSPERPEL);
52     READ(dmPelsWidth, "DefaultSettings.XResolution", DM_PELSWIDTH);
53     READ(dmPelsHeight, "DefaultSettings.YResolution", DM_PELSHEIGHT);
54     READ(dmDisplayFlags, "DefaultSettings.Flags", DM_DISPLAYFLAGS);
55     READ(dmDisplayFrequency, "DefaultSettings.VRefresh", DM_DISPLAYFREQUENCY);
56     READ(dmPanningWidth, "DefaultSettings.XPanning", DM_PANNINGWIDTH);
57     READ(dmPanningHeight, "DefaultSettings.YPanning", DM_PANNINGHEIGHT);
58     READ(dmDisplayOrientation, "DefaultSettings.Orientation", DM_DISPLAYORIENTATION);
59     READ(dmDisplayFixedOutput, "DefaultSettings.FixedOutput", DM_DISPLAYFIXEDOUTPUT);
60     READ(dmPosition.x, "Attach.RelativeX", DM_POSITION);
61     READ(dmPosition.y, "Attach.RelativeY", DM_POSITION);
62 }
63 
64 PGRAPHICS_DEVICE
65 NTAPI
66 InitDisplayDriver(
67     IN PWSTR pwszDeviceName,
68     IN PWSTR pwszRegKey)
69 {
70     PGRAPHICS_DEVICE pGraphicsDevice;
71     UNICODE_STRING ustrDeviceName, ustrDisplayDrivers, ustrDescription;
72     NTSTATUS Status;
73     WCHAR awcBuffer[128];
74     ULONG cbSize;
75     HKEY hkey;
76     DWORD dwVga;
77 
78     TRACE("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 if this is a VGA compatible driver */
129     cbSize = sizeof(DWORD);
130     Status = RegQueryValue(hkey, L"VgaCompatible", REG_DWORD, &dwVga, &cbSize);
131     if (!NT_SUCCESS(Status)) dwVga = 0;
132 
133     /* Close the registry key */
134     ZwClose(hkey);
135 
136     /* Register the device with GDI */
137     RtlInitUnicodeString(&ustrDeviceName, pwszDeviceName);
138     pGraphicsDevice = EngpRegisterGraphicsDevice(&ustrDeviceName,
139                                                  &ustrDisplayDrivers,
140                                                  &ustrDescription);
141     if (pGraphicsDevice && dwVga)
142     {
143         pGraphicsDevice->StateFlags |= DISPLAY_DEVICE_VGA_COMPATIBLE;
144     }
145 
146     return pGraphicsDevice;
147 }
148 
149 NTSTATUS
150 NTAPI
151 InitVideo(VOID)
152 {
153     NTSTATUS Status;
154     HKEY hkey;
155 
156     TRACE("----------------------------- InitVideo() -------------------------------\n");
157 
158     /* Check if VGA mode is requested, by finding the special volatile key created by VIDEOPRT */
159     Status = RegOpenKey(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\GraphicsDrivers\\BaseVideo", &hkey);
160     if (NT_SUCCESS(Status))
161         ZwClose(hkey);
162     gbBaseVideo = NT_SUCCESS(Status);
163     if (gbBaseVideo)
164         ERR("VGA mode requested.\n");
165 
166     /* Initialize all display devices */
167     Status = EngpUpdateGraphicsDeviceList();
168     if (!NT_SUCCESS(Status))
169         return Status;
170 
171     /* Check if we had any success */
172     if (!gpPrimaryGraphicsDevice)
173     {
174         /* Check if there is a VGA device we skipped */
175         if (gpVgaGraphicsDevice)
176         {
177             /* There is, use the VGA device */
178             gpPrimaryGraphicsDevice = gpVgaGraphicsDevice;
179         }
180         else
181         {
182             ERR("No usable display driver was found.\n");
183             return STATUS_UNSUCCESSFUL;
184         }
185     }
186 
187     InitSysParams();
188 
189     return STATUS_SUCCESS;
190 }
191 
192 VOID
193 UserRefreshDisplay(IN PPDEVOBJ ppdev)
194 {
195     ULONG_PTR ulResult;
196     // PVOID pvOldCursor;
197 
198     // TODO: Re-enable the cursor reset code once this function becomes called
199     // from within a Win32 thread... Indeed UserSetCursor() requires this, but
200     // at the moment this function is directly called from a separate thread
201     // from within videoprt, instead of by a separate win32k system thread.
202 
203     if (!ppdev)
204         return;
205 
206     PDEVOBJ_vReference(ppdev);
207 
208     /* Remove mouse pointer */
209     // pvOldCursor = UserSetCursor(NULL, TRUE);
210 
211     /* Do the mode switch -- Use the actual same current mode */
212     ulResult = PDEVOBJ_bSwitchMode(ppdev, ppdev->pdmwDev);
213     ASSERT(ulResult);
214 
215     /* Restore mouse pointer, no hooks called */
216     // pvOldCursor = UserSetCursor(pvOldCursor, TRUE);
217     // ASSERT(pvOldCursor == NULL);
218 
219     /* Update the system metrics */
220     InitMetrics();
221 
222     /* Set new size of the monitor */
223     // UserUpdateMonitorSize((HDEV)ppdev);
224 
225     //co_IntShowDesktop(pdesk, ppdev->gdiinfo.ulHorzRes, ppdev->gdiinfo.ulVertRes);
226     UserRedrawDesktop();
227 
228     PDEVOBJ_vRelease(ppdev);
229 }
230 
231 NTSTATUS
232 NTAPI
233 UserEnumDisplayDevices(
234     PUNICODE_STRING pustrDevice,
235     DWORD iDevNum,
236     PDISPLAY_DEVICEW pdispdev,
237     DWORD dwFlags)
238 {
239     PGRAPHICS_DEVICE pGraphicsDevice;
240     ULONG cbSize;
241     HKEY hkey;
242     NTSTATUS Status;
243 
244     if (!pustrDevice)
245     {
246         /* Check if some devices have been added since last time */
247         EngpUpdateGraphicsDeviceList();
248     }
249 
250     /* Ask gdi for the GRAPHICS_DEVICE */
251     pGraphicsDevice = EngpFindGraphicsDevice(pustrDevice, iDevNum);
252     if (!pGraphicsDevice)
253     {
254         /* No device found */
255         ERR("No GRAPHICS_DEVICE found for '%wZ', iDevNum %lu\n", pustrDevice, iDevNum);
256         return STATUS_UNSUCCESSFUL;
257     }
258 
259     /* Open the device map registry key */
260     Status = RegOpenKey(KEY_VIDEO, &hkey);
261     if (!NT_SUCCESS(Status))
262     {
263         /* No device found */
264         ERR("Could not open reg key\n");
265         return STATUS_UNSUCCESSFUL;
266     }
267 
268     /* Query the registry path */
269     cbSize = sizeof(pdispdev->DeviceKey);
270     RegQueryValue(hkey,
271                   pGraphicsDevice->szNtDeviceName,
272                   REG_SZ,
273                   pdispdev->DeviceKey,
274                   &cbSize);
275 
276     /* Close registry key */
277     ZwClose(hkey);
278 
279     /* Copy device name, device string and StateFlags */
280     RtlStringCbCopyW(pdispdev->DeviceName, sizeof(pdispdev->DeviceName), pGraphicsDevice->szWinDeviceName);
281     RtlStringCbCopyW(pdispdev->DeviceString, sizeof(pdispdev->DeviceString), pGraphicsDevice->pwszDescription);
282     pdispdev->StateFlags = pGraphicsDevice->StateFlags;
283     // FIXME: fill in DEVICE ID
284     pdispdev->DeviceID[0] = UNICODE_NULL;
285 
286     return STATUS_SUCCESS;
287 }
288 
289 //NTSTATUS
290 BOOL
291 NTAPI
292 NtUserEnumDisplayDevices(
293     PUNICODE_STRING pustrDevice,
294     DWORD iDevNum,
295     PDISPLAY_DEVICEW pDisplayDevice,
296     DWORD dwFlags)
297 {
298     UNICODE_STRING ustrDevice;
299     WCHAR awcDevice[CCHDEVICENAME];
300     DISPLAY_DEVICEW dispdev;
301     NTSTATUS Status;
302 
303     TRACE("Enter NtUserEnumDisplayDevices(%wZ, %lu)\n",
304           pustrDevice, iDevNum);
305 
306     dispdev.cb = sizeof(dispdev);
307 
308     if (pustrDevice)
309     {
310         /* Initialize destination string */
311         RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
312 
313         _SEH2_TRY
314         {
315             /* Probe the UNICODE_STRING and the buffer */
316             ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
317             ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
318 
319             /* Copy the string */
320             RtlCopyUnicodeString(&ustrDevice, pustrDevice);
321         }
322         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
323         {
324 //            _SEH2_YIELD(return _SEH2_GetExceptionCode());
325             _SEH2_YIELD(return NT_SUCCESS(_SEH2_GetExceptionCode()));
326         }
327         _SEH2_END
328 
329         if (ustrDevice.Length > 0)
330             pustrDevice = &ustrDevice;
331         else
332             pustrDevice = NULL;
333    }
334 
335     /* If name is given only iDevNum==0 gives results */
336     if (pustrDevice && iDevNum != 0)
337         return FALSE;
338 
339     /* Acquire global USER lock */
340     UserEnterShared();
341 
342     /* Call the internal function */
343     Status = UserEnumDisplayDevices(pustrDevice, iDevNum, &dispdev, dwFlags);
344 
345     /* Release lock */
346     UserLeave();
347 
348     /* On success copy data to caller */
349     if (NT_SUCCESS(Status))
350     {
351         /* Enter SEH */
352         _SEH2_TRY
353         {
354             /* First probe the cb field */
355             ProbeForWrite(&pDisplayDevice->cb, sizeof(DWORD), 1);
356 
357             /* Check the buffer size */
358             if (pDisplayDevice->cb)
359             {
360                 /* Probe the output buffer */
361                 pDisplayDevice->cb = min(pDisplayDevice->cb, sizeof(dispdev));
362                 ProbeForWrite(pDisplayDevice, pDisplayDevice->cb, 1);
363 
364                 /* Copy as much as the given buffer allows */
365                 RtlCopyMemory(pDisplayDevice, &dispdev, pDisplayDevice->cb);
366             }
367         }
368         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
369         {
370             Status = _SEH2_GetExceptionCode();
371         }
372         _SEH2_END
373     }
374 
375     TRACE("Leave NtUserEnumDisplayDevices, Status = 0x%lx\n", Status);
376     /* Return the result */
377 //    return Status;
378     return NT_SUCCESS(Status); // FIXME
379 }
380 
381 NTSTATUS
382 NTAPI
383 UserEnumCurrentDisplaySettings(
384     PUNICODE_STRING pustrDevice,
385     PDEVMODEW *ppdm)
386 {
387     PPDEVOBJ ppdev;
388 
389     /* Get the PDEV for the device */
390     ppdev = EngpGetPDEV(pustrDevice);
391     if (!ppdev)
392     {
393         /* No device found */
394         ERR("No PDEV found!\n");
395         return STATUS_INVALID_PARAMETER_1;
396     }
397 
398     *ppdm = ppdev->pdmwDev;
399     PDEVOBJ_vRelease(ppdev);
400 
401     return STATUS_SUCCESS;
402 }
403 
404 NTSTATUS
405 NTAPI
406 UserEnumDisplaySettings(
407    PUNICODE_STRING pustrDevice,
408    DWORD iModeNum,
409    LPDEVMODEW *ppdm,
410    DWORD dwFlags)
411 {
412     PGRAPHICS_DEVICE pGraphicsDevice;
413     PDEVMODEENTRY pdmentry;
414     ULONG i, iFoundMode;
415     PPDEVOBJ ppdev;
416 
417     TRACE("Enter UserEnumDisplaySettings('%wZ', %lu)\n",
418           pustrDevice, iModeNum);
419 
420     /* Ask GDI for the GRAPHICS_DEVICE */
421     pGraphicsDevice = EngpFindGraphicsDevice(pustrDevice, 0);
422     ppdev = EngpGetPDEV(pustrDevice);
423 
424     if (!pGraphicsDevice || !ppdev)
425     {
426         /* No device found */
427         ERR("No device found!\n");
428         return STATUS_INVALID_PARAMETER_1;
429     }
430 
431     /* let's politely ask the driver for an updated mode list,
432        just in case there's something new in there (vbox) */
433 
434     PDEVOBJ_vRefreshModeList(ppdev);
435     PDEVOBJ_vRelease(ppdev);
436 
437     iFoundMode = 0;
438     for (i = 0; i < pGraphicsDevice->cDevModes; i++)
439     {
440         pdmentry = &pGraphicsDevice->pDevModeList[i];
441 
442         /* FIXME: Consider EDS_RAWMODE */
443 #if 0
444         if ((!(dwFlags & EDS_RAWMODE) && (pdmentry->dwFlags & 1)) ||!
445             (dwFlags & EDS_RAWMODE))
446 #endif
447         {
448             /* Is this the one we want? */
449             if (iFoundMode == iModeNum)
450             {
451                 *ppdm = pdmentry->pdm;
452                 return STATUS_SUCCESS;
453             }
454 
455             /* Increment number of found modes */
456             iFoundMode++;
457         }
458     }
459 
460     /* Nothing was found */
461     return STATUS_INVALID_PARAMETER_2;
462 }
463 
464 NTSTATUS
465 NTAPI
466 UserOpenDisplaySettingsKey(
467     OUT PHKEY phkey,
468     IN PUNICODE_STRING pustrDevice,
469     IN BOOL bGlobal)
470 {
471     HKEY hkey;
472     DISPLAY_DEVICEW dispdev;
473     NTSTATUS Status;
474 
475     /* Get device info */
476     Status = UserEnumDisplayDevices(pustrDevice, 0, &dispdev, 0);
477     if (!NT_SUCCESS(Status))
478         return Status;
479 
480     if (bGlobal)
481     {
482         // FIXME: Need to fix the registry key somehow
483     }
484 
485     /* Open the registry key */
486     Status = RegOpenKey(dispdev.DeviceKey, &hkey);
487     if (!NT_SUCCESS(Status))
488         return Status;
489 
490     *phkey = hkey;
491 
492     return Status;
493 }
494 
495 NTSTATUS
496 NTAPI
497 UserEnumRegistryDisplaySettings(
498     IN PUNICODE_STRING pustrDevice,
499     OUT LPDEVMODEW pdm)
500 {
501     HKEY hkey;
502     NTSTATUS Status = UserOpenDisplaySettingsKey(&hkey, pustrDevice, 0);
503     if(NT_SUCCESS(Status))
504     {
505         RegReadDisplaySettings(hkey, pdm);
506         ZwClose(hkey);
507         return STATUS_SUCCESS;
508     }
509     return Status;
510 }
511 
512 NTSTATUS
513 APIENTRY
514 NtUserEnumDisplaySettings(
515     IN PUNICODE_STRING pustrDevice,
516     IN DWORD iModeNum,
517     OUT LPDEVMODEW lpDevMode,
518     IN DWORD dwFlags)
519 {
520     UNICODE_STRING ustrDeviceUser;
521     UNICODE_STRING ustrDevice;
522     WCHAR awcDevice[CCHDEVICENAME];
523     NTSTATUS Status;
524     ULONG cbSize, cbExtra;
525     DEVMODEW dmReg, *pdm;
526 
527     TRACE("Enter NtUserEnumDisplaySettings(%wZ, %lu, %p, 0x%lx)\n",
528           pustrDevice, iModeNum, lpDevMode, dwFlags);
529 
530     _SEH2_TRY
531     {
532         ProbeForRead(lpDevMode, sizeof(DEVMODEW), sizeof(UCHAR));
533 
534         cbSize = lpDevMode->dmSize;
535         cbExtra = lpDevMode->dmDriverExtra;
536 
537         ProbeForWrite(lpDevMode, cbSize + cbExtra, sizeof(UCHAR));
538     }
539     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
540     {
541         _SEH2_YIELD(return _SEH2_GetExceptionCode());
542     }
543     _SEH2_END;
544 
545     if (cbSize != sizeof(DEVMODEW))
546     {
547         return STATUS_BUFFER_TOO_SMALL;
548     }
549 
550     if (pustrDevice)
551     {
552         /* Initialize destination string */
553         RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
554 
555         _SEH2_TRY
556         {
557             /* Probe the UNICODE_STRING and the buffer */
558             ustrDeviceUser = ProbeForReadUnicodeString(pustrDevice);
559 
560             if (!ustrDeviceUser.Length || !ustrDeviceUser.Buffer)
561                 ExRaiseStatus(STATUS_NO_MEMORY);
562 
563             ProbeForRead(ustrDeviceUser.Buffer,
564                          ustrDeviceUser.Length,
565                          sizeof(UCHAR));
566 
567             /* Copy the string */
568             RtlCopyUnicodeString(&ustrDevice, &ustrDeviceUser);
569         }
570         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
571         {
572             _SEH2_YIELD(return STATUS_INVALID_PARAMETER_1);
573         }
574         _SEH2_END;
575 
576         pustrDevice = &ustrDevice;
577     }
578 
579     /* Acquire global USER lock */
580     UserEnterShared();
581 
582     if (iModeNum == ENUM_REGISTRY_SETTINGS)
583     {
584         /* Get the registry settings */
585         Status = UserEnumRegistryDisplaySettings(pustrDevice, &dmReg);
586         pdm = &dmReg;
587         pdm->dmSize = sizeof(DEVMODEW);
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             /* Output what we got */
610             RtlCopyMemory(lpDevMode, pdm, min(cbSize, pdm->dmSize));
611 
612             /* Output private/extra driver data */
613             if (cbExtra > 0 && pdm->dmDriverExtra > 0)
614             {
615                 RtlCopyMemory((PCHAR)lpDevMode + cbSize,
616                               (PCHAR)pdm + pdm->dmSize,
617                               min(cbExtra, pdm->dmDriverExtra));
618             }
619         }
620         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
621         {
622             Status = _SEH2_GetExceptionCode();
623         }
624         _SEH2_END;
625     }
626 
627     return Status;
628 }
629 
630 VOID
631 UserUpdateFullscreen(
632     DWORD flags)
633 {
634     if (flags & CDS_FULLSCREEN)
635         gpFullscreen = gptiCurrent->ppi;
636     else
637         gpFullscreen = NULL;
638 }
639 
640 LONG
641 APIENTRY
642 UserChangeDisplaySettings(
643    PUNICODE_STRING pustrDevice,
644    LPDEVMODEW pdm,
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     WORD OrigBC;
654     //PDESKTOP pdesk;
655     PDEVMODEW newDevMode = NULL;
656 
657     /* If no DEVMODE is given, use registry settings */
658     if (!pdm)
659     {
660         /* Get the registry settings */
661         Status = UserEnumRegistryDisplaySettings(pustrDevice, &dm);
662         if (!NT_SUCCESS(Status))
663         {
664             ERR("Could not load registry settings\n");
665             return DISP_CHANGE_BADPARAM;
666         }
667     }
668     else if (pdm->dmSize < FIELD_OFFSET(DEVMODEW, dmFields))
669     {
670         return DISP_CHANGE_BADMODE; /* This is what WinXP SP3 returns */
671     }
672     else
673     {
674         dm = *pdm;
675     }
676 
677     /* Save original bit count */
678     OrigBC = gpsi->BitCount;
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     if (!LDEVOBJ_bProbeAndCaptureDevmode(ppdev->pGraphicsDevice, &dm, &newDevMode, FALSE))
707     {
708         ERR("Could not find a matching DEVMODE\n");
709         lResult = DISP_CHANGE_BADMODE;
710         goto leave;
711     }
712     else if (flags & CDS_TEST)
713     {
714         /* It's possible, go ahead! */
715         lResult = DISP_CHANGE_SUCCESSFUL;
716         goto leave;
717     }
718 
719     /* Shall we update the registry? */
720     if (flags & CDS_UPDATEREGISTRY)
721     {
722         /* Open the local or global settings key */
723         Status = UserOpenDisplaySettingsKey(&hkey, pustrDevice, flags & CDS_GLOBAL);
724         if (NT_SUCCESS(Status))
725         {
726             /* Store the settings */
727             RegWriteDisplaySettings(hkey, newDevMode);
728 
729             /* Close the registry key */
730             ZwClose(hkey);
731         }
732         else
733         {
734             ERR("Could not open registry key\n");
735             lResult = DISP_CHANGE_NOTUPDATED;
736         }
737     }
738 
739     /* Check if DEVMODE matches the current mode */
740     if (newDevMode->dmSize == ppdev->pdmwDev->dmSize &&
741         RtlCompareMemory(newDevMode, ppdev->pdmwDev, newDevMode->dmSize) == newDevMode->dmSize &&
742         !(flags & CDS_RESET))
743     {
744         ERR("DEVMODE matches, nothing to do\n");
745         goto leave;
746     }
747 
748     /* Shall we apply the settings? */
749     if (!(flags & CDS_NORESET))
750     {
751         ULONG_PTR ulResult;
752         PVOID pvOldCursor;
753         TEXTMETRICW tmw;
754 
755         /* Remove mouse pointer */
756         pvOldCursor = UserSetCursor(NULL, TRUE);
757 
758         /* Do the mode switch */
759         ulResult = PDEVOBJ_bSwitchMode(ppdev, newDevMode);
760 
761         /* Restore mouse pointer, no hooks called */
762         pvOldCursor = UserSetCursor(pvOldCursor, TRUE);
763         ASSERT(pvOldCursor == NULL);
764 
765         /* Check for success or failure */
766         if (!ulResult)
767         {
768             /* Setting mode failed */
769             ERR("Failed to set mode\n");
770 
771             /* Set the correct return value */
772             if ((flags & CDS_UPDATEREGISTRY) && (lResult != DISP_CHANGE_NOTUPDATED))
773                 lResult = DISP_CHANGE_RESTART;
774             else
775                 lResult = DISP_CHANGE_FAILED;
776         }
777         else
778         {
779             /* Setting mode succeeded */
780             lResult = DISP_CHANGE_SUCCESSFUL;
781             ExFreePoolWithTag(ppdev->pdmwDev, GDITAG_DEVMODE);
782             ppdev->pdmwDev = newDevMode;
783 
784             UserUpdateFullscreen(flags);
785 
786             /* Update the system metrics */
787             InitMetrics();
788 
789             /* Set new size of the monitor */
790             UserUpdateMonitorSize((HDEV)ppdev);
791 
792             /* Update the SERVERINFO */
793             gpsi->dmLogPixels = ppdev->gdiinfo.ulLogPixelsY;
794             gpsi->Planes      = ppdev->gdiinfo.cPlanes;
795             gpsi->BitsPixel   = ppdev->gdiinfo.cBitsPixel;
796             gpsi->BitCount    = gpsi->Planes * gpsi->BitsPixel;
797             gpsi->aiSysMet[SM_CXSCREEN] = ppdev->gdiinfo.ulHorzRes;
798             gpsi->aiSysMet[SM_CYSCREEN] = ppdev->gdiinfo.ulVertRes;
799             if (ppdev->gdiinfo.flRaster & RC_PALETTE)
800             {
801                 gpsi->PUSIFlags |= PUSIF_PALETTEDISPLAY;
802             }
803             else
804             {
805                 gpsi->PUSIFlags &= ~PUSIF_PALETTEDISPLAY;
806             }
807             // Font is realized and this dc was previously set to internal DC_ATTR.
808             gpsi->cxSysFontChar = IntGetCharDimensions(hSystemBM, &tmw, (DWORD*)&gpsi->cySysFontChar);
809             gpsi->tmSysFont     = tmw;
810         }
811 
812         /*
813          * Refresh the display on success and even on failure,
814          * since the display may have been messed up.
815          */
816 
817         /* Remove all cursor clipping */
818         UserClipCursor(NULL);
819 
820         //pdesk = IntGetActiveDesktop();
821         //IntHideDesktop(pdesk);
822 
823         /* Send WM_DISPLAYCHANGE to all toplevel windows */
824         co_IntSendMessageTimeout( HWND_BROADCAST,
825                                   WM_DISPLAYCHANGE,
826                                   gpsi->BitCount,
827                                   MAKELONG(gpsi->aiSysMet[SM_CXSCREEN], gpsi->aiSysMet[SM_CYSCREEN]),
828                                   SMTO_NORMAL,
829                                   100,
830                                   &ulResult );
831 
832         ERR("BitCount New %d Orig %d ChkNew %d\n",gpsi->BitCount,OrigBC,ppdev->gdiinfo.cBitsPixel);
833 
834         /* Not full screen and different bit count, send messages */
835         if (!(flags & CDS_FULLSCREEN) &&
836             gpsi->BitCount != OrigBC)
837         {
838             ERR("Detect settings changed.\n");
839             UserSendNotifyMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0);
840             UserSendNotifyMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0);
841         }
842 
843         //co_IntShowDesktop(pdesk, ppdev->gdiinfo.ulHorzRes, ppdev->gdiinfo.ulVertRes);
844 
845         UserRedrawDesktop();
846     }
847 
848 leave:
849     if (newDevMode && newDevMode != ppdev->pdmwDev)
850         ExFreePoolWithTag(newDevMode, GDITAG_DEVMODE);
851 
852     /* Release the PDEV */
853     PDEVOBJ_vRelease(ppdev);
854 
855     return lResult;
856 }
857 
858 VOID
859 UserDisplayNotifyShutdown(
860     PPROCESSINFO ppiCurrent)
861 {
862     if (ppiCurrent == gpFullscreen)
863     {
864         UserChangeDisplaySettings(NULL, NULL, 0, NULL);
865         if (gpFullscreen)
866             ERR("Failed to restore display mode!\n");
867     }
868 }
869 
870 LONG
871 APIENTRY
872 NtUserChangeDisplaySettings(
873     PUNICODE_STRING pustrDevice,
874     LPDEVMODEW lpDevMode,
875     DWORD dwflags,
876     LPVOID lParam)
877 {
878     WCHAR awcDevice[CCHDEVICENAME];
879     UNICODE_STRING ustrDevice;
880     DEVMODEW dmLocal;
881     LONG lRet;
882 
883     /* Check arguments */
884     if ((dwflags != CDS_VIDEOPARAMETERS) && (lParam != NULL))
885     {
886         EngSetLastError(ERROR_INVALID_PARAMETER);
887         return DISP_CHANGE_BADPARAM;
888     }
889 
890     /* Check flags */
891     if ((dwflags & (CDS_GLOBAL|CDS_NORESET)) && !(dwflags & CDS_UPDATEREGISTRY))
892     {
893         return DISP_CHANGE_BADFLAGS;
894     }
895 
896     if ((dwflags & CDS_RESET) && (dwflags & CDS_NORESET))
897     {
898         return DISP_CHANGE_BADFLAGS;
899     }
900 
901     /* Copy the device name */
902     if (pustrDevice)
903     {
904         /* Initialize destination string */
905         RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
906 
907         _SEH2_TRY
908         {
909             /* Probe the UNICODE_STRING and the buffer */
910             ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
911             ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
912 
913             /* Copy the string */
914             RtlCopyUnicodeString(&ustrDevice, pustrDevice);
915         }
916         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
917         {
918             /* Set and return error */
919             SetLastNtError(_SEH2_GetExceptionCode());
920             _SEH2_YIELD(return DISP_CHANGE_BADPARAM);
921         }
922         _SEH2_END
923 
924         pustrDevice = &ustrDevice;
925    }
926 
927     /* Copy devmode */
928     if (lpDevMode)
929     {
930         _SEH2_TRY
931         {
932             /* Probe the size field of the structure */
933             ProbeForRead(lpDevMode, sizeof(dmLocal.dmSize), 1);
934 
935             /* Calculate usable size */
936             dmLocal.dmSize = min(sizeof(dmLocal), lpDevMode->dmSize);
937 
938             /* Probe and copy the full DEVMODE */
939             ProbeForRead(lpDevMode, dmLocal.dmSize, 1);
940             RtlCopyMemory(&dmLocal, lpDevMode, dmLocal.dmSize);
941         }
942         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
943         {
944             /* Set and return error */
945             SetLastNtError(_SEH2_GetExceptionCode());
946             _SEH2_YIELD(return DISP_CHANGE_BADPARAM);
947         }
948         _SEH2_END
949 
950         /* Check for extra parameters */
951         if (dmLocal.dmDriverExtra > 0)
952         {
953             /* FIXME: TODO */
954             ERR("lpDevMode->dmDriverExtra is IGNORED!\n");
955             dmLocal.dmDriverExtra = 0;
956         }
957 
958         /* Use the local structure */
959         lpDevMode = &dmLocal;
960     }
961 
962     // FIXME: Copy videoparameters
963 
964     /* Acquire global USER lock */
965     UserEnterExclusive();
966 
967     /* Call internal function */
968     lRet = UserChangeDisplaySettings(pustrDevice, lpDevMode, dwflags, NULL);
969 
970     /* Release lock */
971     UserLeave();
972 
973     return lRet;
974 }
975