[WIN32K:NTUSER] Avoid user memory access outside of SEH in NtUserEnumDisplaySettings.
[reactos.git] / win32ss / user / ntuser / display.c
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 }