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