https://git.reactos.org/?p=reactos.git;a=commitdiff;h=4e0f2fc01c8fd44432ccd…
commit 4e0f2fc01c8fd44432ccd3553ad3f9ec6e4e7bef
Author: Hervé Poussineau <hpoussin(a)reactos.org>
AuthorDate: Sun May 8 22:23:32 2022 +0200
Commit: hpoussin <32227662+hpoussin(a)users.noreply.github.com>
CommitDate: Sat May 14 21:29:12 2022 +0200
[WIN32SS] Implement panning driver
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.
---
win32ss/CMakeLists.txt | 1 +
win32ss/gdi/eng/pandisp.c | 501 ++++++++++++++++++++++++++++++++++++++++++++++
win32ss/gdi/eng/pdevobj.c | 22 +-
3 files changed, 523 insertions(+), 1 deletion(-)
diff --git a/win32ss/CMakeLists.txt b/win32ss/CMakeLists.txt
index 0a7dd1492de..16026d6275e 100644
--- a/win32ss/CMakeLists.txt
+++ b/win32ss/CMakeLists.txt
@@ -78,6 +78,7 @@ list(APPEND SOURCE
gdi/eng/engmisc.c
gdi/eng/mouse.c
gdi/eng/multidisp.c
+ gdi/eng/pandisp.c
gdi/eng/paint.c
gdi/eng/pathobj.c
gdi/eng/pdevobj.c
diff --git a/win32ss/gdi/eng/pandisp.c b/win32ss/gdi/eng/pandisp.c
new file mode 100644
index 00000000000..9f1a4f8c916
--- /dev/null
+++ b/win32ss/gdi/eng/pandisp.c
@@ -0,0 +1,501 @@
+/*
+ * 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(a)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);
diff --git a/win32ss/gdi/eng/pdevobj.c b/win32ss/gdi/eng/pdevobj.c
index c16fd43bf4b..293dae4b967 100644
--- a/win32ss/gdi/eng/pdevobj.c
+++ b/win32ss/gdi/eng/pdevobj.c
@@ -21,6 +21,9 @@ MultiEnableDriver(
_In_ ULONG cj,
_Inout_bytecount_(cj) PDRVENABLEDATA pded);
+extern DRVFN gPanDispDrvFn[];
+extern ULONG gPanDispDrvCount;
+
CODE_SEG("INIT")
NTSTATUS
NTAPI
@@ -503,7 +506,24 @@ PDEVOBJ_Create(
ppdev->dwAccelerationLevel = dwAccelerationLevel;
/* Copy the function table */
- ppdev->pfn = ppdev->pldev->pfn;
+ if (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))