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