reactos/win32ss/user/ntuser/display.c

1047 lines
29 KiB
C
Raw Normal View History

/*
* 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;
DEVMODEW dmDefault;
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 the default settings */
RegReadDisplaySettings(hkey, &dmDefault);
/* 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,
&dmDefault);
if (pGraphicsDevice && dwVga)
{
pGraphicsDevice->StateFlags |= DISPLAY_DEVICE_VGA_COMPATIBLE;
}
return pGraphicsDevice;
}
NTSTATUS
NTAPI
InitVideo(VOID)
{
ULONG iDevNum, iVGACompatible = -1, ulMaxObjectNumber = 0;
WCHAR awcDeviceName[20];
WCHAR awcBuffer[256];
NTSTATUS Status;
PGRAPHICS_DEVICE pGraphicsDevice;
ULONG cbValue;
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");
/* Open the key for the adapters */
Status = RegOpenKey(KEY_VIDEO, &hkey);
if (!NT_SUCCESS(Status))
{
ERR("Could not open HARDWARE\\DEVICEMAP\\VIDEO registry key:0x%lx\n", Status);
return Status;
}
/* Read the name of the VGA adapter */
cbValue = sizeof(awcDeviceName);
Status = RegQueryValue(hkey, L"VgaCompatible", REG_SZ, awcDeviceName, &cbValue);
if (NT_SUCCESS(Status))
{
iVGACompatible = _wtoi(&awcDeviceName[sizeof("\\Device\\Video")-1]);
ERR("VGA adapter = %lu\n", iVGACompatible);
}
/* Get the maximum mumber of adapters */
if (!RegReadDWORD(hkey, L"MaxObjectNumber", &ulMaxObjectNumber))
{
ERR("Could not read MaxObjectNumber, defaulting to 0.\n");
}
TRACE("Found %lu devices\n", ulMaxObjectNumber + 1);
/* Loop through all adapters */
for (iDevNum = 0; iDevNum <= ulMaxObjectNumber; iDevNum++)
{
/* Create the adapter's key name */
swprintf(awcDeviceName, L"\\Device\\Video%lu", iDevNum);
/* Read the reg key name */
cbValue = sizeof(awcBuffer);
Status = RegQueryValue(hkey, awcDeviceName, REG_SZ, awcBuffer, &cbValue);
if (!NT_SUCCESS(Status))
{
ERR("failed to query the registry path:0x%lx\n", Status);
continue;
}
/* Initialize the driver for this device */
pGraphicsDevice = InitDisplayDriver(awcDeviceName, awcBuffer);
if (!pGraphicsDevice) continue;
/* Check if this is a VGA compatible adapter */
if (pGraphicsDevice->StateFlags & DISPLAY_DEVICE_VGA_COMPATIBLE)
{
/* Save this as the VGA adapter */
if (!gpVgaGraphicsDevice)
gpVgaGraphicsDevice = pGraphicsDevice;
TRACE("gpVgaGraphicsDevice = %p\n", gpVgaGraphicsDevice);
}
else
{
/* Set the first one as primary device */
if (!gpPrimaryGraphicsDevice)
gpPrimaryGraphicsDevice = pGraphicsDevice;
TRACE("gpPrimaryGraphicsDevice = %p\n", gpPrimaryGraphicsDevice);
}
}
/* Close the device map registry key */
ZwClose(hkey);
/* Was VGA mode requested? */
if (gbBaseVideo)
{
/* Check if we found a VGA compatible device */
if (gpVgaGraphicsDevice)
{
/* Set the VgaAdapter as primary */
gpPrimaryGraphicsDevice = gpVgaGraphicsDevice;
// FIXME: DEVMODE
}
else
{
ERR("Could not find VGA compatible driver. Trying normal.\n");
}
}
/* Check if we had any success */
if (!gpPrimaryGraphicsDevice)
{
/* Check if there is a VGA device we skipped */
if (gpVgaGraphicsDevice)
{
/* There is, use the VGA device */
gpPrimaryGraphicsDevice = gpVgaGraphicsDevice;
}
else
{
ERR("No usable display driver was found.\n");
return STATUS_UNSUCCESSFUL;
}
}
InitSysParams();
return STATUS_SUCCESS;
}
[WIN32K][VIDEOPRT] Improve initialization and interfacing with INBV. CORE-12149 VIDEOPRT: ========= Improve interfacing with INBV, so as to detect when an external module acquired INBV display ownership, and whether ownership is being released later on. (This does NOT rely on hooking!) For this purpose we improve the IntVideoPortResetDisplayParameters(Ex) callback that gets registered with an InbvNotifyDisplayOwnershipLost() call during initialization, and we add a monitoring thread. The callback is called whenever an external module calls InbvAcquireDisplayOwnership(), for example the bugcheck code or the KDBG debugger in SCREEN mode. When this happens, a flag that tells the monitoring thread to start monitoring INBV is set (ReactOS-specific), and the display adapters get reset with HwResetHw() (as done on Windows). Due to the fact that this INBV callback can be called at *ANY* IRQL, we cannot use dispatcher synchronization mechanisms such as events to tell the INBV monitoring thread to start its operations, so we need to rely instead on a flag to be set. And, since INBV doesn't provide with any proper callback/notification system either, we need to actively monitor its state by pooling. To reduce the load on the system the monitoring thread performs 1-second waits between each check for the flag set by the INBV callback, and during checking the INBV ownership status. When the INBV ownership is detected to be released by an external module, the INBV callback is re-registered (this is *MANDATORY* since the external module has called InbvNotifyDisplayOwnershipLost() with a different callback parameter!), and then we callout to Win32k for re-enabling the display. This has the virtue of correctly resetting the display once the KDBG debugger in SCREEN mode is being exited, and fixes CORE-12149 . The following additional fixes were needed: VIDEOPRT & WIN32K: ================== Remove the registration with INBV that was previously done in a ReactOS- specific hacked IRP_MJ_WRITE call; it is now done correctly during the video device opening done by EngpRegisterGraphicsDevice() in the VIDEOPRT's IRP_MJ_CREATE handler, as done on Windows. WIN32K: ======= - Stub the VideoPortCallout() support, for VIDEOPRT -> WIN32 callbacks. This function gets registered with VIDEOPRT through an IOCTL_VIDEO_INIT_WIN32K_CALLBACKS call in EngpRegisterGraphicsDevice(). - Only partially implement the 'VideoFindAdapterCallout' case, that just re-enables the primary display by refreshing it (using the new function UserRefreshDisplay()). VIDEOPRT: ========= - PVIDEO_WIN32K_CALLOUT is an NTAPI (stdcall) callback. - In the IntVideoPortResetDisplayParameters(Ex) callback, reset all the "resettable" adapters registered in the HwResetAdaptersList list. We thus get rid of the global ResetDisplayParametersDeviceExtension. - Make the IntVideoPortResetDisplayParameters(Ex) callback slightly more robust (using SEH) against potential HwResetListEntry list corruption or invalid DriverExtension->InitializationData.HwResetHw() that would otherwise trigger a BSOD, and this would be disastrous since that callback is precisely called when INBV is acquired, typically when the BSOD code initializes the display for displaying its information... Extras: - Validate the IrpStack->MajorFunction in IntVideoPortDispatchDeviceControl() and implement IRP_MJ_SHUTDOWN handling. Stub out the other IOCTLs that are handled by VIDEOPRT only (and not by the miniports). - VIDEOPRT doesn't require IRP_MJ_INTERNAL_DEVICE_CONTROL (unused). - Implement IOCTL_VIDEO_PREPARE_FOR_EARECOVERY that just resets the display to standard VGA 80x25 text mode.
2019-11-26 01:49:35 +00:00
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;
ULONG cbSize;
HKEY hkey;
NTSTATUS Status;
/* Ask gdi for the GRAPHICS_DEVICE */
pGraphicsDevice = EngpFindGraphicsDevice(pustrDevice, iDevNum, 0);
if (!pGraphicsDevice)
{
/* No device found */
ERR("No GRAPHICS_DEVICE found\n");
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;
// FIXME: fill in DEVICE ID
pdispdev->DeviceID[0] = UNICODE_NULL;
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, 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;
}
2019-11-24 20:19:14 +00:00
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;
}
2019-11-24 20:19:14 +00:00
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;
/* 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 */
pdm = PDEVOBJ_pdmMatchDevMode(ppdev, &dm);
if (!pdm)
{
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, pdm);
/* 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 (pdm == ppdev->pdmwDev && !(flags & CDS_RESET))
{
ERR("DEVMODE matches, nothing to do\n");
goto leave;
}
/* Shall we apply the settings? */
if (!(flags & CDS_NORESET))
{
2019-05-22 23:32:41 +00:00
ULONG_PTR ulResult;
PVOID pvOldCursor;
TEXTMETRICW tmw;
/* Remove mouse pointer */
pvOldCursor = UserSetCursor(NULL, TRUE);
/* Do the mode switch */
ulResult = PDEVOBJ_bSwitchMode(ppdev, pdm);
/* 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;
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:
/* 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;
}