mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 12:26:32 +00:00
4e0f2fc01c
This can be configured in registry with DefaultSettings.XPanning and DefaultSettings.YPanning, which describe the real screen resolution. DefaultSettings.XResolution and DefaultSettings.YResolution describe the resolution of the virtual screen.
502 lines
14 KiB
C
502 lines
14 KiB
C
/*
|
|
* PROJECT: ReactOS Win32k subsystem
|
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
|
* PURPOSE: Pan-Display driver
|
|
* COPYRIGHT: Copyright 2022 Hervé Poussineau <hpoussin@reactos.org>
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include <win32k.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#define TAG_PAN 'DnaP'
|
|
#define GETPFN(name) ((PFN_Drv##name)(pandev->apfn[INDEX_Drv##name]))
|
|
|
|
static DHPDEV gPan; /* FIXME: remove once PanSynchronize() is called periodically */
|
|
|
|
typedef struct _PANDEV
|
|
{
|
|
ULONG iBitmapFormat;
|
|
HDEV hdev;
|
|
SIZEL szlDesktop; /* Size of the whole desktop */
|
|
RECTL rclViewport; /* Viewport area */
|
|
HSURF hsurf; /* Global surface */
|
|
HSURF hsurfShadow; /* Our shadow surface, used for drawing */
|
|
SURFOBJ *psoShadow;
|
|
|
|
/* Underlying PDEVOBJ */
|
|
ULONG flUnderlyingGraphicsCaps;
|
|
SIZEL szlScreen;
|
|
DHPDEV dhpdevScreen;
|
|
BOOL enabledScreen; /* underlying surface enabled */
|
|
SURFOBJ *surfObjScreen;
|
|
PFN apfn[INDEX_LAST]; /* Functions of underlying PDEVOBJ */
|
|
} PANDEV, *PPANDEV;
|
|
|
|
BOOL
|
|
APIENTRY
|
|
PanSynchronize(
|
|
_In_ DHPDEV dhpdev,
|
|
_In_ PRECTL prcl)
|
|
{
|
|
PPANDEV pandev = (PPANDEV)dhpdev;
|
|
RECTL rclDest;
|
|
POINTL ptlSrc;
|
|
|
|
/* FIXME: for now, copy the whole shadow buffer. We might try to optimize that */
|
|
|
|
ptlSrc.x = pandev->rclViewport.left;
|
|
ptlSrc.y = pandev->rclViewport.top;
|
|
|
|
rclDest.top = rclDest.left = 0;
|
|
rclDest.bottom = pandev->szlScreen.cy;
|
|
rclDest.right = pandev->szlScreen.cx;
|
|
|
|
return EngCopyBits(pandev->surfObjScreen, pandev->psoShadow, NULL, NULL, &rclDest, &ptlSrc);
|
|
}
|
|
|
|
DHPDEV
|
|
APIENTRY
|
|
PanEnablePDEV(
|
|
_In_ PDEVMODEW pdm,
|
|
_In_ LPWSTR pwszLogAddress,
|
|
_In_ ULONG cPat,
|
|
_In_reads_opt_(cPat) HSURF *phsurfPatterns,
|
|
_In_ ULONG cjCaps,
|
|
_Out_writes_bytes_(cjCaps) PULONG pdevcaps,
|
|
_In_ ULONG cjDevInfo,
|
|
_Out_writes_bytes_(cjDevInfo) PDEVINFO pdi,
|
|
_In_ HDEV hdev,
|
|
_In_ LPWSTR pwszDeviceName,
|
|
_In_ HANDLE hDriver)
|
|
{
|
|
PPANDEV pandev;
|
|
PPDEVOBJ ppdev = (PPDEVOBJ)hdev;
|
|
DEVMODEW underlyingDevmode;
|
|
PGDIINFO pGdiInfo = (PGDIINFO)pdevcaps;
|
|
|
|
DPRINT("PanEnablePDEV(ppdev %p %dx%dx%d -> %dx%dx%d %d Hz)\n",
|
|
ppdev,
|
|
pdm->dmPelsWidth, pdm->dmPelsHeight, pdm->dmBitsPerPel,
|
|
pdm->dmPanningWidth, pdm->dmPanningHeight, pdm->dmBitsPerPel, pdm->dmDisplayFrequency);
|
|
|
|
ASSERT(pdm->dmPanningWidth <= pdm->dmPelsWidth && pdm->dmPanningHeight <= pdm->dmPelsHeight);
|
|
|
|
/* Allocate PANDEV */
|
|
pandev = EngAllocMem(FL_ZERO_MEMORY, sizeof(PANDEV), TAG_PAN);
|
|
if (!pandev)
|
|
{
|
|
DPRINT1("Failed to allocate memory\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Copy device pointers to our structure (do not copy ppdev->apfn,
|
|
* as it contains our local pointers!) */
|
|
RtlCopyMemory(pandev->apfn, ppdev->pldev->apfn, sizeof(pandev->apfn));
|
|
|
|
/* Enable underlying PDEV */
|
|
underlyingDevmode = *pdm;
|
|
underlyingDevmode.dmPelsWidth = pdm->dmPanningWidth;
|
|
underlyingDevmode.dmPelsHeight = pdm->dmPanningHeight;
|
|
pandev->dhpdevScreen = GETPFN(EnablePDEV)(&underlyingDevmode,
|
|
pwszLogAddress,
|
|
cPat,
|
|
phsurfPatterns,
|
|
cjCaps,
|
|
pdevcaps,
|
|
cjDevInfo,
|
|
pdi,
|
|
hdev,
|
|
pwszDeviceName,
|
|
hDriver);
|
|
if (!pandev->dhpdevScreen)
|
|
{
|
|
DPRINT1("Failed to enable underlying PDEV\n");
|
|
EngFreeMem(pandev);
|
|
return NULL;
|
|
}
|
|
|
|
pandev->iBitmapFormat = pdi->iDitherFormat;
|
|
pandev->szlDesktop.cx = pdm->dmPelsWidth;
|
|
pandev->szlDesktop.cy = pdm->dmPelsHeight;
|
|
pandev->szlScreen.cx = pdm->dmPanningWidth;
|
|
pandev->szlScreen.cy = pdm->dmPanningHeight;
|
|
pandev->flUnderlyingGraphicsCaps = pdi->flGraphicsCaps;
|
|
|
|
/* Upgrade some capabilities */
|
|
pGdiInfo->ulHorzRes = pdm->dmPelsWidth;
|
|
pGdiInfo->ulVertRes = pdm->dmPelsHeight;
|
|
pdi->flGraphicsCaps |= GCAPS_PANNING;
|
|
pdi->flGraphicsCaps2 = GCAPS2_SYNCFLUSH | GCAPS2_SYNCTIMER;
|
|
|
|
gPan = (DHPDEV)pandev;
|
|
return (DHPDEV)pandev;
|
|
}
|
|
|
|
VOID
|
|
APIENTRY
|
|
PanCompletePDEV(
|
|
_In_ DHPDEV dhpdev,
|
|
_In_ HDEV hdev)
|
|
{
|
|
PPANDEV pandev = (PPANDEV)dhpdev;
|
|
|
|
DPRINT("PanCompletePDEV(%p %p)\n", dhpdev, hdev);
|
|
|
|
pandev->hdev = hdev;
|
|
GETPFN(CompletePDEV)(pandev->dhpdevScreen, hdev);
|
|
}
|
|
|
|
VOID
|
|
APIENTRY
|
|
PanDisablePDEV(
|
|
_In_ DHPDEV dhpdev)
|
|
{
|
|
PPANDEV pandev = (PPANDEV)dhpdev;
|
|
|
|
DPRINT("PanDisablePDEV(%p)\n", dhpdev);
|
|
|
|
GETPFN(DisablePDEV)(pandev->dhpdevScreen);
|
|
EngFreeMem(pandev);
|
|
}
|
|
|
|
VOID
|
|
APIENTRY
|
|
PanDisableSurface(
|
|
_In_ DHPDEV dhpdev)
|
|
{
|
|
PPANDEV pandev = (PPANDEV)dhpdev;
|
|
|
|
DPRINT("PanDisableSurface(%p)\n", dhpdev);
|
|
|
|
/* Note that we must handle a not fully initialized pandev, as this
|
|
* function is also called from PanEnableSurface in case of error. */
|
|
|
|
if (pandev->psoShadow)
|
|
{
|
|
EngUnlockSurface(pandev->psoShadow);
|
|
pandev->psoShadow = NULL;
|
|
}
|
|
if (pandev->hsurfShadow)
|
|
{
|
|
EngDeleteSurface(pandev->hsurfShadow);
|
|
pandev->hsurfShadow = NULL;
|
|
}
|
|
if (pandev->hsurf)
|
|
{
|
|
EngDeleteSurface(pandev->hsurf);
|
|
pandev->hsurf = NULL;
|
|
}
|
|
if (pandev->surfObjScreen)
|
|
{
|
|
EngUnlockSurface(pandev->surfObjScreen);
|
|
pandev->surfObjScreen = NULL;
|
|
}
|
|
if (pandev->enabledScreen)
|
|
{
|
|
GETPFN(DisableSurface)(pandev->dhpdevScreen);
|
|
pandev->enabledScreen = FALSE;
|
|
}
|
|
}
|
|
|
|
HSURF
|
|
APIENTRY
|
|
PanEnableSurface(
|
|
_In_ DHPDEV dhpdev)
|
|
{
|
|
PPANDEV pandev = (PPANDEV)dhpdev;
|
|
HSURF surfScreen;
|
|
|
|
/* Move viewport to center of screen */
|
|
pandev->rclViewport.left = (pandev->szlDesktop.cx - pandev->szlScreen.cx) / 2;
|
|
pandev->rclViewport.top = (pandev->szlDesktop.cy - pandev->szlScreen.cy) / 2;
|
|
pandev->rclViewport.right = pandev->rclViewport.left + pandev->szlScreen.cx;
|
|
pandev->rclViewport.bottom = pandev->rclViewport.top + pandev->szlScreen.cy;
|
|
|
|
/* Enable underlying device */
|
|
surfScreen = GETPFN(EnableSurface)(pandev->dhpdevScreen);
|
|
if (!surfScreen)
|
|
{
|
|
DPRINT1("Failed to enable underlying surface\n");
|
|
goto failure;
|
|
}
|
|
pandev->enabledScreen = TRUE;
|
|
|
|
pandev->surfObjScreen = EngLockSurface(surfScreen);
|
|
if (!pandev->surfObjScreen)
|
|
{
|
|
DPRINT1("Failed to lock underlying surface\n");
|
|
goto failure;
|
|
}
|
|
|
|
/* Create device surface */
|
|
pandev->hsurf = EngCreateDeviceSurface(NULL, pandev->szlDesktop, pandev->iBitmapFormat);
|
|
if (!pandev->hsurf)
|
|
{
|
|
DPRINT1("EngCreateDeviceSurface() failed\n");
|
|
goto failure;
|
|
}
|
|
|
|
if (!EngAssociateSurface(pandev->hsurf, pandev->hdev, HOOK_ALPHABLEND |
|
|
HOOK_BITBLT |
|
|
HOOK_COPYBITS |
|
|
HOOK_GRADIENTFILL |
|
|
HOOK_STROKEPATH |
|
|
HOOK_SYNCHRONIZE |
|
|
HOOK_TEXTOUT))
|
|
{
|
|
DPRINT1("EngAssociateSurface() failed\n");
|
|
goto failure;
|
|
}
|
|
|
|
/* Create shadow surface */
|
|
pandev->hsurfShadow = (HSURF)EngCreateBitmap(pandev->szlDesktop, pandev->szlDesktop.cx, pandev->iBitmapFormat, BMF_TOPDOWN, NULL);
|
|
if (!pandev->hsurfShadow)
|
|
{
|
|
DPRINT1("EngCreateBitmap() failed\n");
|
|
goto failure;
|
|
}
|
|
|
|
pandev->psoShadow = EngLockSurface(pandev->hsurfShadow);
|
|
if (!pandev->psoShadow)
|
|
{
|
|
DPRINT1("EngLockSurface() failed\n");
|
|
goto failure;
|
|
}
|
|
|
|
return pandev->hsurf;
|
|
|
|
failure:
|
|
PanDisableSurface(dhpdev);
|
|
return NULL;
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
PanBitBlt(
|
|
_Inout_ SURFOBJ *psoTrg,
|
|
_In_opt_ SURFOBJ *psoSrc,
|
|
_In_opt_ SURFOBJ *psoMask,
|
|
_In_ CLIPOBJ *pco,
|
|
_In_opt_ XLATEOBJ *pxlo,
|
|
_In_ PRECTL prclTrg,
|
|
_In_opt_ PPOINTL pptlSrc,
|
|
_In_opt_ PPOINTL pptlMask,
|
|
_In_opt_ BRUSHOBJ *pbo,
|
|
_In_opt_ PPOINTL pptlBrush,
|
|
_In_ ROP4 rop4)
|
|
{
|
|
PPANDEV pandev;
|
|
BOOL res;
|
|
|
|
if (psoTrg->iType == STYPE_DEVICE)
|
|
{
|
|
pandev = (PPANDEV)psoTrg->dhpdev;
|
|
psoTrg = pandev->psoShadow;
|
|
}
|
|
if (psoSrc && psoSrc->iType == STYPE_DEVICE)
|
|
{
|
|
pandev = (PPANDEV)psoSrc->dhpdev;
|
|
psoSrc = pandev->psoShadow;
|
|
}
|
|
|
|
res = EngBitBlt(psoTrg, psoSrc, psoMask, pco, pxlo, prclTrg, pptlSrc, pptlMask, pbo, pptlBrush, rop4);
|
|
|
|
/* FIXME: PanSynchronize must be called periodically, as we have the GCAPS2_SYNCTIMER flag.
|
|
* That's not the case, so call PanSynchronize() manually */
|
|
if (res)
|
|
PanSynchronize(gPan, NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
PanCopyBits(
|
|
_Inout_ SURFOBJ *psoDest,
|
|
_In_opt_ SURFOBJ *psoSrc,
|
|
_In_ CLIPOBJ *pco,
|
|
_In_opt_ XLATEOBJ *pxlo,
|
|
_In_ PRECTL prclDest,
|
|
_In_opt_ PPOINTL pptlSrc)
|
|
{
|
|
/* Easy. Just call the more general function PanBitBlt */
|
|
return PanBitBlt(psoDest, psoSrc, NULL, pco, pxlo, prclDest, pptlSrc, NULL, NULL, NULL, ROP4_SRCCOPY);
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
PanStrokePath(
|
|
_Inout_ SURFOBJ *pso,
|
|
_In_ PATHOBJ *ppo,
|
|
_In_ CLIPOBJ *pco,
|
|
_In_opt_ XFORMOBJ *pxo,
|
|
_In_ BRUSHOBJ *pbo,
|
|
_In_ PPOINTL pptlBrushOrg,
|
|
_In_ PLINEATTRS plineattrs,
|
|
_In_ MIX mix)
|
|
{
|
|
UNIMPLEMENTED;
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
PanTextOut(
|
|
_Inout_ SURFOBJ *pso,
|
|
_In_ STROBJ *pstro,
|
|
_In_ FONTOBJ *pfo,
|
|
_In_ CLIPOBJ *pco,
|
|
_In_opt_ PRECTL prclExtra,
|
|
_In_opt_ PRECTL prclOpaque,
|
|
_In_ BRUSHOBJ *pboFore,
|
|
_In_ BRUSHOBJ *pboOpaque,
|
|
_In_ PPOINTL pptlOrg,
|
|
_In_ MIX mix)
|
|
{
|
|
UNIMPLEMENTED;
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
APIENTRY
|
|
PanMovePointer(
|
|
_Inout_ SURFOBJ *pso,
|
|
_In_ LONG x,
|
|
_In_ LONG y,
|
|
_In_ PRECTL prcl)
|
|
{
|
|
PPANDEV pandev = (PPANDEV)pso->dhpdev;
|
|
PFN_DrvMovePointer pfnMovePointer;
|
|
BOOL bChanged = FALSE;
|
|
|
|
DPRINT("PanMovePointer(%d, %d)\n", x, y);
|
|
|
|
/* FIXME: MouseSafetyOnDrawStart/MouseSafetyOnDrawEnd like to call us to hide/show the cursor,
|
|
* without real reason (see 5b0f6dcceeae3cf21f2535e2c523c0bf2749ea6b). Ignore those calls */
|
|
if (x < 0 || y >= 0) return;
|
|
|
|
/* We don't set DrvSetPointerShape, so we must not be called
|
|
* with x < 0 (hide cursor) or y >= 0 (we have GCAPS_PANNING) */
|
|
ASSERT(x >= 0 && y < 0);
|
|
|
|
/* Call underlying MovePointer function */
|
|
if (pandev->flUnderlyingGraphicsCaps & GCAPS_PANNING)
|
|
{
|
|
pfnMovePointer = GETPFN(MovePointer);
|
|
if (pfnMovePointer)
|
|
pfnMovePointer(pandev->surfObjScreen, x, y, prcl);
|
|
}
|
|
|
|
y += pso->sizlBitmap.cy;
|
|
|
|
if (x < pandev->rclViewport.left)
|
|
{
|
|
pandev->rclViewport.left = x;
|
|
pandev->rclViewport.right = x + pandev->szlScreen.cx;
|
|
bChanged = TRUE;
|
|
}
|
|
else if (x >= pandev->rclViewport.right)
|
|
{
|
|
pandev->rclViewport.right = x + 1;
|
|
pandev->rclViewport.left = x - pandev->szlScreen.cx;
|
|
bChanged = TRUE;
|
|
}
|
|
if (y < pandev->rclViewport.top)
|
|
{
|
|
pandev->rclViewport.top = y;
|
|
pandev->rclViewport.bottom = y + pandev->szlScreen.cy;
|
|
bChanged = TRUE;
|
|
}
|
|
else if (y >= pandev->rclViewport.bottom)
|
|
{
|
|
pandev->rclViewport.bottom = y + 1;
|
|
pandev->rclViewport.top = y - pandev->szlScreen.cy;
|
|
bChanged = TRUE;
|
|
}
|
|
|
|
if (bChanged)
|
|
PanSynchronize(pso->dhpdev, NULL);
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
PanAlphaBlend(
|
|
_Inout_ SURFOBJ *psoDest,
|
|
_In_ SURFOBJ *psoSrc,
|
|
_In_ CLIPOBJ *pco,
|
|
_In_opt_ XLATEOBJ *pxlo,
|
|
_In_ PRECTL prclDest,
|
|
_In_ PRECTL prclSrc,
|
|
_In_ BLENDOBJ *pBlendObj)
|
|
{
|
|
PPANDEV pandev = (PPANDEV)psoDest->dhpdev;
|
|
BOOL res;
|
|
|
|
res = EngAlphaBlend(pandev->psoShadow, psoSrc, pco, pxlo, prclDest, prclSrc, pBlendObj);
|
|
|
|
/* FIXME: PanSynchronize must be called periodically, as we have the GCAPS2_SYNCTIMER flag.
|
|
* That's not the case, so call PanSynchronize() manually */
|
|
if (res)
|
|
PanSynchronize(gPan, NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
BOOL
|
|
APIENTRY
|
|
PanGradientFill(
|
|
_Inout_ SURFOBJ *pso,
|
|
_In_ CLIPOBJ *pco,
|
|
_In_opt_ XLATEOBJ *pxlo,
|
|
_In_ PTRIVERTEX pVertex,
|
|
_In_ ULONG nVertex,
|
|
_In_ PVOID pMesh,
|
|
_In_ ULONG nMesh,
|
|
_In_ PRECTL prclExtents,
|
|
_In_ PPOINTL pptlDitherOrg,
|
|
_In_ ULONG ulMode)
|
|
{
|
|
PPANDEV pandev = (PPANDEV)pso->dhpdev;
|
|
BOOL res;
|
|
|
|
res = EngGradientFill(pandev->psoShadow, pco, pxlo, pVertex, nVertex, pMesh, nMesh, prclExtents, pptlDitherOrg, ulMode);
|
|
|
|
/* FIXME: PanSynchronize must be called periodically, as we have the GCAPS2_SYNCTIMER flag.
|
|
* That's not the case, so call PanSynchronize() manually */
|
|
if (res)
|
|
PanSynchronize(gPan, NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
DRVFN gPanDispDrvFn[] =
|
|
{
|
|
/* required */
|
|
{ INDEX_DrvEnablePDEV, (PFN) PanEnablePDEV },
|
|
{ INDEX_DrvCompletePDEV, (PFN) PanCompletePDEV },
|
|
{ INDEX_DrvDisablePDEV, (PFN) PanDisablePDEV },
|
|
{ INDEX_DrvEnableSurface, (PFN) PanEnableSurface },
|
|
{ INDEX_DrvDisableSurface, (PFN) PanDisableSurface },
|
|
|
|
/* required for device-managed surfaces */
|
|
{ INDEX_DrvCopyBits, (PFN) PanCopyBits },
|
|
{ INDEX_DrvStrokePath, (PFN) PanStrokePath },
|
|
{ INDEX_DrvTextOut, (PFN) PanTextOut },
|
|
|
|
/* optional, but required for panning functionality */
|
|
{ INDEX_DrvSynchronize, (PFN) PanSynchronize },
|
|
{ INDEX_DrvMovePointer, (PFN) PanMovePointer },
|
|
|
|
/* optional */
|
|
{ INDEX_DrvBitBlt, (PFN) PanBitBlt },
|
|
{ INDEX_DrvAlphaBlend, (PFN) PanAlphaBlend },
|
|
{ INDEX_DrvGradientFill, (PFN) PanGradientFill },
|
|
};
|
|
|
|
ULONG gPanDispDrvCount = RTL_NUMBER_OF(gPanDispDrvFn);
|