reactos/win32ss/gdi/eng/pdevobj.c
Oleg Dubinskiy e034377b51
[WIN32SS][ENG][NTDDRAW] Manage DirectDraw instances when switching display mode (#4519)
CORE-17932

[ENG] Implement DirectDraw management in switch display mode functions
(e.g. resolution change, color depth, display frequency etc.):

- Switch DirectDraw instances between the two PDEVs (the current one and
  the new one allocated by ourselves) by calling dxg!DxDdDynamicModeChange
  function.

- Suspend them before and resume after the display mode switch, by calling
  dxg!DxDdsuspendDirectDraw and dxg!DxDdResumeDirectDraw appropriately.

We currently don't have these functions implemented, but MS DXG has, so
it allows to properly manage DirectDraw PDEVs using this driver, similarly
to Windows.
My analysis confirms that these functions are always called in XP/2k3 on
display mode switch, even when there is no any DirectX app running at the
moment. Analyzing their prototypes show that my guesses are correct.

- Initialize hDev and dhpdev members for EDD_DIRECTDRAW_GLOBAL for newly
  created surfaces, switch them during mode change and re-initialize after
  it also. They are commonly used by DirectDraw stack.
  In addition, enable DirectDraw for old and new PDEVs, by calling
  dxg!DxDdEnableDirectDraw function.


[NTDDRAW] Additionally, fix usage of DirectDraw lock count in the PDEVOBJ
structure.

- Enable cDirectDrawDisableLocks member for storing its value, instead of
  DxDd_nCount, which is marked as ROS-specific.

- Use it in win32k!DxEngGet/SetHdevData for getting/setting DirectDraw
  count appropriately.

My analysis also shows that in Windows, the PDEVOBJ::cDirectDrawDisableLocks
method calls DxEngGetHdevData with type 8, which corresponds to our DxDd_nCount.
So there are no doubts that this member is used there.

- Rename DxEngGetHdevData_dd_count alias of type 8 to DxEngGetHdevData_dd_locks,
  to match more accurately an actual member name. Update the enumeration
  and fix all code parts appropriately.

All these changes allow to properly change display mode during executing
DirectDraw applications, when they try to switch in full-screen mode.
At least a bugcheck that happened before my changes, does no longer appear.

There are still some games that don't run correctly, as if there is no
3D acceleration (which actually exists). This requires further investigations.
2023-06-21 18:00:24 +02:00

1372 lines
36 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* PURPOSE: Support for physical devices
* FILE: win32ss/gdi/eng/pdevobj.c
* PROGRAMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
* Oleg Dubinskiy (oleg.dubinskij30@gmail.com)
*/
#include <win32k.h>
#define NDEBUG
#include <debug.h>
DBG_DEFAULT_CHANNEL(EngPDev);
static PPDEVOBJ gppdevList = NULL;
static HSEMAPHORE ghsemPDEV;
BOOL
APIENTRY
MultiEnableDriver(
_In_ ULONG iEngineVersion,
_In_ ULONG cj,
_Inout_bytecount_(cj) PDRVENABLEDATA pded);
extern DRVFN gPanDispDrvFn[];
extern ULONG gPanDispDrvCount;
CODE_SEG("INIT")
NTSTATUS
NTAPI
InitPDEVImpl(VOID)
{
ghsemPDEV = EngCreateSemaphore();
if (!ghsemPDEV) return STATUS_INSUFFICIENT_RESOURCES;
return STATUS_SUCCESS;
}
#if DBG
PPDEVOBJ
NTAPI
DbgLookupDHPDEV(DHPDEV dhpdev)
{
PPDEVOBJ ppdev;
/* Lock PDEV list */
EngAcquireSemaphoreShared(ghsemPDEV);
/* Walk through the list of PDEVs */
for (ppdev = gppdevList; ppdev; ppdev = ppdev->ppdevNext)
{
/* Compare with the given DHPDEV */
if (ppdev->dhpdev == dhpdev) break;
}
/* Unlock PDEV list */
EngReleaseSemaphore(ghsemPDEV);
return ppdev;
}
#endif
PPDEVOBJ
PDEVOBJ_AllocPDEV(VOID)
{
PPDEVOBJ ppdev;
ppdev = ExAllocatePoolWithTag(PagedPool, sizeof(PDEVOBJ), GDITAG_PDEV);
if (!ppdev)
return NULL;
RtlZeroMemory(ppdev, sizeof(PDEVOBJ));
ppdev->hsemDevLock = EngCreateSemaphore();
if (ppdev->hsemDevLock == NULL)
{
ExFreePoolWithTag(ppdev, GDITAG_PDEV);
return NULL;
}
/* Allocate EDD_DIRECTDRAW_GLOBAL for our ReactX driver */
ppdev->pEDDgpl = ExAllocatePoolWithTag(PagedPool, sizeof(EDD_DIRECTDRAW_GLOBAL), GDITAG_PDEV);
if (ppdev->pEDDgpl)
RtlZeroMemory(ppdev->pEDDgpl, sizeof(EDD_DIRECTDRAW_GLOBAL));
ppdev->cPdevRefs = 1;
return ppdev;
}
static
VOID
PDEVOBJ_vDeletePDEV(
PPDEVOBJ ppdev)
{
EngDeleteSemaphore(ppdev->hsemDevLock);
if (ppdev->pdmwDev)
ExFreePoolWithTag(ppdev->pdmwDev, GDITAG_DEVMODE);
if (ppdev->pEDDgpl)
ExFreePoolWithTag(ppdev->pEDDgpl, GDITAG_PDEV);
ExFreePoolWithTag(ppdev, GDITAG_PDEV);
}
VOID
NTAPI
PDEVOBJ_vRelease(
_Inout_ PPDEVOBJ ppdev)
{
/* Lock loader */
EngAcquireSemaphore(ghsemPDEV);
/* Decrease reference count */
InterlockedDecrement(&ppdev->cPdevRefs);
ASSERT(ppdev->cPdevRefs >= 0);
/* Check if references are left */
if (ppdev->cPdevRefs == 0)
{
/* Do we have a surface? */
if (ppdev->pSurface)
{
/* Release the surface and let the driver free it */
SURFACE_ShareUnlockSurface(ppdev->pSurface);
TRACE("DrvDisableSurface(dhpdev %p)\n", ppdev->dhpdev);
ppdev->pfn.DisableSurface(ppdev->dhpdev);
}
/* Do we have a palette? */
if (ppdev->ppalSurf)
{
PALETTE_ShareUnlockPalette(ppdev->ppalSurf);
}
/* Check if the PDEV was enabled */
if (ppdev->dhpdev != NULL)
{
/* Disable the PDEV */
TRACE("DrvDisablePDEV(dhpdev %p)\n", ppdev->dhpdev);
ppdev->pfn.DisablePDEV(ppdev->dhpdev);
}
/* Remove it from list */
if (ppdev == gppdevList)
{
gppdevList = ppdev->ppdevNext;
}
else if (gppdevList)
{
PPDEVOBJ ppdevCurrent = gppdevList;
BOOL found = FALSE;
while (!found && ppdevCurrent->ppdevNext)
{
if (ppdevCurrent->ppdevNext == ppdev)
found = TRUE;
else
ppdevCurrent = ppdevCurrent->ppdevNext;
}
if (found)
ppdevCurrent->ppdevNext = ppdev->ppdevNext;
}
/* Unload display driver */
EngUnloadImage(ppdev->pldev);
/* Free it */
PDEVOBJ_vDeletePDEV(ppdev);
}
/* Unlock loader */
EngReleaseSemaphore(ghsemPDEV);
}
BOOL
NTAPI
PDEVOBJ_bEnablePDEV(
_In_ PPDEVOBJ ppdev,
_In_ PDEVMODEW pdevmode,
_In_ PWSTR pwszLogAddress)
{
PFN_DrvEnablePDEV pfnEnablePDEV;
ULONG i;
/* Get the DrvEnablePDEV function */
pfnEnablePDEV = ppdev->pfn.EnablePDEV;
/* Call the drivers DrvEnablePDEV function */
TRACE("DrvEnablePDEV(pdevmode %p (%dx%dx%d %d Hz) hdev %p (%S))\n",
pdevmode,
ppdev->pGraphicsDevice ? pdevmode->dmPelsWidth : 0,
ppdev->pGraphicsDevice ? pdevmode->dmPelsHeight : 0,
ppdev->pGraphicsDevice ? pdevmode->dmBitsPerPel : 0,
ppdev->pGraphicsDevice ? pdevmode->dmDisplayFrequency : 0,
ppdev,
ppdev->pGraphicsDevice ? ppdev->pGraphicsDevice->szNtDeviceName : L"");
ppdev->dhpdev = pfnEnablePDEV(pdevmode,
pwszLogAddress,
HS_DDI_MAX,
ppdev->ahsurf,
sizeof(GDIINFO),
(PULONG)&ppdev->gdiinfo,
sizeof(DEVINFO),
&ppdev->devinfo,
(HDEV)ppdev,
ppdev->pGraphicsDevice ? ppdev->pGraphicsDevice->pwszDescription : NULL,
ppdev->pGraphicsDevice ? ppdev->pGraphicsDevice->DeviceObject : NULL);
TRACE("DrvEnablePDEV(pdevmode %p hdev %p) => dhpdev %p\n", pdevmode, ppdev, ppdev->dhpdev);
if (ppdev->dhpdev == NULL)
{
ERR("Failed to enable PDEV\n");
return FALSE;
}
/* Fix up some values */
if (ppdev->gdiinfo.ulLogPixelsX == 0)
ppdev->gdiinfo.ulLogPixelsX = 96;
if (ppdev->gdiinfo.ulLogPixelsY == 0)
ppdev->gdiinfo.ulLogPixelsY = 96;
/* Set raster caps */
ppdev->gdiinfo.flRaster = RC_OP_DX_OUTPUT | RC_GDI20_OUTPUT | RC_BIGFONT;
if ((ppdev->gdiinfo.ulTechnology != DT_PLOTTER) && (ppdev->gdiinfo.ulTechnology != DT_CHARSTREAM))
ppdev->gdiinfo.flRaster |= RC_STRETCHDIB | RC_STRETCHBLT | RC_DIBTODEV | RC_DI_BITMAP | RC_BITMAP64 | RC_BITBLT;
if (ppdev->gdiinfo.ulTechnology == DT_RASDISPLAY)
ppdev->gdiinfo.flRaster |= RC_FLOODFILL;
if (ppdev->devinfo.flGraphicsCaps & GCAPS_PALMANAGED)
ppdev->gdiinfo.flRaster |= RC_PALETTE;
/* Setup Palette */
ppdev->ppalSurf = PALETTE_ShareLockPalette(ppdev->devinfo.hpalDefault);
/* Setup hatch brushes */
for (i = 0; i < HS_DDI_MAX; i++)
{
if (ppdev->ahsurf[i] == NULL)
ppdev->ahsurf[i] = gahsurfHatch[i];
}
TRACE("PDEVOBJ_bEnablePDEV - dhpdev = %p\n", ppdev->dhpdev);
return TRUE;
}
VOID
NTAPI
PDEVOBJ_vCompletePDEV(
PPDEVOBJ ppdev)
{
/* Call the drivers DrvCompletePDEV function */
TRACE("DrvCompletePDEV(dhpdev %p hdev %p)\n", ppdev->dhpdev, ppdev);
ppdev->pfn.CompletePDEV(ppdev->dhpdev, (HDEV)ppdev);
}
static
VOID
PDEVOBJ_vFilterDriverHooks(
_In_ PPDEVOBJ ppdev)
{
PLDEVOBJ pldev = ppdev->pldev;
ULONG dwAccelerationLevel = ppdev->dwAccelerationLevel;
if (!pldev->pGdiDriverInfo)
return;
if (pldev->ldevtype != LDEV_DEVICE_DISPLAY)
return;
if (dwAccelerationLevel >= 1)
{
ppdev->apfn[INDEX_DrvSetPointerShape] = NULL;
ppdev->apfn[INDEX_DrvCreateDeviceBitmap] = NULL;
}
if (dwAccelerationLevel >= 2)
{
/* Remove sophisticated display accelerations */
ppdev->pSurface->flags &= ~(HOOK_STRETCHBLT |
HOOK_FILLPATH |
HOOK_GRADIENTFILL |
HOOK_LINETO |
HOOK_ALPHABLEND |
HOOK_TRANSPARENTBLT);
}
if (dwAccelerationLevel >= 3)
{
/* Disable DirectDraw and Direct3D accelerations */
/* FIXME: need to call DxDdSetAccelLevel */
UNIMPLEMENTED;
}
if (dwAccelerationLevel >= 4)
{
/* Remove almost all display accelerations */
ppdev->pSurface->flags &= ~HOOK_FLAGS |
HOOK_BITBLT |
HOOK_COPYBITS |
HOOK_TEXTOUT |
HOOK_STROKEPATH |
HOOK_SYNCHRONIZE;
}
if (dwAccelerationLevel >= 5)
{
/* Disable all display accelerations */
/* (nothing to do. Already handled in PDEVOBJ_Create) */
}
}
PSURFACE
NTAPI
PDEVOBJ_pSurface(
PPDEVOBJ ppdev)
{
HSURF hsurf;
/* Check if there is no surface for this PDEV yet */
if (ppdev->pSurface == NULL)
{
/* Call the drivers DrvEnableSurface */
TRACE("DrvEnableSurface(dhpdev %p)\n", ppdev->dhpdev);
hsurf = ppdev->pfn.EnableSurface(ppdev->dhpdev);
TRACE("DrvEnableSurface(dhpdev %p) => hsurf %p\n", ppdev->dhpdev, hsurf);
if (hsurf== NULL)
{
ERR("Failed to create PDEV surface!\n");
return NULL;
}
/* Get a reference to the surface */
ppdev->pSurface = SURFACE_ShareLockSurface(hsurf);
NT_ASSERT(ppdev->pSurface != NULL);
}
/* Increment reference count */
GDIOBJ_vReferenceObjectByPointer(&ppdev->pSurface->BaseObject);
return ppdev->pSurface;
}
BOOL
PDEVOBJ_bEnableDirectDraw(
_Inout_ PPDEVOBJ ppdev)
{
PGD_DXDDENABLEDIRECTDRAW pfnDdEnableDirectDraw = (PGD_DXDDENABLEDIRECTDRAW)gpDxFuncs[DXG_INDEX_DxDdEnableDirectDraw].pfn;
BOOL Success;
/* Enable DirectDraw */
TRACE("DxDdEnableDirectDraw(ppdev %p)\n", ppdev);
Success = pfnDdEnableDirectDraw((HDEV)ppdev, TRUE);
TRACE("DxDdEnableDirectDraw(ppdev %p) => %d\n", ppdev, Success);
return Success;
}
VOID
PDEVOBJ_vResumeDirectDraw(
_Inout_ PPDEVOBJ ppdev)
{
PGD_DXDDRESUMEDIRECTDRAW pfnDdResumeDirectDraw = (PGD_DXDDRESUMEDIRECTDRAW)gpDxFuncs[DXG_INDEX_DxDdResumeDirectDraw].pfn;
/* Resume DirectDraw after mode change */
TRACE("DxDdResumeDirectDraw(ppdev %p)\n", ppdev);
pfnDdResumeDirectDraw((HDEV)ppdev, 0);
}
VOID
PDEVOBJ_vSuspendDirectDraw(
_Inout_ PPDEVOBJ ppdev)
{
PGD_DXDDSUSPENDDIRECTDRAW pfnDdSuspendDirectDraw = (PGD_DXDDSUSPENDDIRECTDRAW)gpDxFuncs[DXG_INDEX_DxDdSuspendDirectDraw].pfn;
/* Suspend DirectDraw for mode change */
TRACE("DxDdSuspendDirectDraw(ppdev %p)\n", ppdev);
pfnDdSuspendDirectDraw((HDEV)ppdev, 0);
}
VOID
PDEVOBJ_vSwitchDirectDraw(
_Inout_ PPDEVOBJ ppdev,
_Inout_ PPDEVOBJ ppdev2)
{
PGD_DXDDDYNAMICMODECHANGE pfnDdDynamicModeChange = (PGD_DXDDDYNAMICMODECHANGE)gpDxFuncs[DXG_INDEX_DxDdDynamicModeChange].pfn;
/* Switch DirectDraw instances between the PDEVs */
TRACE("DxDdDynamicModeChange(ppdev %p, ppdev2 %p)\n", ppdev, ppdev2);
pfnDdDynamicModeChange((HDEV)ppdev, (HDEV)ppdev2, 0);
}
VOID
PDEVOBJ_vEnableDisplay(
_Inout_ PPDEVOBJ ppdev)
{
BOOL assertVal;
if (!(ppdev->flFlags & PDEV_DISABLED))
return;
/* Try to enable display until success */
do
{
TRACE("DrvAssertMode(dhpdev %p, TRUE)\n", ppdev->dhpdev);
assertVal = ppdev->pfn.AssertMode(ppdev->dhpdev, TRUE);
TRACE("DrvAssertMode(dhpdev %p, TRUE) => %d\n", ppdev->dhpdev, assertVal);
} while (!assertVal);
ppdev->flFlags &= ~PDEV_DISABLED;
}
BOOL
PDEVOBJ_bDisableDisplay(
_Inout_ PPDEVOBJ ppdev)
{
BOOL assertVal;
if (ppdev->flFlags & PDEV_DISABLED)
return TRUE;
PDEVOBJ_vSuspendDirectDraw(ppdev);
TRACE("DrvAssertMode(dhpdev %p, FALSE)\n", ppdev->dhpdev);
assertVal = ppdev->pfn.AssertMode(ppdev->dhpdev, FALSE);
TRACE("DrvAssertMode(dhpdev %p, FALSE) => %d\n", ppdev->dhpdev, assertVal);
if (assertVal)
ppdev->flFlags |= PDEV_DISABLED;
return assertVal;
}
VOID
NTAPI
PDEVOBJ_vRefreshModeList(
PPDEVOBJ ppdev)
{
PGRAPHICS_DEVICE pGraphicsDevice;
PDEVMODEINFO pdminfo, pdmiNext;
/* Lock the PDEV */
EngAcquireSemaphore(ppdev->hsemDevLock);
pGraphicsDevice = ppdev->pGraphicsDevice;
/* Clear out the modes */
for (pdminfo = pGraphicsDevice->pdevmodeInfo;
pdminfo;
pdminfo = pdmiNext)
{
pdmiNext = pdminfo->pdmiNext;
ExFreePoolWithTag(pdminfo, GDITAG_DEVMODE);
}
pGraphicsDevice->pdevmodeInfo = NULL;
ExFreePoolWithTag(pGraphicsDevice->pDevModeList, GDITAG_GDEVICE);
pGraphicsDevice->pDevModeList = NULL;
/* Update available display mode list */
LDEVOBJ_bBuildDevmodeList(pGraphicsDevice);
/* Unlock PDEV */
EngReleaseSemaphore(ppdev->hsemDevLock);
}
PPDEVOBJ
PDEVOBJ_Create(
_In_opt_ PGRAPHICS_DEVICE pGraphicsDevice,
_In_opt_ PDEVMODEW pdm,
_In_ ULONG dwAccelerationLevel,
_In_ ULONG ldevtype)
{
PPDEVOBJ ppdev, ppdevMatch = NULL;
PLDEVOBJ pldev;
PSURFACE pSurface;
TRACE("PDEVOBJ_Create(%p %p %d)\n", pGraphicsDevice, pdm, ldevtype);
if (ldevtype != LDEV_DEVICE_META)
{
ASSERT(pGraphicsDevice);
ASSERT(pdm);
/* Search if we already have a PPDEV with the required characteristics.
* We will compare the graphics device, the devmode and the desktop
*/
for (ppdev = gppdevList; ppdev; ppdev = ppdev->ppdevNext)
{
if (ppdev->pGraphicsDevice == pGraphicsDevice)
{
PDEVOBJ_vReference(ppdev);
if (RtlEqualMemory(pdm, ppdev->pdmwDev, sizeof(DEVMODEW)) &&
ppdev->dwAccelerationLevel == dwAccelerationLevel)
{
PDEVOBJ_vReference(ppdev);
ppdevMatch = ppdev;
}
else
{
PDEVOBJ_bDisableDisplay(ppdev);
}
PDEVOBJ_vRelease(ppdev);
}
}
if (ppdevMatch)
{
PDEVOBJ_vEnableDisplay(ppdevMatch);
return ppdevMatch;
}
}
/* Try to get a display driver */
if (ldevtype == LDEV_DEVICE_META)
pldev = LDEVOBJ_pLoadInternal(MultiEnableDriver, ldevtype);
else
pldev = LDEVOBJ_pLoadDriver(pdm->dmDeviceName, ldevtype);
if (!pldev)
{
ERR("Could not load display driver '%S'\n",
(ldevtype == LDEV_DEVICE_META) ? L"" : pdm->dmDeviceName);
return NULL;
}
/* Allocate a new PDEVOBJ */
ppdev = PDEVOBJ_AllocPDEV();
if (!ppdev)
{
ERR("failed to allocate a PDEV\n");
return NULL;
}
if (ldevtype != LDEV_DEVICE_META)
{
ppdev->pGraphicsDevice = pGraphicsDevice;
// DxEngGetHdevData asks for Graphics DeviceObject in hSpooler field
ppdev->hSpooler = ppdev->pGraphicsDevice->DeviceObject;
/* Keep selected resolution */
if (ppdev->pdmwDev)
ExFreePoolWithTag(ppdev->pdmwDev, GDITAG_DEVMODE);
ppdev->pdmwDev = ExAllocatePoolWithTag(PagedPool, pdm->dmSize + pdm->dmDriverExtra, GDITAG_DEVMODE);
if (ppdev->pdmwDev)
{
RtlCopyMemory(ppdev->pdmwDev, pdm, pdm->dmSize + pdm->dmDriverExtra);
/* FIXME: this must be done in a better way */
pGraphicsDevice->StateFlags |= DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_ATTACHED_TO_DESKTOP;
}
}
/* FIXME! */
ppdev->flFlags = PDEV_DISPLAY;
/* HACK: Don't use the pointer */
ppdev->Pointer.Exclude.right = -1;
/* Initialize PDEV */
ppdev->pldev = pldev;
ppdev->dwAccelerationLevel = dwAccelerationLevel;
/* Copy the function table */
if ((ldevtype == LDEV_DEVICE_DISPLAY && dwAccelerationLevel >= 5) ||
pdm->dmFields & (DM_PANNINGWIDTH | DM_PANNINGHEIGHT))
{
ULONG i;
/* Initialize missing fields */
if (!(pdm->dmFields & DM_PANNINGWIDTH))
pdm->dmPanningWidth = pdm->dmPelsWidth;
if (!(pdm->dmFields & DM_PANNINGHEIGHT))
pdm->dmPanningHeight = pdm->dmPelsHeight;
/* Replace vtable by panning vtable */
for (i = 0; i < gPanDispDrvCount; i++)
ppdev->apfn[gPanDispDrvFn[i].iFunc] = gPanDispDrvFn[i].pfn;
}
else
{
ppdev->pfn = ppdev->pldev->pfn;
}
/* Call the driver to enable the PDEV */
if (!PDEVOBJ_bEnablePDEV(ppdev, pdm, NULL))
{
ERR("Failed to enable PDEV!\n");
PDEVOBJ_vRelease(ppdev);
EngUnloadImage(pldev);
return NULL;
}
/* Tell the driver that the PDEV is ready */
PDEVOBJ_vCompletePDEV(ppdev);
/* Create the initial surface */
pSurface = PDEVOBJ_pSurface(ppdev);
if (!pSurface)
{
ERR("Failed to create surface\n");
PDEVOBJ_vRelease(ppdev);
EngUnloadImage(pldev);
return NULL;
}
/* Enable DirectDraw */
if (!PDEVOBJ_bEnableDirectDraw(ppdev))
{
ERR("Failed to enable DirectDraw\n");
PDEVOBJ_vRelease(ppdev);
EngUnloadImage(pldev);
return NULL;
}
/* Remove some acceleration capabilities from driver */
PDEVOBJ_vFilterDriverHooks(ppdev);
/* Set MovePointer function */
ppdev->pfnMovePointer = ppdev->pfn.MovePointer;
if (!ppdev->pfnMovePointer)
ppdev->pfnMovePointer = EngMovePointer;
/* Insert the PDEV into the list */
ppdev->ppdevNext = gppdevList;
gppdevList = ppdev;
/* Return the PDEV */
return ppdev;
}
FORCEINLINE
VOID
SwitchPointer(
_Inout_ PVOID pvPointer1,
_Inout_ PVOID pvPointer2)
{
PVOID *ppvPointer1 = pvPointer1;
PVOID *ppvPointer2 = pvPointer2;
PVOID pvTemp;
pvTemp = *ppvPointer1;
*ppvPointer1 = *ppvPointer2;
*ppvPointer2 = pvTemp;
}
BOOL
NTAPI
PDEVOBJ_bDynamicModeChange(
PPDEVOBJ ppdev,
PPDEVOBJ ppdev2)
{
union
{
DRIVER_FUNCTIONS pfn;
GDIINFO gdiinfo;
DEVINFO devinfo;
DWORD StateFlags;
} temp;
/* Exchange driver functions */
temp.pfn = ppdev->pfn;
ppdev->pfn = ppdev2->pfn;
ppdev2->pfn = temp.pfn;
/* Exchange LDEVs */
SwitchPointer(&ppdev->pldev, &ppdev2->pldev);
/* Exchange DHPDEV */
SwitchPointer(&ppdev->dhpdev, &ppdev2->dhpdev);
/* Exchange surfaces and associate them with their new PDEV */
SwitchPointer(&ppdev->pSurface, &ppdev2->pSurface);
ppdev->pSurface->SurfObj.hdev = (HDEV)ppdev;
ppdev2->pSurface->SurfObj.hdev = (HDEV)ppdev2;
/* Exchange devinfo */
temp.devinfo = ppdev->devinfo;
ppdev->devinfo = ppdev2->devinfo;
ppdev2->devinfo = temp.devinfo;
/* Exchange gdiinfo */
temp.gdiinfo = ppdev->gdiinfo;
ppdev->gdiinfo = ppdev2->gdiinfo;
ppdev2->gdiinfo = temp.gdiinfo;
/* Exchange DEVMODE */
SwitchPointer(&ppdev->pdmwDev, &ppdev2->pdmwDev);
/* Exchange state flags */
temp.StateFlags = ppdev->pGraphicsDevice->StateFlags;
ppdev->pGraphicsDevice->StateFlags = ppdev2->pGraphicsDevice->StateFlags;
ppdev2->pGraphicsDevice->StateFlags = temp.StateFlags;
/* Notify each driver instance of its new HDEV association */
ppdev->pfn.CompletePDEV(ppdev->dhpdev, (HDEV)ppdev);
ppdev2->pfn.CompletePDEV(ppdev2->dhpdev, (HDEV)ppdev2);
/* Switch DirectDraw mode */
PDEVOBJ_vSwitchDirectDraw(ppdev, ppdev2);
return TRUE;
}
BOOL
NTAPI
PDEVOBJ_bSwitchMode(
PPDEVOBJ ppdev,
PDEVMODEW pdm)
{
PPDEVOBJ ppdevTmp;
PSURFACE pSurface;
BOOL retval = FALSE;
/* Lock the PDEV */
EngAcquireSemaphore(ppdev->hsemDevLock);
/* And everything else */
EngAcquireSemaphore(ghsemPDEV);
DPRINT1("PDEVOBJ_bSwitchMode, ppdev = %p, pSurface = %p\n", ppdev, ppdev->pSurface);
// Lookup the GraphicsDevice + select DEVMODE
// pdm = LDEVOBJ_bProbeAndCaptureDevmode(ppdev, pdm);
/* 1. Temporarily disable the current PDEV and reset video to its default mode */
if (!PDEVOBJ_bDisableDisplay(ppdev))
{
DPRINT1("PDEVOBJ_bDisableDisplay() failed\n");
/* Resume DirectDraw in case of failure */
PDEVOBJ_vResumeDirectDraw(ppdev);
goto leave;
}
/* 2. Create new PDEV */
ppdevTmp = PDEVOBJ_Create(ppdev->pGraphicsDevice, pdm, 0, LDEV_DEVICE_DISPLAY);
if (!ppdevTmp)
{
DPRINT1("Failed to create a new PDEV\n");
goto leave2;
}
/* 3. Create a new surface */
pSurface = PDEVOBJ_pSurface(ppdevTmp);
if (!pSurface)
{
DPRINT1("PDEVOBJ_pSurface failed\n");
PDEVOBJ_vRelease(ppdevTmp);
goto leave2;
}
/* 4. Temporarily suspend DirectDraw for mode change */
PDEVOBJ_vSuspendDirectDraw(ppdev);
PDEVOBJ_vSuspendDirectDraw(ppdevTmp);
/* 5. Switch the PDEVs */
if (!PDEVOBJ_bDynamicModeChange(ppdev, ppdevTmp))
{
DPRINT1("PDEVOBJ_bDynamicModeChange() failed\n");
PDEVOBJ_vRelease(ppdevTmp);
goto leave2;
}
/* 6. Resume DirectDraw */
PDEVOBJ_vResumeDirectDraw(ppdev);
PDEVOBJ_vResumeDirectDraw(ppdevTmp);
/* Release temp PDEV */
PDEVOBJ_vRelease(ppdevTmp);
/* Re-initialize DirectDraw data */
ppdev->pEDDgpl->hDev = (HDEV)ppdev;
ppdev->pEDDgpl->dhpdev = ppdev->dhpdev;
/* Update primary display capabilities */
if (ppdev == gpmdev->ppdevGlobal)
{
PDEVOBJ_vGetDeviceCaps(ppdev, &GdiHandleTable->DevCaps);
}
/* Success! */
retval = TRUE;
leave2:
/* Set the new video mode, or restore the original one in case of failure */
PDEVOBJ_vEnableDisplay(ppdev);
leave:
/* Unlock everything else */
EngReleaseSemaphore(ghsemPDEV);
/* Unlock the PDEV */
EngReleaseSemaphore(ppdev->hsemDevLock);
DPRINT1("leave, ppdev = %p, pSurface = %p\n", ppdev, ppdev->pSurface);
return retval;
}
PPDEVOBJ
NTAPI
EngpGetPDEV(
_In_opt_ PUNICODE_STRING pustrDeviceName)
{
UNICODE_STRING ustrCurrent;
PPDEVOBJ ppdev = NULL;
PGRAPHICS_DEVICE pGraphicsDevice;
ULONG i;
/* Acquire PDEV lock */
EngAcquireSemaphore(ghsemPDEV);
/* Did the caller pass a device name? */
if (pustrDeviceName)
{
/* Loop all present PDEVs */
for (i = 0; i < gpmdev->cDev; i++)
{
/* Get a pointer to the GRAPHICS_DEVICE */
pGraphicsDevice = gpmdev->dev[i].ppdev->pGraphicsDevice;
/* Compare the name */
RtlInitUnicodeString(&ustrCurrent, pGraphicsDevice->szWinDeviceName);
if (RtlEqualUnicodeString(pustrDeviceName, &ustrCurrent, FALSE))
{
/* Found! */
ppdev = gpmdev->dev[i].ppdev;
break;
}
}
}
else if (gpmdev)
{
/* Otherwise use the primary PDEV */
ppdev = gpmdev->ppdevGlobal;
}
/* Did we find one? */
if (ppdev)
{
/* Yes, reference the PDEV */
PDEVOBJ_vReference(ppdev);
}
/* Release PDEV lock */
EngReleaseSemaphore(ghsemPDEV);
return ppdev;
}
LONG
PDEVOBJ_lChangeDisplaySettings(
_In_opt_ PUNICODE_STRING pustrDeviceName,
_In_opt_ PDEVMODEW RequestedMode,
_In_opt_ PMDEVOBJ pmdevOld,
_Out_ PMDEVOBJ *ppmdevNew,
_In_ BOOL bSearchClosestMode)
{
PGRAPHICS_DEVICE pGraphicsDevice = NULL;
PMDEVOBJ pmdev = NULL;
PDEVMODEW pdm = NULL;
ULONG lRet = DISP_CHANGE_SUCCESSFUL;
ULONG i, j;
TRACE("PDEVOBJ_lChangeDisplaySettings('%wZ' '%dx%dx%d (%d Hz)' %p %p)\n",
pustrDeviceName,
RequestedMode ? RequestedMode->dmPelsWidth : 0,
RequestedMode ? RequestedMode->dmPelsHeight : 0,
RequestedMode ? RequestedMode->dmBitsPerPel : 0,
RequestedMode ? RequestedMode->dmDisplayFrequency : 0,
pmdevOld, ppmdevNew);
if (pustrDeviceName)
{
pGraphicsDevice = EngpFindGraphicsDevice(pustrDeviceName, 0);
if (!pGraphicsDevice)
{
ERR("Wrong device name provided: '%wZ'\n", pustrDeviceName);
lRet = DISP_CHANGE_BADPARAM;
goto cleanup;
}
}
else if (RequestedMode)
{
pGraphicsDevice = gpPrimaryGraphicsDevice;
if (!pGraphicsDevice)
{
ERR("Wrong device'\n");
lRet = DISP_CHANGE_BADPARAM;
goto cleanup;
}
}
if (pGraphicsDevice)
{
if (!LDEVOBJ_bProbeAndCaptureDevmode(pGraphicsDevice, RequestedMode, &pdm, bSearchClosestMode))
{
ERR("DrvProbeAndCaptureDevmode() failed\n");
lRet = DISP_CHANGE_BADMODE;
goto cleanup;
}
}
/* Here, we know that input parameters were correct */
{
/* Create new MDEV. Note that if we provide a device name,
* MDEV will only contain one device.
* */
if (pmdevOld)
{
/* Disable old MDEV */
if (MDEVOBJ_bDisable(pmdevOld))
{
/* Create new MDEV. On failure, reenable old MDEV */
pmdev = MDEVOBJ_Create(pustrDeviceName, pdm);
if (!pmdev)
MDEVOBJ_vEnable(pmdevOld);
}
}
else
{
pmdev = MDEVOBJ_Create(pustrDeviceName, pdm);
}
if (!pmdev)
{
ERR("Failed to create new MDEV\n");
lRet = DISP_CHANGE_FAILED;
goto cleanup;
}
lRet = DISP_CHANGE_SUCCESSFUL;
*ppmdevNew = pmdev;
/* We now have to do the mode switch */
if (pustrDeviceName && pmdevOld)
{
/* We changed settings of one device. Add other devices which were already present */
for (i = 0; i < pmdevOld->cDev; i++)
{
for (j = 0; j < pmdev->cDev; j++)
{
if (pmdev->dev[j].ppdev->pGraphicsDevice == pmdevOld->dev[i].ppdev->pGraphicsDevice)
{
if (PDEVOBJ_bDynamicModeChange(pmdevOld->dev[i].ppdev, pmdev->dev[j].ppdev))
{
PPDEVOBJ tmp = pmdevOld->dev[i].ppdev;
pmdevOld->dev[i].ppdev = pmdev->dev[j].ppdev;
pmdev->dev[j].ppdev = tmp;
}
else
{
ERR("Failed to apply new settings\n");
UNIMPLEMENTED;
ASSERT(FALSE);
}
break;
}
}
if (j == pmdev->cDev)
{
PDEVOBJ_vReference(pmdevOld->dev[i].ppdev);
pmdev->dev[pmdev->cDev].ppdev = pmdevOld->dev[i].ppdev;
pmdev->cDev++;
}
}
}
if (pmdev->cDev == 1)
{
pmdev->ppdevGlobal = pmdev->dev[0].ppdev;
}
else
{
/* Enable MultiDriver */
pmdev->ppdevGlobal = PDEVOBJ_Create(NULL, (PDEVMODEW)pmdev, 0, LDEV_DEVICE_META);
if (!pmdev->ppdevGlobal)
{
WARN("Failed to create meta-device. Using only first display\n");
PDEVOBJ_vReference(pmdev->dev[0].ppdev);
pmdev->ppdevGlobal = pmdev->dev[0].ppdev;
}
}
if (pmdevOld)
{
/* Search PDEVs which were in pmdevOld, but are not anymore in pmdev, and disable them */
for (i = 0; i < pmdevOld->cDev; i++)
{
for (j = 0; j < pmdev->cDev; j++)
{
if (pmdev->dev[j].ppdev->pGraphicsDevice == pmdevOld->dev[i].ppdev->pGraphicsDevice)
break;
}
if (j == pmdev->cDev)
PDEVOBJ_bDisableDisplay(pmdevOld->dev[i].ppdev);
}
}
}
cleanup:
if (lRet != DISP_CHANGE_SUCCESSFUL)
{
*ppmdevNew = NULL;
if (pmdev)
MDEVOBJ_vDestroy(pmdev);
if (pdm && pdm != RequestedMode)
ExFreePoolWithTag(pdm, GDITAG_DEVMODE);
}
return lRet;
}
INT
NTAPI
PDEVOBJ_iGetColorManagementCaps(PPDEVOBJ ppdev)
{
INT ret = CM_NONE;
if (ppdev->flFlags & PDEV_DISPLAY)
{
if (ppdev->devinfo.iDitherFormat == BMF_8BPP ||
ppdev->devinfo.flGraphicsCaps2 & GCAPS2_CHANGEGAMMARAMP)
ret = CM_GAMMA_RAMP;
}
if (ppdev->devinfo.flGraphicsCaps & GCAPS_CMYKCOLOR)
ret |= CM_CMYK_COLOR;
if (ppdev->devinfo.flGraphicsCaps & GCAPS_ICM)
ret |= CM_DEVICE_ICM;
return ret;
}
VOID
NTAPI
PDEVOBJ_vGetDeviceCaps(
IN PPDEVOBJ ppdev,
OUT PDEVCAPS pDevCaps)
{
PGDIINFO pGdiInfo = &ppdev->gdiinfo;
pDevCaps->ulVersion = pGdiInfo->ulVersion;
pDevCaps->ulTechnology = pGdiInfo->ulTechnology;
pDevCaps->ulHorzSizeM = (pGdiInfo->ulHorzSize + 500) / 1000;
pDevCaps->ulVertSizeM = (pGdiInfo->ulVertSize + 500) / 1000;
pDevCaps->ulHorzSize = pGdiInfo->ulHorzSize;
pDevCaps->ulVertSize = pGdiInfo->ulVertSize;
pDevCaps->ulHorzRes = pGdiInfo->ulHorzRes;
pDevCaps->ulVertRes = pGdiInfo->ulVertRes;
pDevCaps->ulBitsPixel = pGdiInfo->cBitsPixel;
if (pDevCaps->ulBitsPixel == 15) pDevCaps->ulBitsPixel = 16;
pDevCaps->ulPlanes = pGdiInfo->cPlanes;
pDevCaps->ulNumPens = pGdiInfo->ulNumColors;
if (pDevCaps->ulNumPens != -1) pDevCaps->ulNumPens *= 5;
pDevCaps->ulNumFonts = 0; // PDEVOBJ_cFonts(ppdev);
pDevCaps->ulNumColors = pGdiInfo->ulNumColors;
pDevCaps->ulRasterCaps = pGdiInfo->flRaster;
pDevCaps->ulAspectX = pGdiInfo->ulAspectX;
pDevCaps->ulAspectY = pGdiInfo->ulAspectY;
pDevCaps->ulAspectXY = pGdiInfo->ulAspectXY;
pDevCaps->ulLogPixelsX = pGdiInfo->ulLogPixelsX;
pDevCaps->ulLogPixelsY = pGdiInfo->ulLogPixelsY;
pDevCaps->ulSizePalette = pGdiInfo->ulNumPalReg;
pDevCaps->ulColorRes = pGdiInfo->ulDACRed +
pGdiInfo->ulDACGreen +
pGdiInfo->ulDACBlue;
pDevCaps->ulPhysicalWidth = pGdiInfo->szlPhysSize.cx;
pDevCaps->ulPhysicalHeight = pGdiInfo->szlPhysSize.cy;
pDevCaps->ulPhysicalOffsetX = pGdiInfo->ptlPhysOffset.x;
pDevCaps->ulPhysicalOffsetY = pGdiInfo->ptlPhysOffset.y;
pDevCaps->ulTextCaps = pGdiInfo->flTextCaps;
pDevCaps->ulTextCaps |= (TC_SO_ABLE|TC_UA_ABLE|TC_CP_STROKE|TC_OP_STROKE|TC_OP_CHARACTER);
if (pGdiInfo->ulTechnology != DT_PLOTTER)
pDevCaps->ulTextCaps |= TC_VA_ABLE;
pDevCaps->ulVRefresh = pGdiInfo->ulVRefresh;
pDevCaps->ulDesktopHorzRes = pGdiInfo->ulHorzRes;
pDevCaps->ulDesktopVertRes = pGdiInfo->ulVertRes;
pDevCaps->ulBltAlignment = pGdiInfo->ulBltAlignment;
pDevCaps->ulPanningHorzRes = pGdiInfo->ulPanningHorzRes;
pDevCaps->ulPanningVertRes = pGdiInfo->ulPanningVertRes;
pDevCaps->xPanningAlignment = pGdiInfo->xPanningAlignment;
pDevCaps->yPanningAlignment = pGdiInfo->yPanningAlignment;
pDevCaps->ulShadeBlend = pGdiInfo->flShadeBlend;
pDevCaps->ulColorMgmtCaps = PDEVOBJ_iGetColorManagementCaps(ppdev);
}
/** Exported functions ********************************************************/
/*
* @implemented
*/
BOOL
APIENTRY
EngQueryDeviceAttribute(
_In_ HDEV hdev,
_In_ ENG_DEVICE_ATTRIBUTE devAttr,
_In_reads_bytes_(cjInSize) PVOID pvIn,
_In_ ULONG cjInSize,
_Out_writes_bytes_(cjOutSize) PVOID pvOut,
_In_ ULONG cjOutSize)
{
PPDEVOBJ ppdev = (PPDEVOBJ)hdev;
if (devAttr != QDA_ACCELERATION_LEVEL)
return FALSE;
if (cjOutSize >= sizeof(DWORD))
{
/* Set all Accelerations Level Key to enabled Full 0 to 5 turned off. */
*(DWORD*)pvOut = ppdev->dwAccelerationLevel;
return TRUE;
}
return FALSE;
}
_Must_inspect_result_ _Ret_z_
LPWSTR
APIENTRY
EngGetDriverName(_In_ HDEV hdev)
{
PPDEVOBJ ppdev = (PPDEVOBJ)hdev;
ASSERT(ppdev);
ASSERT(ppdev->pldev);
ASSERT(ppdev->pldev->pGdiDriverInfo);
ASSERT(ppdev->pldev->pGdiDriverInfo->DriverName.Buffer);
return ppdev->pldev->pGdiDriverInfo->DriverName.Buffer;
}
INT
APIENTRY
NtGdiGetDeviceCaps(
HDC hdc,
INT Index)
{
PDC pdc;
DEVCAPS devcaps;
/* Lock the given DC */
pdc = DC_LockDc(hdc);
if (!pdc)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return 0;
}
/* Get the data */
PDEVOBJ_vGetDeviceCaps(pdc->ppdev, &devcaps);
/* Unlock the DC */
DC_UnlockDc(pdc);
/* Return capability */
switch (Index)
{
case DRIVERVERSION:
return devcaps.ulVersion;
case TECHNOLOGY:
return devcaps.ulTechnology;
case HORZSIZE:
return devcaps.ulHorzSize;
case VERTSIZE:
return devcaps.ulVertSize;
case HORZRES:
return devcaps.ulHorzRes;
case VERTRES:
return devcaps.ulVertRes;
case LOGPIXELSX:
return devcaps.ulLogPixelsX;
case LOGPIXELSY:
return devcaps.ulLogPixelsY;
case BITSPIXEL:
return devcaps.ulBitsPixel;
case PLANES:
return devcaps.ulPlanes;
case NUMBRUSHES:
return -1;
case NUMPENS:
return devcaps.ulNumPens;
case NUMFONTS:
return devcaps.ulNumFonts;
case NUMCOLORS:
return devcaps.ulNumColors;
case ASPECTX:
return devcaps.ulAspectX;
case ASPECTY:
return devcaps.ulAspectY;
case ASPECTXY:
return devcaps.ulAspectXY;
case CLIPCAPS:
return CP_RECTANGLE;
case SIZEPALETTE:
return devcaps.ulSizePalette;
case NUMRESERVED:
return 20;
case COLORRES:
return devcaps.ulColorRes;
case DESKTOPVERTRES:
return devcaps.ulVertRes;
case DESKTOPHORZRES:
return devcaps.ulHorzRes;
case BLTALIGNMENT:
return devcaps.ulBltAlignment;
case SHADEBLENDCAPS:
return devcaps.ulShadeBlend;
case COLORMGMTCAPS:
return devcaps.ulColorMgmtCaps;
case PHYSICALWIDTH:
return devcaps.ulPhysicalWidth;
case PHYSICALHEIGHT:
return devcaps.ulPhysicalHeight;
case PHYSICALOFFSETX:
return devcaps.ulPhysicalOffsetX;
case PHYSICALOFFSETY:
return devcaps.ulPhysicalOffsetY;
case VREFRESH:
return devcaps.ulVRefresh;
case RASTERCAPS:
return devcaps.ulRasterCaps;
case CURVECAPS:
return (CC_CIRCLES | CC_PIE | CC_CHORD | CC_ELLIPSES | CC_WIDE |
CC_STYLED | CC_WIDESTYLED | CC_INTERIORS | CC_ROUNDRECT);
case LINECAPS:
return (LC_POLYLINE | LC_MARKER | LC_POLYMARKER | LC_WIDE |
LC_STYLED | LC_WIDESTYLED | LC_INTERIORS);
case POLYGONALCAPS:
return (PC_POLYGON | PC_RECTANGLE | PC_WINDPOLYGON | PC_SCANLINE |
PC_WIDE | PC_STYLED | PC_WIDESTYLED | PC_INTERIORS);
case TEXTCAPS:
return devcaps.ulTextCaps;
case CAPS1:
case PDEVICESIZE:
case SCALINGFACTORX:
case SCALINGFACTORY:
default:
return 0;
}
return 0;
}
_Success_(return!=FALSE)
BOOL
APIENTRY
NtGdiGetDeviceCapsAll(
IN HDC hDC,
OUT PDEVCAPS pDevCaps)
{
PDC pdc;
DEVCAPS devcaps;
BOOL bResult = TRUE;
/* Lock the given DC */
pdc = DC_LockDc(hDC);
if (!pdc)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
/* Get the data */
PDEVOBJ_vGetDeviceCaps(pdc->ppdev, &devcaps);
/* Unlock the DC */
DC_UnlockDc(pdc);
/* Copy data to caller */
_SEH2_TRY
{
ProbeForWrite(pDevCaps, sizeof(DEVCAPS), 1);
RtlCopyMemory(pDevCaps, &devcaps, sizeof(DEVCAPS));
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
SetLastNtError(_SEH2_GetExceptionCode());
bResult = FALSE;
}
_SEH2_END;
return bResult;
}
DHPDEV
APIENTRY
NtGdiGetDhpdev(
IN HDEV hdev)
{
PPDEVOBJ ppdev;
DHPDEV dhpdev = NULL;
/* Check parameter */
if (!hdev || (PCHAR)hdev < (PCHAR)MmSystemRangeStart)
return NULL;
/* Lock PDEV list */
EngAcquireSemaphoreShared(ghsemPDEV);
/* Walk through the list of PDEVs */
for (ppdev = gppdevList; ppdev; ppdev = ppdev->ppdevNext)
{
/* Compare with the given HDEV */
if (ppdev == (PPDEVOBJ)hdev)
{
/* Found the PDEV! Get it's dhpdev and break */
dhpdev = ppdev->dhpdev;
break;
}
}
/* Unlock PDEV list */
EngReleaseSemaphore(ghsemPDEV);
return dhpdev;
}
PSIZEL
FASTCALL
PDEVOBJ_sizl(PPDEVOBJ ppdev, PSIZEL psizl)
{
if (ppdev->flFlags & PDEV_META_DEVICE)
{
*psizl = ppdev->szlMetaRes;
}
else
{
psizl->cx = ppdev->gdiinfo.ulHorzRes;
psizl->cy = ppdev->gdiinfo.ulVertRes;
}
return psizl;
}