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