https://git.reactos.org/?p=reactos.git;a=commitdiff;h=4e0f2fc01c8fd44432ccd3553ad3f9ec6e4e7bef

commit 4e0f2fc01c8fd44432ccd3553ad3f9ec6e4e7bef
Author:     Hervé Poussineau <[email protected]>
AuthorDate: Sun May 8 22:23:32 2022 +0200
Commit:     hpoussin <[email protected]>
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 <[email protected]>
+ */
+
+/* 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))

Reply via email to