[WIN32K]
[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 <win32k.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 /* Output what we got */
652 RtlCopyMemory(lpDevMode, pdm, min(cbSize, pdm->dmSize));
653
654 /* output private/extra driver data */
655 if (cbExtra > 0 && pdm->dmDriverExtra > 0)
656 {
657 RtlCopyMemory((PCHAR)lpDevMode + cbSize,
658 (PCHAR)pdm + pdm->dmSize,
659 min(cbExtra, pdm->dmDriverExtra));
660 }
661 }
662 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
663 {
664 Status = _SEH2_GetExceptionCode();
665 }
666 _SEH2_END;
667 }
668
669 return Status;
670 }
671
672 BOOL APIENTRY UserClipCursor(RECTL *prcl);
673 VOID APIENTRY UserRedrawDesktop();
674 HCURSOR FASTCALL UserSetCursor(PCURICON_OBJECT NewCursor, BOOL ForceChange);
675
676 LONG
677 APIENTRY
678 UserChangeDisplaySettings(
679 PUNICODE_STRING pustrDevice,
680 LPDEVMODEW pdm,
681 HWND hwnd,
682 DWORD flags,
683 LPVOID lParam)
684 {
685 DEVMODEW dm;
686 LONG lResult = DISP_CHANGE_SUCCESSFUL;
687 HKEY hkey;
688 NTSTATUS Status;
689 PPDEVOBJ ppdev;
690 PDESKTOP pdesk;
691
692 /* If no DEVMODE is given, use registry settings */
693 if (!pdm)
694 {
695 /* Get the registry settings */
696 Status = UserEnumRegistryDisplaySettings(pustrDevice, &dm);
697 if (!NT_SUCCESS(Status))
698 {
699 DPRINT1("Could not load registry settings\n");
700 return DISP_CHANGE_BADPARAM;
701 }
702 }
703 else if (pdm->dmSize < FIELD_OFFSET(DEVMODEW, dmFields))
704 return DISP_CHANGE_FAILED;
705 else
706 dm = *pdm;
707
708 /* Check params */
709 if ((dm.dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
710 {
711 DPRINT1("devmode doesn't specify the resolution.\n");
712 return DISP_CHANGE_BADMODE;
713 }
714
715 /* Get the PDEV */
716 ppdev = EngpGetPDEV(pustrDevice);
717 if (!ppdev)
718 {
719 DPRINT1("failed to get PDEV\n");
720 return DISP_CHANGE_BADPARAM;
721 }
722
723 /* Fixup values */
724 if((dm.dmFields & DM_BITSPERPEL) && (dm.dmBitsPerPel == 0))
725 dm.dmBitsPerPel = ppdev->pdmwDev->dmBitsPerPel;
726 if((dm.dmFields & DM_DISPLAYFREQUENCY) && (dm.dmDisplayFrequency == 0))
727 dm.dmDisplayFrequency = ppdev->pdmwDev->dmDisplayFrequency;
728
729 /* Look for the requested DEVMODE */
730 pdm = PDEVOBJ_pdmMatchDevMode(ppdev, &dm);
731 if (!pdm)
732 {
733 DPRINT1("Could not find a matching DEVMODE\n");
734 lResult = DISP_CHANGE_BADMODE;
735 goto leave;
736 }
737 else if (flags & CDS_TEST)
738 {
739 /* It's possible, go ahead! */
740 lResult = DISP_CHANGE_SUCCESSFUL;
741 goto leave;
742 }
743
744 /* Shall we update the registry? */
745 if (flags & CDS_UPDATEREGISTRY)
746 {
747 /* Open the local or global settings key */
748 Status = UserOpenDisplaySettingsKey(&hkey, pustrDevice, flags & CDS_GLOBAL);
749 if (NT_SUCCESS(Status))
750 {
751 /* Store the settings */
752 RegWriteDisplaySettings(hkey, pdm);
753
754 /* Close the registry key */
755 ZwClose(hkey);
756 }
757 else
758 {
759 DPRINT1("Could not open registry key\n");
760 lResult = DISP_CHANGE_NOTUPDATED;
761 }
762 }
763
764 /* Check if DEVMODE matches the current mode */
765 if (pdm == ppdev->pdmwDev && !(flags & CDS_RESET))
766 {
767 DPRINT1("DEVMODE matches, nothing to do\n");
768 goto leave;
769 }
770
771 /* Shall we apply the settings? */
772 if (!(flags & CDS_NORESET))
773 {
774 ULONG ulResult;
775
776 /* Remove mouse pointer */
777 UserSetCursor(NULL, TRUE);
778
779 /* Do the mode switch */
780 ulResult = PDEVOBJ_bSwitchMode(ppdev, pdm);
781
782 /* Restore mouse pointer, no hooks called */
783 UserSetCursorPos(gpsi->ptCursor.x, gpsi->ptCursor.y, FALSE);
784
785 /* Check for failure */
786 if (!ulResult)
787 {
788 DPRINT1("failed to set mode\n");
789 lResult = (lResult == DISP_CHANGE_NOTUPDATED) ?
790 DISP_CHANGE_FAILED : DISP_CHANGE_RESTART;
791
792 goto leave;
793 }
794
795 /* Update the system metrics */
796 InitMetrics();
797
798 //IntvGetDeviceCaps(&PrimarySurface, &GdiHandleTable->DevCaps);
799
800 /* Remove all cursor clipping */
801 UserClipCursor(NULL);
802
803 pdesk = IntGetActiveDesktop();
804 //IntHideDesktop(pdesk);
805
806 /* Send WM_DISPLAYCHANGE to all toplevel windows */
807 co_IntSendMessageTimeout(HWND_BROADCAST,
808 WM_DISPLAYCHANGE,
809 (WPARAM)ppdev->gdiinfo.cBitsPixel,
810 (LPARAM)(ppdev->gdiinfo.ulHorzRes + (ppdev->gdiinfo.ulVertRes << 16)),
811 SMTO_NORMAL,
812 100,
813 &ulResult);
814
815 //co_IntShowDesktop(pdesk, ppdev->gdiinfo.ulHorzRes, ppdev->gdiinfo.ulVertRes);
816
817 UserRedrawDesktop();
818 }
819
820 leave:
821 /* Release the PDEV */
822 PDEVOBJ_vRelease(ppdev);
823
824 return lResult;
825 }
826
827 LONG
828 APIENTRY
829 NtUserChangeDisplaySettings(
830 PUNICODE_STRING pustrDevice,
831 LPDEVMODEW lpDevMode,
832 HWND hwnd,
833 DWORD dwflags,
834 LPVOID lParam)
835 {
836 WCHAR awcDevice[CCHDEVICENAME];
837 UNICODE_STRING ustrDevice;
838 DEVMODEW dmLocal;
839 LONG lRet;
840
841 /* Check arguments */
842 if ((dwflags != CDS_VIDEOPARAMETERS && lParam != NULL) ||
843 (hwnd != NULL))
844 {
845 SetLastWin32Error(ERROR_INVALID_PARAMETER);
846 return DISP_CHANGE_BADPARAM;
847 }
848
849 /* Check flags */
850 if ((dwflags & (CDS_GLOBAL|CDS_NORESET)) && !(dwflags & CDS_UPDATEREGISTRY))
851 {
852 return DISP_CHANGE_BADFLAGS;
853 }
854
855 /* Copy the device name */
856 if (pustrDevice)
857 {
858 /* Initialize destination string */
859 RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
860
861 _SEH2_TRY
862 {
863 /* Probe the UNICODE_STRING and the buffer */
864 ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
865 ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
866
867 /* Copy the string */
868 RtlCopyUnicodeString(&ustrDevice, pustrDevice);
869 }
870 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
871 {
872 /* Set and return error */
873 SetLastNtError(_SEH2_GetExceptionCode());
874 _SEH2_YIELD(return DISP_CHANGE_BADPARAM);
875 }
876 _SEH2_END
877
878 pustrDevice = &ustrDevice;
879 }
880
881 /* Copy devmode */
882 if (lpDevMode)
883 {
884 _SEH2_TRY
885 {
886 /* Probe the size field of the structure */
887 ProbeForRead(lpDevMode, sizeof(dmLocal.dmSize), 1);
888
889 /* Calculate usable size */
890 dmLocal.dmSize = min(sizeof(dmLocal), lpDevMode->dmSize);
891
892 /* Probe and copy the full DEVMODE */
893 ProbeForRead(lpDevMode, dmLocal.dmSize, 1);
894 RtlCopyMemory(&dmLocal, lpDevMode, dmLocal.dmSize);
895 dmLocal.dmSize = sizeof(dmLocal);
896 }
897 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
898 {
899 /* Set and return error */
900 SetLastNtError(_SEH2_GetExceptionCode());
901 _SEH2_YIELD(return DISP_CHANGE_BADPARAM);
902 }
903 _SEH2_END
904
905 /* Check for extra parameters */
906 if (dmLocal.dmDriverExtra > 0)
907 {
908 /* FIXME: TODO */
909 DPRINT1("lpDevMode->dmDriverExtra is IGNORED!\n");
910 dmLocal.dmDriverExtra = 0;
911 }
912
913 /* Use the local structure */
914 lpDevMode = &dmLocal;
915 }
916
917 // FIXME: Copy videoparameters
918
919 /* Acquire global USER lock */
920 UserEnterExclusive();
921
922 /* Call internal function */
923 lRet = UserChangeDisplaySettings(pustrDevice, lpDevMode, hwnd, dwflags, NULL);
924
925 /* Release lock */
926 UserLeave();
927
928 return lRet;
929 }
930