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