xref: /reactos/win32ss/user/ntuser/display.c (revision 9393fc32)
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     DEVMODEW dmDefault;
77     DWORD dwVga;
78 
79     TRACE("InitDisplayDriver(%S, %S);\n",
80           pwszDeviceName, pwszRegKey);
81 
82     /* Open the driver's registry key */
83     Status = RegOpenKey(pwszRegKey, &hkey);
84     if (!NT_SUCCESS(Status))
85     {
86         ERR("Failed to open registry key: %ls\n", pwszRegKey);
87         return NULL;
88     }
89 
90     /* Query the diplay drivers */
91     cbSize = sizeof(awcBuffer) - 10;
92     Status = RegQueryValue(hkey,
93                            L"InstalledDisplayDrivers",
94                            REG_MULTI_SZ,
95                            awcBuffer,
96                            &cbSize);
97     if (!NT_SUCCESS(Status))
98     {
99         ERR("Didn't find 'InstalledDisplayDrivers', status = 0x%lx\n", Status);
100         ZwClose(hkey);
101         return NULL;
102     }
103 
104     /* Initialize the UNICODE_STRING */
105     ustrDisplayDrivers.Buffer = awcBuffer;
106     ustrDisplayDrivers.MaximumLength = (USHORT)cbSize;
107     ustrDisplayDrivers.Length = (USHORT)cbSize;
108 
109     /* Set Buffer for description and size of remaining buffer */
110     ustrDescription.Buffer = awcBuffer + (cbSize / sizeof(WCHAR));
111     cbSize = sizeof(awcBuffer) - cbSize;
112 
113     /* Query the device string */
114     Status = RegQueryValue(hkey,
115                            L"Device Description",
116                            REG_SZ,
117                            ustrDescription.Buffer,
118                            &cbSize);
119     if (NT_SUCCESS(Status))
120     {
121         ustrDescription.MaximumLength = (USHORT)cbSize;
122         ustrDescription.Length = (USHORT)cbSize;
123     }
124     else
125     {
126         RtlInitUnicodeString(&ustrDescription, L"<unknown>");
127     }
128 
129     /* Query the default settings */
130     RegReadDisplaySettings(hkey, &dmDefault);
131 
132     /* Query if this is a VGA compatible driver */
133     cbSize = sizeof(DWORD);
134     Status = RegQueryValue(hkey, L"VgaCompatible", REG_DWORD, &dwVga, &cbSize);
135     if (!NT_SUCCESS(Status)) dwVga = 0;
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     if (pGraphicsDevice && dwVga)
147     {
148         pGraphicsDevice->StateFlags |= DISPLAY_DEVICE_VGA_COMPATIBLE;
149     }
150 
151     return pGraphicsDevice;
152 }
153 
154 NTSTATUS
155 NTAPI
156 InitVideo(VOID)
157 {
158     NTSTATUS Status;
159     HKEY hkey;
160 
161     TRACE("----------------------------- InitVideo() -------------------------------\n");
162 
163     /* Check if VGA mode is requested, by finding the special volatile key created by VIDEOPRT */
164     Status = RegOpenKey(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\GraphicsDrivers\\BaseVideo", &hkey);
165     if (NT_SUCCESS(Status))
166         ZwClose(hkey);
167     gbBaseVideo = NT_SUCCESS(Status);
168     if (gbBaseVideo)
169         ERR("VGA mode requested.\n");
170 
171     /* Initialize all display devices */
172     Status = EngpUpdateGraphicsDeviceList();
173     if (!NT_SUCCESS(Status))
174         return Status;
175 
176     /* Check if we had any success */
177     if (!gpPrimaryGraphicsDevice)
178     {
179         /* Check if there is a VGA device we skipped */
180         if (gpVgaGraphicsDevice)
181         {
182             /* There is, use the VGA device */
183             gpPrimaryGraphicsDevice = gpVgaGraphicsDevice;
184         }
185         else
186         {
187             ERR("No usable display driver was found.\n");
188             return STATUS_UNSUCCESSFUL;
189         }
190     }
191 
192     InitSysParams();
193 
194     return STATUS_SUCCESS;
195 }
196 
197 VOID
198 UserRefreshDisplay(IN PPDEVOBJ ppdev)
199 {
200     ULONG_PTR ulResult;
201     // PVOID pvOldCursor;
202 
203     // TODO: Re-enable the cursor reset code once this function becomes called
204     // from within a Win32 thread... Indeed UserSetCursor() requires this, but
205     // at the moment this function is directly called from a separate thread
206     // from within videoprt, instead of by a separate win32k system thread.
207 
208     if (!ppdev)
209         return;
210 
211     PDEVOBJ_vReference(ppdev);
212 
213     /* Remove mouse pointer */
214     // pvOldCursor = UserSetCursor(NULL, TRUE);
215 
216     /* Do the mode switch -- Use the actual same current mode */
217     ulResult = PDEVOBJ_bSwitchMode(ppdev, ppdev->pdmwDev);
218     ASSERT(ulResult);
219 
220     /* Restore mouse pointer, no hooks called */
221     // pvOldCursor = UserSetCursor(pvOldCursor, TRUE);
222     // ASSERT(pvOldCursor == NULL);
223 
224     /* Update the system metrics */
225     InitMetrics();
226 
227     /* Set new size of the monitor */
228     // UserUpdateMonitorSize((HDEV)ppdev);
229 
230     //co_IntShowDesktop(pdesk, ppdev->gdiinfo.ulHorzRes, ppdev->gdiinfo.ulVertRes);
231     UserRedrawDesktop();
232 
233     PDEVOBJ_vRelease(ppdev);
234 }
235 
236 NTSTATUS
237 NTAPI
238 UserEnumDisplayDevices(
239     PUNICODE_STRING pustrDevice,
240     DWORD iDevNum,
241     PDISPLAY_DEVICEW pdispdev,
242     DWORD dwFlags)
243 {
244     PGRAPHICS_DEVICE pGraphicsDevice;
245     ULONG cbSize;
246     HKEY hkey;
247     NTSTATUS Status;
248 
249     if (!pustrDevice)
250     {
251         /* Check if some devices have been added since last time */
252         EngpUpdateGraphicsDeviceList();
253     }
254 
255     /* Ask gdi for the GRAPHICS_DEVICE */
256     pGraphicsDevice = EngpFindGraphicsDevice(pustrDevice, iDevNum, 0);
257     if (!pGraphicsDevice)
258     {
259         /* No device found */
260         ERR("No GRAPHICS_DEVICE found for '%wZ', iDevNum %lu\n", pustrDevice, iDevNum);
261         return STATUS_UNSUCCESSFUL;
262     }
263 
264     /* Open the device map registry key */
265     Status = RegOpenKey(KEY_VIDEO, &hkey);
266     if (!NT_SUCCESS(Status))
267     {
268         /* No device found */
269         ERR("Could not open reg key\n");
270         return STATUS_UNSUCCESSFUL;
271     }
272 
273     /* Query the registry path */
274     cbSize = sizeof(pdispdev->DeviceKey);
275     RegQueryValue(hkey,
276                   pGraphicsDevice->szNtDeviceName,
277                   REG_SZ,
278                   pdispdev->DeviceKey,
279                   &cbSize);
280 
281     /* Close registry key */
282     ZwClose(hkey);
283 
284     /* Copy device name, device string and StateFlags */
285     RtlStringCbCopyW(pdispdev->DeviceName, sizeof(pdispdev->DeviceName), pGraphicsDevice->szWinDeviceName);
286     RtlStringCbCopyW(pdispdev->DeviceString, sizeof(pdispdev->DeviceString), pGraphicsDevice->pwszDescription);
287     pdispdev->StateFlags = pGraphicsDevice->StateFlags;
288     // FIXME: fill in DEVICE ID
289     pdispdev->DeviceID[0] = UNICODE_NULL;
290 
291     return STATUS_SUCCESS;
292 }
293 
294 //NTSTATUS
295 BOOL
296 NTAPI
297 NtUserEnumDisplayDevices(
298     PUNICODE_STRING pustrDevice,
299     DWORD iDevNum,
300     PDISPLAY_DEVICEW pDisplayDevice,
301     DWORD dwFlags)
302 {
303     UNICODE_STRING ustrDevice;
304     WCHAR awcDevice[CCHDEVICENAME];
305     DISPLAY_DEVICEW dispdev;
306     NTSTATUS Status;
307 
308     TRACE("Enter NtUserEnumDisplayDevices(%wZ, %lu)\n",
309           pustrDevice, iDevNum);
310 
311     dispdev.cb = sizeof(dispdev);
312 
313     if (pustrDevice)
314     {
315         /* Initialize destination string */
316         RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
317 
318         _SEH2_TRY
319         {
320             /* Probe the UNICODE_STRING and the buffer */
321             ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
322             ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
323 
324             /* Copy the string */
325             RtlCopyUnicodeString(&ustrDevice, pustrDevice);
326         }
327         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
328         {
329 //            _SEH2_YIELD(return _SEH2_GetExceptionCode());
330             _SEH2_YIELD(return NT_SUCCESS(_SEH2_GetExceptionCode()));
331         }
332         _SEH2_END
333 
334         if (ustrDevice.Length > 0)
335             pustrDevice = &ustrDevice;
336         else
337             pustrDevice = NULL;
338    }
339 
340     /* If name is given only iDevNum==0 gives results */
341     if (pustrDevice && iDevNum != 0)
342         return FALSE;
343 
344     /* Acquire global USER lock */
345     UserEnterShared();
346 
347     /* Call the internal function */
348     Status = UserEnumDisplayDevices(pustrDevice, iDevNum, &dispdev, dwFlags);
349 
350     /* Release lock */
351     UserLeave();
352 
353     /* On success copy data to caller */
354     if (NT_SUCCESS(Status))
355     {
356         /* Enter SEH */
357         _SEH2_TRY
358         {
359             /* First probe the cb field */
360             ProbeForWrite(&pDisplayDevice->cb, sizeof(DWORD), 1);
361 
362             /* Check the buffer size */
363             if (pDisplayDevice->cb)
364             {
365                 /* Probe the output buffer */
366                 pDisplayDevice->cb = min(pDisplayDevice->cb, sizeof(dispdev));
367                 ProbeForWrite(pDisplayDevice, pDisplayDevice->cb, 1);
368 
369                 /* Copy as much as the given buffer allows */
370                 RtlCopyMemory(pDisplayDevice, &dispdev, pDisplayDevice->cb);
371             }
372         }
373         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
374         {
375             Status = _SEH2_GetExceptionCode();
376         }
377         _SEH2_END
378     }
379 
380     TRACE("Leave NtUserEnumDisplayDevices, Status = 0x%lx\n", Status);
381     /* Return the result */
382 //    return Status;
383     return NT_SUCCESS(Status); // FIXME
384 }
385 
386 NTSTATUS
387 NTAPI
388 UserEnumCurrentDisplaySettings(
389     PUNICODE_STRING pustrDevice,
390     PDEVMODEW *ppdm)
391 {
392     PPDEVOBJ ppdev;
393 
394     /* Get the PDEV for the device */
395     ppdev = EngpGetPDEV(pustrDevice);
396     if (!ppdev)
397     {
398         /* No device found */
399         ERR("No PDEV found!\n");
400         return STATUS_INVALID_PARAMETER_1;
401     }
402 
403     *ppdm = ppdev->pdmwDev;
404     PDEVOBJ_vRelease(ppdev);
405 
406     return STATUS_SUCCESS;
407 }
408 
409 NTSTATUS
410 NTAPI
411 UserEnumDisplaySettings(
412    PUNICODE_STRING pustrDevice,
413    DWORD iModeNum,
414    LPDEVMODEW *ppdm,
415    DWORD dwFlags)
416 {
417     PGRAPHICS_DEVICE pGraphicsDevice;
418     PDEVMODEENTRY pdmentry;
419     ULONG i, iFoundMode;
420     PPDEVOBJ ppdev;
421 
422     TRACE("Enter UserEnumDisplaySettings('%wZ', %lu)\n",
423           pustrDevice, iModeNum);
424 
425     /* Ask GDI for the GRAPHICS_DEVICE */
426     pGraphicsDevice = EngpFindGraphicsDevice(pustrDevice, 0, 0);
427     ppdev = EngpGetPDEV(pustrDevice);
428 
429     if (!pGraphicsDevice || !ppdev)
430     {
431         /* No device found */
432         ERR("No device found!\n");
433         return STATUS_INVALID_PARAMETER_1;
434     }
435 
436     /* let's politely ask the driver for an updated mode list,
437        just in case there's something new in there (vbox) */
438 
439     PDEVOBJ_vRefreshModeList(ppdev);
440     PDEVOBJ_vRelease(ppdev);
441 
442     iFoundMode = 0;
443     for (i = 0; i < pGraphicsDevice->cDevModes; i++)
444     {
445         pdmentry = &pGraphicsDevice->pDevModeList[i];
446 
447         /* FIXME: Consider EDS_RAWMODE */
448 #if 0
449         if ((!(dwFlags & EDS_RAWMODE) && (pdmentry->dwFlags & 1)) ||!
450             (dwFlags & EDS_RAWMODE))
451 #endif
452         {
453             /* Is this the one we want? */
454             if (iFoundMode == iModeNum)
455             {
456                 *ppdm = pdmentry->pdm;
457                 return STATUS_SUCCESS;
458             }
459 
460             /* Increment number of found modes */
461             iFoundMode++;
462         }
463     }
464 
465     /* Nothing was found */
466     return STATUS_INVALID_PARAMETER_2;
467 }
468 
469 NTSTATUS
470 NTAPI
471 UserOpenDisplaySettingsKey(
472     OUT PHKEY phkey,
473     IN PUNICODE_STRING pustrDevice,
474     IN BOOL bGlobal)
475 {
476     HKEY hkey;
477     DISPLAY_DEVICEW dispdev;
478     NTSTATUS Status;
479 
480     /* Get device info */
481     Status = UserEnumDisplayDevices(pustrDevice, 0, &dispdev, 0);
482     if (!NT_SUCCESS(Status))
483         return Status;
484 
485     if (bGlobal)
486     {
487         // FIXME: Need to fix the registry key somehow
488     }
489 
490     /* Open the registry key */
491     Status = RegOpenKey(dispdev.DeviceKey, &hkey);
492     if (!NT_SUCCESS(Status))
493         return Status;
494 
495     *phkey = hkey;
496 
497     return Status;
498 }
499 
500 NTSTATUS
501 NTAPI
502 UserEnumRegistryDisplaySettings(
503     IN PUNICODE_STRING pustrDevice,
504     OUT LPDEVMODEW pdm)
505 {
506     HKEY hkey;
507     NTSTATUS Status = UserOpenDisplaySettingsKey(&hkey, pustrDevice, 0);
508     if(NT_SUCCESS(Status))
509     {
510         RegReadDisplaySettings(hkey, pdm);
511         ZwClose(hkey);
512         return STATUS_SUCCESS;
513     }
514     return Status;
515 }
516 
517 NTSTATUS
518 APIENTRY
519 NtUserEnumDisplaySettings(
520     IN PUNICODE_STRING pustrDevice,
521     IN DWORD iModeNum,
522     OUT LPDEVMODEW lpDevMode,
523     IN DWORD dwFlags)
524 {
525     UNICODE_STRING ustrDeviceUser;
526     UNICODE_STRING ustrDevice;
527     WCHAR awcDevice[CCHDEVICENAME];
528     NTSTATUS Status;
529     ULONG cbSize, cbExtra;
530     DEVMODEW dmReg, *pdm;
531 
532     TRACE("Enter NtUserEnumDisplaySettings(%wZ, %lu, %p, 0x%lx)\n",
533           pustrDevice, iModeNum, lpDevMode, dwFlags);
534 
535     _SEH2_TRY
536     {
537         ProbeForRead(lpDevMode, sizeof(DEVMODEW), sizeof(UCHAR));
538 
539         cbSize = lpDevMode->dmSize;
540         cbExtra = lpDevMode->dmDriverExtra;
541 
542         ProbeForWrite(lpDevMode, cbSize + cbExtra, sizeof(UCHAR));
543     }
544     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
545     {
546         _SEH2_YIELD(return _SEH2_GetExceptionCode());
547     }
548     _SEH2_END;
549 
550     if (cbSize != sizeof(DEVMODEW))
551     {
552         return STATUS_BUFFER_TOO_SMALL;
553     }
554 
555     if (pustrDevice)
556     {
557         /* Initialize destination string */
558         RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
559 
560         _SEH2_TRY
561         {
562             /* Probe the UNICODE_STRING and the buffer */
563             ustrDeviceUser = ProbeForReadUnicodeString(pustrDevice);
564 
565             if (!ustrDeviceUser.Length || !ustrDeviceUser.Buffer)
566                 ExRaiseStatus(STATUS_NO_MEMORY);
567 
568             ProbeForRead(ustrDeviceUser.Buffer,
569                          ustrDeviceUser.Length,
570                          sizeof(UCHAR));
571 
572             /* Copy the string */
573             RtlCopyUnicodeString(&ustrDevice, &ustrDeviceUser);
574         }
575         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
576         {
577             _SEH2_YIELD(return STATUS_INVALID_PARAMETER_1);
578         }
579         _SEH2_END;
580 
581         pustrDevice = &ustrDevice;
582     }
583 
584     /* Acquire global USER lock */
585     UserEnterShared();
586 
587     if (iModeNum == ENUM_REGISTRY_SETTINGS)
588     {
589         /* Get the registry settings */
590         Status = UserEnumRegistryDisplaySettings(pustrDevice, &dmReg);
591         pdm = &dmReg;
592         pdm->dmSize = sizeof(DEVMODEW);
593     }
594     else if (iModeNum == ENUM_CURRENT_SETTINGS)
595     {
596         /* Get the current settings */
597         Status = UserEnumCurrentDisplaySettings(pustrDevice, &pdm);
598     }
599     else
600     {
601         /* Get specified settings */
602         Status = UserEnumDisplaySettings(pustrDevice, iModeNum, &pdm, dwFlags);
603     }
604 
605     /* Release lock */
606     UserLeave();
607 
608     /* Did we succeed? */
609     if (NT_SUCCESS(Status))
610     {
611         /* Copy some information back */
612         _SEH2_TRY
613         {
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 VOID
636 UserUpdateFullscreen(
637     DWORD flags)
638 {
639     if (flags & CDS_FULLSCREEN)
640         gpFullscreen = gptiCurrent->ppi;
641     else
642         gpFullscreen = NULL;
643 }
644 
645 LONG
646 APIENTRY
647 UserChangeDisplaySettings(
648    PUNICODE_STRING pustrDevice,
649    LPDEVMODEW pdm,
650    DWORD flags,
651    LPVOID lParam)
652 {
653     DEVMODEW dm;
654     LONG lResult = DISP_CHANGE_SUCCESSFUL;
655     HKEY hkey;
656     NTSTATUS Status;
657     PPDEVOBJ ppdev;
658     WORD OrigBC;
659     //PDESKTOP pdesk;
660 
661     /* If no DEVMODE is given, use registry settings */
662     if (!pdm)
663     {
664         /* Get the registry settings */
665         Status = UserEnumRegistryDisplaySettings(pustrDevice, &dm);
666         if (!NT_SUCCESS(Status))
667         {
668             ERR("Could not load registry settings\n");
669             return DISP_CHANGE_BADPARAM;
670         }
671     }
672     else if (pdm->dmSize < FIELD_OFFSET(DEVMODEW, dmFields))
673     {
674         return DISP_CHANGE_BADMODE; /* This is what WinXP SP3 returns */
675     }
676     else
677     {
678         dm = *pdm;
679     }
680 
681     /* Save original bit count */
682     OrigBC = gpsi->BitCount;
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_PTR ulResult;
755         PVOID pvOldCursor;
756         TEXTMETRICW tmw;
757 
758         /* Remove mouse pointer */
759         pvOldCursor = UserSetCursor(NULL, TRUE);
760 
761         /* Do the mode switch */
762         ulResult = PDEVOBJ_bSwitchMode(ppdev, pdm);
763 
764         /* Restore mouse pointer, no hooks called */
765         pvOldCursor = UserSetCursor(pvOldCursor, TRUE);
766         ASSERT(pvOldCursor == NULL);
767 
768         /* Check for success or failure */
769         if (!ulResult)
770         {
771             /* Setting mode failed */
772             ERR("Failed to set mode\n");
773 
774             /* Set the correct return value */
775             if ((flags & CDS_UPDATEREGISTRY) && (lResult != DISP_CHANGE_NOTUPDATED))
776                 lResult = DISP_CHANGE_RESTART;
777             else
778                 lResult = DISP_CHANGE_FAILED;
779         }
780         else
781         {
782             /* Setting mode succeeded */
783             lResult = DISP_CHANGE_SUCCESSFUL;
784 
785             UserUpdateFullscreen(flags);
786 
787             /* Update the system metrics */
788             InitMetrics();
789 
790             /* Set new size of the monitor */
791             UserUpdateMonitorSize((HDEV)ppdev);
792 
793             /* Update the SERVERINFO */
794             gpsi->dmLogPixels = ppdev->gdiinfo.ulLogPixelsY;
795             gpsi->Planes      = ppdev->gdiinfo.cPlanes;
796             gpsi->BitsPixel   = ppdev->gdiinfo.cBitsPixel;
797             gpsi->BitCount    = gpsi->Planes * gpsi->BitsPixel;
798             gpsi->aiSysMet[SM_CXSCREEN] = ppdev->gdiinfo.ulHorzRes;
799             gpsi->aiSysMet[SM_CYSCREEN] = ppdev->gdiinfo.ulVertRes;
800             if (ppdev->gdiinfo.flRaster & RC_PALETTE)
801             {
802                 gpsi->PUSIFlags |= PUSIF_PALETTEDISPLAY;
803             }
804             else
805             {
806                 gpsi->PUSIFlags &= ~PUSIF_PALETTEDISPLAY;
807             }
808             // Font is realized and this dc was previously set to internal DC_ATTR.
809             gpsi->cxSysFontChar = IntGetCharDimensions(hSystemBM, &tmw, (DWORD*)&gpsi->cySysFontChar);
810             gpsi->tmSysFont     = tmw;
811         }
812 
813         /*
814          * Refresh the display on success and even on failure,
815          * since the display may have been messed up.
816          */
817 
818         /* Remove all cursor clipping */
819         UserClipCursor(NULL);
820 
821         //pdesk = IntGetActiveDesktop();
822         //IntHideDesktop(pdesk);
823 
824         /* Send WM_DISPLAYCHANGE to all toplevel windows */
825         co_IntSendMessageTimeout( HWND_BROADCAST,
826                                   WM_DISPLAYCHANGE,
827                                   gpsi->BitCount,
828                                   MAKELONG(gpsi->aiSysMet[SM_CXSCREEN], gpsi->aiSysMet[SM_CYSCREEN]),
829                                   SMTO_NORMAL,
830                                   100,
831                                   &ulResult );
832 
833         ERR("BitCount New %d Orig %d ChkNew %d\n",gpsi->BitCount,OrigBC,ppdev->gdiinfo.cBitsPixel);
834 
835         /* Not full screen and different bit count, send messages */
836         if (!(flags & CDS_FULLSCREEN) &&
837             gpsi->BitCount != OrigBC)
838         {
839             ERR("Detect settings changed.\n");
840             UserSendNotifyMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0);
841             UserSendNotifyMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0);
842         }
843 
844         //co_IntShowDesktop(pdesk, ppdev->gdiinfo.ulHorzRes, ppdev->gdiinfo.ulVertRes);
845 
846         UserRedrawDesktop();
847     }
848 
849 leave:
850     /* Release the PDEV */
851     PDEVOBJ_vRelease(ppdev);
852 
853     return lResult;
854 }
855 
856 VOID
857 UserDisplayNotifyShutdown(
858     PPROCESSINFO ppiCurrent)
859 {
860     if (ppiCurrent == gpFullscreen)
861     {
862         UserChangeDisplaySettings(NULL, NULL, 0, NULL);
863         if (gpFullscreen)
864             ERR("Failed to restore display mode!\n");
865     }
866 }
867 
868 LONG
869 APIENTRY
870 NtUserChangeDisplaySettings(
871     PUNICODE_STRING pustrDevice,
872     LPDEVMODEW lpDevMode,
873     DWORD dwflags,
874     LPVOID lParam)
875 {
876     WCHAR awcDevice[CCHDEVICENAME];
877     UNICODE_STRING ustrDevice;
878     DEVMODEW dmLocal;
879     LONG lRet;
880 
881     /* Check arguments */
882     if ((dwflags != CDS_VIDEOPARAMETERS) && (lParam != NULL))
883     {
884         EngSetLastError(ERROR_INVALID_PARAMETER);
885         return DISP_CHANGE_BADPARAM;
886     }
887 
888     /* Check flags */
889     if ((dwflags & (CDS_GLOBAL|CDS_NORESET)) && !(dwflags & CDS_UPDATEREGISTRY))
890     {
891         return DISP_CHANGE_BADFLAGS;
892     }
893 
894     if ((dwflags & CDS_RESET) && (dwflags & CDS_NORESET))
895     {
896         return DISP_CHANGE_BADFLAGS;
897     }
898 
899     /* Copy the device name */
900     if (pustrDevice)
901     {
902         /* Initialize destination string */
903         RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
904 
905         _SEH2_TRY
906         {
907             /* Probe the UNICODE_STRING and the buffer */
908             ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
909             ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
910 
911             /* Copy the string */
912             RtlCopyUnicodeString(&ustrDevice, pustrDevice);
913         }
914         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
915         {
916             /* Set and return error */
917             SetLastNtError(_SEH2_GetExceptionCode());
918             _SEH2_YIELD(return DISP_CHANGE_BADPARAM);
919         }
920         _SEH2_END
921 
922         pustrDevice = &ustrDevice;
923    }
924 
925     /* Copy devmode */
926     if (lpDevMode)
927     {
928         _SEH2_TRY
929         {
930             /* Probe the size field of the structure */
931             ProbeForRead(lpDevMode, sizeof(dmLocal.dmSize), 1);
932 
933             /* Calculate usable size */
934             dmLocal.dmSize = min(sizeof(dmLocal), lpDevMode->dmSize);
935 
936             /* Probe and copy the full DEVMODE */
937             ProbeForRead(lpDevMode, dmLocal.dmSize, 1);
938             RtlCopyMemory(&dmLocal, lpDevMode, dmLocal.dmSize);
939         }
940         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
941         {
942             /* Set and return error */
943             SetLastNtError(_SEH2_GetExceptionCode());
944             _SEH2_YIELD(return DISP_CHANGE_BADPARAM);
945         }
946         _SEH2_END
947 
948         /* Check for extra parameters */
949         if (dmLocal.dmDriverExtra > 0)
950         {
951             /* FIXME: TODO */
952             ERR("lpDevMode->dmDriverExtra is IGNORED!\n");
953             dmLocal.dmDriverExtra = 0;
954         }
955 
956         /* Use the local structure */
957         lpDevMode = &dmLocal;
958     }
959 
960     // FIXME: Copy videoparameters
961 
962     /* Acquire global USER lock */
963     UserEnterExclusive();
964 
965     /* Call internal function */
966     lRet = UserChangeDisplaySettings(pustrDevice, lpDevMode, dwflags, NULL);
967 
968     /* Release lock */
969     UserLeave();
970 
971     return lRet;
972 }
973