mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
ffb20d3330
- Adress -> Address - Currupted -> Corrupted 3rd-party files are not modified.
951 lines
28 KiB
C
951 lines
28 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Win32k subsystem
|
|
* PURPOSE: Support for logical devices
|
|
* FILE: win32ss/gdi/eng/ldevobj.c
|
|
* PROGRAMER: Timo Kreuzer (timo.kreuzer@reactos.org)
|
|
*/
|
|
|
|
#include <win32k.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
DBG_DEFAULT_CHANNEL(EngLDev);
|
|
|
|
#ifndef RVA_TO_ADDR
|
|
#define RVA_TO_ADDR(Base,Rva) ((PVOID)(((ULONG_PTR)(Base)) + (Rva)))
|
|
#endif
|
|
|
|
/** Globals *******************************************************************/
|
|
|
|
static HSEMAPHORE ghsemLDEVList;
|
|
static LIST_ENTRY gleLdevListHead;
|
|
static LDEVOBJ *gpldevWin32k = NULL;
|
|
|
|
|
|
/** Private functions *********************************************************/
|
|
|
|
CODE_SEG("INIT")
|
|
NTSTATUS
|
|
NTAPI
|
|
InitLDEVImpl(VOID)
|
|
{
|
|
ULONG cbSize;
|
|
|
|
/* Initialize the LDEV list head */
|
|
InitializeListHead(&gleLdevListHead);
|
|
|
|
/* Initialize the loader lock */
|
|
ghsemLDEVList = EngCreateSemaphore();
|
|
if (!ghsemLDEVList)
|
|
{
|
|
ERR("Failed to create ghsemLDEVList\n");
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
/* Allocate a LDEVOBJ for win32k */
|
|
gpldevWin32k = ExAllocatePoolWithTag(PagedPool,
|
|
sizeof(LDEVOBJ) +
|
|
sizeof(SYSTEM_GDI_DRIVER_INFORMATION),
|
|
GDITAG_LDEV);
|
|
if (!gpldevWin32k)
|
|
{
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/* Initialize the LDEVOBJ for win32k */
|
|
gpldevWin32k->leLink.Flink = NULL;
|
|
gpldevWin32k->leLink.Blink = NULL;
|
|
gpldevWin32k->ldevtype = LDEV_DEVICE_DISPLAY;
|
|
gpldevWin32k->cRefs = 1;
|
|
gpldevWin32k->ulDriverVersion = GDI_ENGINE_VERSION;
|
|
gpldevWin32k->pGdiDriverInfo = (PVOID)(gpldevWin32k + 1);
|
|
RtlInitUnicodeString(&gpldevWin32k->pGdiDriverInfo->DriverName,
|
|
L"\\SystemRoot\\System32\\win32k.sys");
|
|
gpldevWin32k->pGdiDriverInfo->ImageAddress = &__ImageBase;
|
|
gpldevWin32k->pGdiDriverInfo->SectionPointer = NULL;
|
|
gpldevWin32k->pGdiDriverInfo->EntryPoint = (PVOID)DriverEntry;
|
|
gpldevWin32k->pGdiDriverInfo->ExportSectionPointer =
|
|
RtlImageDirectoryEntryToData(&__ImageBase,
|
|
TRUE,
|
|
IMAGE_DIRECTORY_ENTRY_EXPORT,
|
|
&cbSize);
|
|
gpldevWin32k->pGdiDriverInfo->ImageLength = 0; // FIXME
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
PLDEVOBJ
|
|
LDEVOBJ_AllocLDEV(
|
|
_In_ LDEVTYPE ldevtype)
|
|
{
|
|
PLDEVOBJ pldev;
|
|
|
|
/* Allocate the structure from paged pool */
|
|
pldev = ExAllocatePoolWithTag(PagedPool, sizeof(LDEVOBJ), GDITAG_LDEV);
|
|
if (!pldev)
|
|
{
|
|
ERR("Failed to allocate LDEVOBJ.\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Zero out the structure */
|
|
RtlZeroMemory(pldev, sizeof(LDEVOBJ));
|
|
|
|
/* Set the ldevtype */
|
|
pldev->ldevtype = ldevtype;
|
|
|
|
return pldev;
|
|
}
|
|
|
|
static
|
|
VOID
|
|
LDEVOBJ_vFreeLDEV(
|
|
_In_ _Post_ptr_invalid_ PLDEVOBJ pldev)
|
|
{
|
|
/* Make sure we don't have a driver loaded */
|
|
ASSERT(pldev && pldev->pGdiDriverInfo == NULL);
|
|
ASSERT(pldev->cRefs == 0);
|
|
|
|
/* Free the memory */
|
|
ExFreePoolWithTag(pldev, GDITAG_LDEV);
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
LDEVOBJ_bLoadImage(
|
|
_Inout_ PLDEVOBJ pldev,
|
|
_In_ PUNICODE_STRING pustrPathName)
|
|
{
|
|
PSYSTEM_GDI_DRIVER_INFORMATION pDriverInfo;
|
|
NTSTATUS Status;
|
|
ULONG cbSize;
|
|
|
|
/* Make sure no image is loaded yet */
|
|
ASSERT(pldev && pldev->pGdiDriverInfo == NULL);
|
|
|
|
/* Allocate a SYSTEM_GDI_DRIVER_INFORMATION structure */
|
|
cbSize = sizeof(SYSTEM_GDI_DRIVER_INFORMATION) + pustrPathName->Length;
|
|
pDriverInfo = ExAllocatePoolWithTag(PagedPool, cbSize, GDITAG_LDEV);
|
|
if (!pDriverInfo)
|
|
{
|
|
ERR("Failed to allocate SYSTEM_GDI_DRIVER_INFORMATION\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Initialize the UNICODE_STRING and copy the driver name */
|
|
RtlInitEmptyUnicodeString(&pDriverInfo->DriverName,
|
|
(PWSTR)(pDriverInfo + 1),
|
|
pustrPathName->Length);
|
|
RtlCopyUnicodeString(&pDriverInfo->DriverName, pustrPathName);
|
|
|
|
/* Try to load the driver */
|
|
Status = ZwSetSystemInformation(SystemLoadGdiDriverInformation,
|
|
pDriverInfo,
|
|
sizeof(SYSTEM_GDI_DRIVER_INFORMATION));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Failed to load a GDI driver: '%wZ', Status = 0x%lx\n",
|
|
pustrPathName, Status);
|
|
|
|
/* Free the allocated memory */
|
|
ExFreePoolWithTag(pDriverInfo, GDITAG_LDEV);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Set the driver info */
|
|
pldev->pGdiDriverInfo = pDriverInfo;
|
|
|
|
/* Return success. */
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
LDEVOBJ_bEnableDriver(
|
|
_Inout_ PLDEVOBJ pldev,
|
|
_In_ PFN_DrvEnableDriver pfnEnableDriver)
|
|
{
|
|
DRVENABLEDATA ded;
|
|
ULONG i;
|
|
|
|
TRACE("LDEVOBJ_bEnableDriver('%wZ')\n", &pldev->pGdiDriverInfo->DriverName);
|
|
|
|
ASSERT(pldev);
|
|
ASSERT(pldev->cRefs == 0);
|
|
|
|
if (pldev->ldevtype == LDEV_IMAGE)
|
|
return TRUE;
|
|
|
|
/* Call the drivers DrvEnableDriver function */
|
|
RtlZeroMemory(&ded, sizeof(ded));
|
|
if (!pfnEnableDriver(GDI_ENGINE_VERSION, sizeof(ded), &ded))
|
|
{
|
|
ERR("DrvEnableDriver failed\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Copy the returned driver version */
|
|
pldev->ulDriverVersion = ded.iDriverVersion;
|
|
|
|
/* Fill the driver function array */
|
|
for (i = 0; i < ded.c; i++)
|
|
{
|
|
pldev->apfn[ded.pdrvfn[i].iFunc] = ded.pdrvfn[i].pfn;
|
|
}
|
|
|
|
/* Return success. */
|
|
return TRUE;
|
|
}
|
|
|
|
static
|
|
VOID
|
|
LDEVOBJ_vDisableDriver(
|
|
_Inout_ PLDEVOBJ pldev)
|
|
{
|
|
ASSERT(pldev);
|
|
ASSERT(pldev->cRefs == 0);
|
|
|
|
TRACE("LDEVOBJ_vDisableDriver('%wZ')\n", &pldev->pGdiDriverInfo->DriverName);
|
|
|
|
if (pldev->ldevtype == LDEV_IMAGE)
|
|
return;
|
|
|
|
if (pldev->pfn.DisableDriver)
|
|
{
|
|
/* Call the unload function */
|
|
pldev->pfn.DisableDriver();
|
|
}
|
|
}
|
|
|
|
static
|
|
PVOID
|
|
LDEVOBJ_pvFindImageProcAddress(
|
|
_In_ PLDEVOBJ pldev,
|
|
_In_z_ LPSTR pszProcName)
|
|
{
|
|
PVOID pvImageBase;
|
|
PIMAGE_EXPORT_DIRECTORY pExportDir;
|
|
PVOID pvProcAddress = NULL;
|
|
PUSHORT pOrdinals;
|
|
PULONG pNames, pAddresses;
|
|
ULONG i;
|
|
|
|
/* Make sure we have a driver info */
|
|
ASSERT(pldev && pldev->pGdiDriverInfo != NULL);
|
|
|
|
/* Get the pointer to the export directory */
|
|
pvImageBase = pldev->pGdiDriverInfo->ImageAddress;
|
|
pExportDir = pldev->pGdiDriverInfo->ExportSectionPointer;
|
|
if (!pExportDir)
|
|
{
|
|
ERR("LDEVOBJ_pvFindImageProcAddress: no export section found\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Get pointers to some tables */
|
|
pNames = RVA_TO_ADDR(pvImageBase, pExportDir->AddressOfNames);
|
|
pOrdinals = RVA_TO_ADDR(pvImageBase, pExportDir->AddressOfNameOrdinals);
|
|
pAddresses = RVA_TO_ADDR(pvImageBase, pExportDir->AddressOfFunctions);
|
|
|
|
/* Loop the export table */
|
|
for (i = 0; i < pExportDir->NumberOfNames; i++)
|
|
{
|
|
/* Compare the name */
|
|
if (_stricmp(pszProcName, RVA_TO_ADDR(pvImageBase, pNames[i])) == 0)
|
|
{
|
|
/* Found! Calculate the procedure address */
|
|
pvProcAddress = RVA_TO_ADDR(pvImageBase, pAddresses[pOrdinals[i]]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Return the address */
|
|
return pvProcAddress;
|
|
}
|
|
|
|
static
|
|
BOOL
|
|
LDEVOBJ_bUnloadImage(
|
|
_Inout_ PLDEVOBJ pldev)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
/* Make sure we have a driver info */
|
|
ASSERT(pldev && pldev->pGdiDriverInfo != NULL);
|
|
ASSERT(pldev->cRefs == 0);
|
|
|
|
TRACE("LDEVOBJ_bUnloadImage('%wZ')\n", &pldev->pGdiDriverInfo->DriverName);
|
|
|
|
/* Unload the driver */
|
|
#if 0
|
|
Status = ZwSetSystemInformation(SystemUnloadGdiDriverInformation,
|
|
&pldev->pGdiDriverInfo->SectionPointer,
|
|
sizeof(HANDLE));
|
|
#else
|
|
/* Unfortunately, ntoskrnl allows unloading a driver, but fails loading
|
|
* it again with STATUS_IMAGE_ALREADY_LOADED. Prevent this problem by
|
|
* never unloading any driver.
|
|
*/
|
|
UNIMPLEMENTED;
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
#endif
|
|
if (!NT_SUCCESS(Status))
|
|
return FALSE;
|
|
|
|
ExFreePoolWithTag(pldev->pGdiDriverInfo, GDITAG_LDEV);
|
|
pldev->pGdiDriverInfo = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
PLDEVOBJ
|
|
LDEVOBJ_pLoadInternal(
|
|
_In_ PFN_DrvEnableDriver pfnEnableDriver,
|
|
_In_ ULONG ldevtype)
|
|
{
|
|
PLDEVOBJ pldev;
|
|
|
|
TRACE("LDEVOBJ_pLoadInternal(%lu)\n", ldevtype);
|
|
|
|
/* Lock loader */
|
|
EngAcquireSemaphore(ghsemLDEVList);
|
|
|
|
/* Allocate a new LDEVOBJ */
|
|
pldev = LDEVOBJ_AllocLDEV(ldevtype);
|
|
if (!pldev)
|
|
{
|
|
ERR("Could not allocate LDEV\n");
|
|
goto leave;
|
|
}
|
|
|
|
/* Load the driver */
|
|
if (!LDEVOBJ_bEnableDriver(pldev, pfnEnableDriver))
|
|
{
|
|
ERR("LDEVOBJ_bEnableDriver failed\n");
|
|
LDEVOBJ_vFreeLDEV(pldev);
|
|
pldev = NULL;
|
|
goto leave;
|
|
}
|
|
|
|
/* Insert the LDEV into the global list */
|
|
InsertHeadList(&gleLdevListHead, &pldev->leLink);
|
|
|
|
/* Increase ref count */
|
|
pldev->cRefs++;
|
|
|
|
leave:
|
|
/* Unlock loader */
|
|
EngReleaseSemaphore(ghsemLDEVList);
|
|
|
|
TRACE("LDEVOBJ_pLoadInternal returning %p\n", pldev);
|
|
return pldev;
|
|
}
|
|
|
|
PLDEVOBJ
|
|
NTAPI
|
|
LDEVOBJ_pLoadDriver(
|
|
_In_z_ LPWSTR pwszDriverName,
|
|
_In_ ULONG ldevtype)
|
|
{
|
|
WCHAR acwBuffer[MAX_PATH];
|
|
PLIST_ENTRY pleLink;
|
|
PLDEVOBJ pldev;
|
|
UNICODE_STRING strDriverName;
|
|
SIZE_T cwcLength;
|
|
LPWSTR pwsz;
|
|
|
|
TRACE("LDEVOBJ_pLoadDriver(%ls, %lu)\n", pwszDriverName, ldevtype);
|
|
ASSERT(pwszDriverName);
|
|
|
|
/* Initialize buffer for the the driver name */
|
|
RtlInitEmptyUnicodeString(&strDriverName, acwBuffer, sizeof(acwBuffer));
|
|
|
|
/* Start path with systemroot */
|
|
RtlAppendUnicodeToString(&strDriverName, L"\\SystemRoot\\System32\\");
|
|
|
|
/* Get Length of given string */
|
|
cwcLength = wcslen(pwszDriverName);
|
|
|
|
/* Check if we have a system32 path given */
|
|
pwsz = pwszDriverName + cwcLength;
|
|
while (pwsz > pwszDriverName)
|
|
{
|
|
if ((*pwsz == L'\\') && (_wcsnicmp(pwsz, L"\\system32\\", 10) == 0))
|
|
{
|
|
/* Driver name starts after system32 */
|
|
pwsz += 10;
|
|
break;
|
|
}
|
|
pwsz--;
|
|
}
|
|
|
|
/* Append the driver name */
|
|
RtlAppendUnicodeToString(&strDriverName, pwsz);
|
|
|
|
/* MSDN says "The driver must include this suffix in the pwszDriver string."
|
|
But in fact it's optional. The function can also load .sys files without
|
|
appending the .dll extension. */
|
|
if ((cwcLength < 4) ||
|
|
((_wcsnicmp(pwszDriverName + cwcLength - 4, L".dll", 4) != 0) &&
|
|
(_wcsnicmp(pwszDriverName + cwcLength - 4, L".sys", 4) != 0)) )
|
|
{
|
|
/* Append the .dll suffix */
|
|
RtlAppendUnicodeToString(&strDriverName, L".dll");
|
|
}
|
|
|
|
/* Lock loader */
|
|
EngAcquireSemaphore(ghsemLDEVList);
|
|
|
|
/* Search the List of LDEVS for the driver name */
|
|
for (pleLink = gleLdevListHead.Flink;
|
|
pleLink != &gleLdevListHead;
|
|
pleLink = pleLink->Flink)
|
|
{
|
|
pldev = CONTAINING_RECORD(pleLink, LDEVOBJ, leLink);
|
|
|
|
/* Check if the ldev is associated with a file */
|
|
if (pldev->pGdiDriverInfo)
|
|
{
|
|
/* Check for match (case insensitive) */
|
|
if (RtlEqualUnicodeString(&pldev->pGdiDriverInfo->DriverName, &strDriverName, TRUE))
|
|
{
|
|
/* Image found in LDEV list */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Did we find one? */
|
|
if (pleLink == &gleLdevListHead)
|
|
{
|
|
/* No, allocate a new LDEVOBJ */
|
|
pldev = LDEVOBJ_AllocLDEV(ldevtype);
|
|
if (!pldev)
|
|
{
|
|
ERR("Could not allocate LDEV\n");
|
|
goto leave;
|
|
}
|
|
|
|
/* Load the image */
|
|
if (!LDEVOBJ_bLoadImage(pldev, &strDriverName))
|
|
{
|
|
LDEVOBJ_vFreeLDEV(pldev);
|
|
pldev = NULL;
|
|
ERR("LDEVOBJ_bLoadImage failed\n");
|
|
goto leave;
|
|
}
|
|
|
|
/* Load the driver */
|
|
if (!LDEVOBJ_bEnableDriver(pldev, pldev->pGdiDriverInfo->EntryPoint))
|
|
{
|
|
ERR("LDEVOBJ_bEnableDriver failed\n");
|
|
|
|
/* Unload the image. */
|
|
if (LDEVOBJ_bUnloadImage(pldev))
|
|
LDEVOBJ_vFreeLDEV(pldev);
|
|
else
|
|
ERR("Could not unload driver. Leaking memory\n");
|
|
pldev = NULL;
|
|
goto leave;
|
|
}
|
|
|
|
/* Insert the LDEV into the global list */
|
|
InsertHeadList(&gleLdevListHead, &pldev->leLink);
|
|
}
|
|
|
|
/* Increase ref count */
|
|
pldev->cRefs++;
|
|
|
|
leave:
|
|
/* Unlock loader */
|
|
EngReleaseSemaphore(ghsemLDEVList);
|
|
|
|
TRACE("LDEVOBJ_pLoadDriver returning %p\n", pldev);
|
|
return pldev;
|
|
}
|
|
|
|
static
|
|
VOID
|
|
LDEVOBJ_vDereference(
|
|
_Inout_ PLDEVOBJ pldev)
|
|
{
|
|
/* Lock loader */
|
|
EngAcquireSemaphore(ghsemLDEVList);
|
|
|
|
/* Decrement reference count */
|
|
ASSERT(pldev->cRefs > 0);
|
|
pldev->cRefs--;
|
|
|
|
/* More references left? */
|
|
if (pldev->cRefs > 0)
|
|
{
|
|
EngReleaseSemaphore(ghsemLDEVList);
|
|
return;
|
|
}
|
|
|
|
LDEVOBJ_vDisableDriver(pldev);
|
|
|
|
if (LDEVOBJ_bUnloadImage(pldev))
|
|
{
|
|
/* Remove ldev from the list */
|
|
RemoveEntryList(&pldev->leLink);
|
|
|
|
/* Free the driver info structure */
|
|
LDEVOBJ_vFreeLDEV(pldev);
|
|
}
|
|
else
|
|
{
|
|
WARN("Failed to unload driver '%wZ', trying to re-enable it.\n", &pldev->pGdiDriverInfo->DriverName);
|
|
LDEVOBJ_bEnableDriver(pldev, pldev->pGdiDriverInfo->EntryPoint);
|
|
|
|
/* Increment again reference count */
|
|
pldev->cRefs++;
|
|
}
|
|
|
|
/* Unlock loader */
|
|
EngReleaseSemaphore(ghsemLDEVList);
|
|
}
|
|
|
|
ULONG
|
|
LDEVOBJ_ulGetDriverModes(
|
|
_In_ LPWSTR pwszDriverName,
|
|
_In_ HANDLE hDriver,
|
|
_Out_ PDEVMODEW *ppdm)
|
|
{
|
|
PLDEVOBJ pldev = NULL;
|
|
ULONG cbSize = 0;
|
|
PDEVMODEW pdm = NULL;
|
|
|
|
TRACE("LDEVOBJ_ulGetDriverModes('%ls', %p)\n", pwszDriverName, hDriver);
|
|
|
|
pldev = LDEVOBJ_pLoadDriver(pwszDriverName, LDEV_DEVICE_DISPLAY);
|
|
if (!pldev)
|
|
goto cleanup;
|
|
|
|
/* Mirror drivers may omit this function */
|
|
if (!pldev->pfn.GetModes)
|
|
goto cleanup;
|
|
|
|
/* Call the driver to get the required size */
|
|
cbSize = pldev->pfn.GetModes(hDriver, 0, NULL);
|
|
if (!cbSize)
|
|
{
|
|
ERR("DrvGetModes returned 0\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Allocate a buffer for the DEVMODE array */
|
|
pdm = ExAllocatePoolWithTag(PagedPool, cbSize, GDITAG_DEVMODE);
|
|
if (!pdm)
|
|
{
|
|
ERR("Could not allocate devmodeinfo\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Call the driver again to fill the buffer */
|
|
cbSize = pldev->pfn.GetModes(hDriver, cbSize, pdm);
|
|
if (!cbSize)
|
|
{
|
|
/* Could not get modes */
|
|
ERR("DrvrGetModes returned 0 on second call\n");
|
|
ExFreePoolWithTag(pdm, GDITAG_DEVMODE);
|
|
pdm = NULL;
|
|
}
|
|
|
|
cleanup:
|
|
if (pldev)
|
|
LDEVOBJ_vDereference(pldev);
|
|
|
|
*ppdm = pdm;
|
|
return cbSize;
|
|
}
|
|
|
|
BOOL
|
|
LDEVOBJ_bBuildDevmodeList(
|
|
_Inout_ PGRAPHICS_DEVICE pGraphicsDevice)
|
|
{
|
|
PWSTR pwsz;
|
|
PDEVMODEINFO pdminfo;
|
|
PDEVMODEW pdm, pdmEnd;
|
|
ULONG i, cModes = 0;
|
|
ULONG cbSize, cbFull;
|
|
|
|
if (pGraphicsDevice->pdevmodeInfo)
|
|
return TRUE;
|
|
ASSERT(pGraphicsDevice->pDevModeList == NULL);
|
|
|
|
pwsz = pGraphicsDevice->pDiplayDrivers;
|
|
|
|
/* Loop through the driver names
|
|
* This is a REG_MULTI_SZ string */
|
|
for (; *pwsz; pwsz += wcslen(pwsz) + 1)
|
|
{
|
|
/* Get the mode list from the driver */
|
|
TRACE("Trying driver: %ls\n", pwsz);
|
|
cbSize = LDEVOBJ_ulGetDriverModes(pwsz, pGraphicsDevice->DeviceObject, &pdm);
|
|
if (!cbSize)
|
|
{
|
|
WARN("Driver %ls returned no valid mode\n", pwsz);
|
|
continue;
|
|
}
|
|
|
|
/* Add space for the header */
|
|
cbFull = cbSize + FIELD_OFFSET(DEVMODEINFO, adevmode);
|
|
|
|
/* Allocate a buffer for the DEVMODE array */
|
|
pdminfo = ExAllocatePoolWithTag(PagedPool, cbFull, GDITAG_DEVMODE);
|
|
if (!pdminfo)
|
|
{
|
|
ERR("Could not allocate devmodeinfo\n");
|
|
ExFreePoolWithTag(pdm, GDITAG_DEVMODE);
|
|
continue;
|
|
}
|
|
|
|
pdminfo->cbdevmode = cbSize;
|
|
RtlCopyMemory(pdminfo->adevmode, pdm, cbSize);
|
|
ExFreePoolWithTag(pdm, GDITAG_DEVMODE);
|
|
|
|
/* Attach the mode info to the device */
|
|
pdminfo->pdmiNext = pGraphicsDevice->pdevmodeInfo;
|
|
pGraphicsDevice->pdevmodeInfo = pdminfo;
|
|
|
|
/* Loop all DEVMODEs */
|
|
pdmEnd = (DEVMODEW*)((PCHAR)pdminfo->adevmode + pdminfo->cbdevmode);
|
|
for (pdm = pdminfo->adevmode;
|
|
(pdm + 1 <= pdmEnd) && (pdm->dmSize != 0);
|
|
pdm = (DEVMODEW*)((PCHAR)pdm + pdm->dmSize + pdm->dmDriverExtra))
|
|
{
|
|
/* Count this DEVMODE */
|
|
cModes++;
|
|
|
|
/* Some drivers like the VBox driver don't fill the dmDeviceName
|
|
with the name of the display driver. So fix that here. */
|
|
RtlStringCbCopyW(pdm->dmDeviceName, sizeof(pdm->dmDeviceName), pwsz);
|
|
}
|
|
}
|
|
|
|
if (!pGraphicsDevice->pdevmodeInfo || cModes == 0)
|
|
{
|
|
ERR("No devmodes\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Allocate an index buffer */
|
|
pGraphicsDevice->cDevModes = cModes;
|
|
pGraphicsDevice->pDevModeList = ExAllocatePoolWithTag(PagedPool,
|
|
cModes * sizeof(DEVMODEENTRY),
|
|
GDITAG_GDEVICE);
|
|
if (!pGraphicsDevice->pDevModeList)
|
|
{
|
|
ERR("No devmode list\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Loop through all DEVMODEINFOs */
|
|
for (pdminfo = pGraphicsDevice->pdevmodeInfo, i = 0;
|
|
pdminfo;
|
|
pdminfo = pdminfo->pdmiNext)
|
|
{
|
|
/* Calculate End of the DEVMODEs */
|
|
pdmEnd = (DEVMODEW*)((PCHAR)pdminfo->adevmode + pdminfo->cbdevmode);
|
|
|
|
/* Loop through the DEVMODEs */
|
|
for (pdm = pdminfo->adevmode;
|
|
(pdm + 1 <= pdmEnd) && (pdm->dmSize != 0);
|
|
pdm = (PDEVMODEW)((PCHAR)pdm + pdm->dmSize + pdm->dmDriverExtra))
|
|
{
|
|
TRACE(" %S has mode %lux%lux%lu(%lu Hz)\n",
|
|
pdm->dmDeviceName,
|
|
pdm->dmPelsWidth,
|
|
pdm->dmPelsHeight,
|
|
pdm->dmBitsPerPel,
|
|
pdm->dmDisplayFrequency);
|
|
|
|
/* Initialize the entry */
|
|
pGraphicsDevice->pDevModeList[i].dwFlags = 0;
|
|
pGraphicsDevice->pDevModeList[i].pdm = pdm;
|
|
i++;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* Search the closest display mode according to some settings.
|
|
* Note that we don't care about the DM_* flags in dmFields, but check if value != 0 instead */
|
|
static
|
|
BOOL
|
|
LDEVOBJ_bGetClosestMode(
|
|
_Inout_ PGRAPHICS_DEVICE pGraphicsDevice,
|
|
_In_ PDEVMODEW RequestedMode,
|
|
_Out_ PDEVMODEW *pSelectedMode)
|
|
{
|
|
DEVMODEW dmDiff;
|
|
PDEVMODEW pdmCurrent, pdmBest = NULL;
|
|
ULONG i;
|
|
|
|
/* Use a DEVMODE to keep the differences between best mode found and expected mode.
|
|
* Initialize fields to max value so we can find better modes. */
|
|
dmDiff.dmPelsWidth = 0xffffffff;
|
|
dmDiff.dmPelsHeight = 0xffffffff;
|
|
dmDiff.dmBitsPerPel = 0xffffffff;
|
|
dmDiff.dmDisplayFrequency = 0xffffffff;
|
|
|
|
/* Search the closest mode */
|
|
#define DM_DIFF(field) (RequestedMode->field > pdmCurrent->field ? (RequestedMode->field - pdmCurrent->field) : (pdmCurrent->field - RequestedMode->field))
|
|
for (i = 0; i < pGraphicsDevice->cDevModes; i++)
|
|
{
|
|
pdmCurrent = pGraphicsDevice->pDevModeList[i].pdm;
|
|
|
|
/* Skip current mode if it is worse than best mode found */
|
|
if (RequestedMode->dmPelsWidth != 0 && DM_DIFF(dmPelsWidth) > dmDiff.dmPelsWidth)
|
|
continue;
|
|
if (RequestedMode->dmPelsHeight != 0 && DM_DIFF(dmPelsHeight) > dmDiff.dmPelsHeight)
|
|
continue;
|
|
if (RequestedMode->dmBitsPerPel != 0 && DM_DIFF(dmBitsPerPel) > dmDiff.dmBitsPerPel)
|
|
continue;
|
|
if (RequestedMode->dmDisplayFrequency != 0 && DM_DIFF(dmDisplayFrequency) > dmDiff.dmDisplayFrequency)
|
|
continue;
|
|
|
|
/* Better (or equivalent) mode found. Update differences */
|
|
dmDiff.dmPelsWidth = DM_DIFF(dmPelsWidth);
|
|
dmDiff.dmPelsHeight = DM_DIFF(dmPelsHeight);
|
|
dmDiff.dmBitsPerPel = DM_DIFF(dmBitsPerPel);
|
|
dmDiff.dmDisplayFrequency = DM_DIFF(dmDisplayFrequency);
|
|
pdmBest = pdmCurrent;
|
|
}
|
|
#undef DM_DIFF
|
|
|
|
if (pdmBest)
|
|
{
|
|
TRACE("Closest display mode to '%dx%dx%d %d Hz' is '%dx%dx%d %d Hz'\n",
|
|
RequestedMode->dmPelsWidth,
|
|
RequestedMode->dmPelsHeight,
|
|
RequestedMode->dmBitsPerPel,
|
|
RequestedMode->dmDisplayFrequency,
|
|
pdmBest->dmPelsWidth,
|
|
pdmBest->dmPelsHeight,
|
|
pdmBest->dmBitsPerPel,
|
|
pdmBest->dmDisplayFrequency);
|
|
}
|
|
|
|
*pSelectedMode = pdmBest;
|
|
return pdmBest != NULL;
|
|
}
|
|
|
|
BOOL
|
|
LDEVOBJ_bProbeAndCaptureDevmode(
|
|
_Inout_ PGRAPHICS_DEVICE pGraphicsDevice,
|
|
_In_ PDEVMODEW RequestedMode,
|
|
_Out_ PDEVMODEW *pSelectedMode,
|
|
_In_ BOOL bSearchClosestMode)
|
|
{
|
|
DEVMODEW dmSearch;
|
|
PDEVMODEW pdmCurrent, pdm, pdmSelected = NULL;
|
|
ULONG i;
|
|
ULONG ulVirtualWidth = 0, ulVirtualHeight = 0;
|
|
BOOL bResult = TRUE;
|
|
NTSTATUS Status;
|
|
|
|
if (!LDEVOBJ_bBuildDevmodeList(pGraphicsDevice))
|
|
return FALSE;
|
|
|
|
/* At first, load information from registry */
|
|
RtlZeroMemory(&dmSearch, sizeof(dmSearch));
|
|
Status = EngpGetDisplayDriverParameters(pGraphicsDevice, &dmSearch);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("EngpGetDisplayDriverParameters() failed with status 0x%08x\n", Status);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Override values with the new ones provided */
|
|
|
|
_SEH2_TRY
|
|
{
|
|
bSearchClosestMode |= RequestedMode->dmFields == 0;
|
|
|
|
/* Copy standard fields (if provided) */
|
|
if (RequestedMode->dmFields & DM_BITSPERPEL && RequestedMode->dmBitsPerPel != 0)
|
|
dmSearch.dmBitsPerPel = RequestedMode->dmBitsPerPel;
|
|
if (RequestedMode->dmFields & DM_PELSWIDTH && RequestedMode->dmPelsWidth != 0)
|
|
dmSearch.dmPelsWidth = RequestedMode->dmPelsWidth;
|
|
if (RequestedMode->dmFields & DM_PELSHEIGHT && RequestedMode->dmPelsHeight != 0)
|
|
dmSearch.dmPelsHeight = RequestedMode->dmPelsHeight;
|
|
if (RequestedMode->dmFields & DM_DISPLAYFREQUENCY && RequestedMode->dmDisplayFrequency != 0)
|
|
dmSearch.dmDisplayFrequency = RequestedMode->dmDisplayFrequency;
|
|
|
|
if ((RequestedMode->dmFields & (DM_PANNINGWIDTH | DM_PANNINGHEIGHT)) == (DM_PANNINGWIDTH | DM_PANNINGHEIGHT) &&
|
|
RequestedMode->dmPanningWidth != 0 && RequestedMode->dmPanningHeight != 0 &&
|
|
RequestedMode->dmPanningWidth < dmSearch.dmPelsWidth &&
|
|
RequestedMode->dmPanningHeight < dmSearch.dmPelsHeight)
|
|
{
|
|
/* Get new panning values */
|
|
ulVirtualWidth = RequestedMode->dmPelsWidth;
|
|
ulVirtualHeight = RequestedMode->dmPelsHeight;
|
|
dmSearch.dmPelsWidth = RequestedMode->dmPanningWidth;
|
|
dmSearch.dmPelsHeight = RequestedMode->dmPanningHeight;
|
|
}
|
|
else if (dmSearch.dmPanningWidth != 0 && dmSearch.dmPanningHeight != 0 &&
|
|
dmSearch.dmPanningWidth < dmSearch.dmPelsWidth &&
|
|
dmSearch.dmPanningHeight < dmSearch.dmPelsHeight)
|
|
{
|
|
/* Keep existing panning values */
|
|
ulVirtualWidth = dmSearch.dmPelsWidth;
|
|
ulVirtualHeight = dmSearch.dmPelsHeight;
|
|
dmSearch.dmPelsWidth = dmSearch.dmPanningWidth;
|
|
dmSearch.dmPelsHeight = dmSearch.dmPanningHeight;
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
bResult = FALSE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (!bResult)
|
|
return FALSE;
|
|
|
|
if (LDEVOBJ_bGetClosestMode(pGraphicsDevice, &dmSearch, &pdmSelected))
|
|
{
|
|
if (bSearchClosestMode)
|
|
{
|
|
/* Ok, found a closest mode. Update search */
|
|
dmSearch.dmBitsPerPel = pdmSelected->dmBitsPerPel;
|
|
dmSearch.dmPelsWidth = pdmSelected->dmPelsWidth;
|
|
dmSearch.dmPelsHeight = pdmSelected->dmPelsHeight;
|
|
dmSearch.dmDisplayFrequency = pdmSelected->dmDisplayFrequency;
|
|
}
|
|
else
|
|
{
|
|
/* Only update not provided fields */
|
|
_SEH2_TRY
|
|
{
|
|
if (!(RequestedMode->dmFields & DM_BITSPERPEL) || RequestedMode->dmBitsPerPel == 0)
|
|
dmSearch.dmBitsPerPel = pdmSelected->dmBitsPerPel;
|
|
if (!(RequestedMode->dmFields & DM_PELSWIDTH) || RequestedMode->dmPelsWidth == 0)
|
|
dmSearch.dmPelsWidth = pdmSelected->dmPelsWidth;
|
|
if (!(RequestedMode->dmFields & DM_PELSHEIGHT) || RequestedMode->dmPelsHeight == 0)
|
|
dmSearch.dmPelsHeight = pdmSelected->dmPelsHeight;
|
|
if (!(RequestedMode->dmFields & DM_DISPLAYFREQUENCY) || RequestedMode->dmDisplayFrequency == 0)
|
|
dmSearch.dmDisplayFrequency = pdmSelected->dmDisplayFrequency;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
bResult = FALSE;
|
|
}
|
|
_SEH2_END;
|
|
|
|
if (!bResult)
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Now, search the exact mode to return to caller */
|
|
for (i = 0; i < pGraphicsDevice->cDevModes; i++)
|
|
{
|
|
pdmCurrent = pGraphicsDevice->pDevModeList[i].pdm;
|
|
|
|
/* For now, we only need those */
|
|
if (pdmCurrent->dmBitsPerPel != dmSearch.dmBitsPerPel)
|
|
continue;
|
|
if (pdmCurrent->dmPelsWidth != dmSearch.dmPelsWidth)
|
|
continue;
|
|
if (pdmCurrent->dmPelsHeight != dmSearch.dmPelsHeight)
|
|
continue;
|
|
if (pdmCurrent->dmDisplayFrequency != dmSearch.dmDisplayFrequency)
|
|
continue;
|
|
|
|
pdmSelected = pdmCurrent;
|
|
break;
|
|
}
|
|
|
|
if (!pdmSelected)
|
|
{
|
|
ERR("Requested mode not found (%dx%dx%d %d Hz)\n",
|
|
dmSearch.dmPelsWidth,
|
|
dmSearch.dmPelsHeight,
|
|
dmSearch.dmBitsPerPel,
|
|
dmSearch.dmDisplayFrequency);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Allocate memory for output */
|
|
pdm = ExAllocatePoolZero(PagedPool, pdmSelected->dmSize + pdmSelected->dmDriverExtra, GDITAG_DEVMODE);
|
|
if (!pdm)
|
|
return FALSE;
|
|
|
|
/* Copy selected mode */
|
|
RtlCopyMemory(pdm, pdmSelected, pdmSelected->dmSize);
|
|
RtlCopyMemory((PVOID)((ULONG_PTR)pdm + pdm->dmSize),
|
|
(PVOID)((ULONG_PTR)pdmSelected + pdmSelected->dmSize),
|
|
pdmSelected->dmDriverExtra);
|
|
|
|
/* Add back panning */
|
|
if (ulVirtualWidth != 0 && ulVirtualHeight != 0 &&
|
|
pdm->dmPelsWidth < ulVirtualWidth &&
|
|
pdm->dmPelsHeight < ulVirtualHeight)
|
|
{
|
|
pdm->dmFields |= DM_PANNINGWIDTH | DM_PANNINGHEIGHT;
|
|
pdm->dmPanningWidth = pdm->dmPelsWidth;
|
|
pdm->dmPanningHeight = pdm->dmPelsHeight;
|
|
pdm->dmPelsWidth = ulVirtualWidth;
|
|
pdm->dmPelsHeight = ulVirtualHeight;
|
|
}
|
|
|
|
*pSelectedMode = pdm;
|
|
return TRUE;
|
|
}
|
|
|
|
/** Exported functions ********************************************************/
|
|
|
|
HANDLE
|
|
APIENTRY
|
|
EngLoadImage(
|
|
_In_ LPWSTR pwszDriverName)
|
|
{
|
|
return (HANDLE)LDEVOBJ_pLoadDriver(pwszDriverName, LDEV_IMAGE);
|
|
}
|
|
|
|
|
|
VOID
|
|
APIENTRY
|
|
EngUnloadImage(
|
|
_In_ HANDLE hModule)
|
|
{
|
|
PLDEVOBJ pldev = (PLDEVOBJ)hModule;
|
|
|
|
/* Make sure the LDEV is in the list */
|
|
ASSERT((pldev->leLink.Flink != NULL) && (pldev->leLink.Blink != NULL));
|
|
|
|
LDEVOBJ_vDereference(pldev);
|
|
}
|
|
|
|
|
|
PVOID
|
|
APIENTRY
|
|
EngFindImageProcAddress(
|
|
_In_ HANDLE hModule,
|
|
_In_ LPSTR lpProcName)
|
|
{
|
|
PLDEVOBJ pldev = (PLDEVOBJ)hModule;
|
|
|
|
ASSERT(gpldevWin32k != NULL);
|
|
|
|
/* Check if win32k is requested */
|
|
if (!pldev)
|
|
{
|
|
pldev = gpldevWin32k;
|
|
}
|
|
|
|
/* Check if the drivers entry point is requested */
|
|
if (_strnicmp(lpProcName, "DrvEnableDriver", 15) == 0)
|
|
{
|
|
return pldev->pGdiDriverInfo->EntryPoint;
|
|
}
|
|
|
|
/* Try to find the address */
|
|
return LDEVOBJ_pvFindImageProcAddress(pldev, lpProcName);
|
|
}
|
|
|
|
/* EOF */
|