1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS kernel 4 * PURPOSE: Support for physical devices 5 * FILE: win32ss/gdi/eng/pdevobj.c 6 * PROGRAMER: Timo Kreuzer (timo.kreuzer@reactos.org) 7 */ 8 9 #include <win32k.h> 10 #define NDEBUG 11 #include <debug.h> 12 DBG_DEFAULT_CHANNEL(EngPDev); 13 14 static PPDEVOBJ gppdevList = NULL; 15 static HSEMAPHORE ghsemPDEV; 16 17 BOOL 18 APIENTRY 19 MultiEnableDriver( 20 _In_ ULONG iEngineVersion, 21 _In_ ULONG cj, 22 _Inout_bytecount_(cj) PDRVENABLEDATA pded); 23 24 extern DRVFN gPanDispDrvFn[]; 25 extern ULONG gPanDispDrvCount; 26 27 CODE_SEG("INIT") 28 NTSTATUS 29 NTAPI 30 InitPDEVImpl(VOID) 31 { 32 ghsemPDEV = EngCreateSemaphore(); 33 if (!ghsemPDEV) return STATUS_INSUFFICIENT_RESOURCES; 34 return STATUS_SUCCESS; 35 } 36 37 #if DBG 38 PPDEVOBJ 39 NTAPI 40 DbgLookupDHPDEV(DHPDEV dhpdev) 41 { 42 PPDEVOBJ ppdev; 43 44 /* Lock PDEV list */ 45 EngAcquireSemaphoreShared(ghsemPDEV); 46 47 /* Walk through the list of PDEVs */ 48 for (ppdev = gppdevList; ppdev; ppdev = ppdev->ppdevNext) 49 { 50 /* Compare with the given DHPDEV */ 51 if (ppdev->dhpdev == dhpdev) break; 52 } 53 54 /* Unlock PDEV list */ 55 EngReleaseSemaphore(ghsemPDEV); 56 57 return ppdev; 58 } 59 #endif 60 61 PPDEVOBJ 62 PDEVOBJ_AllocPDEV(VOID) 63 { 64 PPDEVOBJ ppdev; 65 66 ppdev = ExAllocatePoolWithTag(PagedPool, sizeof(PDEVOBJ), GDITAG_PDEV); 67 if (!ppdev) 68 return NULL; 69 70 RtlZeroMemory(ppdev, sizeof(PDEVOBJ)); 71 72 ppdev->hsemDevLock = EngCreateSemaphore(); 73 if (ppdev->hsemDevLock == NULL) 74 { 75 ExFreePoolWithTag(ppdev, GDITAG_PDEV); 76 return NULL; 77 } 78 79 /* Allocate EDD_DIRECTDRAW_GLOBAL for our ReactX driver */ 80 ppdev->pEDDgpl = ExAllocatePoolWithTag(PagedPool, sizeof(EDD_DIRECTDRAW_GLOBAL), GDITAG_PDEV); 81 if (ppdev->pEDDgpl) 82 RtlZeroMemory(ppdev->pEDDgpl, sizeof(EDD_DIRECTDRAW_GLOBAL)); 83 84 ppdev->cPdevRefs = 1; 85 86 return ppdev; 87 } 88 89 static 90 VOID 91 PDEVOBJ_vDeletePDEV( 92 PPDEVOBJ ppdev) 93 { 94 EngDeleteSemaphore(ppdev->hsemDevLock); 95 if (ppdev->pdmwDev) 96 ExFreePoolWithTag(ppdev->pdmwDev, GDITAG_DEVMODE); 97 if (ppdev->pEDDgpl) 98 ExFreePoolWithTag(ppdev->pEDDgpl, GDITAG_PDEV); 99 ExFreePoolWithTag(ppdev, GDITAG_PDEV); 100 } 101 102 VOID 103 NTAPI 104 PDEVOBJ_vRelease( 105 _Inout_ PPDEVOBJ ppdev) 106 { 107 /* Lock loader */ 108 EngAcquireSemaphore(ghsemPDEV); 109 110 /* Decrease reference count */ 111 InterlockedDecrement(&ppdev->cPdevRefs); 112 ASSERT(ppdev->cPdevRefs >= 0); 113 114 /* Check if references are left */ 115 if (ppdev->cPdevRefs == 0) 116 { 117 /* Do we have a surface? */ 118 if (ppdev->pSurface) 119 { 120 /* Release the surface and let the driver free it */ 121 SURFACE_ShareUnlockSurface(ppdev->pSurface); 122 TRACE("DrvDisableSurface(dhpdev %p)\n", ppdev->dhpdev); 123 ppdev->pfn.DisableSurface(ppdev->dhpdev); 124 } 125 126 /* Do we have a palette? */ 127 if (ppdev->ppalSurf) 128 { 129 PALETTE_ShareUnlockPalette(ppdev->ppalSurf); 130 } 131 132 /* Check if the PDEV was enabled */ 133 if (ppdev->dhpdev != NULL) 134 { 135 /* Disable the PDEV */ 136 TRACE("DrvDisablePDEV(dhpdev %p)\n", ppdev->dhpdev); 137 ppdev->pfn.DisablePDEV(ppdev->dhpdev); 138 } 139 140 /* Remove it from list */ 141 if (ppdev == gppdevList) 142 { 143 gppdevList = ppdev->ppdevNext; 144 } 145 else if (gppdevList) 146 { 147 PPDEVOBJ ppdevCurrent = gppdevList; 148 BOOL found = FALSE; 149 while (!found && ppdevCurrent->ppdevNext) 150 { 151 if (ppdevCurrent->ppdevNext == ppdev) 152 found = TRUE; 153 else 154 ppdevCurrent = ppdevCurrent->ppdevNext; 155 } 156 if (found) 157 ppdevCurrent->ppdevNext = ppdev->ppdevNext; 158 } 159 160 /* Unload display driver */ 161 EngUnloadImage(ppdev->pldev); 162 163 /* Free it */ 164 PDEVOBJ_vDeletePDEV(ppdev); 165 } 166 167 /* Unlock loader */ 168 EngReleaseSemaphore(ghsemPDEV); 169 } 170 171 BOOL 172 NTAPI 173 PDEVOBJ_bEnablePDEV( 174 _In_ PPDEVOBJ ppdev, 175 _In_ PDEVMODEW pdevmode, 176 _In_ PWSTR pwszLogAddress) 177 { 178 PFN_DrvEnablePDEV pfnEnablePDEV; 179 ULONG i; 180 181 /* Get the DrvEnablePDEV function */ 182 pfnEnablePDEV = ppdev->pfn.EnablePDEV; 183 184 /* Call the drivers DrvEnablePDEV function */ 185 TRACE("DrvEnablePDEV(pdevmode %p (%dx%dx%d %d Hz) hdev %p (%S))\n", 186 pdevmode, 187 ppdev->pGraphicsDevice ? pdevmode->dmPelsWidth : 0, 188 ppdev->pGraphicsDevice ? pdevmode->dmPelsHeight : 0, 189 ppdev->pGraphicsDevice ? pdevmode->dmBitsPerPel : 0, 190 ppdev->pGraphicsDevice ? pdevmode->dmDisplayFrequency : 0, 191 ppdev, 192 ppdev->pGraphicsDevice ? ppdev->pGraphicsDevice->szNtDeviceName : L""); 193 ppdev->dhpdev = pfnEnablePDEV(pdevmode, 194 pwszLogAddress, 195 HS_DDI_MAX, 196 ppdev->ahsurf, 197 sizeof(GDIINFO), 198 (PULONG)&ppdev->gdiinfo, 199 sizeof(DEVINFO), 200 &ppdev->devinfo, 201 (HDEV)ppdev, 202 ppdev->pGraphicsDevice ? ppdev->pGraphicsDevice->pwszDescription : NULL, 203 ppdev->pGraphicsDevice ? ppdev->pGraphicsDevice->DeviceObject : NULL); 204 TRACE("DrvEnablePDEV(pdevmode %p hdev %p) => dhpdev %p\n", pdevmode, ppdev, ppdev->dhpdev); 205 if (ppdev->dhpdev == NULL) 206 { 207 ERR("Failed to enable PDEV\n"); 208 return FALSE; 209 } 210 211 /* Fix up some values */ 212 if (ppdev->gdiinfo.ulLogPixelsX == 0) 213 ppdev->gdiinfo.ulLogPixelsX = 96; 214 215 if (ppdev->gdiinfo.ulLogPixelsY == 0) 216 ppdev->gdiinfo.ulLogPixelsY = 96; 217 218 /* Set raster caps */ 219 ppdev->gdiinfo.flRaster = RC_OP_DX_OUTPUT | RC_GDI20_OUTPUT | RC_BIGFONT; 220 if ((ppdev->gdiinfo.ulTechnology != DT_PLOTTER) && (ppdev->gdiinfo.ulTechnology != DT_CHARSTREAM)) 221 ppdev->gdiinfo.flRaster |= RC_STRETCHDIB | RC_STRETCHBLT | RC_DIBTODEV | RC_DI_BITMAP | RC_BITMAP64 | RC_BITBLT; 222 if (ppdev->gdiinfo.ulTechnology == DT_RASDISPLAY) 223 ppdev->gdiinfo.flRaster |= RC_FLOODFILL; 224 if (ppdev->devinfo.flGraphicsCaps & GCAPS_PALMANAGED) 225 ppdev->gdiinfo.flRaster |= RC_PALETTE; 226 227 /* Setup Palette */ 228 ppdev->ppalSurf = PALETTE_ShareLockPalette(ppdev->devinfo.hpalDefault); 229 230 /* Setup hatch brushes */ 231 for (i = 0; i < HS_DDI_MAX; i++) 232 { 233 if (ppdev->ahsurf[i] == NULL) 234 ppdev->ahsurf[i] = gahsurfHatch[i]; 235 } 236 237 TRACE("PDEVOBJ_bEnablePDEV - dhpdev = %p\n", ppdev->dhpdev); 238 239 return TRUE; 240 } 241 242 VOID 243 NTAPI 244 PDEVOBJ_vCompletePDEV( 245 PPDEVOBJ ppdev) 246 { 247 /* Call the drivers DrvCompletePDEV function */ 248 TRACE("DrvCompletePDEV(dhpdev %p hdev %p)\n", ppdev->dhpdev, ppdev); 249 ppdev->pfn.CompletePDEV(ppdev->dhpdev, (HDEV)ppdev); 250 } 251 252 static 253 VOID 254 PDEVOBJ_vFilterDriverHooks( 255 _In_ PPDEVOBJ ppdev) 256 { 257 PLDEVOBJ pldev = ppdev->pldev; 258 ULONG dwAccelerationLevel = ppdev->dwAccelerationLevel; 259 260 if (!pldev->pGdiDriverInfo) 261 return; 262 if (pldev->ldevtype != LDEV_DEVICE_DISPLAY) 263 return; 264 265 if (dwAccelerationLevel >= 1) 266 { 267 ppdev->apfn[INDEX_DrvSetPointerShape] = NULL; 268 ppdev->apfn[INDEX_DrvCreateDeviceBitmap] = NULL; 269 } 270 271 if (dwAccelerationLevel >= 2) 272 { 273 /* Remove sophisticated display accelerations */ 274 ppdev->pSurface->flags &= ~(HOOK_STRETCHBLT | 275 HOOK_FILLPATH | 276 HOOK_GRADIENTFILL | 277 HOOK_LINETO | 278 HOOK_ALPHABLEND | 279 HOOK_TRANSPARENTBLT); 280 } 281 282 if (dwAccelerationLevel >= 3) 283 { 284 /* Disable DirectDraw and Direct3D accelerations */ 285 /* FIXME: need to call DxDdSetAccelLevel */ 286 UNIMPLEMENTED; 287 } 288 289 if (dwAccelerationLevel >= 4) 290 { 291 /* Remove almost all display accelerations */ 292 ppdev->pSurface->flags &= ~HOOK_FLAGS | 293 HOOK_BITBLT | 294 HOOK_COPYBITS | 295 HOOK_TEXTOUT | 296 HOOK_STROKEPATH | 297 HOOK_SYNCHRONIZE; 298 299 } 300 301 if (dwAccelerationLevel >= 5) 302 { 303 /* Disable all display accelerations */ 304 /* (nothing to do. Already handled in PDEVOBJ_Create) */ 305 } 306 } 307 308 PSURFACE 309 NTAPI 310 PDEVOBJ_pSurface( 311 PPDEVOBJ ppdev) 312 { 313 HSURF hsurf; 314 315 /* Check if there is no surface for this PDEV yet */ 316 if (ppdev->pSurface == NULL) 317 { 318 /* Call the drivers DrvEnableSurface */ 319 TRACE("DrvEnableSurface(dhpdev %p)\n", ppdev->dhpdev); 320 hsurf = ppdev->pfn.EnableSurface(ppdev->dhpdev); 321 TRACE("DrvEnableSurface(dhpdev %p) => hsurf %p\n", ppdev->dhpdev, hsurf); 322 if (hsurf== NULL) 323 { 324 ERR("Failed to create PDEV surface!\n"); 325 return NULL; 326 } 327 328 /* Get a reference to the surface */ 329 ppdev->pSurface = SURFACE_ShareLockSurface(hsurf); 330 NT_ASSERT(ppdev->pSurface != NULL); 331 } 332 333 /* Increment reference count */ 334 GDIOBJ_vReferenceObjectByPointer(&ppdev->pSurface->BaseObject); 335 336 return ppdev->pSurface; 337 } 338 339 VOID 340 PDEVOBJ_vEnableDisplay( 341 _Inout_ PPDEVOBJ ppdev) 342 { 343 BOOL assertVal; 344 345 if (!(ppdev->flFlags & PDEV_DISABLED)) 346 return; 347 348 /* Try to enable display until success */ 349 do 350 { 351 TRACE("DrvAssertMode(dhpdev %p, TRUE)\n", ppdev->dhpdev); 352 assertVal = ppdev->pfn.AssertMode(ppdev->dhpdev, TRUE); 353 TRACE("DrvAssertMode(dhpdev %p, TRUE) => %d\n", ppdev->dhpdev, assertVal); 354 } while (!assertVal); 355 356 ppdev->flFlags &= ~PDEV_DISABLED; 357 } 358 359 BOOL 360 PDEVOBJ_bDisableDisplay( 361 _Inout_ PPDEVOBJ ppdev) 362 { 363 BOOL assertVal; 364 365 if (ppdev->flFlags & PDEV_DISABLED) 366 return TRUE; 367 368 TRACE("DrvAssertMode(dhpdev %p, FALSE)\n", ppdev->dhpdev); 369 assertVal = ppdev->pfn.AssertMode(ppdev->dhpdev, FALSE); 370 TRACE("DrvAssertMode(dhpdev %p, FALSE) => %d\n", ppdev->dhpdev, assertVal); 371 372 if (assertVal) 373 ppdev->flFlags |= PDEV_DISABLED; 374 375 return assertVal; 376 } 377 378 VOID 379 NTAPI 380 PDEVOBJ_vRefreshModeList( 381 PPDEVOBJ ppdev) 382 { 383 PGRAPHICS_DEVICE pGraphicsDevice; 384 PDEVMODEINFO pdminfo, pdmiNext; 385 386 /* Lock the PDEV */ 387 EngAcquireSemaphore(ppdev->hsemDevLock); 388 389 pGraphicsDevice = ppdev->pGraphicsDevice; 390 391 /* Clear out the modes */ 392 for (pdminfo = pGraphicsDevice->pdevmodeInfo; 393 pdminfo; 394 pdminfo = pdmiNext) 395 { 396 pdmiNext = pdminfo->pdmiNext; 397 ExFreePoolWithTag(pdminfo, GDITAG_DEVMODE); 398 } 399 pGraphicsDevice->pdevmodeInfo = NULL; 400 ExFreePoolWithTag(pGraphicsDevice->pDevModeList, GDITAG_GDEVICE); 401 pGraphicsDevice->pDevModeList = NULL; 402 403 /* Update available display mode list */ 404 LDEVOBJ_bBuildDevmodeList(pGraphicsDevice); 405 406 /* Unlock PDEV */ 407 EngReleaseSemaphore(ppdev->hsemDevLock); 408 } 409 410 PPDEVOBJ 411 PDEVOBJ_Create( 412 _In_opt_ PGRAPHICS_DEVICE pGraphicsDevice, 413 _In_opt_ PDEVMODEW pdm, 414 _In_ ULONG dwAccelerationLevel, 415 _In_ ULONG ldevtype) 416 { 417 PPDEVOBJ ppdev, ppdevMatch = NULL; 418 PLDEVOBJ pldev; 419 PSURFACE pSurface; 420 421 TRACE("PDEVOBJ_Create(%p %p %d)\n", pGraphicsDevice, pdm, ldevtype); 422 423 if (ldevtype != LDEV_DEVICE_META) 424 { 425 ASSERT(pGraphicsDevice); 426 ASSERT(pdm); 427 /* Search if we already have a PPDEV with the required characteristics. 428 * We will compare the graphics device, the devmode and the desktop 429 */ 430 for (ppdev = gppdevList; ppdev; ppdev = ppdev->ppdevNext) 431 { 432 if (ppdev->pGraphicsDevice == pGraphicsDevice) 433 { 434 PDEVOBJ_vReference(ppdev); 435 436 if (RtlEqualMemory(pdm, ppdev->pdmwDev, sizeof(DEVMODEW)) && 437 ppdev->dwAccelerationLevel == dwAccelerationLevel) 438 { 439 PDEVOBJ_vReference(ppdev); 440 ppdevMatch = ppdev; 441 } 442 else 443 { 444 PDEVOBJ_bDisableDisplay(ppdev); 445 } 446 447 PDEVOBJ_vRelease(ppdev); 448 } 449 } 450 451 if (ppdevMatch) 452 { 453 PDEVOBJ_vEnableDisplay(ppdevMatch); 454 455 return ppdevMatch; 456 } 457 } 458 459 /* Try to get a display driver */ 460 if (ldevtype == LDEV_DEVICE_META) 461 pldev = LDEVOBJ_pLoadInternal(MultiEnableDriver, ldevtype); 462 else 463 pldev = LDEVOBJ_pLoadDriver(pdm->dmDeviceName, ldevtype); 464 if (!pldev) 465 { 466 ERR("Could not load display driver '%S'\n", 467 (ldevtype == LDEV_DEVICE_META) ? L"" : pdm->dmDeviceName); 468 return NULL; 469 } 470 471 /* Allocate a new PDEVOBJ */ 472 ppdev = PDEVOBJ_AllocPDEV(); 473 if (!ppdev) 474 { 475 ERR("failed to allocate a PDEV\n"); 476 return NULL; 477 } 478 479 if (ldevtype != LDEV_DEVICE_META) 480 { 481 ppdev->pGraphicsDevice = pGraphicsDevice; 482 483 // DxEngGetHdevData asks for Graphics DeviceObject in hSpooler field 484 ppdev->hSpooler = ppdev->pGraphicsDevice->DeviceObject; 485 486 /* Keep selected resolution */ 487 if (ppdev->pdmwDev) 488 ExFreePoolWithTag(ppdev->pdmwDev, GDITAG_DEVMODE); 489 ppdev->pdmwDev = ExAllocatePoolWithTag(PagedPool, pdm->dmSize + pdm->dmDriverExtra, GDITAG_DEVMODE); 490 if (ppdev->pdmwDev) 491 { 492 RtlCopyMemory(ppdev->pdmwDev, pdm, pdm->dmSize + pdm->dmDriverExtra); 493 /* FIXME: this must be done in a better way */ 494 pGraphicsDevice->StateFlags |= DISPLAY_DEVICE_PRIMARY_DEVICE | DISPLAY_DEVICE_ATTACHED_TO_DESKTOP; 495 } 496 } 497 498 /* FIXME! */ 499 ppdev->flFlags = PDEV_DISPLAY; 500 501 /* HACK: Don't use the pointer */ 502 ppdev->Pointer.Exclude.right = -1; 503 504 /* Initialize PDEV */ 505 ppdev->pldev = pldev; 506 ppdev->dwAccelerationLevel = dwAccelerationLevel; 507 508 /* Copy the function table */ 509 if ((ldevtype == LDEV_DEVICE_DISPLAY && dwAccelerationLevel >= 5) || 510 pdm->dmFields & (DM_PANNINGWIDTH | DM_PANNINGHEIGHT)) 511 { 512 ULONG i; 513 514 /* Initialize missing fields */ 515 if (!(pdm->dmFields & DM_PANNINGWIDTH)) 516 pdm->dmPanningWidth = pdm->dmPelsWidth; 517 if (!(pdm->dmFields & DM_PANNINGHEIGHT)) 518 pdm->dmPanningHeight = pdm->dmPelsHeight; 519 520 /* Replace vtable by panning vtable */ 521 for (i = 0; i < gPanDispDrvCount; i++) 522 ppdev->apfn[gPanDispDrvFn[i].iFunc] = gPanDispDrvFn[i].pfn; 523 } 524 else 525 { 526 ppdev->pfn = ppdev->pldev->pfn; 527 } 528 529 /* Call the driver to enable the PDEV */ 530 if (!PDEVOBJ_bEnablePDEV(ppdev, pdm, NULL)) 531 { 532 ERR("Failed to enable PDEV!\n"); 533 PDEVOBJ_vRelease(ppdev); 534 EngUnloadImage(pldev); 535 return NULL; 536 } 537 538 /* Tell the driver that the PDEV is ready */ 539 PDEVOBJ_vCompletePDEV(ppdev); 540 541 /* Create the initial surface */ 542 pSurface = PDEVOBJ_pSurface(ppdev); 543 if (!pSurface) 544 { 545 ERR("Failed to create surface\n"); 546 PDEVOBJ_vRelease(ppdev); 547 EngUnloadImage(pldev); 548 return NULL; 549 } 550 551 /* Remove some acceleration capabilities from driver */ 552 PDEVOBJ_vFilterDriverHooks(ppdev); 553 554 /* Set MovePointer function */ 555 ppdev->pfnMovePointer = ppdev->pfn.MovePointer; 556 if (!ppdev->pfnMovePointer) 557 ppdev->pfnMovePointer = EngMovePointer; 558 559 /* Insert the PDEV into the list */ 560 ppdev->ppdevNext = gppdevList; 561 gppdevList = ppdev; 562 563 /* Return the PDEV */ 564 return ppdev; 565 } 566 567 FORCEINLINE 568 VOID 569 SwitchPointer( 570 _Inout_ PVOID pvPointer1, 571 _Inout_ PVOID pvPointer2) 572 { 573 PVOID *ppvPointer1 = pvPointer1; 574 PVOID *ppvPointer2 = pvPointer2; 575 PVOID pvTemp; 576 577 pvTemp = *ppvPointer1; 578 *ppvPointer1 = *ppvPointer2; 579 *ppvPointer2 = pvTemp; 580 } 581 582 BOOL 583 NTAPI 584 PDEVOBJ_bDynamicModeChange( 585 PPDEVOBJ ppdev, 586 PPDEVOBJ ppdev2) 587 { 588 union 589 { 590 DRIVER_FUNCTIONS pfn; 591 GDIINFO gdiinfo; 592 DEVINFO devinfo; 593 DWORD StateFlags; 594 } temp; 595 596 /* Exchange driver functions */ 597 temp.pfn = ppdev->pfn; 598 ppdev->pfn = ppdev2->pfn; 599 ppdev2->pfn = temp.pfn; 600 601 /* Exchange LDEVs */ 602 SwitchPointer(&ppdev->pldev, &ppdev2->pldev); 603 604 /* Exchange DHPDEV */ 605 SwitchPointer(&ppdev->dhpdev, &ppdev2->dhpdev); 606 607 /* Exchange surfaces and associate them with their new PDEV */ 608 SwitchPointer(&ppdev->pSurface, &ppdev2->pSurface); 609 ppdev->pSurface->SurfObj.hdev = (HDEV)ppdev; 610 ppdev2->pSurface->SurfObj.hdev = (HDEV)ppdev2; 611 612 /* Exchange devinfo */ 613 temp.devinfo = ppdev->devinfo; 614 ppdev->devinfo = ppdev2->devinfo; 615 ppdev2->devinfo = temp.devinfo; 616 617 /* Exchange gdiinfo */ 618 temp.gdiinfo = ppdev->gdiinfo; 619 ppdev->gdiinfo = ppdev2->gdiinfo; 620 ppdev2->gdiinfo = temp.gdiinfo; 621 622 /* Exchange DEVMODE */ 623 SwitchPointer(&ppdev->pdmwDev, &ppdev2->pdmwDev); 624 625 /* Exchange state flags */ 626 temp.StateFlags = ppdev->pGraphicsDevice->StateFlags; 627 ppdev->pGraphicsDevice->StateFlags = ppdev2->pGraphicsDevice->StateFlags; 628 ppdev2->pGraphicsDevice->StateFlags = temp.StateFlags; 629 630 /* Notify each driver instance of its new HDEV association */ 631 ppdev->pfn.CompletePDEV(ppdev->dhpdev, (HDEV)ppdev); 632 ppdev2->pfn.CompletePDEV(ppdev2->dhpdev, (HDEV)ppdev2); 633 634 return TRUE; 635 } 636 637 638 BOOL 639 NTAPI 640 PDEVOBJ_bSwitchMode( 641 PPDEVOBJ ppdev, 642 PDEVMODEW pdm) 643 { 644 PPDEVOBJ ppdevTmp; 645 PSURFACE pSurface; 646 BOOL retval = FALSE; 647 648 /* Lock the PDEV */ 649 EngAcquireSemaphore(ppdev->hsemDevLock); 650 651 /* And everything else */ 652 EngAcquireSemaphore(ghsemPDEV); 653 654 DPRINT1("PDEVOBJ_bSwitchMode, ppdev = %p, pSurface = %p\n", ppdev, ppdev->pSurface); 655 656 // Lookup the GraphicsDevice + select DEVMODE 657 // pdm = LDEVOBJ_bProbeAndCaptureDevmode(ppdev, pdm); 658 659 /* 1. Temporarily disable the current PDEV and reset video to its default mode */ 660 if (!PDEVOBJ_bDisableDisplay(ppdev)) 661 { 662 DPRINT1("PDEVOBJ_bDisableDisplay() failed\n"); 663 goto leave; 664 } 665 666 /* 2. Create new PDEV */ 667 ppdevTmp = PDEVOBJ_Create(ppdev->pGraphicsDevice, pdm, 0, LDEV_DEVICE_DISPLAY); 668 if (!ppdevTmp) 669 { 670 DPRINT1("Failed to create a new PDEV\n"); 671 goto leave2; 672 } 673 674 /* 3. Create a new surface */ 675 pSurface = PDEVOBJ_pSurface(ppdevTmp); 676 if (!pSurface) 677 { 678 DPRINT1("PDEVOBJ_pSurface failed\n"); 679 PDEVOBJ_vRelease(ppdevTmp); 680 goto leave2; 681 } 682 683 /* 4. Get DirectDraw information */ 684 /* 5. Enable DirectDraw Not traced */ 685 /* 6. Copy old PDEV state to new PDEV instance */ 686 687 /* 7. Switch the PDEVs */ 688 if (!PDEVOBJ_bDynamicModeChange(ppdev, ppdevTmp)) 689 { 690 DPRINT1("PDEVOBJ_bDynamicModeChange() failed\n"); 691 PDEVOBJ_vRelease(ppdevTmp); 692 goto leave2; 693 } 694 695 /* 8. Disable DirectDraw */ 696 697 PDEVOBJ_vRelease(ppdevTmp); 698 699 /* Update primary display capabilities */ 700 if (ppdev == gpmdev->ppdevGlobal) 701 { 702 PDEVOBJ_vGetDeviceCaps(ppdev, &GdiHandleTable->DevCaps); 703 } 704 705 /* Success! */ 706 retval = TRUE; 707 708 leave2: 709 /* Set the new video mode, or restore the original one in case of failure */ 710 PDEVOBJ_vEnableDisplay(ppdev); 711 712 leave: 713 /* Unlock everything else */ 714 EngReleaseSemaphore(ghsemPDEV); 715 /* Unlock the PDEV */ 716 EngReleaseSemaphore(ppdev->hsemDevLock); 717 718 DPRINT1("leave, ppdev = %p, pSurface = %p\n", ppdev, ppdev->pSurface); 719 720 return retval; 721 } 722 723 724 PPDEVOBJ 725 NTAPI 726 EngpGetPDEV( 727 _In_opt_ PUNICODE_STRING pustrDeviceName) 728 { 729 UNICODE_STRING ustrCurrent; 730 PPDEVOBJ ppdev = NULL; 731 PGRAPHICS_DEVICE pGraphicsDevice; 732 ULONG i; 733 734 /* Acquire PDEV lock */ 735 EngAcquireSemaphore(ghsemPDEV); 736 737 /* Did the caller pass a device name? */ 738 if (pustrDeviceName) 739 { 740 /* Loop all present PDEVs */ 741 for (i = 0; i < gpmdev->cDev; i++) 742 { 743 /* Get a pointer to the GRAPHICS_DEVICE */ 744 pGraphicsDevice = gpmdev->dev[i].ppdev->pGraphicsDevice; 745 746 /* Compare the name */ 747 RtlInitUnicodeString(&ustrCurrent, pGraphicsDevice->szWinDeviceName); 748 if (RtlEqualUnicodeString(pustrDeviceName, &ustrCurrent, FALSE)) 749 { 750 /* Found! */ 751 ppdev = gpmdev->dev[i].ppdev; 752 break; 753 } 754 } 755 } 756 else if (gpmdev) 757 { 758 /* Otherwise use the primary PDEV */ 759 ppdev = gpmdev->ppdevGlobal; 760 } 761 762 /* Did we find one? */ 763 if (ppdev) 764 { 765 /* Yes, reference the PDEV */ 766 PDEVOBJ_vReference(ppdev); 767 } 768 769 /* Release PDEV lock */ 770 EngReleaseSemaphore(ghsemPDEV); 771 772 return ppdev; 773 } 774 775 LONG 776 PDEVOBJ_lChangeDisplaySettings( 777 _In_opt_ PUNICODE_STRING pustrDeviceName, 778 _In_opt_ PDEVMODEW RequestedMode, 779 _In_opt_ PMDEVOBJ pmdevOld, 780 _Out_ PMDEVOBJ *ppmdevNew, 781 _In_ BOOL bSearchClosestMode) 782 { 783 PGRAPHICS_DEVICE pGraphicsDevice = NULL; 784 PMDEVOBJ pmdev = NULL; 785 PDEVMODEW pdm = NULL; 786 ULONG lRet = DISP_CHANGE_SUCCESSFUL; 787 ULONG i, j; 788 789 TRACE("PDEVOBJ_lChangeDisplaySettings('%wZ' '%dx%dx%d (%d Hz)' %p %p)\n", 790 pustrDeviceName, 791 RequestedMode ? RequestedMode->dmPelsWidth : 0, 792 RequestedMode ? RequestedMode->dmPelsHeight : 0, 793 RequestedMode ? RequestedMode->dmBitsPerPel : 0, 794 RequestedMode ? RequestedMode->dmDisplayFrequency : 0, 795 pmdevOld, ppmdevNew); 796 797 if (pustrDeviceName) 798 { 799 pGraphicsDevice = EngpFindGraphicsDevice(pustrDeviceName, 0); 800 if (!pGraphicsDevice) 801 { 802 ERR("Wrong device name provided: '%wZ'\n", pustrDeviceName); 803 lRet = DISP_CHANGE_BADPARAM; 804 goto cleanup; 805 } 806 } 807 else if (RequestedMode) 808 { 809 pGraphicsDevice = gpPrimaryGraphicsDevice; 810 if (!pGraphicsDevice) 811 { 812 ERR("Wrong device'\n"); 813 lRet = DISP_CHANGE_BADPARAM; 814 goto cleanup; 815 } 816 } 817 818 if (pGraphicsDevice) 819 { 820 if (!LDEVOBJ_bProbeAndCaptureDevmode(pGraphicsDevice, RequestedMode, &pdm, bSearchClosestMode)) 821 { 822 ERR("DrvProbeAndCaptureDevmode() failed\n"); 823 lRet = DISP_CHANGE_BADMODE; 824 goto cleanup; 825 } 826 } 827 828 /* Here, we know that input parameters were correct */ 829 830 { 831 /* Create new MDEV. Note that if we provide a device name, 832 * MDEV will only contain one device. 833 * */ 834 835 if (pmdevOld) 836 { 837 /* Disable old MDEV */ 838 if (MDEVOBJ_bDisable(pmdevOld)) 839 { 840 /* Create new MDEV. On failure, reenable old MDEV */ 841 pmdev = MDEVOBJ_Create(pustrDeviceName, pdm); 842 if (!pmdev) 843 MDEVOBJ_vEnable(pmdevOld); 844 } 845 } 846 else 847 { 848 pmdev = MDEVOBJ_Create(pustrDeviceName, pdm); 849 } 850 851 if (!pmdev) 852 { 853 ERR("Failed to create new MDEV\n"); 854 lRet = DISP_CHANGE_FAILED; 855 goto cleanup; 856 } 857 858 lRet = DISP_CHANGE_SUCCESSFUL; 859 *ppmdevNew = pmdev; 860 861 /* We now have to do the mode switch */ 862 863 if (pustrDeviceName && pmdevOld) 864 { 865 /* We changed settings of one device. Add other devices which were already present */ 866 for (i = 0; i < pmdevOld->cDev; i++) 867 { 868 for (j = 0; j < pmdev->cDev; j++) 869 { 870 if (pmdev->dev[j].ppdev->pGraphicsDevice == pmdevOld->dev[i].ppdev->pGraphicsDevice) 871 { 872 if (PDEVOBJ_bDynamicModeChange(pmdevOld->dev[i].ppdev, pmdev->dev[j].ppdev)) 873 { 874 PPDEVOBJ tmp = pmdevOld->dev[i].ppdev; 875 pmdevOld->dev[i].ppdev = pmdev->dev[j].ppdev; 876 pmdev->dev[j].ppdev = tmp; 877 } 878 else 879 { 880 ERR("Failed to apply new settings\n"); 881 UNIMPLEMENTED; 882 ASSERT(FALSE); 883 } 884 break; 885 } 886 } 887 if (j == pmdev->cDev) 888 { 889 PDEVOBJ_vReference(pmdevOld->dev[i].ppdev); 890 pmdev->dev[pmdev->cDev].ppdev = pmdevOld->dev[i].ppdev; 891 pmdev->cDev++; 892 } 893 } 894 } 895 896 if (pmdev->cDev == 1) 897 { 898 pmdev->ppdevGlobal = pmdev->dev[0].ppdev; 899 } 900 else 901 { 902 /* Enable MultiDriver */ 903 pmdev->ppdevGlobal = PDEVOBJ_Create(NULL, (PDEVMODEW)pmdev, 0, LDEV_DEVICE_META); 904 if (!pmdev->ppdevGlobal) 905 { 906 WARN("Failed to create meta-device. Using only first display\n"); 907 PDEVOBJ_vReference(pmdev->dev[0].ppdev); 908 pmdev->ppdevGlobal = pmdev->dev[0].ppdev; 909 } 910 } 911 912 if (pmdevOld) 913 { 914 /* Search PDEVs which were in pmdevOld, but are not anymore in pmdev, and disable them */ 915 for (i = 0; i < pmdevOld->cDev; i++) 916 { 917 for (j = 0; j < pmdev->cDev; j++) 918 { 919 if (pmdev->dev[j].ppdev->pGraphicsDevice == pmdevOld->dev[i].ppdev->pGraphicsDevice) 920 break; 921 } 922 if (j == pmdev->cDev) 923 PDEVOBJ_bDisableDisplay(pmdevOld->dev[i].ppdev); 924 } 925 } 926 } 927 928 cleanup: 929 if (lRet != DISP_CHANGE_SUCCESSFUL) 930 { 931 *ppmdevNew = NULL; 932 if (pmdev) 933 MDEVOBJ_vDestroy(pmdev); 934 if (pdm && pdm != RequestedMode) 935 ExFreePoolWithTag(pdm, GDITAG_DEVMODE); 936 } 937 938 return lRet; 939 } 940 941 INT 942 NTAPI 943 PDEVOBJ_iGetColorManagementCaps(PPDEVOBJ ppdev) 944 { 945 INT ret = CM_NONE; 946 947 if (ppdev->flFlags & PDEV_DISPLAY) 948 { 949 if (ppdev->devinfo.iDitherFormat == BMF_8BPP || 950 ppdev->devinfo.flGraphicsCaps2 & GCAPS2_CHANGEGAMMARAMP) 951 ret = CM_GAMMA_RAMP; 952 } 953 954 if (ppdev->devinfo.flGraphicsCaps & GCAPS_CMYKCOLOR) 955 ret |= CM_CMYK_COLOR; 956 if (ppdev->devinfo.flGraphicsCaps & GCAPS_ICM) 957 ret |= CM_DEVICE_ICM; 958 959 return ret; 960 } 961 962 VOID 963 NTAPI 964 PDEVOBJ_vGetDeviceCaps( 965 IN PPDEVOBJ ppdev, 966 OUT PDEVCAPS pDevCaps) 967 { 968 PGDIINFO pGdiInfo = &ppdev->gdiinfo; 969 970 pDevCaps->ulVersion = pGdiInfo->ulVersion; 971 pDevCaps->ulTechnology = pGdiInfo->ulTechnology; 972 pDevCaps->ulHorzSizeM = (pGdiInfo->ulHorzSize + 500) / 1000; 973 pDevCaps->ulVertSizeM = (pGdiInfo->ulVertSize + 500) / 1000; 974 pDevCaps->ulHorzSize = pGdiInfo->ulHorzSize; 975 pDevCaps->ulVertSize = pGdiInfo->ulVertSize; 976 pDevCaps->ulHorzRes = pGdiInfo->ulHorzRes; 977 pDevCaps->ulVertRes = pGdiInfo->ulVertRes; 978 pDevCaps->ulBitsPixel = pGdiInfo->cBitsPixel; 979 if (pDevCaps->ulBitsPixel == 15) pDevCaps->ulBitsPixel = 16; 980 pDevCaps->ulPlanes = pGdiInfo->cPlanes; 981 pDevCaps->ulNumPens = pGdiInfo->ulNumColors; 982 if (pDevCaps->ulNumPens != -1) pDevCaps->ulNumPens *= 5; 983 pDevCaps->ulNumFonts = 0; // PDEVOBJ_cFonts(ppdev); 984 pDevCaps->ulNumColors = pGdiInfo->ulNumColors; 985 pDevCaps->ulRasterCaps = pGdiInfo->flRaster; 986 pDevCaps->ulAspectX = pGdiInfo->ulAspectX; 987 pDevCaps->ulAspectY = pGdiInfo->ulAspectY; 988 pDevCaps->ulAspectXY = pGdiInfo->ulAspectXY; 989 pDevCaps->ulLogPixelsX = pGdiInfo->ulLogPixelsX; 990 pDevCaps->ulLogPixelsY = pGdiInfo->ulLogPixelsY; 991 pDevCaps->ulSizePalette = pGdiInfo->ulNumPalReg; 992 pDevCaps->ulColorRes = pGdiInfo->ulDACRed + 993 pGdiInfo->ulDACGreen + 994 pGdiInfo->ulDACBlue; 995 pDevCaps->ulPhysicalWidth = pGdiInfo->szlPhysSize.cx; 996 pDevCaps->ulPhysicalHeight = pGdiInfo->szlPhysSize.cy; 997 pDevCaps->ulPhysicalOffsetX = pGdiInfo->ptlPhysOffset.x; 998 pDevCaps->ulPhysicalOffsetY = pGdiInfo->ptlPhysOffset.y; 999 pDevCaps->ulTextCaps = pGdiInfo->flTextCaps; 1000 pDevCaps->ulTextCaps |= (TC_SO_ABLE|TC_UA_ABLE|TC_CP_STROKE|TC_OP_STROKE|TC_OP_CHARACTER); 1001 if (pGdiInfo->ulTechnology != DT_PLOTTER) 1002 pDevCaps->ulTextCaps |= TC_VA_ABLE; 1003 pDevCaps->ulVRefresh = pGdiInfo->ulVRefresh; 1004 pDevCaps->ulDesktopHorzRes = pGdiInfo->ulHorzRes; 1005 pDevCaps->ulDesktopVertRes = pGdiInfo->ulVertRes; 1006 pDevCaps->ulBltAlignment = pGdiInfo->ulBltAlignment; 1007 pDevCaps->ulPanningHorzRes = pGdiInfo->ulPanningHorzRes; 1008 pDevCaps->ulPanningVertRes = pGdiInfo->ulPanningVertRes; 1009 pDevCaps->xPanningAlignment = pGdiInfo->xPanningAlignment; 1010 pDevCaps->yPanningAlignment = pGdiInfo->yPanningAlignment; 1011 pDevCaps->ulShadeBlend = pGdiInfo->flShadeBlend; 1012 pDevCaps->ulColorMgmtCaps = PDEVOBJ_iGetColorManagementCaps(ppdev); 1013 } 1014 1015 1016 /** Exported functions ********************************************************/ 1017 1018 /* 1019 * @implemented 1020 */ 1021 BOOL 1022 APIENTRY 1023 EngQueryDeviceAttribute( 1024 _In_ HDEV hdev, 1025 _In_ ENG_DEVICE_ATTRIBUTE devAttr, 1026 _In_reads_bytes_(cjInSize) PVOID pvIn, 1027 _In_ ULONG cjInSize, 1028 _Out_writes_bytes_(cjOutSize) PVOID pvOut, 1029 _In_ ULONG cjOutSize) 1030 { 1031 PPDEVOBJ ppdev = (PPDEVOBJ)hdev; 1032 1033 if (devAttr != QDA_ACCELERATION_LEVEL) 1034 return FALSE; 1035 1036 if (cjOutSize >= sizeof(DWORD)) 1037 { 1038 /* Set all Accelerations Level Key to enabled Full 0 to 5 turned off. */ 1039 *(DWORD*)pvOut = ppdev->dwAccelerationLevel; 1040 return TRUE; 1041 } 1042 1043 return FALSE; 1044 } 1045 1046 _Must_inspect_result_ _Ret_z_ 1047 LPWSTR 1048 APIENTRY 1049 EngGetDriverName(_In_ HDEV hdev) 1050 { 1051 PPDEVOBJ ppdev = (PPDEVOBJ)hdev; 1052 1053 ASSERT(ppdev); 1054 ASSERT(ppdev->pldev); 1055 ASSERT(ppdev->pldev->pGdiDriverInfo); 1056 ASSERT(ppdev->pldev->pGdiDriverInfo->DriverName.Buffer); 1057 1058 return ppdev->pldev->pGdiDriverInfo->DriverName.Buffer; 1059 } 1060 1061 1062 INT 1063 APIENTRY 1064 NtGdiGetDeviceCaps( 1065 HDC hdc, 1066 INT Index) 1067 { 1068 PDC pdc; 1069 DEVCAPS devcaps; 1070 1071 /* Lock the given DC */ 1072 pdc = DC_LockDc(hdc); 1073 if (!pdc) 1074 { 1075 EngSetLastError(ERROR_INVALID_HANDLE); 1076 return 0; 1077 } 1078 1079 /* Get the data */ 1080 PDEVOBJ_vGetDeviceCaps(pdc->ppdev, &devcaps); 1081 1082 /* Unlock the DC */ 1083 DC_UnlockDc(pdc); 1084 1085 /* Return capability */ 1086 switch (Index) 1087 { 1088 case DRIVERVERSION: 1089 return devcaps.ulVersion; 1090 1091 case TECHNOLOGY: 1092 return devcaps.ulTechnology; 1093 1094 case HORZSIZE: 1095 return devcaps.ulHorzSize; 1096 1097 case VERTSIZE: 1098 return devcaps.ulVertSize; 1099 1100 case HORZRES: 1101 return devcaps.ulHorzRes; 1102 1103 case VERTRES: 1104 return devcaps.ulVertRes; 1105 1106 case LOGPIXELSX: 1107 return devcaps.ulLogPixelsX; 1108 1109 case LOGPIXELSY: 1110 return devcaps.ulLogPixelsY; 1111 1112 case BITSPIXEL: 1113 return devcaps.ulBitsPixel; 1114 1115 case PLANES: 1116 return devcaps.ulPlanes; 1117 1118 case NUMBRUSHES: 1119 return -1; 1120 1121 case NUMPENS: 1122 return devcaps.ulNumPens; 1123 1124 case NUMFONTS: 1125 return devcaps.ulNumFonts; 1126 1127 case NUMCOLORS: 1128 return devcaps.ulNumColors; 1129 1130 case ASPECTX: 1131 return devcaps.ulAspectX; 1132 1133 case ASPECTY: 1134 return devcaps.ulAspectY; 1135 1136 case ASPECTXY: 1137 return devcaps.ulAspectXY; 1138 1139 case CLIPCAPS: 1140 return CP_RECTANGLE; 1141 1142 case SIZEPALETTE: 1143 return devcaps.ulSizePalette; 1144 1145 case NUMRESERVED: 1146 return 20; 1147 1148 case COLORRES: 1149 return devcaps.ulColorRes; 1150 1151 case DESKTOPVERTRES: 1152 return devcaps.ulVertRes; 1153 1154 case DESKTOPHORZRES: 1155 return devcaps.ulHorzRes; 1156 1157 case BLTALIGNMENT: 1158 return devcaps.ulBltAlignment; 1159 1160 case SHADEBLENDCAPS: 1161 return devcaps.ulShadeBlend; 1162 1163 case COLORMGMTCAPS: 1164 return devcaps.ulColorMgmtCaps; 1165 1166 case PHYSICALWIDTH: 1167 return devcaps.ulPhysicalWidth; 1168 1169 case PHYSICALHEIGHT: 1170 return devcaps.ulPhysicalHeight; 1171 1172 case PHYSICALOFFSETX: 1173 return devcaps.ulPhysicalOffsetX; 1174 1175 case PHYSICALOFFSETY: 1176 return devcaps.ulPhysicalOffsetY; 1177 1178 case VREFRESH: 1179 return devcaps.ulVRefresh; 1180 1181 case RASTERCAPS: 1182 return devcaps.ulRasterCaps; 1183 1184 case CURVECAPS: 1185 return (CC_CIRCLES | CC_PIE | CC_CHORD | CC_ELLIPSES | CC_WIDE | 1186 CC_STYLED | CC_WIDESTYLED | CC_INTERIORS | CC_ROUNDRECT); 1187 1188 case LINECAPS: 1189 return (LC_POLYLINE | LC_MARKER | LC_POLYMARKER | LC_WIDE | 1190 LC_STYLED | LC_WIDESTYLED | LC_INTERIORS); 1191 1192 case POLYGONALCAPS: 1193 return (PC_POLYGON | PC_RECTANGLE | PC_WINDPOLYGON | PC_SCANLINE | 1194 PC_WIDE | PC_STYLED | PC_WIDESTYLED | PC_INTERIORS); 1195 1196 case TEXTCAPS: 1197 return devcaps.ulTextCaps; 1198 1199 case CAPS1: 1200 case PDEVICESIZE: 1201 case SCALINGFACTORX: 1202 case SCALINGFACTORY: 1203 default: 1204 return 0; 1205 } 1206 1207 return 0; 1208 } 1209 1210 _Success_(return!=FALSE) 1211 BOOL 1212 APIENTRY 1213 NtGdiGetDeviceCapsAll( 1214 IN HDC hDC, 1215 OUT PDEVCAPS pDevCaps) 1216 { 1217 PDC pdc; 1218 DEVCAPS devcaps; 1219 BOOL bResult = TRUE; 1220 1221 /* Lock the given DC */ 1222 pdc = DC_LockDc(hDC); 1223 if (!pdc) 1224 { 1225 EngSetLastError(ERROR_INVALID_HANDLE); 1226 return FALSE; 1227 } 1228 1229 /* Get the data */ 1230 PDEVOBJ_vGetDeviceCaps(pdc->ppdev, &devcaps); 1231 1232 /* Unlock the DC */ 1233 DC_UnlockDc(pdc); 1234 1235 /* Copy data to caller */ 1236 _SEH2_TRY 1237 { 1238 ProbeForWrite(pDevCaps, sizeof(DEVCAPS), 1); 1239 RtlCopyMemory(pDevCaps, &devcaps, sizeof(DEVCAPS)); 1240 } 1241 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 1242 { 1243 SetLastNtError(_SEH2_GetExceptionCode()); 1244 bResult = FALSE; 1245 } 1246 _SEH2_END; 1247 1248 return bResult; 1249 } 1250 1251 DHPDEV 1252 APIENTRY 1253 NtGdiGetDhpdev( 1254 IN HDEV hdev) 1255 { 1256 PPDEVOBJ ppdev; 1257 DHPDEV dhpdev = NULL; 1258 1259 /* Check parameter */ 1260 if (!hdev || (PCHAR)hdev < (PCHAR)MmSystemRangeStart) 1261 return NULL; 1262 1263 /* Lock PDEV list */ 1264 EngAcquireSemaphoreShared(ghsemPDEV); 1265 1266 /* Walk through the list of PDEVs */ 1267 for (ppdev = gppdevList; ppdev; ppdev = ppdev->ppdevNext) 1268 { 1269 /* Compare with the given HDEV */ 1270 if (ppdev == (PPDEVOBJ)hdev) 1271 { 1272 /* Found the PDEV! Get it's dhpdev and break */ 1273 dhpdev = ppdev->dhpdev; 1274 break; 1275 } 1276 } 1277 1278 /* Unlock PDEV list */ 1279 EngReleaseSemaphore(ghsemPDEV); 1280 1281 return dhpdev; 1282 } 1283 1284 PSIZEL 1285 FASTCALL 1286 PDEVOBJ_sizl(PPDEVOBJ ppdev, PSIZEL psizl) 1287 { 1288 if (ppdev->flFlags & PDEV_META_DEVICE) 1289 { 1290 *psizl = ppdev->szlMetaRes; 1291 } 1292 else 1293 { 1294 psizl->cx = ppdev->gdiinfo.ulHorzRes; 1295 psizl->cy = ppdev->gdiinfo.ulVertRes; 1296 } 1297 return psizl; 1298 } 1299