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