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