xref: /reactos/win32ss/user/ntuser/display.c (revision c2c66aff)
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 = 256;
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 = 20;
197     Status = RegQueryValue(hkey, L"VgaCompatible", REG_SZ, awcDeviceName, &cbValue);
198     if (NT_SUCCESS(Status))
199     {
200         iVGACompatible = _wtoi(&awcDeviceName[13]);
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         return DISP_CHANGE_BADMODE; /* This is what winXP SP3 returns */
716     else
717         dm = *pdm;
718 
719     /* Save original bit count */
720     OrigBC = gpsi->BitCount;
721 
722     /* Check params */
723     if ((dm.dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
724     {
725         ERR("Devmode doesn't specify the resolution.\n");
726         return DISP_CHANGE_BADMODE;
727     }
728 
729     /* Get the PDEV */
730     ppdev = EngpGetPDEV(pustrDevice);
731     if (!ppdev)
732     {
733         ERR("Failed to get PDEV\n");
734         return DISP_CHANGE_BADPARAM;
735     }
736 
737     /* Fixup values */
738     if(dm.dmBitsPerPel == 0 || !(dm.dmFields & DM_BITSPERPEL))
739     {
740         dm.dmBitsPerPel = ppdev->pdmwDev->dmBitsPerPel;
741         dm.dmFields |= DM_BITSPERPEL;
742     }
743 
744     if((dm.dmFields & DM_DISPLAYFREQUENCY) && (dm.dmDisplayFrequency == 0))
745         dm.dmDisplayFrequency = ppdev->pdmwDev->dmDisplayFrequency;
746 
747     /* Look for the requested DEVMODE */
748     pdm = PDEVOBJ_pdmMatchDevMode(ppdev, &dm);
749     if (!pdm)
750     {
751         ERR("Could not find a matching DEVMODE\n");
752         lResult = DISP_CHANGE_BADMODE;
753         goto leave;
754     }
755     else if (flags & CDS_TEST)
756     {
757         /* It's possible, go ahead! */
758         lResult = DISP_CHANGE_SUCCESSFUL;
759         goto leave;
760     }
761 
762     /* Shall we update the registry? */
763     if (flags & CDS_UPDATEREGISTRY)
764     {
765         /* Open the local or global settings key */
766         Status = UserOpenDisplaySettingsKey(&hkey, pustrDevice, flags & CDS_GLOBAL);
767         if (NT_SUCCESS(Status))
768         {
769             /* Store the settings */
770             RegWriteDisplaySettings(hkey, pdm);
771 
772             /* Close the registry key */
773             ZwClose(hkey);
774         }
775         else
776         {
777             ERR("Could not open registry key\n");
778             lResult = DISP_CHANGE_NOTUPDATED;
779         }
780     }
781 
782     /* Shall we apply the settings? */
783     if (!(flags & CDS_NORESET))
784     {
785         ULONG ulResult;
786         PVOID pvOldCursor;
787         TEXTMETRICW tmw;
788 
789         /* Remove mouse pointer */
790         pvOldCursor = UserSetCursor(NULL, TRUE);
791 
792         /* Do the mode switch */
793         ulResult = PDEVOBJ_bSwitchMode(ppdev, pdm);
794 
795         /* Restore mouse pointer, no hooks called */
796         pvOldCursor = UserSetCursor(pvOldCursor, TRUE);
797         ASSERT(pvOldCursor == NULL);
798 
799         /* Check for failure */
800         if (!ulResult)
801         {
802             ERR("Failed to set mode\n");
803             lResult = (lResult == DISP_CHANGE_NOTUPDATED) ?
804                 DISP_CHANGE_FAILED : DISP_CHANGE_RESTART;
805 
806             goto leave;
807         }
808 
809         UserUpdateFullscreen(flags);
810 
811         /* Update the system metrics */
812         InitMetrics();
813 
814         /* Set new size of the monitor */
815         UserUpdateMonitorSize((HDEV)ppdev);
816 
817         /* Update the SERVERINFO */
818         gpsi->dmLogPixels = ppdev->gdiinfo.ulLogPixelsY;
819         gpsi->Planes      = ppdev->gdiinfo.cPlanes;
820         gpsi->BitsPixel   = ppdev->gdiinfo.cBitsPixel;
821         gpsi->BitCount    = gpsi->Planes * gpsi->BitsPixel;
822         if (ppdev->gdiinfo.flRaster & RC_PALETTE)
823         {
824             gpsi->PUSIFlags |= PUSIF_PALETTEDISPLAY;
825         }
826         else
827             gpsi->PUSIFlags &= ~PUSIF_PALETTEDISPLAY;
828         // Font is realized and this dc was previously set to internal DC_ATTR.
829         gpsi->cxSysFontChar = IntGetCharDimensions(hSystemBM, &tmw, (DWORD*)&gpsi->cySysFontChar);
830         gpsi->tmSysFont     = tmw;
831 
832         /* Remove all cursor clipping */
833         UserClipCursor(NULL);
834 
835         //pdesk = IntGetActiveDesktop();
836         //IntHideDesktop(pdesk);
837 
838         /* Send WM_DISPLAYCHANGE to all toplevel windows */
839         UserSendNotifyMessage( HWND_BROADCAST,
840                                WM_DISPLAYCHANGE,
841                                gpsi->BitCount,
842                                MAKELONG(gpsi->aiSysMet[SM_CXSCREEN], gpsi->aiSysMet[SM_CYSCREEN]) );
843 
844         ERR("BitCount New %d Orig %d ChkNew %d\n",gpsi->BitCount,OrigBC,ppdev->gdiinfo.cBitsPixel);
845 
846         /* Not full screen and different bit count, send messages */
847         if (!(flags & CDS_FULLSCREEN) &&
848               gpsi->BitCount != OrigBC )
849         {
850            ERR("Detect settings changed.\n");
851            UserSendNotifyMessage( HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0 );
852            UserSendNotifyMessage( HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0 );
853         }
854 
855         //co_IntShowDesktop(pdesk, ppdev->gdiinfo.ulHorzRes, ppdev->gdiinfo.ulVertRes);
856 
857         UserRedrawDesktop();
858     }
859 
860 leave:
861     /* Release the PDEV */
862     PDEVOBJ_vRelease(ppdev);
863 
864     return lResult;
865 }
866 
867 VOID
868 UserDisplayNotifyShutdown(
869     PPROCESSINFO ppiCurrent)
870 {
871     if (ppiCurrent == gpFullscreen)
872     {
873         UserChangeDisplaySettings(NULL, NULL, 0, NULL);
874         if (gpFullscreen)
875             ERR("Failed to restore display mode!\n");
876     }
877 }
878 
879 LONG
880 APIENTRY
881 NtUserChangeDisplaySettings(
882     PUNICODE_STRING pustrDevice,
883     LPDEVMODEW lpDevMode,
884     DWORD dwflags,
885     LPVOID lParam)
886 {
887     WCHAR awcDevice[CCHDEVICENAME];
888     UNICODE_STRING ustrDevice;
889     DEVMODEW dmLocal;
890     LONG lRet;
891 
892     /* Check arguments */
893     if ((dwflags != CDS_VIDEOPARAMETERS) && (lParam != NULL))
894     {
895         EngSetLastError(ERROR_INVALID_PARAMETER);
896         return DISP_CHANGE_BADPARAM;
897     }
898 
899     /* Check flags */
900     if ((dwflags & (CDS_GLOBAL|CDS_NORESET)) && !(dwflags & CDS_UPDATEREGISTRY))
901     {
902         return DISP_CHANGE_BADFLAGS;
903     }
904 
905     /* Copy the device name */
906     if (pustrDevice)
907     {
908         /* Initialize destination string */
909         RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
910 
911         _SEH2_TRY
912         {
913             /* Probe the UNICODE_STRING and the buffer */
914             ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
915             ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
916 
917             /* Copy the string */
918             RtlCopyUnicodeString(&ustrDevice, pustrDevice);
919         }
920         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
921         {
922             /* Set and return error */
923             SetLastNtError(_SEH2_GetExceptionCode());
924             _SEH2_YIELD(return DISP_CHANGE_BADPARAM);
925         }
926         _SEH2_END
927 
928         pustrDevice = &ustrDevice;
929    }
930 
931     /* Copy devmode */
932     if (lpDevMode)
933     {
934         _SEH2_TRY
935         {
936             /* Probe the size field of the structure */
937             ProbeForRead(lpDevMode, sizeof(dmLocal.dmSize), 1);
938 
939             /* Calculate usable size */
940             dmLocal.dmSize = min(sizeof(dmLocal), lpDevMode->dmSize);
941 
942             /* Probe and copy the full DEVMODE */
943             ProbeForRead(lpDevMode, dmLocal.dmSize, 1);
944             RtlCopyMemory(&dmLocal, lpDevMode, dmLocal.dmSize);
945         }
946         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
947         {
948             /* Set and return error */
949             SetLastNtError(_SEH2_GetExceptionCode());
950             _SEH2_YIELD(return DISP_CHANGE_BADPARAM);
951         }
952         _SEH2_END
953 
954         /* Check for extra parameters */
955         if (dmLocal.dmDriverExtra > 0)
956         {
957             /* FIXME: TODO */
958             ERR("lpDevMode->dmDriverExtra is IGNORED!\n");
959             dmLocal.dmDriverExtra = 0;
960         }
961 
962         /* Use the local structure */
963         lpDevMode = &dmLocal;
964     }
965 
966     // FIXME: Copy videoparameters
967 
968     /* Acquire global USER lock */
969     UserEnterExclusive();
970 
971     /* Call internal function */
972     lRet = UserChangeDisplaySettings(pustrDevice, lpDevMode, dwflags, NULL);
973 
974     /* Release lock */
975     UserLeave();
976 
977     return lRet;
978 }
979 
980