reactos/win32ss/user/ntuser/display.c
Hervé Poussineau 8b7770aa2c [WIN32SS:ENG] Rework EngpUpdateGraphicsDeviceList
- choose VGA adapter outside of driver initialization loop
- choose primary adapter outside of driver initialization loop
- link VGA adapter to primary adapter at the end
- only set DISPLAY_DEVICE_PRIMARY_DEVICE in this function

Also mark VgaSave driver as SystemStart instead of Disabled,
so it is available if main display driver doesn't work.

CORE-7728, CORE-19224
2024-02-07 22:41:36 +01:00

1040 lines
29 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* PURPOSE: Video initialization and display settings
* FILE: win32ss/user/ntuser/display.c
* PROGRAMER: Timo Kreuzer (timo.kreuzer@reactos.org)
*/
#include <win32k.h>
DBG_DEFAULT_CHANNEL(UserDisplay);
BOOL gbBaseVideo = FALSE;
static PPROCESSINFO gpFullscreen = NULL;
static const PWCHAR KEY_VIDEO = L"\\Registry\\Machine\\HARDWARE\\DEVICEMAP\\VIDEO";
VOID
RegWriteDisplaySettings(HKEY hkey, PDEVMODEW pdm)
{
RegWriteDWORD(hkey, L"DefaultSettings.BitsPerPel", pdm->dmBitsPerPel);
RegWriteDWORD(hkey, L"DefaultSettings.XResolution", pdm->dmPelsWidth);
RegWriteDWORD(hkey, L"DefaultSettings.YResolution", pdm->dmPelsHeight);
RegWriteDWORD(hkey, L"DefaultSettings.Flags", pdm->dmDisplayFlags);
RegWriteDWORD(hkey, L"DefaultSettings.VRefresh", pdm->dmDisplayFrequency);
RegWriteDWORD(hkey, L"DefaultSettings.XPanning", pdm->dmPanningWidth);
RegWriteDWORD(hkey, L"DefaultSettings.YPanning", pdm->dmPanningHeight);
RegWriteDWORD(hkey, L"DefaultSettings.Orientation", pdm->dmDisplayOrientation);
RegWriteDWORD(hkey, L"DefaultSettings.FixedOutput", pdm->dmDisplayFixedOutput);
RegWriteDWORD(hkey, L"Attach.RelativeX", pdm->dmPosition.x);
RegWriteDWORD(hkey, L"Attach.RelativeY", pdm->dmPosition.y);
// RegWriteDWORD(hkey, L"Attach.ToDesktop, pdm->dmBitsPerPel", pdm->);
}
VOID
RegReadDisplaySettings(HKEY hkey, PDEVMODEW pdm)
{
DWORD dwValue;
/* Zero out the structure */
RtlZeroMemory(pdm, sizeof(DEVMODEW));
/* Helper macro */
#define READ(field, str, flag) \
if (RegReadDWORD(hkey, L##str, &dwValue)) \
{ \
pdm->field = dwValue; \
pdm->dmFields |= flag; \
}
/* Read all present settings */
READ(dmBitsPerPel, "DefaultSettings.BitsPerPel", DM_BITSPERPEL);
READ(dmPelsWidth, "DefaultSettings.XResolution", DM_PELSWIDTH);
READ(dmPelsHeight, "DefaultSettings.YResolution", DM_PELSHEIGHT);
READ(dmDisplayFlags, "DefaultSettings.Flags", DM_DISPLAYFLAGS);
READ(dmDisplayFrequency, "DefaultSettings.VRefresh", DM_DISPLAYFREQUENCY);
READ(dmPanningWidth, "DefaultSettings.XPanning", DM_PANNINGWIDTH);
READ(dmPanningHeight, "DefaultSettings.YPanning", DM_PANNINGHEIGHT);
READ(dmDisplayOrientation, "DefaultSettings.Orientation", DM_DISPLAYORIENTATION);
READ(dmDisplayFixedOutput, "DefaultSettings.FixedOutput", DM_DISPLAYFIXEDOUTPUT);
READ(dmPosition.x, "Attach.RelativeX", DM_POSITION);
READ(dmPosition.y, "Attach.RelativeY", DM_POSITION);
}
PGRAPHICS_DEVICE
NTAPI
InitDisplayDriver(
IN PWSTR pwszDeviceName,
IN PWSTR pwszRegKey)
{
PGRAPHICS_DEVICE pGraphicsDevice;
UNICODE_STRING ustrDeviceName, ustrDisplayDrivers, ustrDescription;
NTSTATUS Status;
WCHAR awcBuffer[128];
ULONG cbSize;
HKEY hkey;
DWORD dwVga;
TRACE("InitDisplayDriver(%S, %S);\n",
pwszDeviceName, pwszRegKey);
/* Open the driver's registry key */
Status = RegOpenKey(pwszRegKey, &hkey);
if (!NT_SUCCESS(Status))
{
ERR("Failed to open registry key: %ls\n", pwszRegKey);
return NULL;
}
/* Query the diplay drivers */
cbSize = sizeof(awcBuffer) - 10;
Status = RegQueryValue(hkey,
L"InstalledDisplayDrivers",
REG_MULTI_SZ,
awcBuffer,
&cbSize);
if (!NT_SUCCESS(Status))
{
ERR("Didn't find 'InstalledDisplayDrivers', status = 0x%lx\n", Status);
ZwClose(hkey);
return NULL;
}
/* Initialize the UNICODE_STRING */
ustrDisplayDrivers.Buffer = awcBuffer;
ustrDisplayDrivers.MaximumLength = (USHORT)cbSize;
ustrDisplayDrivers.Length = (USHORT)cbSize;
/* Set Buffer for description and size of remaining buffer */
ustrDescription.Buffer = awcBuffer + (cbSize / sizeof(WCHAR));
cbSize = sizeof(awcBuffer) - cbSize;
/* Query the device string */
Status = RegQueryValue(hkey,
L"Device Description",
REG_SZ,
ustrDescription.Buffer,
&cbSize);
if (NT_SUCCESS(Status))
{
ustrDescription.MaximumLength = (USHORT)cbSize;
ustrDescription.Length = (USHORT)cbSize;
}
else
{
RtlInitUnicodeString(&ustrDescription, L"<unknown>");
}
/* Query if this is a VGA compatible driver */
cbSize = sizeof(DWORD);
Status = RegQueryValue(hkey, L"VgaCompatible", REG_DWORD, &dwVga, &cbSize);
if (!NT_SUCCESS(Status)) dwVga = 0;
/* Close the registry key */
ZwClose(hkey);
/* Register the device with GDI */
RtlInitUnicodeString(&ustrDeviceName, pwszDeviceName);
pGraphicsDevice = EngpRegisterGraphicsDevice(&ustrDeviceName,
&ustrDisplayDrivers,
&ustrDescription);
if (pGraphicsDevice && dwVga)
{
pGraphicsDevice->StateFlags |= DISPLAY_DEVICE_VGA_COMPATIBLE;
}
return pGraphicsDevice;
}
NTSTATUS
NTAPI
InitVideo(VOID)
{
NTSTATUS Status;
HKEY hkey;
TRACE("----------------------------- InitVideo() -------------------------------\n");
/* Check if VGA mode is requested, by finding the special volatile key created by VIDEOPRT */
Status = RegOpenKey(L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\GraphicsDrivers\\BaseVideo", &hkey);
if (NT_SUCCESS(Status))
ZwClose(hkey);
gbBaseVideo = NT_SUCCESS(Status);
if (gbBaseVideo)
ERR("VGA mode requested.\n");
/* Initialize all display devices */
Status = EngpUpdateGraphicsDeviceList();
if (!NT_SUCCESS(Status))
return Status;
/* Check if we had any success */
if (!gpPrimaryGraphicsDevice)
{
ERR("No usable display driver was found.\n");
return STATUS_UNSUCCESSFUL;
}
InitSysParams();
return STATUS_SUCCESS;
}
VOID
UserRefreshDisplay(IN PPDEVOBJ ppdev)
{
ULONG_PTR ulResult;
// PVOID pvOldCursor;
// TODO: Re-enable the cursor reset code once this function becomes called
// from within a Win32 thread... Indeed UserSetCursor() requires this, but
// at the moment this function is directly called from a separate thread
// from within videoprt, instead of by a separate win32k system thread.
if (!ppdev)
return;
PDEVOBJ_vReference(ppdev);
/* Remove mouse pointer */
// pvOldCursor = UserSetCursor(NULL, TRUE);
/* Do the mode switch -- Use the actual same current mode */
ulResult = PDEVOBJ_bSwitchMode(ppdev, ppdev->pdmwDev);
ASSERT(ulResult);
/* Restore mouse pointer, no hooks called */
// pvOldCursor = UserSetCursor(pvOldCursor, TRUE);
// ASSERT(pvOldCursor == NULL);
/* Update the system metrics */
InitMetrics();
/* Set new size of the monitor */
// UserUpdateMonitorSize((HDEV)ppdev);
//co_IntShowDesktop(pdesk, ppdev->gdiinfo.ulHorzRes, ppdev->gdiinfo.ulVertRes);
UserRedrawDesktop();
PDEVOBJ_vRelease(ppdev);
}
NTSTATUS
NTAPI
UserEnumDisplayDevices(
PUNICODE_STRING pustrDevice,
DWORD iDevNum,
PDISPLAY_DEVICEW pdispdev,
DWORD dwFlags)
{
PGRAPHICS_DEVICE pGraphicsDevice;
PDEVICE_OBJECT pdo;
PWCHAR pHardwareId;
ULONG cbSize, dwLength;
HKEY hkey;
NTSTATUS Status;
if (!pustrDevice)
{
/* Check if some devices have been added since last time */
EngpUpdateGraphicsDeviceList();
}
/* Ask gdi for the GRAPHICS_DEVICE */
pGraphicsDevice = EngpFindGraphicsDevice(pustrDevice, iDevNum);
if (!pGraphicsDevice)
{
/* No device found */
if (iDevNum == 0)
ERR("No GRAPHICS_DEVICE found for '%wZ', iDevNum %lu\n", pustrDevice, iDevNum);
else
TRACE("No GRAPHICS_DEVICE found for '%wZ', iDevNum %lu\n", pustrDevice, iDevNum);
return STATUS_UNSUCCESSFUL;
}
/* Open the device map registry key */
Status = RegOpenKey(KEY_VIDEO, &hkey);
if (!NT_SUCCESS(Status))
{
/* No device found */
ERR("Could not open reg key\n");
return STATUS_UNSUCCESSFUL;
}
/* Query the registry path */
cbSize = sizeof(pdispdev->DeviceKey);
RegQueryValue(hkey,
pGraphicsDevice->szNtDeviceName,
REG_SZ,
pdispdev->DeviceKey,
&cbSize);
/* Close registry key */
ZwClose(hkey);
/* Copy device name, device string and StateFlags */
RtlStringCbCopyW(pdispdev->DeviceName, sizeof(pdispdev->DeviceName), pGraphicsDevice->szWinDeviceName);
RtlStringCbCopyW(pdispdev->DeviceString, sizeof(pdispdev->DeviceString), pGraphicsDevice->pwszDescription);
pdispdev->StateFlags = pGraphicsDevice->StateFlags;
pdispdev->DeviceID[0] = UNICODE_NULL;
/* Fill in DeviceID */
if (!pustrDevice)
pdo = pGraphicsDevice->PhysDeviceHandle;
else
#if 0
pdo = pGraphicsDevice->pvMonDev[iDevNum].pdo;
#else
/* FIXME: pvMonDev not initialized, see EngpRegisterGraphicsDevice */
pdo = NULL;
#endif
if (pdo != NULL)
{
Status = IoGetDeviceProperty(pdo,
DevicePropertyHardwareID,
0,
NULL,
&dwLength);
if (Status == STATUS_BUFFER_TOO_SMALL)
{
pHardwareId = ExAllocatePoolWithTag(PagedPool,
dwLength,
USERTAG_DISPLAYINFO);
if (!pHardwareId)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = IoGetDeviceProperty(pdo,
DevicePropertyHardwareID,
dwLength,
pHardwareId,
&dwLength);
if (!NT_SUCCESS(Status))
{
ERR("IoGetDeviceProperty() failed with status 0x%08lx\n", Status);
}
else
{
/* For video adapters it should be the first Hardware ID
* which usually is the longest one and unique enough */
RtlStringCbCopyW(pdispdev->DeviceID, sizeof(pdispdev->DeviceID), pHardwareId);
if (pustrDevice)
{
/* For monitors it should be the first Hardware ID,
* which we already have obtained above,
* concatenated with the unique driver registry key */
RtlStringCbCatW(pdispdev->DeviceID, sizeof(pdispdev->DeviceID), L"\\");
/* FIXME: DevicePropertyDriverKeyName string should be appended */
pHardwareId[0] = UNICODE_NULL;
RtlStringCbCatW(pdispdev->DeviceID, sizeof(pdispdev->DeviceID), pHardwareId);
}
TRACE("Hardware ID: %ls\n", pdispdev->DeviceID);
}
ExFreePoolWithTag(pHardwareId, USERTAG_DISPLAYINFO);
}
else
{
ERR("IoGetDeviceProperty() failed with status 0x%08lx\n", Status);
}
}
return STATUS_SUCCESS;
}
//NTSTATUS
BOOL
NTAPI
NtUserEnumDisplayDevices(
PUNICODE_STRING pustrDevice,
DWORD iDevNum,
PDISPLAY_DEVICEW pDisplayDevice,
DWORD dwFlags)
{
UNICODE_STRING ustrDevice;
WCHAR awcDevice[CCHDEVICENAME];
DISPLAY_DEVICEW dispdev;
NTSTATUS Status;
TRACE("Enter NtUserEnumDisplayDevices(%wZ, %lu)\n",
pustrDevice, iDevNum);
dispdev.cb = sizeof(dispdev);
if (pustrDevice)
{
/* Initialize destination string */
RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
_SEH2_TRY
{
/* Probe the UNICODE_STRING and the buffer */
ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
/* Copy the string */
RtlCopyUnicodeString(&ustrDevice, pustrDevice);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
// _SEH2_YIELD(return _SEH2_GetExceptionCode());
_SEH2_YIELD(return NT_SUCCESS(_SEH2_GetExceptionCode()));
}
_SEH2_END
if (ustrDevice.Length > 0)
pustrDevice = &ustrDevice;
else
pustrDevice = NULL;
}
/* If name is given only iDevNum==0 gives results */
if (pustrDevice && iDevNum != 0)
return FALSE;
/* Acquire global USER lock */
UserEnterShared();
/* Call the internal function */
Status = UserEnumDisplayDevices(pustrDevice, iDevNum, &dispdev, dwFlags);
/* Release lock */
UserLeave();
/* On success copy data to caller */
if (NT_SUCCESS(Status))
{
/* Enter SEH */
_SEH2_TRY
{
/* First probe the cb field */
ProbeForWrite(&pDisplayDevice->cb, sizeof(DWORD), 1);
/* Check the buffer size */
if (pDisplayDevice->cb)
{
/* Probe the output buffer */
pDisplayDevice->cb = min(pDisplayDevice->cb, sizeof(dispdev));
ProbeForWrite(pDisplayDevice, pDisplayDevice->cb, 1);
/* Copy as much as the given buffer allows */
RtlCopyMemory(pDisplayDevice, &dispdev, pDisplayDevice->cb);
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END
}
TRACE("Leave NtUserEnumDisplayDevices, Status = 0x%lx\n", Status);
/* Return the result */
// return Status;
return NT_SUCCESS(Status); // FIXME
}
NTSTATUS
NTAPI
UserEnumCurrentDisplaySettings(
PUNICODE_STRING pustrDevice,
PDEVMODEW *ppdm)
{
PPDEVOBJ ppdev;
/* Get the PDEV for the device */
ppdev = EngpGetPDEV(pustrDevice);
if (!ppdev)
{
/* No device found */
ERR("No PDEV found!\n");
return STATUS_INVALID_PARAMETER_1;
}
*ppdm = ppdev->pdmwDev;
PDEVOBJ_vRelease(ppdev);
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
UserEnumDisplaySettings(
PUNICODE_STRING pustrDevice,
DWORD iModeNum,
LPDEVMODEW *ppdm,
DWORD dwFlags)
{
PGRAPHICS_DEVICE pGraphicsDevice;
PDEVMODEENTRY pdmentry;
ULONG i, iFoundMode;
PPDEVOBJ ppdev;
TRACE("Enter UserEnumDisplaySettings('%wZ', %lu)\n",
pustrDevice, iModeNum);
/* Ask GDI for the GRAPHICS_DEVICE */
pGraphicsDevice = EngpFindGraphicsDevice(pustrDevice, 0);
ppdev = EngpGetPDEV(pustrDevice);
if (!pGraphicsDevice || !ppdev)
{
/* No device found */
ERR("No device found!\n");
return STATUS_INVALID_PARAMETER_1;
}
/* let's politely ask the driver for an updated mode list,
just in case there's something new in there (vbox) */
PDEVOBJ_vRefreshModeList(ppdev);
PDEVOBJ_vRelease(ppdev);
iFoundMode = 0;
for (i = 0; i < pGraphicsDevice->cDevModes; i++)
{
pdmentry = &pGraphicsDevice->pDevModeList[i];
/* FIXME: Consider EDS_RAWMODE */
#if 0
if ((!(dwFlags & EDS_RAWMODE) && (pdmentry->dwFlags & 1)) ||!
(dwFlags & EDS_RAWMODE))
#endif
{
/* Is this the one we want? */
if (iFoundMode == iModeNum)
{
*ppdm = pdmentry->pdm;
return STATUS_SUCCESS;
}
/* Increment number of found modes */
iFoundMode++;
}
}
/* Nothing was found */
return STATUS_INVALID_PARAMETER_2;
}
NTSTATUS
NTAPI
UserOpenDisplaySettingsKey(
OUT PHKEY phkey,
IN PUNICODE_STRING pustrDevice,
IN BOOL bGlobal)
{
HKEY hkey;
DISPLAY_DEVICEW dispdev;
NTSTATUS Status;
/* Get device info */
Status = UserEnumDisplayDevices(pustrDevice, 0, &dispdev, 0);
if (!NT_SUCCESS(Status))
return Status;
if (bGlobal)
{
// FIXME: Need to fix the registry key somehow
}
/* Open the registry key */
Status = RegOpenKey(dispdev.DeviceKey, &hkey);
if (!NT_SUCCESS(Status))
return Status;
*phkey = hkey;
return Status;
}
NTSTATUS
NTAPI
UserEnumRegistryDisplaySettings(
IN PUNICODE_STRING pustrDevice,
OUT LPDEVMODEW pdm)
{
HKEY hkey;
NTSTATUS Status = UserOpenDisplaySettingsKey(&hkey, pustrDevice, 0);
if(NT_SUCCESS(Status))
{
RegReadDisplaySettings(hkey, pdm);
ZwClose(hkey);
return STATUS_SUCCESS;
}
return Status;
}
NTSTATUS
APIENTRY
NtUserEnumDisplaySettings(
IN PUNICODE_STRING pustrDevice,
IN DWORD iModeNum,
OUT LPDEVMODEW lpDevMode,
IN DWORD dwFlags)
{
UNICODE_STRING ustrDeviceUser;
UNICODE_STRING ustrDevice;
WCHAR awcDevice[CCHDEVICENAME];
NTSTATUS Status;
ULONG cbSize, cbExtra;
DEVMODEW dmReg, *pdm;
TRACE("Enter NtUserEnumDisplaySettings(%wZ, %lu, %p, 0x%lx)\n",
pustrDevice, iModeNum, lpDevMode, dwFlags);
_SEH2_TRY
{
ProbeForRead(lpDevMode, sizeof(DEVMODEW), sizeof(UCHAR));
cbSize = lpDevMode->dmSize;
cbExtra = lpDevMode->dmDriverExtra;
ProbeForWrite(lpDevMode, cbSize + cbExtra, sizeof(UCHAR));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return _SEH2_GetExceptionCode());
}
_SEH2_END;
if (cbSize != sizeof(DEVMODEW))
{
return STATUS_BUFFER_TOO_SMALL;
}
if (pustrDevice)
{
/* Initialize destination string */
RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
_SEH2_TRY
{
/* Probe the UNICODE_STRING and the buffer */
ustrDeviceUser = ProbeForReadUnicodeString(pustrDevice);
if (!ustrDeviceUser.Length || !ustrDeviceUser.Buffer)
ExRaiseStatus(STATUS_NO_MEMORY);
ProbeForRead(ustrDeviceUser.Buffer,
ustrDeviceUser.Length,
sizeof(UCHAR));
/* Copy the string */
RtlCopyUnicodeString(&ustrDevice, &ustrDeviceUser);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return STATUS_INVALID_PARAMETER_1);
}
_SEH2_END;
pustrDevice = &ustrDevice;
}
/* Acquire global USER lock */
UserEnterShared();
if (iModeNum == ENUM_REGISTRY_SETTINGS)
{
/* Get the registry settings */
Status = UserEnumRegistryDisplaySettings(pustrDevice, &dmReg);
pdm = &dmReg;
pdm->dmSize = sizeof(DEVMODEW);
}
else if (iModeNum == ENUM_CURRENT_SETTINGS)
{
/* Get the current settings */
Status = UserEnumCurrentDisplaySettings(pustrDevice, &pdm);
}
else
{
/* Get specified settings */
Status = UserEnumDisplaySettings(pustrDevice, iModeNum, &pdm, dwFlags);
}
/* Release lock */
UserLeave();
/* Did we succeed? */
if (NT_SUCCESS(Status))
{
/* Copy some information back */
_SEH2_TRY
{
/* Output what we got */
RtlCopyMemory(lpDevMode, pdm, min(cbSize, pdm->dmSize));
/* Output private/extra driver data */
if (cbExtra > 0 && pdm->dmDriverExtra > 0)
{
RtlCopyMemory((PCHAR)lpDevMode + cbSize,
(PCHAR)pdm + pdm->dmSize,
min(cbExtra, pdm->dmDriverExtra));
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
return Status;
}
VOID
UserUpdateFullscreen(
DWORD flags)
{
if (flags & CDS_FULLSCREEN)
gpFullscreen = gptiCurrent->ppi;
else
gpFullscreen = NULL;
}
LONG
APIENTRY
UserChangeDisplaySettings(
PUNICODE_STRING pustrDevice,
LPDEVMODEW pdm,
DWORD flags,
LPVOID lParam)
{
DEVMODEW dm;
LONG lResult = DISP_CHANGE_SUCCESSFUL;
HKEY hkey;
NTSTATUS Status;
PPDEVOBJ ppdev;
WORD OrigBC;
//PDESKTOP pdesk;
PDEVMODEW newDevMode = NULL;
/* If no DEVMODE is given, use registry settings */
if (!pdm)
{
/* Get the registry settings */
Status = UserEnumRegistryDisplaySettings(pustrDevice, &dm);
if (!NT_SUCCESS(Status))
{
ERR("Could not load registry settings\n");
return DISP_CHANGE_BADPARAM;
}
}
else if (pdm->dmSize < FIELD_OFFSET(DEVMODEW, dmFields))
{
return DISP_CHANGE_BADMODE; /* This is what WinXP SP3 returns */
}
else
{
dm = *pdm;
}
/* Save original bit count */
OrigBC = gpsi->BitCount;
/* Check params */
if ((dm.dmFields & (DM_PELSWIDTH | DM_PELSHEIGHT)) != (DM_PELSWIDTH | DM_PELSHEIGHT))
{
ERR("Devmode doesn't specify the resolution.\n");
return DISP_CHANGE_BADMODE;
}
/* Get the PDEV */
ppdev = EngpGetPDEV(pustrDevice);
if (!ppdev)
{
ERR("Failed to get PDEV\n");
return DISP_CHANGE_BADPARAM;
}
/* Fixup values */
if (dm.dmBitsPerPel == 0 || !(dm.dmFields & DM_BITSPERPEL))
{
dm.dmBitsPerPel = ppdev->pdmwDev->dmBitsPerPel;
dm.dmFields |= DM_BITSPERPEL;
}
if ((dm.dmFields & DM_DISPLAYFREQUENCY) && (dm.dmDisplayFrequency == 0))
dm.dmDisplayFrequency = ppdev->pdmwDev->dmDisplayFrequency;
/* Look for the requested DEVMODE */
if (!LDEVOBJ_bProbeAndCaptureDevmode(ppdev->pGraphicsDevice, &dm, &newDevMode, FALSE))
{
ERR("Could not find a matching DEVMODE\n");
lResult = DISP_CHANGE_BADMODE;
goto leave;
}
else if (flags & CDS_TEST)
{
/* It's possible, go ahead! */
lResult = DISP_CHANGE_SUCCESSFUL;
goto leave;
}
/* Shall we update the registry? */
if (flags & CDS_UPDATEREGISTRY)
{
/* Open the local or global settings key */
Status = UserOpenDisplaySettingsKey(&hkey, pustrDevice, flags & CDS_GLOBAL);
if (NT_SUCCESS(Status))
{
/* Store the settings */
RegWriteDisplaySettings(hkey, newDevMode);
/* Close the registry key */
ZwClose(hkey);
}
else
{
ERR("Could not open registry key\n");
lResult = DISP_CHANGE_NOTUPDATED;
}
}
/* Check if DEVMODE matches the current mode */
if (newDevMode->dmSize == ppdev->pdmwDev->dmSize &&
RtlCompareMemory(newDevMode, ppdev->pdmwDev, newDevMode->dmSize) == newDevMode->dmSize &&
!(flags & CDS_RESET))
{
ERR("DEVMODE matches, nothing to do\n");
goto leave;
}
/* Shall we apply the settings? */
if (!(flags & CDS_NORESET))
{
ULONG_PTR ulResult;
PVOID pvOldCursor;
TEXTMETRICW tmw;
/* Remove mouse pointer */
pvOldCursor = UserSetCursor(NULL, TRUE);
/* Do the mode switch */
ulResult = PDEVOBJ_bSwitchMode(ppdev, newDevMode);
/* Restore mouse pointer, no hooks called */
pvOldCursor = UserSetCursor(pvOldCursor, TRUE);
ASSERT(pvOldCursor == NULL);
/* Check for success or failure */
if (!ulResult)
{
/* Setting mode failed */
ERR("Failed to set mode\n");
/* Set the correct return value */
if ((flags & CDS_UPDATEREGISTRY) && (lResult != DISP_CHANGE_NOTUPDATED))
lResult = DISP_CHANGE_RESTART;
else
lResult = DISP_CHANGE_FAILED;
}
else
{
/* Setting mode succeeded */
lResult = DISP_CHANGE_SUCCESSFUL;
ExFreePoolWithTag(ppdev->pdmwDev, GDITAG_DEVMODE);
ppdev->pdmwDev = newDevMode;
UserUpdateFullscreen(flags);
/* Update the system metrics */
InitMetrics();
/* Set new size of the monitor */
UserUpdateMonitorSize((HDEV)ppdev);
/* Update the SERVERINFO */
gpsi->dmLogPixels = ppdev->gdiinfo.ulLogPixelsY;
gpsi->Planes = ppdev->gdiinfo.cPlanes;
gpsi->BitsPixel = ppdev->gdiinfo.cBitsPixel;
gpsi->BitCount = gpsi->Planes * gpsi->BitsPixel;
gpsi->aiSysMet[SM_CXSCREEN] = ppdev->gdiinfo.ulHorzRes;
gpsi->aiSysMet[SM_CYSCREEN] = ppdev->gdiinfo.ulVertRes;
if (ppdev->gdiinfo.flRaster & RC_PALETTE)
{
gpsi->PUSIFlags |= PUSIF_PALETTEDISPLAY;
}
else
{
gpsi->PUSIFlags &= ~PUSIF_PALETTEDISPLAY;
}
// Font is realized and this dc was previously set to internal DC_ATTR.
gpsi->cxSysFontChar = IntGetCharDimensions(hSystemBM, &tmw, (DWORD*)&gpsi->cySysFontChar);
gpsi->tmSysFont = tmw;
}
/*
* Refresh the display on success and even on failure,
* since the display may have been messed up.
*/
/* Remove all cursor clipping */
UserClipCursor(NULL);
//pdesk = IntGetActiveDesktop();
//IntHideDesktop(pdesk);
/* Send WM_DISPLAYCHANGE to all toplevel windows */
co_IntSendMessageTimeout( HWND_BROADCAST,
WM_DISPLAYCHANGE,
gpsi->BitCount,
MAKELONG(gpsi->aiSysMet[SM_CXSCREEN], gpsi->aiSysMet[SM_CYSCREEN]),
SMTO_NORMAL,
100,
&ulResult );
ERR("BitCount New %d Orig %d ChkNew %d\n",gpsi->BitCount,OrigBC,ppdev->gdiinfo.cBitsPixel);
/* Not full screen and different bit count, send messages */
if (!(flags & CDS_FULLSCREEN) &&
gpsi->BitCount != OrigBC)
{
ERR("Detect settings changed.\n");
UserSendNotifyMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0);
UserSendNotifyMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0);
}
//co_IntShowDesktop(pdesk, ppdev->gdiinfo.ulHorzRes, ppdev->gdiinfo.ulVertRes);
UserRedrawDesktop();
}
leave:
if (newDevMode && newDevMode != ppdev->pdmwDev)
ExFreePoolWithTag(newDevMode, GDITAG_DEVMODE);
/* Release the PDEV */
PDEVOBJ_vRelease(ppdev);
return lResult;
}
VOID
UserDisplayNotifyShutdown(
PPROCESSINFO ppiCurrent)
{
if (ppiCurrent == gpFullscreen)
{
UserChangeDisplaySettings(NULL, NULL, 0, NULL);
if (gpFullscreen)
ERR("Failed to restore display mode!\n");
}
}
LONG
APIENTRY
NtUserChangeDisplaySettings(
PUNICODE_STRING pustrDevice,
LPDEVMODEW lpDevMode,
DWORD dwflags,
LPVOID lParam)
{
WCHAR awcDevice[CCHDEVICENAME];
UNICODE_STRING ustrDevice;
DEVMODEW dmLocal;
LONG lRet;
/* Check arguments */
if ((dwflags != CDS_VIDEOPARAMETERS) && (lParam != NULL))
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return DISP_CHANGE_BADPARAM;
}
/* Check flags */
if ((dwflags & (CDS_GLOBAL|CDS_NORESET)) && !(dwflags & CDS_UPDATEREGISTRY))
{
return DISP_CHANGE_BADFLAGS;
}
if ((dwflags & CDS_RESET) && (dwflags & CDS_NORESET))
{
return DISP_CHANGE_BADFLAGS;
}
/* Copy the device name */
if (pustrDevice)
{
/* Initialize destination string */
RtlInitEmptyUnicodeString(&ustrDevice, awcDevice, sizeof(awcDevice));
_SEH2_TRY
{
/* Probe the UNICODE_STRING and the buffer */
ProbeForRead(pustrDevice, sizeof(UNICODE_STRING), 1);
ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, 1);
/* Copy the string */
RtlCopyUnicodeString(&ustrDevice, pustrDevice);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Set and return error */
SetLastNtError(_SEH2_GetExceptionCode());
_SEH2_YIELD(return DISP_CHANGE_BADPARAM);
}
_SEH2_END
pustrDevice = &ustrDevice;
}
/* Copy devmode */
if (lpDevMode)
{
_SEH2_TRY
{
/* Probe the size field of the structure */
ProbeForRead(lpDevMode, sizeof(dmLocal.dmSize), 1);
/* Calculate usable size */
dmLocal.dmSize = min(sizeof(dmLocal), lpDevMode->dmSize);
/* Probe and copy the full DEVMODE */
ProbeForRead(lpDevMode, dmLocal.dmSize, 1);
RtlCopyMemory(&dmLocal, lpDevMode, dmLocal.dmSize);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Set and return error */
SetLastNtError(_SEH2_GetExceptionCode());
_SEH2_YIELD(return DISP_CHANGE_BADPARAM);
}
_SEH2_END
/* Check for extra parameters */
if (dmLocal.dmDriverExtra > 0)
{
/* FIXME: TODO */
ERR("lpDevMode->dmDriverExtra is IGNORED!\n");
dmLocal.dmDriverExtra = 0;
}
/* Use the local structure */
lpDevMode = &dmLocal;
}
// FIXME: Copy videoparameters
/* Acquire global USER lock */
UserEnterExclusive();
/* Call internal function */
lRet = UserChangeDisplaySettings(pustrDevice, lpDevMode, dwflags, NULL);
/* Release lock */
UserLeave();
return lRet;
}