xref: /reactos/win32ss/gdi/eng/pandisp.c (revision 4e0f2fc0)
1 /*
2  * PROJECT:     ReactOS Win32k subsystem
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Pan-Display driver
5  * COPYRIGHT:   Copyright 2022 Hervé Poussineau <hpoussin@reactos.org>
6  */
7 
8 /* INCLUDES *******************************************************************/
9 
10 #include <win32k.h>
11 #define NDEBUG
12 #include <debug.h>
13 
14 #define TAG_PAN 'DnaP'
15 #define GETPFN(name) ((PFN_Drv##name)(pandev->apfn[INDEX_Drv##name]))
16 
17 static DHPDEV gPan; /* FIXME: remove once PanSynchronize() is called periodically */
18 
19 typedef struct _PANDEV
20 {
21     ULONG iBitmapFormat;
22     HDEV hdev;
23     SIZEL szlDesktop; /* Size of the whole desktop */
24     RECTL rclViewport; /* Viewport area */
25     HSURF hsurf; /* Global surface */
26     HSURF hsurfShadow; /* Our shadow surface, used for drawing */
27     SURFOBJ *psoShadow;
28 
29     /* Underlying PDEVOBJ */
30     ULONG flUnderlyingGraphicsCaps;
31     SIZEL szlScreen;
32     DHPDEV dhpdevScreen;
33     BOOL enabledScreen; /* underlying surface enabled */
34     SURFOBJ *surfObjScreen;
35     PFN apfn[INDEX_LAST]; /* Functions of underlying PDEVOBJ */
36 } PANDEV, *PPANDEV;
37 
38 BOOL
39 APIENTRY
PanSynchronize(_In_ DHPDEV dhpdev,_In_ PRECTL prcl)40 PanSynchronize(
41     _In_ DHPDEV dhpdev,
42     _In_ PRECTL prcl)
43 {
44     PPANDEV pandev = (PPANDEV)dhpdev;
45     RECTL rclDest;
46     POINTL ptlSrc;
47 
48     /* FIXME: for now, copy the whole shadow buffer. We might try to optimize that */
49 
50     ptlSrc.x = pandev->rclViewport.left;
51     ptlSrc.y = pandev->rclViewport.top;
52 
53     rclDest.top = rclDest.left = 0;
54     rclDest.bottom = pandev->szlScreen.cy;
55     rclDest.right = pandev->szlScreen.cx;
56 
57     return EngCopyBits(pandev->surfObjScreen, pandev->psoShadow, NULL, NULL, &rclDest, &ptlSrc);
58 }
59 
60 DHPDEV
61 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)62 PanEnablePDEV(
63     _In_ PDEVMODEW pdm,
64     _In_ LPWSTR pwszLogAddress,
65     _In_ ULONG cPat,
66     _In_reads_opt_(cPat) HSURF *phsurfPatterns,
67     _In_ ULONG cjCaps,
68     _Out_writes_bytes_(cjCaps) PULONG pdevcaps,
69     _In_ ULONG cjDevInfo,
70     _Out_writes_bytes_(cjDevInfo) PDEVINFO pdi,
71     _In_ HDEV hdev,
72     _In_ LPWSTR pwszDeviceName,
73     _In_ HANDLE hDriver)
74 {
75     PPANDEV pandev;
76     PPDEVOBJ ppdev = (PPDEVOBJ)hdev;
77     DEVMODEW underlyingDevmode;
78     PGDIINFO pGdiInfo = (PGDIINFO)pdevcaps;
79 
80     DPRINT("PanEnablePDEV(ppdev %p %dx%dx%d -> %dx%dx%d %d Hz)\n",
81            ppdev,
82            pdm->dmPelsWidth, pdm->dmPelsHeight, pdm->dmBitsPerPel,
83            pdm->dmPanningWidth, pdm->dmPanningHeight, pdm->dmBitsPerPel, pdm->dmDisplayFrequency);
84 
85     ASSERT(pdm->dmPanningWidth <= pdm->dmPelsWidth && pdm->dmPanningHeight <= pdm->dmPelsHeight);
86 
87     /* Allocate PANDEV */
88     pandev = EngAllocMem(FL_ZERO_MEMORY, sizeof(PANDEV), TAG_PAN);
89     if (!pandev)
90     {
91         DPRINT1("Failed to allocate memory\n");
92         return NULL;
93     }
94 
95     /* Copy device pointers to our structure (do not copy ppdev->apfn,
96      * as it contains our local pointers!) */
97     RtlCopyMemory(pandev->apfn, ppdev->pldev->apfn, sizeof(pandev->apfn));
98 
99     /* Enable underlying PDEV */
100     underlyingDevmode = *pdm;
101     underlyingDevmode.dmPelsWidth = pdm->dmPanningWidth;
102     underlyingDevmode.dmPelsHeight = pdm->dmPanningHeight;
103     pandev->dhpdevScreen = GETPFN(EnablePDEV)(&underlyingDevmode,
104                                               pwszLogAddress,
105                                               cPat,
106                                               phsurfPatterns,
107                                               cjCaps,
108                                               pdevcaps,
109                                               cjDevInfo,
110                                               pdi,
111                                               hdev,
112                                               pwszDeviceName,
113                                               hDriver);
114     if (!pandev->dhpdevScreen)
115     {
116         DPRINT1("Failed to enable underlying PDEV\n");
117         EngFreeMem(pandev);
118         return NULL;
119     }
120 
121     pandev->iBitmapFormat = pdi->iDitherFormat;
122     pandev->szlDesktop.cx = pdm->dmPelsWidth;
123     pandev->szlDesktop.cy = pdm->dmPelsHeight;
124     pandev->szlScreen.cx = pdm->dmPanningWidth;
125     pandev->szlScreen.cy = pdm->dmPanningHeight;
126     pandev->flUnderlyingGraphicsCaps = pdi->flGraphicsCaps;
127 
128     /* Upgrade some capabilities */
129     pGdiInfo->ulHorzRes = pdm->dmPelsWidth;
130     pGdiInfo->ulVertRes = pdm->dmPelsHeight;
131     pdi->flGraphicsCaps |= GCAPS_PANNING;
132     pdi->flGraphicsCaps2 = GCAPS2_SYNCFLUSH | GCAPS2_SYNCTIMER;
133 
134     gPan = (DHPDEV)pandev;
135     return (DHPDEV)pandev;
136 }
137 
138 VOID
139 APIENTRY
PanCompletePDEV(_In_ DHPDEV dhpdev,_In_ HDEV hdev)140 PanCompletePDEV(
141     _In_ DHPDEV dhpdev,
142     _In_ HDEV hdev)
143 {
144     PPANDEV pandev = (PPANDEV)dhpdev;
145 
146     DPRINT("PanCompletePDEV(%p %p)\n", dhpdev, hdev);
147 
148     pandev->hdev = hdev;
149     GETPFN(CompletePDEV)(pandev->dhpdevScreen, hdev);
150 }
151 
152 VOID
153 APIENTRY
PanDisablePDEV(_In_ DHPDEV dhpdev)154 PanDisablePDEV(
155     _In_ DHPDEV dhpdev)
156 {
157     PPANDEV pandev = (PPANDEV)dhpdev;
158 
159     DPRINT("PanDisablePDEV(%p)\n", dhpdev);
160 
161     GETPFN(DisablePDEV)(pandev->dhpdevScreen);
162     EngFreeMem(pandev);
163 }
164 
165 VOID
166 APIENTRY
PanDisableSurface(_In_ DHPDEV dhpdev)167 PanDisableSurface(
168     _In_ DHPDEV dhpdev)
169 {
170     PPANDEV pandev = (PPANDEV)dhpdev;
171 
172     DPRINT("PanDisableSurface(%p)\n", dhpdev);
173 
174     /* Note that we must handle a not fully initialized pandev, as this
175      * function is also called from PanEnableSurface in case of error. */
176 
177     if (pandev->psoShadow)
178     {
179         EngUnlockSurface(pandev->psoShadow);
180         pandev->psoShadow = NULL;
181     }
182     if (pandev->hsurfShadow)
183     {
184         EngDeleteSurface(pandev->hsurfShadow);
185         pandev->hsurfShadow = NULL;
186     }
187     if (pandev->hsurf)
188     {
189         EngDeleteSurface(pandev->hsurf);
190         pandev->hsurf = NULL;
191     }
192     if (pandev->surfObjScreen)
193     {
194         EngUnlockSurface(pandev->surfObjScreen);
195         pandev->surfObjScreen = NULL;
196     }
197     if (pandev->enabledScreen)
198     {
199         GETPFN(DisableSurface)(pandev->dhpdevScreen);
200         pandev->enabledScreen = FALSE;
201     }
202 }
203 
204 HSURF
205 APIENTRY
PanEnableSurface(_In_ DHPDEV dhpdev)206 PanEnableSurface(
207     _In_ DHPDEV dhpdev)
208 {
209     PPANDEV pandev = (PPANDEV)dhpdev;
210     HSURF surfScreen;
211 
212     /* Move viewport to center of screen */
213     pandev->rclViewport.left = (pandev->szlDesktop.cx - pandev->szlScreen.cx) / 2;
214     pandev->rclViewport.top = (pandev->szlDesktop.cy - pandev->szlScreen.cy) / 2;
215     pandev->rclViewport.right = pandev->rclViewport.left + pandev->szlScreen.cx;
216     pandev->rclViewport.bottom  = pandev->rclViewport.top + pandev->szlScreen.cy;
217 
218     /* Enable underlying device */
219     surfScreen = GETPFN(EnableSurface)(pandev->dhpdevScreen);
220     if (!surfScreen)
221     {
222         DPRINT1("Failed to enable underlying surface\n");
223         goto failure;
224     }
225     pandev->enabledScreen = TRUE;
226 
227     pandev->surfObjScreen = EngLockSurface(surfScreen);
228     if (!pandev->surfObjScreen)
229     {
230         DPRINT1("Failed to lock underlying surface\n");
231         goto failure;
232     }
233 
234     /* Create device surface */
235     pandev->hsurf = EngCreateDeviceSurface(NULL, pandev->szlDesktop, pandev->iBitmapFormat);
236     if (!pandev->hsurf)
237     {
238         DPRINT1("EngCreateDeviceSurface() failed\n");
239         goto failure;
240     }
241 
242     if (!EngAssociateSurface(pandev->hsurf, pandev->hdev, HOOK_ALPHABLEND |
243                                                           HOOK_BITBLT |
244                                                           HOOK_COPYBITS |
245                                                           HOOK_GRADIENTFILL |
246                                                           HOOK_STROKEPATH |
247                                                           HOOK_SYNCHRONIZE |
248                                                           HOOK_TEXTOUT))
249     {
250         DPRINT1("EngAssociateSurface() failed\n");
251         goto failure;
252     }
253 
254     /* Create shadow surface */
255     pandev->hsurfShadow = (HSURF)EngCreateBitmap(pandev->szlDesktop, pandev->szlDesktop.cx, pandev->iBitmapFormat, BMF_TOPDOWN, NULL);
256     if (!pandev->hsurfShadow)
257     {
258         DPRINT1("EngCreateBitmap() failed\n");
259         goto failure;
260     }
261 
262     pandev->psoShadow = EngLockSurface(pandev->hsurfShadow);
263     if (!pandev->psoShadow)
264     {
265         DPRINT1("EngLockSurface() failed\n");
266         goto failure;
267     }
268 
269     return pandev->hsurf;
270 
271 failure:
272     PanDisableSurface(dhpdev);
273     return NULL;
274 }
275 
276 BOOL
277 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)278 PanBitBlt(
279     _Inout_ SURFOBJ *psoTrg,
280     _In_opt_ SURFOBJ *psoSrc,
281     _In_opt_ SURFOBJ *psoMask,
282     _In_ CLIPOBJ *pco,
283     _In_opt_ XLATEOBJ *pxlo,
284     _In_ PRECTL prclTrg,
285     _In_opt_ PPOINTL pptlSrc,
286     _In_opt_ PPOINTL pptlMask,
287     _In_opt_ BRUSHOBJ *pbo,
288     _In_opt_ PPOINTL pptlBrush,
289     _In_ ROP4 rop4)
290 {
291     PPANDEV pandev;
292     BOOL res;
293 
294     if (psoTrg->iType == STYPE_DEVICE)
295     {
296         pandev = (PPANDEV)psoTrg->dhpdev;
297         psoTrg = pandev->psoShadow;
298     }
299     if (psoSrc && psoSrc->iType == STYPE_DEVICE)
300     {
301         pandev = (PPANDEV)psoSrc->dhpdev;
302         psoSrc = pandev->psoShadow;
303     }
304 
305     res = EngBitBlt(psoTrg, psoSrc, psoMask, pco, pxlo, prclTrg, pptlSrc, pptlMask, pbo, pptlBrush, rop4);
306 
307     /* FIXME: PanSynchronize must be called periodically, as we have the GCAPS2_SYNCTIMER flag.
308      * That's not the case, so call PanSynchronize() manually */
309     if (res)
310         PanSynchronize(gPan, NULL);
311 
312     return res;
313 }
314 
315 BOOL
316 APIENTRY
PanCopyBits(_Inout_ SURFOBJ * psoDest,_In_opt_ SURFOBJ * psoSrc,_In_ CLIPOBJ * pco,_In_opt_ XLATEOBJ * pxlo,_In_ PRECTL prclDest,_In_opt_ PPOINTL pptlSrc)317 PanCopyBits(
318     _Inout_ SURFOBJ *psoDest,
319     _In_opt_ SURFOBJ *psoSrc,
320     _In_ CLIPOBJ *pco,
321     _In_opt_ XLATEOBJ *pxlo,
322     _In_ PRECTL prclDest,
323     _In_opt_ PPOINTL pptlSrc)
324 {
325     /* Easy. Just call the more general function PanBitBlt */
326     return PanBitBlt(psoDest, psoSrc, NULL, pco, pxlo, prclDest, pptlSrc, NULL, NULL, NULL, ROP4_SRCCOPY);
327 }
328 
329 BOOL
330 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)331 PanStrokePath(
332      _Inout_ SURFOBJ *pso,
333      _In_ PATHOBJ *ppo,
334      _In_ CLIPOBJ *pco,
335      _In_opt_ XFORMOBJ *pxo,
336      _In_ BRUSHOBJ *pbo,
337      _In_ PPOINTL pptlBrushOrg,
338      _In_ PLINEATTRS plineattrs,
339      _In_ MIX mix)
340 {
341     UNIMPLEMENTED;
342     ASSERT(FALSE);
343     return FALSE;
344 }
345 
346 BOOL
347 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)348 PanTextOut(
349     _Inout_ SURFOBJ *pso,
350     _In_ STROBJ *pstro,
351     _In_ FONTOBJ *pfo,
352     _In_ CLIPOBJ *pco,
353     _In_opt_ PRECTL prclExtra,
354     _In_opt_ PRECTL prclOpaque,
355     _In_ BRUSHOBJ *pboFore,
356     _In_ BRUSHOBJ *pboOpaque,
357     _In_ PPOINTL pptlOrg,
358     _In_ MIX mix)
359 {
360     UNIMPLEMENTED;
361     ASSERT(FALSE);
362     return FALSE;
363 }
364 
365 VOID
366 APIENTRY
PanMovePointer(_Inout_ SURFOBJ * pso,_In_ LONG x,_In_ LONG y,_In_ PRECTL prcl)367 PanMovePointer(
368     _Inout_ SURFOBJ *pso,
369     _In_ LONG x,
370     _In_ LONG y,
371     _In_ PRECTL prcl)
372 {
373     PPANDEV pandev = (PPANDEV)pso->dhpdev;
374     PFN_DrvMovePointer pfnMovePointer;
375     BOOL bChanged = FALSE;
376 
377     DPRINT("PanMovePointer(%d, %d)\n", x, y);
378 
379     /* FIXME: MouseSafetyOnDrawStart/MouseSafetyOnDrawEnd like to call us to hide/show the cursor,
380      * without real reason (see 5b0f6dcceeae3cf21f2535e2c523c0bf2749ea6b). Ignore those calls */
381     if (x < 0 || y >= 0) return;
382 
383     /* We don't set DrvSetPointerShape, so we must not be called
384      * with x < 0 (hide cursor) or y >= 0 (we have GCAPS_PANNING) */
385     ASSERT(x >= 0 && y < 0);
386 
387     /* Call underlying MovePointer function */
388     if (pandev->flUnderlyingGraphicsCaps & GCAPS_PANNING)
389     {
390         pfnMovePointer = GETPFN(MovePointer);
391         if (pfnMovePointer)
392             pfnMovePointer(pandev->surfObjScreen, x, y, prcl);
393     }
394 
395     y += pso->sizlBitmap.cy;
396 
397     if (x < pandev->rclViewport.left)
398     {
399         pandev->rclViewport.left = x;
400         pandev->rclViewport.right = x + pandev->szlScreen.cx;
401         bChanged = TRUE;
402     }
403     else if (x >= pandev->rclViewport.right)
404     {
405         pandev->rclViewport.right = x + 1;
406         pandev->rclViewport.left = x - pandev->szlScreen.cx;
407         bChanged = TRUE;
408     }
409     if (y < pandev->rclViewport.top)
410     {
411         pandev->rclViewport.top = y;
412         pandev->rclViewport.bottom = y + pandev->szlScreen.cy;
413         bChanged = TRUE;
414     }
415     else if (y >= pandev->rclViewport.bottom)
416     {
417         pandev->rclViewport.bottom = y + 1;
418         pandev->rclViewport.top = y - pandev->szlScreen.cy;
419         bChanged = TRUE;
420     }
421 
422     if (bChanged)
423         PanSynchronize(pso->dhpdev, NULL);
424 }
425 
426 BOOL
427 APIENTRY
PanAlphaBlend(_Inout_ SURFOBJ * psoDest,_In_ SURFOBJ * psoSrc,_In_ CLIPOBJ * pco,_In_opt_ XLATEOBJ * pxlo,_In_ PRECTL prclDest,_In_ PRECTL prclSrc,_In_ BLENDOBJ * pBlendObj)428 PanAlphaBlend(
429     _Inout_ SURFOBJ *psoDest,
430     _In_ SURFOBJ *psoSrc,
431     _In_ CLIPOBJ *pco,
432     _In_opt_ XLATEOBJ *pxlo,
433     _In_ PRECTL prclDest,
434     _In_ PRECTL prclSrc,
435     _In_ BLENDOBJ *pBlendObj)
436 {
437     PPANDEV pandev = (PPANDEV)psoDest->dhpdev;
438     BOOL res;
439 
440     res = EngAlphaBlend(pandev->psoShadow, psoSrc, pco, pxlo, prclDest, prclSrc, pBlendObj);
441 
442     /* FIXME: PanSynchronize must be called periodically, as we have the GCAPS2_SYNCTIMER flag.
443      * That's not the case, so call PanSynchronize() manually */
444     if (res)
445         PanSynchronize(gPan, NULL);
446 
447     return res;
448 }
449 
450 BOOL
451 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)452 PanGradientFill(
453     _Inout_ SURFOBJ *pso,
454     _In_ CLIPOBJ *pco,
455     _In_opt_ XLATEOBJ *pxlo,
456     _In_ PTRIVERTEX pVertex,
457     _In_ ULONG nVertex,
458     _In_ PVOID pMesh,
459     _In_ ULONG nMesh,
460     _In_ PRECTL prclExtents,
461     _In_ PPOINTL pptlDitherOrg,
462     _In_ ULONG ulMode)
463 {
464     PPANDEV pandev = (PPANDEV)pso->dhpdev;
465     BOOL res;
466 
467     res = EngGradientFill(pandev->psoShadow, pco, pxlo, pVertex, nVertex, pMesh, nMesh, prclExtents, pptlDitherOrg, ulMode);
468 
469     /* FIXME: PanSynchronize must be called periodically, as we have the GCAPS2_SYNCTIMER flag.
470      * That's not the case, so call PanSynchronize() manually */
471     if (res)
472         PanSynchronize(gPan, NULL);
473 
474     return res;
475 }
476 
477 DRVFN gPanDispDrvFn[] =
478 {
479     /* required */
480     { INDEX_DrvEnablePDEV,     (PFN) PanEnablePDEV     },
481     { INDEX_DrvCompletePDEV,   (PFN) PanCompletePDEV   },
482     { INDEX_DrvDisablePDEV,    (PFN) PanDisablePDEV    },
483     { INDEX_DrvEnableSurface,  (PFN) PanEnableSurface  },
484     { INDEX_DrvDisableSurface, (PFN) PanDisableSurface },
485 
486     /* required for device-managed surfaces */
487     { INDEX_DrvCopyBits,       (PFN) PanCopyBits       },
488     { INDEX_DrvStrokePath,     (PFN) PanStrokePath     },
489     { INDEX_DrvTextOut,        (PFN) PanTextOut        },
490 
491     /* optional, but required for panning functionality */
492     { INDEX_DrvSynchronize,    (PFN) PanSynchronize    },
493     { INDEX_DrvMovePointer,    (PFN) PanMovePointer    },
494 
495     /* optional */
496     { INDEX_DrvBitBlt,         (PFN) PanBitBlt         },
497     { INDEX_DrvAlphaBlend,     (PFN) PanAlphaBlend     },
498     { INDEX_DrvGradientFill,   (PFN) PanGradientFill   },
499 };
500 
501 ULONG gPanDispDrvCount = RTL_NUMBER_OF(gPanDispDrvFn);
502