mirror of
https://github.com/reactos/reactos.git
synced 2024-08-27 05:38:41 +00:00
![Hermès Bélusca-Maïto](/assets/img/avatar_default.png)
This is done by creating the volatile key \Registry\Machine\System\CurrentControlSet\Control\GraphicsDrivers\BaseVideo . Also cache during first initialization of VIDEOPRT the state of the boot options BASEVIDEO and NOVESA, so that they can be readily retrieved later (for example, in VideoPortIsNoVesa()).
1044 lines
29 KiB
C
1044 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;
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
APIENTRY
|
|
NtUserEnumDisplaySettings(
|
|
IN PUNICODE_STRING pustrDevice,
|
|
IN DWORD iModeNum,
|
|
OUT LPDEVMODEW lpDevMode,
|
|
IN DWORD dwFlags)
|
|
{
|
|
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 (lpDevMode->dmSize != 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 */
|
|
ProbeForReadUnicodeString(pustrDevice);
|
|
|
|
if (!pustrDevice->Length || !pustrDevice->Buffer)
|
|
ExRaiseStatus(STATUS_NO_MEMORY);
|
|
|
|
ProbeForRead(pustrDevice->Buffer, pustrDevice->Length, sizeof(UCHAR));
|
|
|
|
/* Copy the string */
|
|
RtlCopyUnicodeString(&ustrDevice, pustrDevice);
|
|
}
|
|
_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;
|
|
|
|
/* 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))
|
|
{
|
|
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;
|
|
}
|