xref: /reactos/win32ss/user/ntuser/display.c (revision b4af5597)
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 = 0;
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     ULONG iDevNum, iVGACompatible = -1, ulMaxObjectNumber = 0;
159     WCHAR awcDeviceName[20];
160     WCHAR awcBuffer[256];
161     NTSTATUS Status;
162     PGRAPHICS_DEVICE pGraphicsDevice;
163     ULONG cbValue;
164     HKEY hkey;
165 
166     TRACE("----------------------------- InitVideo() -------------------------------\n");
167 
168     /* Open the key for the boot command line */
169     Status = RegOpenKey(L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control", &hkey);
170     if (NT_SUCCESS(Status))
171     {
172         cbValue = sizeof(awcBuffer);
173         Status = RegQueryValue(hkey, L"SystemStartOptions", REG_SZ, awcBuffer, &cbValue);
174         if (NT_SUCCESS(Status))
175         {
176             /* Check if VGA mode is requested. */
177             if (wcsstr(awcBuffer, L"BASEVIDEO") != 0)
178             {
179                 ERR("VGA mode requested.\n");
180                 gbBaseVideo = TRUE;
181             }
182         }
183 
184         ZwClose(hkey);
185     }
186 
187     /* Open the key for the adapters */
188     Status = RegOpenKey(KEY_VIDEO, &hkey);
189     if (!NT_SUCCESS(Status))
190     {
191         ERR("Could not open HARDWARE\\DEVICEMAP\\VIDEO registry key:0x%lx\n", Status);
192         return Status;
193     }
194 
195     /* Read the name of the VGA adapter */
196     cbValue = sizeof(awcDeviceName);
197     Status = RegQueryValue(hkey, L"VgaCompatible", REG_SZ, awcDeviceName, &cbValue);
198     if (NT_SUCCESS(Status))
199     {
200         iVGACompatible = _wtoi(&awcDeviceName[sizeof("\\Device\\Video")-1]);
201         ERR("VGA adapter = %lu\n", iVGACompatible);
202     }
203 
204     /* Get the maximum mumber of adapters */
205     if (!RegReadDWORD(hkey, L"MaxObjectNumber", &ulMaxObjectNumber))
206     {
207         ERR("Could not read MaxObjectNumber, defaulting to 0.\n");
208     }
209 
210     TRACE("Found %lu devices\n", ulMaxObjectNumber + 1);
211 
212     /* Loop through all adapters */
213     for (iDevNum = 0; iDevNum <= ulMaxObjectNumber; iDevNum++)
214     {
215         /* Create the adapter's key name */
216         swprintf(awcDeviceName, L"\\Device\\Video%lu", iDevNum);
217 
218         /* Read the reg key name */
219         cbValue = sizeof(awcBuffer);
220         Status = RegQueryValue(hkey, awcDeviceName, REG_SZ, awcBuffer, &cbValue);
221         if (!NT_SUCCESS(Status))
222         {
223             ERR("failed to query the registry path:0x%lx\n", Status);
224             continue;
225         }
226 
227         /* Initialize the driver for this device */
228         pGraphicsDevice = InitDisplayDriver(awcDeviceName, awcBuffer);
229         if (!pGraphicsDevice) continue;
230 
231         /* Check if this is a VGA compatible adapter */
232         if (pGraphicsDevice->StateFlags & DISPLAY_DEVICE_VGA_COMPATIBLE)
233         {
234             /* Save this as the VGA adapter */
235             if (!gpVgaGraphicsDevice)
236                 gpVgaGraphicsDevice = pGraphicsDevice;
237             TRACE("gpVgaGraphicsDevice = %p\n", gpVgaGraphicsDevice);
238         }
239         else
240         {
241             /* Set the first one as primary device */
242             if (!gpPrimaryGraphicsDevice)
243                 gpPrimaryGraphicsDevice = pGraphicsDevice;
244             TRACE("gpPrimaryGraphicsDevice = %p\n", gpPrimaryGraphicsDevice);
245         }
246     }
247 
248     /* Close the device map registry key */
249     ZwClose(hkey);
250 
251     /* Was VGA mode requested? */
252     if (gbBaseVideo)
253     {
254         /* Check if we found a VGA compatible device */
255         if (gpVgaGraphicsDevice)
256         {
257             /* Set the VgaAdapter as primary */
258             gpPrimaryGraphicsDevice = gpVgaGraphicsDevice;
259             // FIXME: DEVMODE
260         }
261         else
262         {
263             ERR("Could not find VGA compatible driver. Trying normal.\n");
264         }
265     }
266 
267     /* Check if we had any success */
268     if (!gpPrimaryGraphicsDevice)
269     {
270         /* Check if there is a VGA device we skipped */
271         if (gpVgaGraphicsDevice)
272         {
273             /* There is, use the VGA device */
274             gpPrimaryGraphicsDevice = gpVgaGraphicsDevice;
275         }
276         else
277         {
278             ERR("No usable display driver was found.\n");
279             return STATUS_UNSUCCESSFUL;
280         }
281     }
282 
283     InitSysParams();
284 
285     return 1;
286 }
287 
288 NTSTATUS
289 NTAPI
290 UserEnumDisplayDevices(
291     PUNICODE_STRING pustrDevice,
292     DWORD iDevNum,
293     PDISPLAY_DEVICEW pdispdev,
294     DWORD dwFlags)
295 {
296     PGRAPHICS_DEVICE pGraphicsDevice;
297     ULONG cbSize;
298     HKEY hkey;
299     NTSTATUS Status;
300 
301     /* Ask gdi for the GRAPHICS_DEVICE */
302     pGraphicsDevice = EngpFindGraphicsDevice(pustrDevice, iDevNum, 0);
303     if (!pGraphicsDevice)
304     {
305         /* No device found */
306         ERR("No GRAPHICS_DEVICE found\n");
307         return STATUS_UNSUCCESSFUL;
308     }
309 
310     /* Open the device map registry key */
311     Status = RegOpenKey(KEY_VIDEO, &hkey);
312     if (!NT_SUCCESS(Status))
313     {
314         /* No device found */
315         ERR("Could not open reg key\n");
316         return STATUS_UNSUCCESSFUL;
317     }
318 
319     /* Query the registry path */
320     cbSize = sizeof(pdispdev->DeviceKey);
321     RegQueryValue(hkey,
322                   pGraphicsDevice->szNtDeviceName,
323                   REG_SZ,
324                   pdispdev->DeviceKey,
325                   &cbSize);
326 
327     /* Close registry key */
328     ZwClose(hkey);
329 
330     /* Copy device name, device string and StateFlags */
331     RtlStringCbCopyW(pdispdev->DeviceName, sizeof(pdispdev->DeviceName), pGraphicsDevice->szWinDeviceName);
332     RtlStringCbCopyW(pdispdev->DeviceString, sizeof(pdispdev->DeviceString), pGraphicsDevice->pwszDescription);
333     pdispdev->StateFlags = pGraphicsDevice->StateFlags;
334     // FIXME: fill in DEVICE ID
335     pdispdev->DeviceID[0] = UNICODE_NULL;
336 
337     return STATUS_SUCCESS;
338 }
339 
340 //NTSTATUS
341 BOOL
342 NTAPI
343 NtUserEnumDisplayDevices(
344     PUNICODE_STRING pustrDevice,
345     DWORD iDevNum,
346     PDISPLAY_DEVICEW pDisplayDevice,
347     DWORD dwFlags)
348 {
349     UNICODE_STRING ustrDevice;
350     WCHAR awcDevice[CCHDEVICENAME];
351     DISPLAY_DEVICEW dispdev;
352     NTSTATUS Status;
353 
354     TRACE("Enter NtUserEnumDisplayDevices(%wZ, %lu)\n",
355           pustrDevice, iDevNum);
356 
357     dispdev.cb = sizeof(dispdev);
358 
359     if (pustrDevice)
360     {
361         /* Initialize destination string */
362         RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
363 
364         _SEH2_TRY
365         {
366             /* Probe the UNICODE_STRING and the buffer */
367             ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
368             ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
369 
370             /* Copy the string */
371             RtlCopyUnicodeString(&ustrDevice, pustrDevice);
372         }
373         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
374         {
375 //            _SEH2_YIELD(return _SEH2_GetExceptionCode());
376             _SEH2_YIELD(return NT_SUCCESS(_SEH2_GetExceptionCode()));
377         }
378         _SEH2_END
379 
380         if (ustrDevice.Length > 0)
381             pustrDevice = &ustrDevice;
382         else
383             pustrDevice = NULL;
384    }
385 
386     /* If name is given only iDevNum==0 gives results */
387     if (pustrDevice && iDevNum != 0)
388         return FALSE;
389 
390     /* Acquire global USER lock */
391     UserEnterShared();
392 
393     /* Call the internal function */
394     Status = UserEnumDisplayDevices(pustrDevice, iDevNum, &dispdev, dwFlags);
395 
396     /* Release lock */
397     UserLeave();
398 
399     /* On success copy data to caller */
400     if (NT_SUCCESS(Status))
401     {
402         /* Enter SEH */
403         _SEH2_TRY
404         {
405             /* First probe the cb field */
406             ProbeForWrite(&pDisplayDevice->cb, sizeof(DWORD), 1);
407 
408             /* Check the buffer size */
409             if (pDisplayDevice->cb)
410             {
411                 /* Probe the output buffer */
412                 pDisplayDevice->cb = min(pDisplayDevice->cb, sizeof(dispdev));
413                 ProbeForWrite(pDisplayDevice, pDisplayDevice->cb, 1);
414 
415                 /* Copy as much as the given buffer allows */
416                 RtlCopyMemory(pDisplayDevice, &dispdev, pDisplayDevice->cb);
417             }
418         }
419         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
420         {
421             Status = _SEH2_GetExceptionCode();
422         }
423         _SEH2_END
424     }
425 
426     TRACE("Leave NtUserEnumDisplayDevices, Status = 0x%lx\n", Status);
427     /* Return the result */
428 //    return Status;
429     return NT_SUCCESS(Status); // FIXME
430 }
431 
432 NTSTATUS
433 NTAPI
434 UserEnumCurrentDisplaySettings(
435     PUNICODE_STRING pustrDevice,
436     PDEVMODEW *ppdm)
437 {
438     PPDEVOBJ ppdev;
439 
440     /* Get the PDEV for the device */
441     ppdev = EngpGetPDEV(pustrDevice);
442     if (!ppdev)
443     {
444         /* No device found */
445         ERR("No PDEV found!\n");
446         return STATUS_INVALID_PARAMETER_1;
447     }
448 
449     *ppdm = ppdev->pdmwDev;
450     PDEVOBJ_vRelease(ppdev);
451 
452     return STATUS_SUCCESS;
453 }
454 
455 NTSTATUS
456 NTAPI
457 UserEnumDisplaySettings(
458    PUNICODE_STRING pustrDevice,
459    DWORD iModeNum,
460    LPDEVMODEW *ppdm,
461    DWORD dwFlags)
462 {
463     PGRAPHICS_DEVICE pGraphicsDevice;
464     PDEVMODEENTRY pdmentry;
465     ULONG i, iFoundMode;
466     PPDEVOBJ ppdev;
467 
468     TRACE("Enter UserEnumDisplaySettings('%wZ', %lu)\n",
469           pustrDevice, iModeNum);
470 
471     /* Ask GDI for the GRAPHICS_DEVICE */
472     pGraphicsDevice = EngpFindGraphicsDevice(pustrDevice, 0, 0);
473     ppdev = EngpGetPDEV(pustrDevice);
474 
475     if (!pGraphicsDevice || !ppdev)
476     {
477         /* No device found */
478         ERR("No device found!\n");
479         return STATUS_INVALID_PARAMETER_1;
480     }
481 
482     /* let's politely ask the driver for an updated mode list,
483        just in case there's something new in there (vbox) */
484 
485     PDEVOBJ_vRefreshModeList(ppdev);
486     PDEVOBJ_vRelease(ppdev);
487 
488     iFoundMode = 0;
489     for (i = 0; i < pGraphicsDevice->cDevModes; i++)
490     {
491         pdmentry = &pGraphicsDevice->pDevModeList[i];
492 
493         /* FIXME: Consider EDS_RAWMODE */
494 #if 0
495         if ((!(dwFlags & EDS_RAWMODE) && (pdmentry->dwFlags & 1)) ||!
496             (dwFlags & EDS_RAWMODE))
497 #endif
498         {
499             /* Is this the one we want? */
500             if (iFoundMode == iModeNum)
501             {
502                 *ppdm = pdmentry->pdm;
503                 return STATUS_SUCCESS;
504             }
505 
506             /* Increment number of found modes */
507             iFoundMode++;
508         }
509     }
510 
511     /* Nothing was found */
512     return STATUS_INVALID_PARAMETER_2;
513 }
514 
515 NTSTATUS
516 NTAPI
517 UserOpenDisplaySettingsKey(
518     OUT PHKEY phkey,
519     IN PUNICODE_STRING pustrDevice,
520     IN BOOL bGlobal)
521 {
522     HKEY hkey;
523     DISPLAY_DEVICEW dispdev;
524     NTSTATUS Status;
525 
526     /* Get device info */
527     Status = UserEnumDisplayDevices(pustrDevice, 0, &dispdev, 0);
528     if (!NT_SUCCESS(Status))
529         return Status;
530 
531     if (bGlobal)
532     {
533         // FIXME: Need to fix the registry key somehow
534     }
535 
536     /* Open the registry key */
537     Status = RegOpenKey(dispdev.DeviceKey, &hkey);
538     if (!NT_SUCCESS(Status))
539         return Status;
540 
541     *phkey = hkey;
542 
543     return Status;
544 }
545 
546 NTSTATUS
547 NTAPI
548 UserEnumRegistryDisplaySettings(
549     IN PUNICODE_STRING pustrDevice,
550     OUT LPDEVMODEW pdm)
551 {
552     HKEY hkey;
553     NTSTATUS Status = UserOpenDisplaySettingsKey(&hkey, pustrDevice, 0);
554     if(NT_SUCCESS(Status))
555     {
556         RegReadDisplaySettings(hkey, pdm);
557         ZwClose(hkey);
558         return STATUS_SUCCESS;
559     }
560     return Status ;
561 }
562 
563 NTSTATUS
564 APIENTRY
565 NtUserEnumDisplaySettings(
566     IN PUNICODE_STRING pustrDevice,
567     IN DWORD iModeNum,
568     OUT LPDEVMODEW lpDevMode,
569     IN DWORD dwFlags)
570 {
571     UNICODE_STRING ustrDevice;
572     WCHAR awcDevice[CCHDEVICENAME];
573     NTSTATUS Status;
574     ULONG cbSize, cbExtra;
575     DEVMODEW dmReg, *pdm;
576 
577     TRACE("Enter NtUserEnumDisplaySettings(%wZ, %lu, %p, 0x%lx)\n",
578           pustrDevice, iModeNum, lpDevMode, dwFlags);
579 
580     _SEH2_TRY
581     {
582         ProbeForRead(lpDevMode, sizeof(DEVMODEW), sizeof(UCHAR));
583 
584         cbSize = lpDevMode->dmSize;
585         cbExtra = lpDevMode->dmDriverExtra;
586 
587         ProbeForWrite(lpDevMode, cbSize + cbExtra, sizeof(UCHAR));
588     }
589     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
590     {
591         _SEH2_YIELD(return _SEH2_GetExceptionCode());
592     }
593     _SEH2_END;
594 
595     if (lpDevMode->dmSize != sizeof(DEVMODEW))
596     {
597         return STATUS_BUFFER_TOO_SMALL;
598     }
599 
600     if (pustrDevice)
601     {
602         /* Initialize destination string */
603         RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
604 
605         _SEH2_TRY
606         {
607             /* Probe the UNICODE_STRING and the buffer */
608             ProbeForReadUnicodeString(pustrDevice);
609 
610             if (!pustrDevice->Length || !pustrDevice->Buffer)
611                 ExRaiseStatus(STATUS_NO_MEMORY);
612 
613             ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, sizeof(UCHAR));
614 
615             /* Copy the string */
616             RtlCopyUnicodeString(&ustrDevice, pustrDevice);
617         }
618         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
619         {
620             _SEH2_YIELD(return STATUS_INVALID_PARAMETER_1);
621         }
622         _SEH2_END;
623 
624         pustrDevice = &ustrDevice;
625     }
626 
627     /* Acquire global USER lock */
628     UserEnterShared();
629 
630     if (iModeNum == ENUM_REGISTRY_SETTINGS)
631     {
632         /* Get the registry settings */
633         Status = UserEnumRegistryDisplaySettings(pustrDevice, &dmReg);
634         pdm = &dmReg;
635         pdm->dmSize = sizeof(DEVMODEW);
636     }
637     else if (iModeNum == ENUM_CURRENT_SETTINGS)
638     {
639         /* Get the current settings */
640         Status = UserEnumCurrentDisplaySettings(pustrDevice, &pdm);
641     }
642     else
643     {
644         /* Get specified settings */
645         Status = UserEnumDisplaySettings(pustrDevice, iModeNum, &pdm, dwFlags);
646     }
647 
648     /* Release lock */
649     UserLeave();
650 
651     /* Did we succeed? */
652     if (NT_SUCCESS(Status))
653     {
654         /* Copy some information back */
655         _SEH2_TRY
656         {
657             /* Output what we got */
658             RtlCopyMemory(lpDevMode, pdm, min(cbSize, pdm->dmSize));
659 
660             /* Output private/extra driver data */
661             if (cbExtra > 0 && pdm->dmDriverExtra > 0)
662             {
663                 RtlCopyMemory((PCHAR)lpDevMode + cbSize,
664                               (PCHAR)pdm + pdm->dmSize,
665                               min(cbExtra, pdm->dmDriverExtra));
666             }
667         }
668         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
669         {
670             Status = _SEH2_GetExceptionCode();
671         }
672         _SEH2_END;
673     }
674 
675     return Status;
676 }
677 VOID
678 UserUpdateFullscreen(
679     DWORD flags)
680 {
681     if (flags & CDS_FULLSCREEN)
682         gpFullscreen = gptiCurrent->ppi;
683     else
684         gpFullscreen = NULL;
685 }
686 
687 LONG
688 APIENTRY
689 UserChangeDisplaySettings(
690    PUNICODE_STRING pustrDevice,
691    LPDEVMODEW pdm,
692    DWORD flags,
693    LPVOID lParam)
694 {
695     DEVMODEW dm;
696     LONG lResult = DISP_CHANGE_SUCCESSFUL;
697     HKEY hkey;
698     NTSTATUS Status;
699     PPDEVOBJ ppdev;
700     WORD OrigBC;
701     //PDESKTOP pdesk;
702 
703     /* If no DEVMODE is given, use registry settings */
704     if (!pdm)
705     {
706         /* Get the registry settings */
707         Status = UserEnumRegistryDisplaySettings(pustrDevice, &dm);
708         if (!NT_SUCCESS(Status))
709         {
710             ERR("Could not load registry settings\n");
711             return DISP_CHANGE_BADPARAM;
712         }
713     }
714     else if (pdm->dmSize < FIELD_OFFSET(DEVMODEW, dmFields))
715     {
716         return DISP_CHANGE_BADMODE; /* This is what WinXP SP3 returns */
717     }
718     else
719     {
720         dm = *pdm;
721     }
722 
723     /* Save original bit count */
724     OrigBC = gpsi->BitCount;
725 
726     /* Check params */
727     if ((dm.dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
728     {
729         ERR("Devmode doesn't specify the resolution.\n");
730         return DISP_CHANGE_BADMODE;
731     }
732 
733     /* Get the PDEV */
734     ppdev = EngpGetPDEV(pustrDevice);
735     if (!ppdev)
736     {
737         ERR("Failed to get PDEV\n");
738         return DISP_CHANGE_BADPARAM;
739     }
740 
741     /* Fixup values */
742     if (dm.dmBitsPerPel == 0 || !(dm.dmFields & DM_BITSPERPEL))
743     {
744         dm.dmBitsPerPel = ppdev->pdmwDev->dmBitsPerPel;
745         dm.dmFields |= DM_BITSPERPEL;
746     }
747 
748     if ((dm.dmFields & DM_DISPLAYFREQUENCY) && (dm.dmDisplayFrequency == 0))
749         dm.dmDisplayFrequency = ppdev->pdmwDev->dmDisplayFrequency;
750 
751     /* Look for the requested DEVMODE */
752     pdm = PDEVOBJ_pdmMatchDevMode(ppdev, &dm);
753     if (!pdm)
754     {
755         ERR("Could not find a matching DEVMODE\n");
756         lResult = DISP_CHANGE_BADMODE;
757         goto leave;
758     }
759     else if (flags & CDS_TEST)
760     {
761         /* It's possible, go ahead! */
762         lResult = DISP_CHANGE_SUCCESSFUL;
763         goto leave;
764     }
765 
766     /* Shall we update the registry? */
767     if (flags & CDS_UPDATEREGISTRY)
768     {
769         /* Open the local or global settings key */
770         Status = UserOpenDisplaySettingsKey(&hkey, pustrDevice, flags & CDS_GLOBAL);
771         if (NT_SUCCESS(Status))
772         {
773             /* Store the settings */
774             RegWriteDisplaySettings(hkey, pdm);
775 
776             /* Close the registry key */
777             ZwClose(hkey);
778         }
779         else
780         {
781             ERR("Could not open registry key\n");
782             lResult = DISP_CHANGE_NOTUPDATED;
783         }
784     }
785 
786     /* Shall we apply the settings? */
787     if (!(flags & CDS_NORESET))
788     {
789         ULONG_PTR ulResult;
790         PVOID pvOldCursor;
791         TEXTMETRICW tmw;
792 
793         /* Remove mouse pointer */
794         pvOldCursor = UserSetCursor(NULL, TRUE);
795 
796         /* Do the mode switch */
797         ulResult = PDEVOBJ_bSwitchMode(ppdev, pdm);
798 
799         /* Restore mouse pointer, no hooks called */
800         pvOldCursor = UserSetCursor(pvOldCursor, TRUE);
801         ASSERT(pvOldCursor == NULL);
802 
803         /* Check for success or failure */
804         if (!ulResult)
805         {
806             /* Setting mode failed */
807             ERR("Failed to set mode\n");
808 
809             /* Set the correct return value */
810             if ((flags & CDS_UPDATEREGISTRY) && (lResult != DISP_CHANGE_NOTUPDATED))
811                 lResult = DISP_CHANGE_RESTART;
812             else
813                 lResult = DISP_CHANGE_FAILED;
814         }
815         else
816         {
817             /* Setting mode succeeded */
818             lResult = DISP_CHANGE_SUCCESSFUL;
819 
820             UserUpdateFullscreen(flags);
821 
822             /* Update the system metrics */
823             InitMetrics();
824 
825             /* Set new size of the monitor */
826             UserUpdateMonitorSize((HDEV)ppdev);
827 
828             /* Update the SERVERINFO */
829             gpsi->dmLogPixels = ppdev->gdiinfo.ulLogPixelsY;
830             gpsi->Planes      = ppdev->gdiinfo.cPlanes;
831             gpsi->BitsPixel   = ppdev->gdiinfo.cBitsPixel;
832             gpsi->BitCount    = gpsi->Planes * gpsi->BitsPixel;
833             gpsi->aiSysMet[SM_CXSCREEN] = ppdev->gdiinfo.ulHorzRes;
834             gpsi->aiSysMet[SM_CYSCREEN] = ppdev->gdiinfo.ulVertRes;
835             if (ppdev->gdiinfo.flRaster & RC_PALETTE)
836             {
837                 gpsi->PUSIFlags |= PUSIF_PALETTEDISPLAY;
838             }
839             else
840             {
841                 gpsi->PUSIFlags &= ~PUSIF_PALETTEDISPLAY;
842             }
843             // Font is realized and this dc was previously set to internal DC_ATTR.
844             gpsi->cxSysFontChar = IntGetCharDimensions(hSystemBM, &tmw, (DWORD*)&gpsi->cySysFontChar);
845             gpsi->tmSysFont     = tmw;
846         }
847 
848         /*
849          * Refresh the display on success and even on failure,
850          * since the display may have been messed up.
851          */
852 
853         /* Remove all cursor clipping */
854         UserClipCursor(NULL);
855 
856         //pdesk = IntGetActiveDesktop();
857         //IntHideDesktop(pdesk);
858 
859         /* Send WM_DISPLAYCHANGE to all toplevel windows */
860         co_IntSendMessageTimeout( HWND_BROADCAST,
861                                   WM_DISPLAYCHANGE,
862                                   gpsi->BitCount,
863                                   MAKELONG(gpsi->aiSysMet[SM_CXSCREEN], gpsi->aiSysMet[SM_CYSCREEN]),
864                                   SMTO_NORMAL,
865                                   100,
866                                   &ulResult );
867 
868         ERR("BitCount New %d Orig %d ChkNew %d\n",gpsi->BitCount,OrigBC,ppdev->gdiinfo.cBitsPixel);
869 
870         /* Not full screen and different bit count, send messages */
871         if (!(flags & CDS_FULLSCREEN) &&
872             gpsi->BitCount != OrigBC)
873         {
874             ERR("Detect settings changed.\n");
875             UserSendNotifyMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0);
876             UserSendNotifyMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0);
877         }
878 
879         //co_IntShowDesktop(pdesk, ppdev->gdiinfo.ulHorzRes, ppdev->gdiinfo.ulVertRes);
880 
881         UserRedrawDesktop();
882     }
883 
884 leave:
885     /* Release the PDEV */
886     PDEVOBJ_vRelease(ppdev);
887 
888     return lResult;
889 }
890 
891 VOID
892 UserDisplayNotifyShutdown(
893     PPROCESSINFO ppiCurrent)
894 {
895     if (ppiCurrent == gpFullscreen)
896     {
897         UserChangeDisplaySettings(NULL, NULL, 0, NULL);
898         if (gpFullscreen)
899             ERR("Failed to restore display mode!\n");
900     }
901 }
902 
903 LONG
904 APIENTRY
905 NtUserChangeDisplaySettings(
906     PUNICODE_STRING pustrDevice,
907     LPDEVMODEW lpDevMode,
908     DWORD dwflags,
909     LPVOID lParam)
910 {
911     WCHAR awcDevice[CCHDEVICENAME];
912     UNICODE_STRING ustrDevice;
913     DEVMODEW dmLocal;
914     LONG lRet;
915 
916     /* Check arguments */
917     if ((dwflags != CDS_VIDEOPARAMETERS) && (lParam != NULL))
918     {
919         EngSetLastError(ERROR_INVALID_PARAMETER);
920         return DISP_CHANGE_BADPARAM;
921     }
922 
923     /* Check flags */
924     if ((dwflags & (CDS_GLOBAL|CDS_NORESET)) && !(dwflags & CDS_UPDATEREGISTRY))
925     {
926         return DISP_CHANGE_BADFLAGS;
927     }
928 
929     /* Copy the device name */
930     if (pustrDevice)
931     {
932         /* Initialize destination string */
933         RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
934 
935         _SEH2_TRY
936         {
937             /* Probe the UNICODE_STRING and the buffer */
938             ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
939             ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
940 
941             /* Copy the string */
942             RtlCopyUnicodeString(&ustrDevice, pustrDevice);
943         }
944         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
945         {
946             /* Set and return error */
947             SetLastNtError(_SEH2_GetExceptionCode());
948             _SEH2_YIELD(return DISP_CHANGE_BADPARAM);
949         }
950         _SEH2_END
951 
952         pustrDevice = &ustrDevice;
953    }
954 
955     /* Copy devmode */
956     if (lpDevMode)
957     {
958         _SEH2_TRY
959         {
960             /* Probe the size field of the structure */
961             ProbeForRead(lpDevMode, sizeof(dmLocal.dmSize), 1);
962 
963             /* Calculate usable size */
964             dmLocal.dmSize = min(sizeof(dmLocal), lpDevMode->dmSize);
965 
966             /* Probe and copy the full DEVMODE */
967             ProbeForRead(lpDevMode, dmLocal.dmSize, 1);
968             RtlCopyMemory(&dmLocal, lpDevMode, dmLocal.dmSize);
969         }
970         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
971         {
972             /* Set and return error */
973             SetLastNtError(_SEH2_GetExceptionCode());
974             _SEH2_YIELD(return DISP_CHANGE_BADPARAM);
975         }
976         _SEH2_END
977 
978         /* Check for extra parameters */
979         if (dmLocal.dmDriverExtra > 0)
980         {
981             /* FIXME: TODO */
982             ERR("lpDevMode->dmDriverExtra is IGNORED!\n");
983             dmLocal.dmDriverExtra = 0;
984         }
985 
986         /* Use the local structure */
987         lpDevMode = &dmLocal;
988     }
989 
990     // FIXME: Copy videoparameters
991 
992     /* Acquire global USER lock */
993     UserEnterExclusive();
994 
995     /* Call internal function */
996     lRet = UserChangeDisplaySettings(pustrDevice, lpDevMode, dwflags, NULL);
997 
998     /* Release lock */
999     UserLeave();
1000 
1001     return lRet;
1002 }
1003 
1004