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 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 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 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 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 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 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 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 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 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 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 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 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 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