xref: /reactos/win32ss/gdi/eng/pdevobj.c (revision bbccad0e)
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