mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
12ef61ba10
That way, we don't have anymore the VGA device together with primary device in device list. Change also EngpUnlinkGraphicsDevice() function to add back VGA device to device list when removing its parent from device list. CORE-18522
919 lines
28 KiB
C
919 lines
28 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* PURPOSE: GDI Driver Device Functions
|
|
* FILE: win32ss/gdi/eng/device.c
|
|
* PROGRAMER: Jason Filby
|
|
* Timo Kreuzer
|
|
*/
|
|
|
|
#include <win32k.h>
|
|
#include <ntddvdeo.h>
|
|
|
|
DBG_DEFAULT_CHANNEL(EngDev);
|
|
|
|
PGRAPHICS_DEVICE gpPrimaryGraphicsDevice;
|
|
PGRAPHICS_DEVICE gpVgaGraphicsDevice;
|
|
|
|
static PGRAPHICS_DEVICE gpGraphicsDeviceFirst = NULL;
|
|
static PGRAPHICS_DEVICE gpGraphicsDeviceLast = NULL;
|
|
static HSEMAPHORE ghsemGraphicsDeviceList;
|
|
static ULONG giDevNum = 1;
|
|
|
|
CODE_SEG("INIT")
|
|
NTSTATUS
|
|
NTAPI
|
|
InitDeviceImpl(VOID)
|
|
{
|
|
ghsemGraphicsDeviceList = EngCreateSemaphore();
|
|
if (!ghsemGraphicsDeviceList)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
BOOLEAN
|
|
EngpHasVgaDriver(
|
|
_In_ PGRAPHICS_DEVICE pGraphicsDevice)
|
|
{
|
|
WCHAR awcDeviceKey[256], awcServiceName[100];
|
|
PWSTR lastBkSlash;
|
|
NTSTATUS Status;
|
|
ULONG cbValue;
|
|
HKEY hkey;
|
|
|
|
/* Open the key for the adapters */
|
|
Status = RegOpenKey(L"\\Registry\\Machine\\HARDWARE\\DEVICEMAP\\VIDEO", &hkey);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Could not open HARDWARE\\DEVICEMAP\\VIDEO registry key: 0x%08lx\n", Status);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Read the name of the device key */
|
|
cbValue = sizeof(awcDeviceKey);
|
|
Status = RegQueryValue(hkey, pGraphicsDevice->szNtDeviceName, REG_SZ, awcDeviceKey, &cbValue);
|
|
ZwClose(hkey);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Could not read '%S' registry value: 0x%08lx\n", Status);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Replace 'DeviceN' by 'Video' */
|
|
lastBkSlash = wcsrchr(awcDeviceKey, L'\\');
|
|
if (!lastBkSlash)
|
|
{
|
|
ERR("Invalid registry key '%S'\n", lastBkSlash);
|
|
return FALSE;
|
|
}
|
|
if (!NT_SUCCESS(RtlStringCchCopyW(lastBkSlash + 1,
|
|
ARRAYSIZE(awcDeviceKey) - (lastBkSlash + 1 - awcDeviceKey),
|
|
L"Video")))
|
|
{
|
|
ERR("Failed to add 'Video' to registry key '%S'\n", awcDeviceKey);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Open device key */
|
|
Status = RegOpenKey(awcDeviceKey, &hkey);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Could not open %S registry key: 0x%08lx\n", awcDeviceKey, Status);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Read service name */
|
|
cbValue = sizeof(awcServiceName);
|
|
Status = RegQueryValue(hkey, L"Service", REG_SZ, awcServiceName, &cbValue);
|
|
ZwClose(hkey);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Could not read Service registry value in %S: 0x%08lx\n", awcDeviceKey, Status);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Device is using VGA driver if service name starts with 'VGA' (case insensitive) */
|
|
return (_wcsnicmp(awcServiceName, L"VGA", 3) == 0);
|
|
}
|
|
|
|
/*
|
|
* Add a device to gpGraphicsDeviceFirst/gpGraphicsDeviceLast list (if not already present).
|
|
*/
|
|
_Requires_lock_held_(ghsemGraphicsDeviceList)
|
|
static
|
|
VOID
|
|
EngpLinkGraphicsDevice(
|
|
_In_ PGRAPHICS_DEVICE pToAdd)
|
|
{
|
|
PGRAPHICS_DEVICE pGraphicsDevice;
|
|
|
|
TRACE("EngLinkGraphicsDevice(%p)\n", pToAdd);
|
|
|
|
/* Search if device is not already linked */
|
|
for (pGraphicsDevice = gpGraphicsDeviceFirst;
|
|
pGraphicsDevice;
|
|
pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice)
|
|
{
|
|
if (pGraphicsDevice == pToAdd)
|
|
return;
|
|
}
|
|
|
|
pToAdd->pNextGraphicsDevice = NULL;
|
|
if (gpGraphicsDeviceLast)
|
|
gpGraphicsDeviceLast->pNextGraphicsDevice = pToAdd;
|
|
gpGraphicsDeviceLast = pToAdd;
|
|
if (!gpGraphicsDeviceFirst)
|
|
gpGraphicsDeviceFirst = pToAdd;
|
|
}
|
|
|
|
/*
|
|
* Remove a device from gpGraphicsDeviceFirst/gpGraphicsDeviceLast list.
|
|
*/
|
|
_Requires_lock_held_(ghsemGraphicsDeviceList)
|
|
static
|
|
VOID
|
|
EngpUnlinkGraphicsDevice(
|
|
_In_ PGRAPHICS_DEVICE pToDelete)
|
|
{
|
|
PGRAPHICS_DEVICE pPrevGraphicsDevice = NULL;
|
|
PGRAPHICS_DEVICE pGraphicsDevice = gpGraphicsDeviceFirst;
|
|
|
|
TRACE("EngpUnlinkGraphicsDevice('%S')\n", pToDelete->szNtDeviceName);
|
|
|
|
while (pGraphicsDevice)
|
|
{
|
|
if (pGraphicsDevice != pToDelete)
|
|
{
|
|
/* Keep current device */
|
|
pPrevGraphicsDevice = pGraphicsDevice;
|
|
pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice;
|
|
}
|
|
else
|
|
{
|
|
/* At first, link again associated VGA Device */
|
|
if (pGraphicsDevice->pVgaDevice)
|
|
EngpLinkGraphicsDevice(pGraphicsDevice->pVgaDevice);
|
|
|
|
/* We need to remove current device */
|
|
pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice;
|
|
|
|
/* Unlink chain */
|
|
if (!pPrevGraphicsDevice)
|
|
gpGraphicsDeviceFirst = pToDelete->pNextGraphicsDevice;
|
|
else
|
|
pPrevGraphicsDevice->pNextGraphicsDevice = pToDelete->pNextGraphicsDevice;
|
|
if (gpGraphicsDeviceLast == pToDelete)
|
|
gpGraphicsDeviceLast = pPrevGraphicsDevice;
|
|
}
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
EngpUpdateGraphicsDeviceList(VOID)
|
|
{
|
|
ULONG iDevNum, iVGACompatible = -1, ulMaxObjectNumber = 0;
|
|
WCHAR awcDeviceName[20], awcWinDeviceName[20];
|
|
UNICODE_STRING ustrDeviceName;
|
|
WCHAR awcBuffer[256];
|
|
NTSTATUS Status;
|
|
PGRAPHICS_DEVICE pGraphicsDevice;
|
|
BOOLEAN bFoundNewDevice = FALSE;
|
|
ULONG cbValue;
|
|
HKEY hkey;
|
|
|
|
/* Open the key for the adapters */
|
|
Status = RegOpenKey(L"\\Registry\\Machine\\HARDWARE\\DEVICEMAP\\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);
|
|
|
|
/* Create the display device name */
|
|
swprintf(awcWinDeviceName, L"\\\\.\\DISPLAY%lu", iDevNum + 1);
|
|
RtlInitUnicodeString(&ustrDeviceName, awcWinDeviceName);
|
|
|
|
/* Check if the device exists already */
|
|
pGraphicsDevice = EngpFindGraphicsDevice(&ustrDeviceName, iDevNum);
|
|
if (pGraphicsDevice != NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
}
|
|
bFoundNewDevice = TRUE;
|
|
|
|
/* Set the first one as primary device */
|
|
if (!gpPrimaryGraphicsDevice || EngpHasVgaDriver(gpPrimaryGraphicsDevice))
|
|
{
|
|
gpPrimaryGraphicsDevice = pGraphicsDevice;
|
|
TRACE("gpPrimaryGraphicsDevice = %p\n", gpPrimaryGraphicsDevice);
|
|
}
|
|
}
|
|
|
|
/* Close the device map registry key */
|
|
ZwClose(hkey);
|
|
|
|
/* Can we link VGA device to primary device? */
|
|
if (gpPrimaryGraphicsDevice &&
|
|
gpVgaGraphicsDevice &&
|
|
gpPrimaryGraphicsDevice != gpVgaGraphicsDevice &&
|
|
!gpPrimaryGraphicsDevice->pVgaDevice)
|
|
{
|
|
/* Yes. Remove VGA device from global list, and attach it to primary device */
|
|
TRACE("Linking VGA device %S to primary device %S\n", gpVgaGraphicsDevice->szNtDeviceName, gpPrimaryGraphicsDevice->szNtDeviceName);
|
|
EngpUnlinkGraphicsDevice(gpVgaGraphicsDevice);
|
|
gpPrimaryGraphicsDevice->pVgaDevice = gpVgaGraphicsDevice;
|
|
}
|
|
|
|
if (bFoundNewDevice && gbBaseVideo)
|
|
{
|
|
PGRAPHICS_DEVICE pToDelete;
|
|
|
|
/* Lock list */
|
|
EngAcquireSemaphore(ghsemGraphicsDeviceList);
|
|
|
|
/* Remove every device from linked list, except base-video one */
|
|
pGraphicsDevice = gpGraphicsDeviceFirst;
|
|
while (pGraphicsDevice)
|
|
{
|
|
if (!EngpHasVgaDriver(pGraphicsDevice))
|
|
{
|
|
/* Not base-video device. Remove it */
|
|
pToDelete = pGraphicsDevice;
|
|
TRACE("Removing non-base-video device %S (%S)\n", pToDelete->szWinDeviceName, pToDelete->szNtDeviceName);
|
|
|
|
EngpUnlinkGraphicsDevice(pGraphicsDevice);
|
|
pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice;
|
|
|
|
/* Free memory */
|
|
ExFreePoolWithTag(pToDelete->pDiplayDrivers, GDITAG_DRVSUP);
|
|
ExFreePoolWithTag(pToDelete, GDITAG_GDEVICE);
|
|
}
|
|
else
|
|
{
|
|
pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice;
|
|
}
|
|
}
|
|
|
|
/* Unlock list */
|
|
EngReleaseSemaphore(ghsemGraphicsDeviceList);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Open display settings registry key
|
|
* Returns NULL in case of error. */
|
|
static HKEY
|
|
EngpGetRegistryHandleFromDeviceMap(
|
|
_In_ PGRAPHICS_DEVICE pGraphicsDevice)
|
|
{
|
|
static const PWCHAR KEY_VIDEO = L"\\Registry\\Machine\\HARDWARE\\DEVICEMAP\\VIDEO";
|
|
HKEY hKey;
|
|
WCHAR szDeviceKey[256];
|
|
ULONG cbSize;
|
|
NTSTATUS Status;
|
|
|
|
/* Open the device map registry key */
|
|
Status = RegOpenKey(KEY_VIDEO, &hKey);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Could not open HARDWARE\\DEVICEMAP\\VIDEO registry key: status 0x%08x\n", Status);
|
|
return NULL;
|
|
}
|
|
|
|
/* Query the registry path */
|
|
cbSize = sizeof(szDeviceKey);
|
|
RegQueryValue(hKey,
|
|
pGraphicsDevice->szNtDeviceName,
|
|
REG_SZ,
|
|
szDeviceKey,
|
|
&cbSize);
|
|
ZwClose(hKey);
|
|
|
|
/* Open the registry key */
|
|
Status = RegOpenKey(szDeviceKey, &hKey);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Could not open registry key '%S': status 0x%08x\n", szDeviceKey, Status);
|
|
return NULL;
|
|
}
|
|
|
|
return hKey;
|
|
}
|
|
|
|
NTSTATUS
|
|
EngpGetDisplayDriverParameters(
|
|
_In_ PGRAPHICS_DEVICE pGraphicsDevice,
|
|
_Out_ PDEVMODEW pdm)
|
|
{
|
|
HKEY hKey;
|
|
NTSTATUS Status;
|
|
RTL_QUERY_REGISTRY_TABLE DisplaySettingsTable[] =
|
|
{
|
|
#define READ(field, str) \
|
|
{ \
|
|
NULL, \
|
|
RTL_QUERY_REGISTRY_DIRECT, \
|
|
L ##str, \
|
|
&pdm->field, \
|
|
REG_NONE, NULL, 0 \
|
|
},
|
|
READ(dmBitsPerPel, "DefaultSettings.BitsPerPel")
|
|
READ(dmPelsWidth, "DefaultSettings.XResolution")
|
|
READ(dmPelsHeight, "DefaultSettings.YResolution")
|
|
READ(dmDisplayFlags, "DefaultSettings.Flags")
|
|
READ(dmDisplayFrequency, "DefaultSettings.VRefresh")
|
|
READ(dmPanningWidth, "DefaultSettings.XPanning")
|
|
READ(dmPanningHeight, "DefaultSettings.YPanning")
|
|
READ(dmDisplayOrientation, "DefaultSettings.Orientation")
|
|
READ(dmDisplayFixedOutput, "DefaultSettings.FixedOutput")
|
|
READ(dmPosition.x, "Attach.RelativeX")
|
|
READ(dmPosition.y, "Attach.RelativeY")
|
|
#undef READ
|
|
{0}
|
|
};
|
|
|
|
hKey = EngpGetRegistryHandleFromDeviceMap(pGraphicsDevice);
|
|
if (!hKey)
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
|
|
(PWSTR)hKey,
|
|
DisplaySettingsTable,
|
|
NULL,
|
|
NULL);
|
|
|
|
ZwClose(hKey);
|
|
return Status;
|
|
}
|
|
|
|
DWORD
|
|
EngpGetDisplayDriverAccelerationLevel(
|
|
_In_ PGRAPHICS_DEVICE pGraphicsDevice)
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwAccelerationLevel = 0;
|
|
RTL_QUERY_REGISTRY_TABLE DisplaySettingsTable[] =
|
|
{
|
|
{
|
|
NULL,
|
|
RTL_QUERY_REGISTRY_DIRECT,
|
|
L"Acceleration.Level",
|
|
&dwAccelerationLevel,
|
|
REG_NONE, NULL, 0
|
|
},
|
|
{0}
|
|
};
|
|
|
|
hKey = EngpGetRegistryHandleFromDeviceMap(pGraphicsDevice);
|
|
if (!hKey)
|
|
return 0;
|
|
|
|
RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
|
|
(PWSTR)hKey,
|
|
DisplaySettingsTable,
|
|
NULL,
|
|
NULL);
|
|
ZwClose(hKey);
|
|
|
|
return dwAccelerationLevel;
|
|
}
|
|
|
|
extern VOID
|
|
UserRefreshDisplay(IN PPDEVOBJ ppdev);
|
|
|
|
// PVIDEO_WIN32K_CALLOUT
|
|
VOID
|
|
NTAPI
|
|
VideoPortCallout(
|
|
_In_ PVOID Params)
|
|
{
|
|
/*
|
|
* IMPORTANT NOTICE!! On Windows XP/2003 this function triggers the creation of
|
|
* a specific VideoPortCalloutThread() system thread using the same mechanism
|
|
* as the RIT/desktop/Ghost system threads.
|
|
*/
|
|
|
|
PVIDEO_WIN32K_CALLBACKS_PARAMS CallbackParams = (PVIDEO_WIN32K_CALLBACKS_PARAMS)Params;
|
|
|
|
TRACE("VideoPortCallout(0x%p, 0x%x)\n",
|
|
CallbackParams, CallbackParams ? CallbackParams->CalloutType : -1);
|
|
|
|
if (!CallbackParams)
|
|
return;
|
|
|
|
switch (CallbackParams->CalloutType)
|
|
{
|
|
case VideoFindAdapterCallout:
|
|
{
|
|
TRACE("VideoPortCallout: VideoFindAdapterCallout called - Param = %s\n",
|
|
CallbackParams->Param ? "TRUE" : "FALSE");
|
|
if (CallbackParams->Param == TRUE)
|
|
{
|
|
/* Re-enable the display */
|
|
UserRefreshDisplay(gpmdev->ppdevGlobal);
|
|
}
|
|
else
|
|
{
|
|
/* Disable the display */
|
|
NOTHING; // Nothing to do for the moment...
|
|
}
|
|
|
|
CallbackParams->Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
case VideoPowerNotifyCallout:
|
|
case VideoDisplaySwitchCallout:
|
|
case VideoEnumChildPdoNotifyCallout:
|
|
case VideoWakeupCallout:
|
|
case VideoChangeDisplaySettingsCallout:
|
|
case VideoPnpNotifyCallout:
|
|
case VideoDxgkDisplaySwitchCallout:
|
|
case VideoDxgkMonitorEventCallout:
|
|
case VideoDxgkFindAdapterTdrCallout:
|
|
ERR("VideoPortCallout: CalloutType 0x%x is UNIMPLEMENTED!\n", CallbackParams->CalloutType);
|
|
CallbackParams->Status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
default:
|
|
ERR("VideoPortCallout: Unknown CalloutType 0x%x\n", CallbackParams->CalloutType);
|
|
CallbackParams->Status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
PGRAPHICS_DEVICE
|
|
NTAPI
|
|
EngpRegisterGraphicsDevice(
|
|
_In_ PUNICODE_STRING pustrDeviceName,
|
|
_In_ PUNICODE_STRING pustrDiplayDrivers,
|
|
_In_ PUNICODE_STRING pustrDescription)
|
|
{
|
|
PGRAPHICS_DEVICE pGraphicsDevice;
|
|
PDEVICE_OBJECT pDeviceObject;
|
|
PFILE_OBJECT pFileObject;
|
|
NTSTATUS Status;
|
|
VIDEO_WIN32K_CALLBACKS Win32kCallbacks;
|
|
ULONG ulReturn;
|
|
PWSTR pwsz;
|
|
ULONG cj;
|
|
|
|
TRACE("EngpRegisterGraphicsDevice(%wZ)\n", pustrDeviceName);
|
|
|
|
/* Allocate a GRAPHICS_DEVICE structure */
|
|
pGraphicsDevice = ExAllocatePoolZero(PagedPool,
|
|
sizeof(GRAPHICS_DEVICE),
|
|
GDITAG_GDEVICE);
|
|
if (!pGraphicsDevice)
|
|
{
|
|
ERR("ExAllocatePoolWithTag failed\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Try to open and enable the device */
|
|
Status = IoGetDeviceObjectPointer(pustrDeviceName,
|
|
FILE_READ_DATA | FILE_WRITE_DATA,
|
|
&pFileObject,
|
|
&pDeviceObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Could not open device %wZ, 0x%lx\n", pustrDeviceName, Status);
|
|
ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE);
|
|
return NULL;
|
|
}
|
|
|
|
/* Copy the device and file object pointers */
|
|
pGraphicsDevice->DeviceObject = pDeviceObject;
|
|
pGraphicsDevice->FileObject = pFileObject;
|
|
|
|
/* Initialize and register the device with videoprt for Win32k callbacks */
|
|
Win32kCallbacks.PhysDisp = pGraphicsDevice;
|
|
Win32kCallbacks.Callout = VideoPortCallout;
|
|
// Reset the data being returned prior to the call.
|
|
Win32kCallbacks.bACPI = FALSE;
|
|
Win32kCallbacks.pPhysDeviceObject = NULL;
|
|
Win32kCallbacks.DualviewFlags = 0;
|
|
Status = (NTSTATUS)EngDeviceIoControl((HANDLE)pDeviceObject,
|
|
IOCTL_VIDEO_INIT_WIN32K_CALLBACKS,
|
|
&Win32kCallbacks,
|
|
sizeof(Win32kCallbacks),
|
|
&Win32kCallbacks,
|
|
sizeof(Win32kCallbacks),
|
|
&ulReturn);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
ERR("EngDeviceIoControl(0x%p, IOCTL_VIDEO_INIT_WIN32K_CALLBACKS) failed, Status 0x%lx\n",
|
|
pDeviceObject, Status);
|
|
}
|
|
// TODO: Set flags according to the results.
|
|
// if (Win32kCallbacks.bACPI)
|
|
// if (Win32kCallbacks.DualviewFlags & ???)
|
|
pGraphicsDevice->PhysDeviceHandle = Win32kCallbacks.pPhysDeviceObject;
|
|
|
|
/* FIXME: Enumerate children monitor devices for this video adapter
|
|
*
|
|
* - Force the adapter to re-enumerate its monitors:
|
|
* IoSynchronousInvalidateDeviceRelations(pdo, BusRelations)
|
|
*
|
|
* - Retrieve all monitor PDOs from VideoPrt:
|
|
* EngDeviceIoControl(0x%p, IOCTL_VIDEO_ENUM_MONITOR_PDO)
|
|
*
|
|
* - Initialize these fields and structures accordingly:
|
|
* pGraphicsDevice->dwMonCnt
|
|
* pGraphicsDevice->pvMonDev[0..dwMonCnt-1]
|
|
*/
|
|
|
|
/* Copy the device name */
|
|
RtlStringCbCopyNW(pGraphicsDevice->szNtDeviceName,
|
|
sizeof(pGraphicsDevice->szNtDeviceName),
|
|
pustrDeviceName->Buffer,
|
|
pustrDeviceName->Length);
|
|
|
|
/* Create a Win32 device name (FIXME: virtual devices!) */
|
|
RtlStringCbPrintfW(pGraphicsDevice->szWinDeviceName,
|
|
sizeof(pGraphicsDevice->szWinDeviceName),
|
|
L"\\\\.\\DISPLAY%d",
|
|
(int)giDevNum);
|
|
|
|
/* Allocate a buffer for the strings */
|
|
cj = pustrDiplayDrivers->Length + pustrDescription->Length + sizeof(WCHAR);
|
|
pwsz = ExAllocatePoolWithTag(PagedPool, cj, GDITAG_DRVSUP);
|
|
if (!pwsz)
|
|
{
|
|
ERR("Could not allocate string buffer\n");
|
|
ASSERT(FALSE); // FIXME
|
|
ExFreePoolWithTag(pGraphicsDevice, GDITAG_GDEVICE);
|
|
return NULL;
|
|
}
|
|
|
|
/* Copy the display driver names */
|
|
pGraphicsDevice->pDiplayDrivers = pwsz;
|
|
RtlCopyMemory(pGraphicsDevice->pDiplayDrivers,
|
|
pustrDiplayDrivers->Buffer,
|
|
pustrDiplayDrivers->Length);
|
|
|
|
/* Copy the description */
|
|
pGraphicsDevice->pwszDescription = pwsz + pustrDiplayDrivers->Length / sizeof(WCHAR);
|
|
RtlCopyMemory(pGraphicsDevice->pwszDescription,
|
|
pustrDescription->Buffer,
|
|
pustrDescription->Length);
|
|
pGraphicsDevice->pwszDescription[pustrDescription->Length/sizeof(WCHAR)] = 0;
|
|
|
|
/* Lock loader */
|
|
EngAcquireSemaphore(ghsemGraphicsDeviceList);
|
|
|
|
/* Insert the device into the global list */
|
|
EngpLinkGraphicsDevice(pGraphicsDevice);
|
|
|
|
/* Increment the device number */
|
|
giDevNum++;
|
|
|
|
/* Unlock loader */
|
|
EngReleaseSemaphore(ghsemGraphicsDeviceList);
|
|
TRACE("Prepared %lu modes for %ls\n", pGraphicsDevice->cDevModes, pGraphicsDevice->pwszDescription);
|
|
|
|
/* HACK: already in graphic mode; display wallpaper on this new display */
|
|
if (ScreenDeviceContext)
|
|
{
|
|
UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"DISPLAY");
|
|
UNICODE_STRING DisplayName;
|
|
HDC hdc;
|
|
RtlInitUnicodeString(&DisplayName, pGraphicsDevice->szWinDeviceName);
|
|
hdc = IntGdiCreateDC(&DriverName, &DisplayName, NULL, NULL, FALSE);
|
|
IntPaintDesktop(hdc);
|
|
}
|
|
|
|
return pGraphicsDevice;
|
|
}
|
|
|
|
PGRAPHICS_DEVICE
|
|
NTAPI
|
|
EngpFindGraphicsDevice(
|
|
_In_opt_ PUNICODE_STRING pustrDevice,
|
|
_In_ ULONG iDevNum)
|
|
{
|
|
UNICODE_STRING ustrCurrent;
|
|
PGRAPHICS_DEVICE pGraphicsDevice;
|
|
ULONG i;
|
|
TRACE("EngpFindGraphicsDevice('%wZ', %lu)\n",
|
|
pustrDevice, iDevNum);
|
|
|
|
/* Lock list */
|
|
EngAcquireSemaphore(ghsemGraphicsDeviceList);
|
|
|
|
if (pustrDevice && pustrDevice->Buffer)
|
|
{
|
|
/* Find specified video adapter by name */
|
|
for (pGraphicsDevice = gpGraphicsDeviceFirst;
|
|
pGraphicsDevice;
|
|
pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice)
|
|
{
|
|
/* Compare the device name */
|
|
RtlInitUnicodeString(&ustrCurrent, pGraphicsDevice->szWinDeviceName);
|
|
if (RtlEqualUnicodeString(&ustrCurrent, pustrDevice, FALSE))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pGraphicsDevice)
|
|
{
|
|
/* Validate selected monitor number */
|
|
#if 0
|
|
if (iDevNum >= pGraphicsDevice->dwMonCnt)
|
|
pGraphicsDevice = NULL;
|
|
#else
|
|
/* FIXME: dwMonCnt not initialized, see EngpRegisterGraphicsDevice */
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Select video adapter by device number */
|
|
for (pGraphicsDevice = gpGraphicsDeviceFirst, i = 0;
|
|
pGraphicsDevice && i < iDevNum;
|
|
pGraphicsDevice = pGraphicsDevice->pNextGraphicsDevice, i++);
|
|
}
|
|
|
|
/* Unlock list */
|
|
EngReleaseSemaphore(ghsemGraphicsDeviceList);
|
|
|
|
return pGraphicsDevice;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
EngpFileIoRequest(
|
|
_In_ PFILE_OBJECT pFileObject,
|
|
_In_ ULONG ulMajorFunction,
|
|
_In_reads_(nBufferSize) PVOID lpBuffer,
|
|
_In_ SIZE_T nBufferSize,
|
|
_In_ ULONGLONG ullStartOffset,
|
|
_Out_ PULONG_PTR lpInformation)
|
|
{
|
|
PDEVICE_OBJECT pDeviceObject;
|
|
KEVENT Event;
|
|
PIRP pIrp;
|
|
IO_STATUS_BLOCK Iosb;
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER liStartOffset;
|
|
|
|
/* Get corresponding device object */
|
|
pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
|
|
if (!pDeviceObject)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Initialize an event */
|
|
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
|
|
|
|
/* Build IRP */
|
|
liStartOffset.QuadPart = ullStartOffset;
|
|
pIrp = IoBuildSynchronousFsdRequest(ulMajorFunction,
|
|
pDeviceObject,
|
|
lpBuffer,
|
|
(ULONG)nBufferSize,
|
|
&liStartOffset,
|
|
&Event,
|
|
&Iosb);
|
|
if (!pIrp)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
/* Call the driver */
|
|
Status = IoCallDriver(pDeviceObject, pIrp);
|
|
|
|
/* Wait if neccessary */
|
|
if (STATUS_PENDING == Status)
|
|
{
|
|
KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
|
|
Status = Iosb.Status;
|
|
}
|
|
|
|
/* Return information to the caller about the operation. */
|
|
*lpInformation = Iosb.Information;
|
|
|
|
/* Return NTSTATUS */
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
APIENTRY
|
|
EngFileWrite(
|
|
_In_ PFILE_OBJECT pFileObject,
|
|
_In_reads_(nLength) PVOID lpBuffer,
|
|
_In_ SIZE_T nLength,
|
|
_Out_ PSIZE_T lpBytesWritten)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
status = EngpFileIoRequest(pFileObject,
|
|
IRP_MJ_WRITE,
|
|
lpBuffer,
|
|
nLength,
|
|
0,
|
|
lpBytesWritten);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
*lpBytesWritten = 0;
|
|
}
|
|
}
|
|
|
|
_Success_(return>=0)
|
|
NTSTATUS
|
|
APIENTRY
|
|
EngFileIoControl(
|
|
_In_ PFILE_OBJECT pFileObject,
|
|
_In_ DWORD dwIoControlCode,
|
|
_In_reads_(nInBufferSize) PVOID lpInBuffer,
|
|
_In_ SIZE_T nInBufferSize,
|
|
_Out_writes_(nOutBufferSize) PVOID lpOutBuffer,
|
|
_In_ SIZE_T nOutBufferSize,
|
|
_Out_ PULONG_PTR lpInformation)
|
|
{
|
|
PDEVICE_OBJECT pDeviceObject;
|
|
KEVENT Event;
|
|
PIRP pIrp;
|
|
IO_STATUS_BLOCK Iosb;
|
|
NTSTATUS Status;
|
|
|
|
/* Get corresponding device object */
|
|
pDeviceObject = IoGetRelatedDeviceObject(pFileObject);
|
|
if (!pDeviceObject)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Initialize an event */
|
|
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
|
|
|
|
/* Build IO control IRP */
|
|
pIrp = IoBuildDeviceIoControlRequest(dwIoControlCode,
|
|
pDeviceObject,
|
|
lpInBuffer,
|
|
(ULONG)nInBufferSize,
|
|
lpOutBuffer,
|
|
(ULONG)nOutBufferSize,
|
|
FALSE,
|
|
&Event,
|
|
&Iosb);
|
|
if (!pIrp)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
/* Call the driver */
|
|
Status = IoCallDriver(pDeviceObject, pIrp);
|
|
|
|
/* Wait if neccessary */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
|
|
Status = Iosb.Status;
|
|
}
|
|
|
|
/* Return information to the caller about the operation. */
|
|
*lpInformation = Iosb.Information;
|
|
|
|
/* This function returns NTSTATUS */
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
_Success_(return==0)
|
|
DWORD
|
|
APIENTRY
|
|
EngDeviceIoControl(
|
|
_In_ HANDLE hDevice,
|
|
_In_ DWORD dwIoControlCode,
|
|
_In_reads_bytes_opt_(cjInBufferSize) LPVOID lpInBuffer,
|
|
_In_ DWORD cjInBufferSize,
|
|
_Out_writes_bytes_opt_(cjOutBufferSize) LPVOID lpOutBuffer,
|
|
_In_ DWORD cjOutBufferSize,
|
|
_Out_ LPDWORD lpBytesReturned)
|
|
{
|
|
PIRP Irp;
|
|
NTSTATUS Status;
|
|
KEVENT Event;
|
|
IO_STATUS_BLOCK Iosb;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
|
|
TRACE("EngDeviceIoControl() called\n");
|
|
|
|
if (!hDevice)
|
|
{
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
|
|
|
|
DeviceObject = (PDEVICE_OBJECT) hDevice;
|
|
|
|
Irp = IoBuildDeviceIoControlRequest(dwIoControlCode,
|
|
DeviceObject,
|
|
lpInBuffer,
|
|
cjInBufferSize,
|
|
lpOutBuffer,
|
|
cjOutBufferSize,
|
|
FALSE,
|
|
&Event,
|
|
&Iosb);
|
|
if (!Irp) return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
Status = IoCallDriver(DeviceObject, Irp);
|
|
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
(VOID)KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, 0);
|
|
Status = Iosb.Status;
|
|
}
|
|
|
|
TRACE("EngDeviceIoControl(): Returning %X/%X\n", Iosb.Status,
|
|
Iosb.Information);
|
|
|
|
/* Return information to the caller about the operation. */
|
|
*lpBytesReturned = (DWORD)Iosb.Information;
|
|
|
|
/* Convert NT status values to win32 error codes. */
|
|
switch (Status)
|
|
{
|
|
case STATUS_INSUFFICIENT_RESOURCES:
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
case STATUS_BUFFER_OVERFLOW:
|
|
return ERROR_MORE_DATA;
|
|
|
|
case STATUS_NOT_IMPLEMENTED:
|
|
return ERROR_INVALID_FUNCTION;
|
|
|
|
case STATUS_INVALID_PARAMETER:
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
case STATUS_BUFFER_TOO_SMALL:
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
|
|
case STATUS_DEVICE_DOES_NOT_EXIST:
|
|
return ERROR_DEV_NOT_EXIST;
|
|
|
|
case STATUS_PENDING:
|
|
return ERROR_IO_PENDING;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/* EOF */
|