xref: /reactos/win32ss/gdi/eng/pdevobj.c (revision c2c66aff)
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 
13 PPDEVOBJ gppdevPrimary = NULL;
14 
15 static PPDEVOBJ gppdevList = NULL;
16 static HSEMAPHORE ghsemPDEV;
17 
18 INIT_FUNCTION
19 NTSTATUS
20 NTAPI
21 InitPDEVImpl(VOID)
22 {
23     ghsemPDEV = EngCreateSemaphore();
24     if (!ghsemPDEV) return STATUS_INSUFFICIENT_RESOURCES;
25     return STATUS_SUCCESS;
26 }
27 
28 #if DBG
29 PPDEVOBJ
30 NTAPI
31 DbgLookupDHPDEV(DHPDEV dhpdev)
32 {
33     PPDEVOBJ ppdev;
34 
35     /* Lock PDEV list */
36     EngAcquireSemaphoreShared(ghsemPDEV);
37 
38     /* Walk through the list of PDEVs */
39     for (ppdev = gppdevList;  ppdev; ppdev = ppdev->ppdevNext)
40     {
41         /* Compare with the given DHPDEV */
42         if (ppdev->dhpdev == dhpdev) break;
43     }
44 
45     /* Unlock PDEV list */
46     EngReleaseSemaphore(ghsemPDEV);
47 
48     return ppdev;
49 }
50 #endif
51 
52 PPDEVOBJ
53 PDEVOBJ_AllocPDEV(VOID)
54 {
55     PPDEVOBJ ppdev;
56 
57     ppdev = ExAllocatePoolWithTag(PagedPool, sizeof(PDEVOBJ), GDITAG_PDEV);
58     if (!ppdev)
59         return NULL;
60 
61     RtlZeroMemory(ppdev, sizeof(PDEVOBJ));
62 
63     ppdev->hsemDevLock = EngCreateSemaphore();
64     if (ppdev->hsemDevLock == NULL)
65     {
66         ExFreePoolWithTag(ppdev, GDITAG_PDEV);
67         return NULL;
68     }
69 
70     /* Allocate EDD_DIRECTDRAW_GLOBAL for our ReactX driver */
71     ppdev->pEDDgpl = ExAllocatePoolWithTag(PagedPool, sizeof(EDD_DIRECTDRAW_GLOBAL), GDITAG_PDEV);
72     if (ppdev->pEDDgpl)
73         RtlZeroMemory(ppdev->pEDDgpl, sizeof(EDD_DIRECTDRAW_GLOBAL));
74 
75     ppdev->cPdevRefs = 1;
76 
77     return ppdev;
78 }
79 
80 static
81 VOID
82 PDEVOBJ_vDeletePDEV(
83     PPDEVOBJ ppdev)
84 {
85     EngDeleteSemaphore(ppdev->hsemDevLock);
86     if (ppdev->pEDDgpl)
87         ExFreePoolWithTag(ppdev->pEDDgpl, GDITAG_PDEV);
88     ExFreePoolWithTag(ppdev, GDITAG_PDEV);
89 }
90 
91 VOID
92 NTAPI
93 PDEVOBJ_vRelease(PPDEVOBJ ppdev)
94 {
95     /* Lock loader */
96     EngAcquireSemaphore(ghsemPDEV);
97 
98     /* Decrease reference count */
99     --ppdev->cPdevRefs;
100 
101     ASSERT(ppdev->cPdevRefs >= 0) ;
102 
103     /* Check if references are left */
104     if (ppdev->cPdevRefs == 0)
105     {
106         /* Do we have a surface? */
107         if (ppdev->pSurface)
108         {
109             /* Release the surface and let the driver free it */
110             SURFACE_ShareUnlockSurface(ppdev->pSurface);
111             ppdev->pfn.DisableSurface(ppdev->dhpdev);
112         }
113 
114         /* Do we have a palette? */
115         if(ppdev->ppalSurf)
116         {
117             PALETTE_ShareUnlockPalette(ppdev->ppalSurf);
118         }
119 
120         /* Check if the PDEV was enabled */
121         if (ppdev->dhpdev != NULL)
122         {
123             /* Disable the PDEV */
124             ppdev->pfn.DisablePDEV(ppdev->dhpdev);
125         }
126 
127         /* Remove it from list */
128         if( ppdev == gppdevList )
129             gppdevList = ppdev->ppdevNext ;
130         else
131         {
132             PPDEVOBJ ppdevCurrent = gppdevList;
133             BOOL found = FALSE ;
134             while (!found && ppdevCurrent->ppdevNext)
135             {
136                 if (ppdevCurrent->ppdevNext == ppdev)
137                     found = TRUE;
138                 else
139                     ppdevCurrent = ppdevCurrent->ppdevNext ;
140             }
141             if(found)
142                 ppdevCurrent->ppdevNext = ppdev->ppdevNext;
143         }
144 
145         /* Is this the primary one ? */
146         if (ppdev == gppdevPrimary)
147             gppdevPrimary = NULL;
148 
149         /* Free it */
150         PDEVOBJ_vDeletePDEV(ppdev);
151     }
152 
153     /* Unlock loader */
154     EngReleaseSemaphore(ghsemPDEV);
155 
156 }
157 
158 BOOL
159 NTAPI
160 PDEVOBJ_bEnablePDEV(
161     PPDEVOBJ ppdev,
162     PDEVMODEW pdevmode,
163     PWSTR pwszLogAddress)
164 {
165     PFN_DrvEnablePDEV pfnEnablePDEV;
166     ULONG i;
167 
168     DPRINT("PDEVOBJ_bEnablePDEV()\n");
169 
170     /* Get the DrvEnablePDEV function */
171     pfnEnablePDEV = ppdev->pldev->pfn.EnablePDEV;
172 
173     /* Call the drivers DrvEnablePDEV function */
174     ppdev->dhpdev = pfnEnablePDEV(pdevmode,
175                                   pwszLogAddress,
176                                   HS_DDI_MAX,
177                                   ppdev->ahsurf,
178                                   sizeof(GDIINFO),
179                                   (PULONG)&ppdev->gdiinfo,
180                                   sizeof(DEVINFO),
181                                   &ppdev->devinfo,
182                                   (HDEV)ppdev,
183                                   ppdev->pGraphicsDevice->pwszDescription,
184                                   ppdev->pGraphicsDevice->DeviceObject);
185     if (ppdev->dhpdev == NULL)
186     {
187         DPRINT1("Failed to enable PDEV\n");
188         return FALSE;
189     }
190 
191     /* Fix up some values */
192     if (ppdev->gdiinfo.ulLogPixelsX == 0)
193         ppdev->gdiinfo.ulLogPixelsX = 96;
194 
195     if (ppdev->gdiinfo.ulLogPixelsY == 0)
196         ppdev->gdiinfo.ulLogPixelsY = 96;
197 
198     /* Set raster caps */
199     ppdev->gdiinfo.flRaster = RC_OP_DX_OUTPUT | RC_GDI20_OUTPUT | RC_BIGFONT;
200     if ((ppdev->gdiinfo.ulTechnology != DT_PLOTTER) && (ppdev->gdiinfo.ulTechnology != DT_CHARSTREAM))
201         ppdev->gdiinfo.flRaster |= RC_STRETCHDIB | RC_STRETCHBLT | RC_DIBTODEV | RC_DI_BITMAP | RC_BITMAP64 | RC_BITBLT;
202     if (ppdev->gdiinfo.ulTechnology == DT_RASDISPLAY)
203         ppdev->gdiinfo.flRaster |= RC_FLOODFILL;
204     if (ppdev->devinfo.flGraphicsCaps & GCAPS_PALMANAGED)
205         ppdev->gdiinfo.flRaster |= RC_PALETTE;
206 
207     /* Setup Palette */
208     ppdev->ppalSurf = PALETTE_ShareLockPalette(ppdev->devinfo.hpalDefault);
209 
210     /* Setup hatch brushes */
211     for (i = 0; i < HS_DDI_MAX; i++)
212     {
213         if (ppdev->ahsurf[i] == NULL)
214             ppdev->ahsurf[i] = gahsurfHatch[i];
215     }
216 
217     DPRINT("PDEVOBJ_bEnablePDEV - dhpdev = %p\n", ppdev->dhpdev);
218 
219     return TRUE;
220 }
221 
222 VOID
223 NTAPI
224 PDEVOBJ_vCompletePDEV(
225     PPDEVOBJ ppdev)
226 {
227     /* Call the drivers DrvCompletePDEV function */
228     ppdev->pldev->pfn.CompletePDEV(ppdev->dhpdev, (HDEV)ppdev);
229 }
230 
231 PSURFACE
232 NTAPI
233 PDEVOBJ_pSurface(
234     PPDEVOBJ ppdev)
235 {
236     HSURF hsurf;
237 
238     /* Check if there is no surface for this PDEV yet */
239     if (ppdev->pSurface == NULL)
240     {
241         /* Call the drivers DrvEnableSurface */
242         hsurf = ppdev->pldev->pfn.EnableSurface(ppdev->dhpdev);
243         if (hsurf== NULL)
244         {
245             DPRINT1("Failed to create PDEV surface!\n");
246             return NULL;
247         }
248 
249         /* Get a reference to the surface */
250         ppdev->pSurface = SURFACE_ShareLockSurface(hsurf);
251         NT_ASSERT(ppdev->pSurface != NULL);
252     }
253 
254     /* Increment reference count */
255     GDIOBJ_vReferenceObjectByPointer(&ppdev->pSurface->BaseObject);
256 
257     DPRINT("PDEVOBJ_pSurface() returning %p\n", ppdev->pSurface);
258     return ppdev->pSurface;
259 }
260 
261 VOID
262 NTAPI
263 PDEVOBJ_vRefreshModeList(
264     PPDEVOBJ ppdev)
265 {
266     PGRAPHICS_DEVICE pGraphicsDevice;
267     PDEVMODEINFO pdminfo, pdmiNext;
268     DEVMODEW dmDefault;
269 
270     /* Lock the PDEV */
271     EngAcquireSemaphore(ppdev->hsemDevLock);
272 
273     pGraphicsDevice = ppdev->pGraphicsDevice;
274 
275     /* Remember our default mode */
276     dmDefault = *pGraphicsDevice->pDevModeList[pGraphicsDevice->iDefaultMode].pdm;
277 
278     /* Clear out the modes */
279     for (pdminfo = pGraphicsDevice->pdevmodeInfo;
280          pdminfo;
281          pdminfo = pdmiNext)
282     {
283         pdmiNext = pdminfo->pdmiNext;
284         ExFreePoolWithTag(pdminfo, GDITAG_DEVMODE);
285     }
286     pGraphicsDevice->pdevmodeInfo = NULL;
287     ExFreePoolWithTag(pGraphicsDevice->pDevModeList, GDITAG_GDEVICE);
288     pGraphicsDevice->pDevModeList = NULL;
289 
290     /* Now re-populate the list */
291     if (!EngpPopulateDeviceModeList(pGraphicsDevice, &dmDefault))
292     {
293         DPRINT1("FIXME: EngpPopulateDeviceModeList failed, we just destroyed a perfectly good mode list\n");
294     }
295 
296     ppdev->pdmwDev = pGraphicsDevice->pDevModeList[pGraphicsDevice->iCurrentMode].pdm;
297 
298     /* Unlock PDEV */
299     EngReleaseSemaphore(ppdev->hsemDevLock);
300 }
301 
302 PDEVMODEW
303 NTAPI
304 PDEVOBJ_pdmMatchDevMode(
305     PPDEVOBJ ppdev,
306     PDEVMODEW pdm)
307 {
308     PGRAPHICS_DEVICE pGraphicsDevice;
309     PDEVMODEW pdmCurrent;
310     ULONG i;
311     DWORD dwFields;
312 
313     pGraphicsDevice = ppdev->pGraphicsDevice;
314 
315     for (i = 0; i < pGraphicsDevice->cDevModes; i++)
316     {
317         pdmCurrent = pGraphicsDevice->pDevModeList[i].pdm;
318 
319         /* Compare asked DEVMODE fields
320          * Only compare those that are valid in both DEVMODE structs */
321         dwFields = pdmCurrent->dmFields & pdm->dmFields ;
322 
323         /* For now, we only need those */
324         if ((dwFields & DM_BITSPERPEL) &&
325             (pdmCurrent->dmBitsPerPel != pdm->dmBitsPerPel)) continue;
326         if ((dwFields & DM_PELSWIDTH) &&
327             (pdmCurrent->dmPelsWidth != pdm->dmPelsWidth)) continue;
328         if ((dwFields & DM_PELSHEIGHT) &&
329             (pdmCurrent->dmPelsHeight != pdm->dmPelsHeight)) continue;
330         if ((dwFields & DM_DISPLAYFREQUENCY) &&
331             (pdmCurrent->dmDisplayFrequency != pdm->dmDisplayFrequency)) continue;
332 
333         /* Match! Return the DEVMODE */
334         return pdmCurrent;
335     }
336 
337     /* Nothing found */
338     return NULL;
339 }
340 
341 static
342 PPDEVOBJ
343 EngpCreatePDEV(
344     PUNICODE_STRING pustrDeviceName,
345     PDEVMODEW pdm)
346 {
347     PGRAPHICS_DEVICE pGraphicsDevice;
348     PPDEVOBJ ppdev;
349     DPRINT("EngpCreatePDEV(%wZ, %p)\n", pustrDeviceName, pdm);
350 
351     /* Try to find the GRAPHICS_DEVICE */
352     if (pustrDeviceName)
353     {
354         pGraphicsDevice = EngpFindGraphicsDevice(pustrDeviceName, 0, 0);
355         if (!pGraphicsDevice)
356         {
357             DPRINT1("No GRAPHICS_DEVICE found for %ls!\n",
358                     pustrDeviceName ? pustrDeviceName->Buffer : 0);
359             return NULL;
360         }
361     }
362     else
363     {
364         pGraphicsDevice = gpPrimaryGraphicsDevice;
365     }
366 
367     /* Allocate a new PDEVOBJ */
368     ppdev = PDEVOBJ_AllocPDEV();
369     if (!ppdev)
370     {
371         DPRINT1("failed to allocate a PDEV\n");
372         return NULL;
373     }
374 
375     /* If no DEVMODEW is given, ... */
376     if (!pdm)
377     {
378         /* ... use the device's default one */
379         pdm = pGraphicsDevice->pDevModeList[pGraphicsDevice->iDefaultMode].pdm;
380         DPRINT("Using iDefaultMode = %lu\n", pGraphicsDevice->iDefaultMode);
381     }
382 
383     /* Try to get a diplay driver */
384     ppdev->pldev = EngLoadImageEx(pdm->dmDeviceName, LDEV_DEVICE_DISPLAY);
385     if (!ppdev->pldev)
386     {
387         DPRINT1("Could not load display driver '%ls', '%ls'\n",
388                 pGraphicsDevice->pDiplayDrivers,
389                 pdm->dmDeviceName);
390         PDEVOBJ_vRelease(ppdev);
391         return NULL;
392     }
393 
394     /* Copy the function table */
395     ppdev->pfn = ppdev->pldev->pfn;
396 
397     /* Set MovePointer function */
398     ppdev->pfnMovePointer = ppdev->pfn.MovePointer;
399     if (!ppdev->pfnMovePointer)
400         ppdev->pfnMovePointer = EngMovePointer;
401 
402     ppdev->pGraphicsDevice = pGraphicsDevice;
403 
404     // DxEngGetHdevData asks for Graphics DeviceObject in hSpooler field
405     ppdev->hSpooler = ppdev->pGraphicsDevice->DeviceObject;
406 
407     // Should we change the ative mode of pGraphicsDevice ?
408     ppdev->pdmwDev = PDEVOBJ_pdmMatchDevMode(ppdev, pdm) ;
409 
410     /* FIXME! */
411     ppdev->flFlags = PDEV_DISPLAY;
412 
413     /* HACK: Don't use the pointer */
414     ppdev->Pointer.Exclude.right = -1;
415 
416     /* Call the driver to enable the PDEV */
417     if (!PDEVOBJ_bEnablePDEV(ppdev, pdm, NULL))
418     {
419         DPRINT1("Failed to enable PDEV!\n");
420         PDEVOBJ_vRelease(ppdev);
421         return NULL;
422     }
423 
424     /* FIXME: this must be done in a better way */
425     pGraphicsDevice->StateFlags |= DISPLAY_DEVICE_ATTACHED_TO_DESKTOP;
426 
427     /* Tell the driver that the PDEV is ready */
428     PDEVOBJ_vCompletePDEV(ppdev);
429 
430     /* Return the PDEV */
431     return ppdev;
432 }
433 
434 FORCEINLINE
435 VOID
436 SwitchPointer(
437     _Inout_ PVOID pvPointer1,
438     _Inout_ PVOID pvPointer2)
439 {
440     PVOID *ppvPointer1 = pvPointer1;
441     PVOID *ppvPointer2 = pvPointer2;
442     PVOID pvTemp;
443 
444     pvTemp = *ppvPointer1;
445     *ppvPointer1 = *ppvPointer2;
446     *ppvPointer2 = pvTemp;
447 }
448 
449 VOID
450 NTAPI
451 PDEVOBJ_vSwitchPdev(
452     PPDEVOBJ ppdev,
453     PPDEVOBJ ppdev2)
454 {
455     union
456     {
457         DRIVER_FUNCTIONS pfn;
458         GDIINFO gdiinfo;
459         DEVINFO devinfo;
460         DWORD StateFlags;
461     } temp;
462 
463     /* Exchange driver functions */
464     temp.pfn = ppdev->pfn;
465     ppdev->pfn = ppdev2->pfn;
466     ppdev2->pfn = temp.pfn;
467 
468     /* Exchange LDEVs */
469     SwitchPointer(&ppdev->pldev, &ppdev2->pldev);
470 
471     /* Exchange DHPDEV */
472     SwitchPointer(&ppdev->dhpdev, &ppdev2->dhpdev);
473 
474     /* Exchange surfaces and associate them with their new PDEV */
475     SwitchPointer(&ppdev->pSurface, &ppdev2->pSurface);
476     ppdev->pSurface->SurfObj.hdev = (HDEV)ppdev;
477     ppdev2->pSurface->SurfObj.hdev = (HDEV)ppdev2;
478 
479     /* Exchange devinfo */
480     temp.devinfo = ppdev->devinfo;
481     ppdev->devinfo = ppdev2->devinfo;
482     ppdev2->devinfo = temp.devinfo;
483 
484     /* Exchange gdiinfo */
485     temp.gdiinfo = ppdev->gdiinfo;
486     ppdev->gdiinfo = ppdev2->gdiinfo;
487     ppdev2->gdiinfo = temp.gdiinfo;
488 
489     /* Exchange DEVMODE */
490     SwitchPointer(&ppdev->pdmwDev, &ppdev2->pdmwDev);
491 
492     /* Exchange state flags */
493     temp.StateFlags = ppdev->pGraphicsDevice->StateFlags;
494     ppdev->pGraphicsDevice->StateFlags = ppdev2->pGraphicsDevice->StateFlags;
495     ppdev2->pGraphicsDevice->StateFlags = temp.StateFlags;
496 
497     /* Notify each driver instance of its new HDEV association */
498     ppdev->pfn.CompletePDEV(ppdev->dhpdev, (HDEV)ppdev);
499     ppdev2->pfn.CompletePDEV(ppdev2->dhpdev, (HDEV)ppdev2);
500 }
501 
502 
503 BOOL
504 NTAPI
505 PDEVOBJ_bSwitchMode(
506     PPDEVOBJ ppdev,
507     PDEVMODEW pdm)
508 {
509     UNICODE_STRING ustrDevice;
510     PPDEVOBJ ppdevTmp;
511     PSURFACE pSurface;
512     BOOL retval = FALSE;
513 
514     /* Lock the PDEV */
515     EngAcquireSemaphore(ppdev->hsemDevLock);
516 
517     /* And everything else */
518     EngAcquireSemaphore(ghsemPDEV);
519 
520     DPRINT1("PDEVOBJ_bSwitchMode, ppdev = %p, pSurface = %p\n", ppdev, ppdev->pSurface);
521 
522     // Lookup the GraphicsDevice + select DEVMODE
523     // pdm = PDEVOBJ_pdmMatchDevMode(ppdev, pdm);
524 
525     /* 1. Temporarily disable the current PDEV */
526     if (!ppdev->pfn.AssertMode(ppdev->dhpdev, FALSE))
527     {
528         DPRINT1("DrvAssertMode failed\n");
529         goto leave;
530     }
531 
532     /* 2. Create new PDEV */
533     RtlInitUnicodeString(&ustrDevice, ppdev->pGraphicsDevice->szWinDeviceName);
534     ppdevTmp = EngpCreatePDEV(&ustrDevice, pdm);
535     if (!ppdevTmp)
536     {
537         DPRINT1("Failed to create a new PDEV\n");
538         goto leave;
539     }
540 
541     /* 3. Create a new surface */
542     pSurface = PDEVOBJ_pSurface(ppdevTmp);
543     if (!pSurface)
544     {
545         DPRINT1("PDEVOBJ_pSurface failed\n");
546         goto leave;
547     }
548 
549     /* 4. Get DirectDraw information */
550     /* 5. Enable DirectDraw Not traced */
551     /* 6. Copy old PDEV state to new PDEV instance */
552 
553     /* 7. Switch the PDEVs */
554     PDEVOBJ_vSwitchPdev(ppdev, ppdevTmp);
555 
556     /* 8. Disable DirectDraw */
557 
558     PDEVOBJ_vRelease(ppdevTmp);
559 
560     /* Update primary display capabilities */
561     if(ppdev == gppdevPrimary)
562     {
563         PDEVOBJ_vGetDeviceCaps(ppdev, &GdiHandleTable->DevCaps);
564     }
565 
566     /* Success! */
567     retval = TRUE;
568 leave:
569     /* Unlock PDEV */
570     EngReleaseSemaphore(ppdev->hsemDevLock);
571     EngReleaseSemaphore(ghsemPDEV);
572 
573     DPRINT1("leave, ppdev = %p, pSurface = %p\n", ppdev, ppdev->pSurface);
574 
575     return retval;
576 }
577 
578 
579 PPDEVOBJ
580 NTAPI
581 EngpGetPDEV(
582     _In_opt_ PUNICODE_STRING pustrDeviceName)
583 {
584     UNICODE_STRING ustrCurrent;
585     PPDEVOBJ ppdev;
586     PGRAPHICS_DEVICE pGraphicsDevice;
587 
588     /* Acquire PDEV lock */
589     EngAcquireSemaphore(ghsemPDEV);
590 
591     /* Did the caller pass a device name? */
592     if (pustrDeviceName)
593     {
594         /* Loop all present PDEVs */
595         for (ppdev = gppdevList; ppdev; ppdev = ppdev->ppdevNext)
596         {
597             /* Get a pointer to the GRAPHICS_DEVICE */
598             pGraphicsDevice = ppdev->pGraphicsDevice;
599 
600             /* Compare the name */
601             RtlInitUnicodeString(&ustrCurrent, pGraphicsDevice->szWinDeviceName);
602             if (RtlEqualUnicodeString(pustrDeviceName, &ustrCurrent, FALSE))
603             {
604                 /* Found! */
605                 break;
606             }
607         }
608     }
609     else
610     {
611         /* Otherwise use the primary PDEV */
612         ppdev = gppdevPrimary;
613     }
614 
615     /* Did we find one? */
616     if (ppdev)
617     {
618         /* Yes, reference the PDEV */
619         InterlockedIncrement(&ppdev->cPdevRefs);
620     }
621     else
622     {
623         /* No, create a new PDEV for the given device */
624         ppdev = EngpCreatePDEV(pustrDeviceName, NULL);
625         if (ppdev)
626         {
627             /* Insert the PDEV into the list */
628             ppdev->ppdevNext = gppdevList;
629             gppdevList = ppdev;
630 
631             /* Set as primary PDEV, if we don't have one yet */
632             if (!gppdevPrimary)
633             {
634                 gppdevPrimary = ppdev;
635                 ppdev->pGraphicsDevice->StateFlags |= DISPLAY_DEVICE_PRIMARY_DEVICE;
636             }
637         }
638     }
639 
640     /* Release PDEV lock */
641     EngReleaseSemaphore(ghsemPDEV);
642 
643     return ppdev;
644 }
645 
646 INT
647 NTAPI
648 PDEVOBJ_iGetColorManagementCaps(PPDEVOBJ ppdev)
649 {
650     INT ret = CM_NONE;
651 
652     if (ppdev->flFlags & PDEV_DISPLAY)
653     {
654         if (ppdev->devinfo.iDitherFormat == BMF_8BPP ||
655             ppdev->devinfo.flGraphicsCaps2 & GCAPS2_CHANGEGAMMARAMP)
656             ret = CM_GAMMA_RAMP;
657     }
658 
659     if (ppdev->devinfo.flGraphicsCaps & GCAPS_CMYKCOLOR)
660         ret |= CM_CMYK_COLOR;
661     if (ppdev->devinfo.flGraphicsCaps & GCAPS_ICM)
662         ret |= CM_DEVICE_ICM;
663 
664     return ret;
665 }
666 
667 VOID
668 NTAPI
669 PDEVOBJ_vGetDeviceCaps(
670     IN PPDEVOBJ ppdev,
671     OUT PDEVCAPS pDevCaps)
672 {
673     PGDIINFO pGdiInfo = &ppdev->gdiinfo;
674 
675     pDevCaps->ulVersion = pGdiInfo->ulVersion;
676     pDevCaps->ulTechnology = pGdiInfo->ulTechnology;
677     pDevCaps->ulHorzSizeM = (pGdiInfo->ulHorzSize + 500) / 1000;
678     pDevCaps->ulVertSizeM = (pGdiInfo->ulVertSize + 500) / 1000;
679     pDevCaps->ulHorzSize = pGdiInfo->ulHorzSize;
680     pDevCaps->ulVertSize = pGdiInfo->ulVertSize;
681     pDevCaps->ulHorzRes = pGdiInfo->ulHorzRes;
682     pDevCaps->ulVertRes = pGdiInfo->ulVertRes;
683     pDevCaps->ulBitsPixel = pGdiInfo->cBitsPixel;
684     if (pDevCaps->ulBitsPixel == 15) pDevCaps->ulBitsPixel = 16;
685     pDevCaps->ulPlanes = pGdiInfo->cPlanes;
686     pDevCaps->ulNumPens = pGdiInfo->ulNumColors;
687     if (pDevCaps->ulNumPens != -1) pDevCaps->ulNumPens *= 5;
688     pDevCaps->ulNumFonts = 0; // PDEVOBJ_cFonts(ppdev);
689     pDevCaps->ulNumColors = pGdiInfo->ulNumColors;
690     pDevCaps->ulRasterCaps = pGdiInfo->flRaster;
691     pDevCaps->ulAspectX = pGdiInfo->ulAspectX;
692     pDevCaps->ulAspectY = pGdiInfo->ulAspectY;
693     pDevCaps->ulAspectXY = pGdiInfo->ulAspectXY;
694     pDevCaps->ulLogPixelsX = pGdiInfo->ulLogPixelsX;
695     pDevCaps->ulLogPixelsY = pGdiInfo->ulLogPixelsY;
696     pDevCaps->ulSizePalette = pGdiInfo->ulNumPalReg;
697     pDevCaps->ulColorRes = pGdiInfo->ulDACRed +
698                            pGdiInfo->ulDACGreen +
699                            pGdiInfo->ulDACBlue;
700     pDevCaps->ulPhysicalWidth = pGdiInfo->szlPhysSize.cx;
701     pDevCaps->ulPhysicalHeight = pGdiInfo->szlPhysSize.cy;
702     pDevCaps->ulPhysicalOffsetX = pGdiInfo->ptlPhysOffset.x;
703     pDevCaps->ulPhysicalOffsetY = pGdiInfo->ptlPhysOffset.y;
704     pDevCaps->ulTextCaps = pGdiInfo->flTextCaps;
705     pDevCaps->ulTextCaps |= (TC_SO_ABLE|TC_UA_ABLE|TC_CP_STROKE|TC_OP_STROKE|TC_OP_CHARACTER);
706     if (pGdiInfo->ulTechnology != DT_PLOTTER)
707         pDevCaps->ulTextCaps |= TC_VA_ABLE;
708     pDevCaps->ulVRefresh = pGdiInfo->ulVRefresh;
709     pDevCaps->ulDesktopHorzRes = pGdiInfo->ulHorzRes;
710     pDevCaps->ulDesktopVertRes = pGdiInfo->ulVertRes;
711     pDevCaps->ulBltAlignment = pGdiInfo->ulBltAlignment;
712     pDevCaps->ulPanningHorzRes = pGdiInfo->ulPanningHorzRes;
713     pDevCaps->ulPanningVertRes = pGdiInfo->ulPanningVertRes;
714     pDevCaps->xPanningAlignment = pGdiInfo->xPanningAlignment;
715     pDevCaps->yPanningAlignment = pGdiInfo->yPanningAlignment;
716     pDevCaps->ulShadeBlend = pGdiInfo->flShadeBlend;
717     pDevCaps->ulColorMgmtCaps = PDEVOBJ_iGetColorManagementCaps(ppdev);
718 }
719 
720 
721 /** Exported functions ********************************************************/
722 
723 _Must_inspect_result_ _Ret_z_
724 LPWSTR
725 APIENTRY
726 EngGetDriverName(_In_ HDEV hdev)
727 {
728     PPDEVOBJ ppdev = (PPDEVOBJ)hdev;
729 
730     ASSERT(ppdev);
731     ASSERT(ppdev->pldev);
732     ASSERT(ppdev->pldev->pGdiDriverInfo);
733     ASSERT(ppdev->pldev->pGdiDriverInfo->DriverName.Buffer);
734 
735     return ppdev->pldev->pGdiDriverInfo->DriverName.Buffer;
736 }
737 
738 
739 INT
740 APIENTRY
741 NtGdiGetDeviceCaps(
742     HDC hdc,
743     INT Index)
744 {
745     PDC pdc;
746     DEVCAPS devcaps;
747 
748     /* Lock the given DC */
749     pdc = DC_LockDc(hdc);
750     if (!pdc)
751     {
752         EngSetLastError(ERROR_INVALID_HANDLE);
753         return 0;
754     }
755 
756     /* Get the data */
757     PDEVOBJ_vGetDeviceCaps(pdc->ppdev, &devcaps);
758 
759     /* Unlock the DC */
760     DC_UnlockDc(pdc);
761 
762     /* Return capability */
763     switch (Index)
764     {
765         case DRIVERVERSION:
766             return devcaps.ulVersion;
767 
768         case TECHNOLOGY:
769             return devcaps.ulTechnology;
770 
771         case HORZSIZE:
772             return devcaps.ulHorzSize;
773 
774         case VERTSIZE:
775             return devcaps.ulVertSize;
776 
777         case HORZRES:
778             return devcaps.ulHorzRes;
779 
780         case VERTRES:
781             return devcaps.ulVertRes;
782 
783         case LOGPIXELSX:
784             return devcaps.ulLogPixelsX;
785 
786         case LOGPIXELSY:
787             return devcaps.ulLogPixelsY;
788 
789         case BITSPIXEL:
790             return devcaps.ulBitsPixel;
791 
792         case PLANES:
793             return devcaps.ulPlanes;
794 
795         case NUMBRUSHES:
796             return -1;
797 
798         case NUMPENS:
799             return devcaps.ulNumPens;
800 
801         case NUMFONTS:
802             return devcaps.ulNumFonts;
803 
804         case NUMCOLORS:
805             return devcaps.ulNumColors;
806 
807         case ASPECTX:
808             return devcaps.ulAspectX;
809 
810         case ASPECTY:
811             return devcaps.ulAspectY;
812 
813         case ASPECTXY:
814             return devcaps.ulAspectXY;
815 
816         case CLIPCAPS:
817             return CP_RECTANGLE;
818 
819         case SIZEPALETTE:
820             return devcaps.ulSizePalette;
821 
822         case NUMRESERVED:
823             return 20;
824 
825         case COLORRES:
826             return devcaps.ulColorRes;
827 
828         case DESKTOPVERTRES:
829             return devcaps.ulVertRes;
830 
831         case DESKTOPHORZRES:
832             return devcaps.ulHorzRes;
833 
834         case BLTALIGNMENT:
835             return devcaps.ulBltAlignment;
836 
837         case SHADEBLENDCAPS:
838             return devcaps.ulShadeBlend;
839 
840         case COLORMGMTCAPS:
841             return devcaps.ulColorMgmtCaps;
842 
843         case PHYSICALWIDTH:
844             return devcaps.ulPhysicalWidth;
845 
846         case PHYSICALHEIGHT:
847             return devcaps.ulPhysicalHeight;
848 
849         case PHYSICALOFFSETX:
850             return devcaps.ulPhysicalOffsetX;
851 
852         case PHYSICALOFFSETY:
853             return devcaps.ulPhysicalOffsetY;
854 
855         case VREFRESH:
856             return devcaps.ulVRefresh;
857 
858         case RASTERCAPS:
859             return devcaps.ulRasterCaps;
860 
861         case CURVECAPS:
862             return (CC_CIRCLES | CC_PIE | CC_CHORD | CC_ELLIPSES | CC_WIDE |
863                     CC_STYLED | CC_WIDESTYLED | CC_INTERIORS | CC_ROUNDRECT);
864 
865         case LINECAPS:
866             return (LC_POLYLINE | LC_MARKER | LC_POLYMARKER | LC_WIDE |
867                     LC_STYLED | LC_WIDESTYLED | LC_INTERIORS);
868 
869         case POLYGONALCAPS:
870             return (PC_POLYGON | PC_RECTANGLE | PC_WINDPOLYGON | PC_SCANLINE |
871                     PC_WIDE | PC_STYLED | PC_WIDESTYLED | PC_INTERIORS);
872 
873         case TEXTCAPS:
874             return devcaps.ulTextCaps;
875 
876         case CAPS1:
877         case PDEVICESIZE:
878         case SCALINGFACTORX:
879         case SCALINGFACTORY:
880         default:
881             return 0;
882     }
883 
884     return 0;
885 }
886 
887 _Success_(return!=FALSE)
888 BOOL
889 APIENTRY
890 NtGdiGetDeviceCapsAll(
891     IN HDC hDC,
892     OUT PDEVCAPS pDevCaps)
893 {
894     PDC pdc;
895     DEVCAPS devcaps;
896     BOOL bResult = TRUE;
897 
898     /* Lock the given DC */
899     pdc = DC_LockDc(hDC);
900     if (!pdc)
901     {
902         EngSetLastError(ERROR_INVALID_HANDLE);
903         return FALSE;
904     }
905 
906     /* Get the data */
907     PDEVOBJ_vGetDeviceCaps(pdc->ppdev, &devcaps);
908 
909     /* Unlock the DC */
910     DC_UnlockDc(pdc);
911 
912     /* Copy data to caller */
913     _SEH2_TRY
914     {
915         ProbeForWrite(pDevCaps, sizeof(DEVCAPS), 1);
916         RtlCopyMemory(pDevCaps, &devcaps, sizeof(DEVCAPS));
917     }
918     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
919     {
920         SetLastNtError(_SEH2_GetExceptionCode());
921         bResult = FALSE;
922     }
923     _SEH2_END;
924 
925     return bResult;
926 }
927 
928 DHPDEV
929 APIENTRY
930 NtGdiGetDhpdev(
931     IN HDEV hdev)
932 {
933     PPDEVOBJ ppdev;
934     DHPDEV dhpdev = NULL;
935 
936     /* Check parameter */
937     if (!hdev || (PCHAR)hdev < (PCHAR)MmSystemRangeStart)
938         return NULL;
939 
940     /* Lock PDEV list */
941     EngAcquireSemaphoreShared(ghsemPDEV);
942 
943     /* Walk through the list of PDEVs */
944     for (ppdev = gppdevList;  ppdev; ppdev = ppdev->ppdevNext)
945     {
946         /* Compare with the given HDEV */
947         if (ppdev == (PPDEVOBJ)hdev)
948         {
949             /* Found the PDEV! Get it's dhpdev and break */
950             dhpdev = ppdev->dhpdev;
951             break;
952         }
953     }
954 
955     /* Unlock PDEV list */
956     EngReleaseSemaphore(ghsemPDEV);
957 
958     return dhpdev;
959 }
960 
961 PSIZEL
962 FASTCALL
963 PDEVOBJ_sizl(PPDEVOBJ ppdev, PSIZEL psizl)
964 {
965     if (ppdev->flFlags & PDEV_META_DEVICE)
966     {
967         psizl->cx = ppdev->ulHorzRes;
968         psizl->cy = ppdev->ulVertRes;
969     }
970     else
971     {
972         psizl->cx = ppdev->gdiinfo.ulHorzRes;
973         psizl->cy = ppdev->gdiinfo.ulVertRes;
974     }
975     return psizl;
976 }
977