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