[WIN32K]
[reactos.git] / reactos / subsystems / win32 / win32k / 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: subsystems/win32/win32k/ntuser/display.c
6 * PROGRAMER: Timo Kreuzer (timo.kreuzer@reactos.org)
7 */
8
9 #include <win32k.h>
10
11 #include <intrin.h>
12
13 #define NDEBUG
14 #include <debug.h>
15
16 BOOL InitSysParams();
17
18 BOOL gbBaseVideo = 0;
19
20 static const PWCHAR KEY_ROOT = L"";
21 static const PWCHAR KEY_VIDEO = L"\\Registry\\Machine\\HARDWARE\\DEVICEMAP\\VIDEO";
22
23 VOID
24 RegWriteDisplaySettings(HKEY hkey, PDEVMODEW pdm)
25 {
26 RegWriteDWORD(hkey, L"DefaultSettings.BitsPerPel", pdm->dmBitsPerPel);
27 RegWriteDWORD(hkey, L"DefaultSettings.XResolution", pdm->dmPelsWidth);
28 RegWriteDWORD(hkey, L"DefaultSettings.YResolution", pdm->dmPelsHeight);
29 RegWriteDWORD(hkey, L"DefaultSettings.Flags", pdm->dmDisplayFlags);
30 RegWriteDWORD(hkey, L"DefaultSettings.VRefresh", pdm->dmDisplayFrequency);
31 RegWriteDWORD(hkey, L"DefaultSettings.XPanning", pdm->dmPanningWidth);
32 RegWriteDWORD(hkey, L"DefaultSettings.YPanning", pdm->dmPanningHeight);
33 RegWriteDWORD(hkey, L"DefaultSettings.Orientation", pdm->dmDisplayOrientation);
34 RegWriteDWORD(hkey, L"DefaultSettings.FixedOutput", pdm->dmDisplayFixedOutput);
35 RegWriteDWORD(hkey, L"Attach.RelativeX", pdm->dmPosition.x);
36 RegWriteDWORD(hkey, L"Attach.RelativeY", pdm->dmPosition.y);
37 // RegWriteDWORD(hkey, L"Attach.ToDesktop, pdm->dmBitsPerPel", pdm->);
38 }
39
40 VOID
41 RegReadDisplaySettings(HKEY hkey, PDEVMODEW pdm)
42 {
43 DWORD dwValue;
44
45 /* Zero out the structure */
46 RtlZeroMemory(pdm, sizeof(DEVMODEW));
47
48 /* Helper macro */
49 #define READ(field, str, flag) \
50 if (RegReadDWORD(hkey, L##str, &dwValue)) \
51 { \
52 pdm->field = dwValue; \
53 pdm->dmFields |= flag; \
54 }
55
56 /* Read all present settings */
57 READ(dmBitsPerPel, "DefaultSettings.BitsPerPel", DM_BITSPERPEL);
58 READ(dmPelsWidth, "DefaultSettings.XResolution", DM_PELSWIDTH);
59 READ(dmPelsHeight, "DefaultSettings.YResolution", DM_PELSHEIGHT);
60 READ(dmDisplayFlags, "DefaultSettings.Flags", DM_DISPLAYFLAGS);
61 READ(dmDisplayFrequency, "DefaultSettings.VRefresh", DM_DISPLAYFREQUENCY);
62 READ(dmPanningWidth, "DefaultSettings.XPanning", DM_PANNINGWIDTH);
63 READ(dmPanningHeight, "DefaultSettings.YPanning", DM_PANNINGHEIGHT);
64 READ(dmDisplayOrientation, "DefaultSettings.Orientation", DM_DISPLAYORIENTATION);
65 READ(dmDisplayFixedOutput, "DefaultSettings.FixedOutput", DM_DISPLAYFIXEDOUTPUT);
66 READ(dmPosition.x, "Attach.RelativeX", DM_POSITION);
67 READ(dmPosition.y, "Attach.RelativeY", DM_POSITION);
68 }
69
70 PGRAPHICS_DEVICE
71 NTAPI
72 InitDisplayDriver(
73 IN PWSTR pwszDeviceName,
74 IN PWSTR pwszRegKey)
75 {
76 PGRAPHICS_DEVICE pGraphicsDevice;
77 UNICODE_STRING ustrDeviceName, ustrDisplayDrivers, ustrDescription;
78 NTSTATUS Status;
79 WCHAR awcBuffer[128];
80 ULONG cbSize;
81 HKEY hkey;
82 DEVMODEW dmDefault;
83
84 DPRINT1("InitDisplayDriver(%S, %S);\n",
85 pwszDeviceName, pwszRegKey);
86
87 /* Open the driver's registry key */
88 Status = RegOpenKey(pwszRegKey, &hkey);
89 if (!NT_SUCCESS(Status))
90 {
91 DPRINT1("Failed to open registry key: %ls\n", pwszRegKey);
92 return NULL;
93 }
94
95 /* Query the diplay drivers */
96 cbSize = sizeof(awcBuffer) - 10;
97 Status = RegQueryValue(hkey,
98 L"InstalledDisplayDrivers",
99 REG_MULTI_SZ,
100 awcBuffer,
101 &cbSize);
102 if (!NT_SUCCESS(Status))
103 {
104 DPRINT1("Didn't find 'InstalledDisplayDrivers', status = 0x%lx\n", Status);
105 ZwClose(hkey);
106 return NULL;
107 }
108
109 /* Initialize the UNICODE_STRING */
110 ustrDisplayDrivers.Buffer = awcBuffer;
111 ustrDisplayDrivers.MaximumLength = cbSize;
112 ustrDisplayDrivers.Length = cbSize;
113
114 /* Set Buffer for description and size of remaining buffer */
115 ustrDescription.Buffer = awcBuffer + (cbSize / sizeof(WCHAR));
116 cbSize = sizeof(awcBuffer) - cbSize;
117
118 /* Query the device string */
119 Status = RegQueryValue(hkey,
120 L"Device Description",
121 REG_SZ,
122 ustrDescription.Buffer,
123 &cbSize);
124 if (NT_SUCCESS(Status))
125 {
126 ustrDescription.MaximumLength = cbSize;
127 ustrDescription.Length = cbSize;
128 }
129 else
130 {
131 RtlInitUnicodeString(&ustrDescription, L"<unknown>");
132 }
133
134 /* Query the default settings */
135 RegReadDisplaySettings(hkey, &dmDefault);
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
147 return pGraphicsDevice;
148 }
149
150 BOOL
151 InitVideo(
152 PUNICODE_STRING pustrRegPath,
153 FLONG flags)
154 {
155 ULONG iDevNum, iVGACompatible = -1, ulMaxObjectNumber = 0;
156 WCHAR awcDeviceName[20];
157 WCHAR awcBuffer[256];
158 NTSTATUS Status;
159 PGRAPHICS_DEVICE pGraphicsDevice;
160 ULONG cbValue;
161 HKEY hkey;
162
163 DPRINT1("----------------------------- InitVideo() -------------------------------\n");
164
165 Status = RegOpenKey(L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control", &hkey);
166 if (NT_SUCCESS(Status))
167 {
168 cbValue = 256;
169 Status = RegQueryValue(hkey, L"SystemStartOptions", REG_SZ, awcBuffer, &cbValue);
170 if (NT_SUCCESS(Status))
171 {
172 /* Check if VGA mode is requested. */
173 if (wcsstr(awcBuffer, L"/BASEVIDEO") != 0)
174 {
175 DPRINT1("VGA mode requested.\n");
176 gbBaseVideo = TRUE;
177 }
178 }
179
180 ZwClose(hkey);
181 }
182
183 /* Open the key for the adapters */
184 Status = RegOpenKey(KEY_VIDEO, &hkey);
185 if (!NT_SUCCESS(Status))
186 {
187 DPRINT1("Could not open device registry key!\n");
188 ASSERT(FALSE);
189 }
190
191 /* Read the name of the VGA adapter */
192 cbValue = 20;
193 Status = RegQueryValue(hkey, L"VgaCompatible", REG_SZ, awcDeviceName, &cbValue);
194 if (NT_SUCCESS(Status))
195 {
196 iVGACompatible = _wtoi(&awcDeviceName[13]);
197 DPRINT1("VGA adapter = %ld\n", iVGACompatible);
198 }
199
200 /* Get the maximum mumber of adapters */
201 if (!RegReadDWORD(hkey, L"MaxObjectNumber", &ulMaxObjectNumber))
202 {
203 DPRINT1("Could not read MaxObjectNumber, defaulting to 0.\n");
204 }
205
206 DPRINT("Found %ld devices\n", ulMaxObjectNumber);
207
208 /* Loop through all adapters */
209 for (iDevNum = 0; iDevNum <= ulMaxObjectNumber; iDevNum++)
210 {
211 /* Create the adapter's key name */
212 swprintf(awcDeviceName, L"\\Device\\Video%lu", iDevNum);
213
214 /* Read the reg key name */
215 cbValue = sizeof(awcBuffer);
216 Status = RegQueryValue(hkey, awcDeviceName, REG_SZ, awcBuffer, &cbValue);
217 if (!NT_SUCCESS(Status))
218 {
219 DPRINT1("failed to query the registry path:0x%lx\n", Status);
220 continue;
221 }
222
223 pGraphicsDevice = InitDisplayDriver(awcDeviceName, awcBuffer);
224
225 /* Check if this is the VGA adapter */
226 if (iDevNum == iVGACompatible)
227 {
228 /* Set the VGA device as primary */
229 gpVgaGraphicsDevice = pGraphicsDevice;
230 DPRINT1("gpVgaGraphicsDevice = %p\n", gpVgaGraphicsDevice);
231 }
232
233 /* Set the first one as primary device */
234 if (!gpPrimaryGraphicsDevice)
235 gpPrimaryGraphicsDevice = pGraphicsDevice;
236 }
237
238 ZwClose(hkey);
239
240 if (gbBaseVideo)
241 {
242 if (gpVgaGraphicsDevice)
243 {
244 /* Set the VgaAdapter as primary */
245 gpPrimaryGraphicsDevice = gpVgaGraphicsDevice;
246 // FIXME: DEVMODE
247 }
248 else
249 {
250 DPRINT1("Could not find VGA compatible driver. Trying normal.\n");
251 }
252 }
253
254 InitSysParams();
255
256 return 1;
257 }
258
259 NTSTATUS
260 NTAPI
261 UserEnumDisplayDevices(
262 PUNICODE_STRING pustrDevice,
263 DWORD iDevNum,
264 PDISPLAY_DEVICEW pdispdev,
265 DWORD dwFlags)
266 {
267 PGRAPHICS_DEVICE pGraphicsDevice;
268 ULONG cbSize;
269 HKEY hkey;
270 NTSTATUS Status;
271
272 /* Ask gdi for the GRAPHICS_DEVICE */
273 pGraphicsDevice = EngpFindGraphicsDevice(pustrDevice, iDevNum, 0);
274 if (!pGraphicsDevice)
275 {
276 /* No device found */
277 DPRINT1("No GRAPHICS_DEVICE found\n");
278 return STATUS_UNSUCCESSFUL;
279 }
280
281 /* Open thhe device map registry key */
282 Status = RegOpenKey(KEY_VIDEO, &hkey);
283 if (!NT_SUCCESS(Status))
284 {
285 /* No device found */
286 DPRINT1("Could not open reg key\n");
287 return STATUS_UNSUCCESSFUL;
288 }
289
290 /* Query the registry path */
291 cbSize = sizeof(pdispdev->DeviceKey);
292 RegQueryValue(hkey,
293 pGraphicsDevice->szNtDeviceName,
294 REG_SZ,
295 pdispdev->DeviceKey,
296 &cbSize);
297
298 /* Close registry key */
299 ZwClose(hkey);
300
301 /* Copy device name, device string and StateFlags */
302 wcsncpy(pdispdev->DeviceName, pGraphicsDevice->szWinDeviceName, 32);
303 wcsncpy(pdispdev->DeviceString, pGraphicsDevice->pwszDescription, 128);
304 pdispdev->StateFlags = pGraphicsDevice->StateFlags;
305
306 // FIXME: fill in DEVICE ID
307
308 return STATUS_SUCCESS;
309 }
310
311 //NTSTATUS
312 BOOL
313 NTAPI
314 NtUserEnumDisplayDevices(
315 PUNICODE_STRING pustrDevice,
316 DWORD iDevNum,
317 PDISPLAY_DEVICEW pDisplayDevice,
318 DWORD dwFlags)
319 {
320 UNICODE_STRING ustrDevice;
321 WCHAR awcDevice[CCHDEVICENAME];
322 DISPLAY_DEVICEW dispdev;
323 NTSTATUS Status;
324
325 DPRINT("Enter NtUserEnumDisplayDevices(%wZ, %ld)\n",
326 pustrDevice, iDevNum);
327
328 // FIXME: HACK, desk.cpl passes broken crap
329 if (pustrDevice && iDevNum != 0)
330 return FALSE;
331
332 dispdev.cb = sizeof(DISPLAY_DEVICEW);
333
334 if (pustrDevice)
335 {
336 /* Initialize destination string */
337 RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
338
339 _SEH2_TRY
340 {
341 /* Probe the UNICODE_STRING and the buffer */
342 ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
343 ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
344
345 /* Copy the string */
346 RtlCopyUnicodeString(&ustrDevice, pustrDevice);
347 }
348 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
349 {
350 // _SEH2_YIELD(return _SEH2_GetExceptionCode());
351 _SEH2_YIELD(return NT_SUCCESS(_SEH2_GetExceptionCode()));
352 }
353 _SEH2_END
354
355 if (ustrDevice.Length > 0)
356 pustrDevice = &ustrDevice;
357 else
358 pustrDevice = NULL;
359 }
360
361 /* Acquire global USER lock */
362 UserEnterExclusive();
363
364 /* Call the internal function */
365 Status = UserEnumDisplayDevices(pustrDevice, iDevNum, &dispdev, dwFlags);
366
367 /* Release lock */
368 UserLeave();
369
370 /* On success copy data to caller */
371 if (NT_SUCCESS(Status))
372 {
373 /* Enter SEH */
374 _SEH2_TRY
375 {
376 /* First probe the cb field */
377 ProbeForWrite(&pDisplayDevice->cb, sizeof(DWORD), 1);
378
379 /* Check the buffer size */
380 if (pDisplayDevice->cb)
381 {
382 /* Probe the output buffer */
383 pDisplayDevice->cb = min(pDisplayDevice->cb, sizeof(dispdev));
384 ProbeForWrite(pDisplayDevice, pDisplayDevice->cb, 1);
385
386 /* Copy as much as the given buffer allows */
387 RtlCopyMemory(pDisplayDevice, &dispdev, pDisplayDevice->cb);
388 }
389 }
390 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
391 {
392 Status = _SEH2_GetExceptionCode();
393 }
394 _SEH2_END
395 }
396
397 DPRINT1("Leave NtUserEnumDisplayDevices, Status = 0x%lx\n", Status);
398 /* Return the result */
399 // return Status;
400 return NT_SUCCESS(Status); // FIXME
401 }
402
403 NTSTATUS
404 NTAPI
405 UserEnumCurrentDisplaySettings(
406 PUNICODE_STRING pustrDevice,
407 PDEVMODEW *ppdm)
408 {
409 PPDEVOBJ ppdev;
410
411 /* Get the PDEV for the device */
412 ppdev = EngpGetPDEV(pustrDevice);
413 if (!ppdev)
414 {
415 /* No device found */
416 DPRINT1("No PDEV found!\n");
417 return STATUS_UNSUCCESSFUL;
418 }
419
420 *ppdm = ppdev->pdmwDev;
421 PDEVOBJ_vRelease(ppdev);
422
423 return STATUS_SUCCESS;
424 }
425
426 NTSTATUS
427 NTAPI
428 UserEnumDisplaySettings(
429 PUNICODE_STRING pustrDevice,
430 DWORD iModeNum,
431 LPDEVMODEW *ppdm,
432 DWORD dwFlags)
433 {
434 PGRAPHICS_DEVICE pGraphicsDevice;
435 PDEVMODEENTRY pdmentry;
436 ULONG i, iFoundMode;
437
438 DPRINT("Enter UserEnumDisplaySettings('%ls', %ld)\n",
439 pustrDevice ? pustrDevice->Buffer : NULL, iModeNum);
440
441 /* Ask gdi for the GRAPHICS_DEVICE */
442 pGraphicsDevice = EngpFindGraphicsDevice(pustrDevice, 0, 0);
443
444 if (!pGraphicsDevice)
445 {
446 /* No device found */
447 DPRINT1("No device found!\n");
448 return STATUS_UNSUCCESSFUL;
449 }
450
451 if (iModeNum >= pGraphicsDevice->cDevModes)
452 return STATUS_NO_MORE_ENTRIES;
453
454 iFoundMode = 0;
455 for (i = 0; i < pGraphicsDevice->cDevModes; i++)
456 {
457 pdmentry = &pGraphicsDevice->pDevModeList[i];
458
459 /* FIXME: consider EDS_RAWMODE */
460 #if 0
461 if ((!(dwFlags & EDS_RAWMODE) && (pdmentry->dwFlags & 1)) ||!
462 (dwFlags & EDS_RAWMODE))
463 #endif
464 {
465 /* Is this the one we want? */
466 if (iFoundMode == iModeNum)
467 {
468 *ppdm = pdmentry->pdm;
469 return STATUS_SUCCESS;
470 }
471
472 /* Increment number of found modes */
473 iFoundMode++;
474 }
475 }
476
477 /* Nothing was found */
478 return STATUS_INVALID_PARAMETER;
479 }
480
481 NTSTATUS
482 NTAPI
483 UserOpenDisplaySettingsKey(
484 OUT PHKEY phkey,
485 IN PUNICODE_STRING pustrDevice,
486 IN BOOL bGlobal)
487 {
488 HKEY hkey;
489 DISPLAY_DEVICEW dispdev;
490 NTSTATUS Status;
491
492 /* Get device info */
493 Status = UserEnumDisplayDevices(pustrDevice, 0, &dispdev, 0);
494 if (!NT_SUCCESS(Status))
495 return Status;
496
497 if (bGlobal)
498 {
499 // FIXME: need to fix the registry key somehow
500 }
501
502 /* Open the registry key */
503 Status = RegOpenKey(dispdev.DeviceKey, &hkey);
504 if (!NT_SUCCESS(Status))
505 return Status;
506
507 *phkey = hkey;
508
509 return Status;
510 }
511
512 NTSTATUS
513 NTAPI
514 UserEnumRegistryDisplaySettings(
515 IN PUNICODE_STRING pustrDevice,
516 OUT LPDEVMODEW pdm)
517 {
518 HKEY hkey;
519 NTSTATUS Status = UserOpenDisplaySettingsKey(&hkey, pustrDevice, 0);
520 if(NT_SUCCESS(Status))
521 {
522 RegReadDisplaySettings(hkey, pdm);
523 ZwClose(hkey);
524 return STATUS_SUCCESS;
525 }
526 return Status ;
527 }
528
529
530 NTSTATUS
531 APIENTRY
532 NtUserEnumDisplaySettings(
533 IN PUNICODE_STRING pustrDevice,
534 IN DWORD iModeNum,
535 OUT LPDEVMODEW lpDevMode,
536 IN DWORD dwFlags)
537 {
538 UNICODE_STRING ustrDevice;
539 WCHAR awcDevice[CCHDEVICENAME];
540 NTSTATUS Status;
541 ULONG cbSize, cbExtra;
542 DEVMODEW dmReg, *pdm;
543
544 DPRINT1("Enter NtUserEnumDisplaySettings(%ls, %ld)\n",
545 pustrDevice ? pustrDevice->Buffer : 0, iModeNum);
546
547 if (pustrDevice)
548 {
549 /* Initialize destination string */
550 RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
551
552 _SEH2_TRY
553 {
554 /* Probe the UNICODE_STRING and the buffer */
555 ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
556 ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
557
558 /* Copy the string */
559 RtlCopyUnicodeString(&ustrDevice, pustrDevice);
560 }
561 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
562 {
563 _SEH2_YIELD(return _SEH2_GetExceptionCode());
564 }
565 _SEH2_END
566
567 pustrDevice = &ustrDevice;
568 }
569
570 /* Acquire global USER lock */
571 UserEnterExclusive();
572
573 if (iModeNum == ENUM_REGISTRY_SETTINGS)
574 {
575 /* Get the registry settings */
576 Status = UserEnumRegistryDisplaySettings(pustrDevice, &dmReg);
577 pdm = &dmReg;
578 }
579 else if (iModeNum == ENUM_CURRENT_SETTINGS)
580 {
581 /* Get the current settings */
582 Status = UserEnumCurrentDisplaySettings(pustrDevice, &pdm);
583 }
584 else
585 {
586 /* Get specified settings */
587 Status = UserEnumDisplaySettings(pustrDevice, iModeNum, &pdm, dwFlags);
588 }
589
590 /* Release lock */
591 UserLeave();
592
593 /* Did we succeed? */
594 if (NT_SUCCESS(Status))
595 {
596 /* Copy some information back */
597 _SEH2_TRY
598 {
599 ProbeForRead(lpDevMode, sizeof(DEVMODEW), 1);
600 cbSize = lpDevMode->dmSize;
601 cbExtra = lpDevMode->dmDriverExtra;
602
603 ProbeForWrite(lpDevMode, cbSize + cbExtra, 1);
604 /* Output what we got */
605 RtlCopyMemory(lpDevMode, pdm, min(cbSize, pdm->dmSize));
606
607 /* output private/extra driver data */
608 if (cbExtra > 0 && pdm->dmDriverExtra > 0)
609 {
610 RtlCopyMemory((PCHAR)lpDevMode + cbSize,
611 (PCHAR)pdm + pdm->dmSize,
612 min(cbExtra, pdm->dmDriverExtra));
613 }
614 }
615 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
616 {
617 Status = _SEH2_GetExceptionCode();
618 }
619 _SEH2_END;
620 }
621
622 return Status;
623 }
624
625 BOOL APIENTRY UserClipCursor(RECTL *prcl);
626 VOID APIENTRY UserRedrawDesktop();
627 HCURSOR FASTCALL UserSetCursor(PCURICON_OBJECT NewCursor, BOOL ForceChange);
628
629 LONG
630 APIENTRY
631 UserChangeDisplaySettings(
632 PUNICODE_STRING pustrDevice,
633 LPDEVMODEW pdm,
634 HWND hwnd,
635 DWORD flags,
636 LPVOID lParam)
637 {
638 DEVMODEW dm;
639 LONG lResult = DISP_CHANGE_SUCCESSFUL;
640 HKEY hkey;
641 NTSTATUS Status;
642 PPDEVOBJ ppdev;
643 PDESKTOP pdesk;
644
645 /* If no DEVMODE is given, use registry settings */
646 if (!pdm)
647 {
648 /* Get the registry settings */
649 Status = UserEnumRegistryDisplaySettings(pustrDevice, &dm);
650 if (!NT_SUCCESS(Status))
651 {
652 DPRINT1("Could not load registry settings\n");
653 return DISP_CHANGE_BADPARAM;
654 }
655 }
656 else if (pdm->dmSize < FIELD_OFFSET(DEVMODEW, dmFields))
657 return DISP_CHANGE_BADMODE; /* This is what winXP SP3 returns */
658 else
659 dm = *pdm;
660
661 /* Check params */
662 if ((dm.dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
663 {
664 DPRINT1("devmode doesn't specify the resolution.\n");
665 return DISP_CHANGE_BADMODE;
666 }
667
668 /* Get the PDEV */
669 ppdev = EngpGetPDEV(pustrDevice);
670 if (!ppdev)
671 {
672 DPRINT1("failed to get PDEV\n");
673 return DISP_CHANGE_BADPARAM;
674 }
675
676 /* Fixup values */
677 if(dm.dmBitsPerPel == 0 || !(dm.dmFields & DM_BITSPERPEL))
678 {
679 dm.dmBitsPerPel = ppdev->pdmwDev->dmBitsPerPel;
680 dm.dmFields |= DM_BITSPERPEL;
681 }
682
683 if((dm.dmFields & DM_DISPLAYFREQUENCY) && (dm.dmDisplayFrequency == 0))
684 dm.dmDisplayFrequency = ppdev->pdmwDev->dmDisplayFrequency;
685
686 /* Look for the requested DEVMODE */
687 pdm = PDEVOBJ_pdmMatchDevMode(ppdev, &dm);
688 if (!pdm)
689 {
690 DPRINT1("Could not find a matching DEVMODE\n");
691 lResult = DISP_CHANGE_BADMODE;
692 goto leave;
693 }
694 else if (flags & CDS_TEST)
695 {
696 /* It's possible, go ahead! */
697 lResult = DISP_CHANGE_SUCCESSFUL;
698 goto leave;
699 }
700
701 /* Shall we update the registry? */
702 if (flags & CDS_UPDATEREGISTRY)
703 {
704 /* Open the local or global settings key */
705 Status = UserOpenDisplaySettingsKey(&hkey, pustrDevice, flags & CDS_GLOBAL);
706 if (NT_SUCCESS(Status))
707 {
708 /* Store the settings */
709 RegWriteDisplaySettings(hkey, pdm);
710
711 /* Close the registry key */
712 ZwClose(hkey);
713 }
714 else
715 {
716 DPRINT1("Could not open registry key\n");
717 lResult = DISP_CHANGE_NOTUPDATED;
718 }
719 }
720
721 /* Check if DEVMODE matches the current mode */
722 if (pdm == ppdev->pdmwDev && !(flags & CDS_RESET))
723 {
724 DPRINT1("DEVMODE matches, nothing to do\n");
725 goto leave;
726 }
727
728 /* Shall we apply the settings? */
729 if (!(flags & CDS_NORESET))
730 {
731 ULONG ulResult;
732
733 /* Remove mouse pointer */
734 UserSetCursor(NULL, TRUE);
735
736 /* Do the mode switch */
737 ulResult = PDEVOBJ_bSwitchMode(ppdev, pdm);
738
739 /* Restore mouse pointer, no hooks called */
740 UserSetCursorPos(gpsi->ptCursor.x, gpsi->ptCursor.y, FALSE);
741
742 /* Check for failure */
743 if (!ulResult)
744 {
745 DPRINT1("failed to set mode\n");
746 lResult = (lResult == DISP_CHANGE_NOTUPDATED) ?
747 DISP_CHANGE_FAILED : DISP_CHANGE_RESTART;
748
749 goto leave;
750 }
751
752 /* Update the system metrics */
753 InitMetrics();
754
755 //IntvGetDeviceCaps(&PrimarySurface, &GdiHandleTable->DevCaps);
756
757 /* Remove all cursor clipping */
758 UserClipCursor(NULL);
759
760 pdesk = IntGetActiveDesktop();
761 //IntHideDesktop(pdesk);
762
763 /* Send WM_DISPLAYCHANGE to all toplevel windows */
764 co_IntSendMessageTimeout(HWND_BROADCAST,
765 WM_DISPLAYCHANGE,
766 (WPARAM)ppdev->gdiinfo.cBitsPixel,
767 (LPARAM)(ppdev->gdiinfo.ulHorzRes + (ppdev->gdiinfo.ulVertRes << 16)),
768 SMTO_NORMAL,
769 100,
770 &ulResult);
771
772 //co_IntShowDesktop(pdesk, ppdev->gdiinfo.ulHorzRes, ppdev->gdiinfo.ulVertRes);
773
774 UserRedrawDesktop();
775 }
776
777 leave:
778 /* Release the PDEV */
779 PDEVOBJ_vRelease(ppdev);
780
781 return lResult;
782 }
783
784 LONG
785 APIENTRY
786 NtUserChangeDisplaySettings(
787 PUNICODE_STRING pustrDevice,
788 LPDEVMODEW lpDevMode,
789 HWND hwnd,
790 DWORD dwflags,
791 LPVOID lParam)
792 {
793 WCHAR awcDevice[CCHDEVICENAME];
794 UNICODE_STRING ustrDevice;
795 DEVMODEW dmLocal;
796 LONG lRet;
797
798 /* Check arguments */
799 if ((dwflags != CDS_VIDEOPARAMETERS && lParam != NULL) ||
800 (hwnd != NULL))
801 {
802 SetLastWin32Error(ERROR_INVALID_PARAMETER);
803 return DISP_CHANGE_BADPARAM;
804 }
805
806 /* Check flags */
807 if ((dwflags & (CDS_GLOBAL|CDS_NORESET)) && !(dwflags & CDS_UPDATEREGISTRY))
808 {
809 return DISP_CHANGE_BADFLAGS;
810 }
811
812 /* Copy the device name */
813 if (pustrDevice)
814 {
815 /* Initialize destination string */
816 RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
817
818 _SEH2_TRY
819 {
820 /* Probe the UNICODE_STRING and the buffer */
821 ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
822 ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
823
824 /* Copy the string */
825 RtlCopyUnicodeString(&ustrDevice, pustrDevice);
826 }
827 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
828 {
829 /* Set and return error */
830 SetLastNtError(_SEH2_GetExceptionCode());
831 _SEH2_YIELD(return DISP_CHANGE_BADPARAM);
832 }
833 _SEH2_END
834
835 pustrDevice = &ustrDevice;
836 }
837
838 /* Copy devmode */
839 if (lpDevMode)
840 {
841 _SEH2_TRY
842 {
843 /* Probe the size field of the structure */
844 ProbeForRead(lpDevMode, sizeof(dmLocal.dmSize), 1);
845
846 /* Calculate usable size */
847 dmLocal.dmSize = min(sizeof(dmLocal), lpDevMode->dmSize);
848
849 /* Probe and copy the full DEVMODE */
850 ProbeForRead(lpDevMode, dmLocal.dmSize, 1);
851 RtlCopyMemory(&dmLocal, lpDevMode, dmLocal.dmSize);
852 }
853 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
854 {
855 /* Set and return error */
856 SetLastNtError(_SEH2_GetExceptionCode());
857 _SEH2_YIELD(return DISP_CHANGE_BADPARAM);
858 }
859 _SEH2_END
860
861 /* Check for extra parameters */
862 if (dmLocal.dmDriverExtra > 0)
863 {
864 /* FIXME: TODO */
865 DPRINT1("lpDevMode->dmDriverExtra is IGNORED!\n");
866 dmLocal.dmDriverExtra = 0;
867 }
868
869 /* Use the local structure */
870 lpDevMode = &dmLocal;
871 }
872
873 // FIXME: Copy videoparameters
874
875 /* Acquire global USER lock */
876 UserEnterExclusive();
877
878 /* Call internal function */
879 lRet = UserChangeDisplaySettings(pustrDevice, lpDevMode, hwnd, dwflags, NULL);
880
881 /* Release lock */
882 UserLeave();
883
884 return lRet;
885 }
886