xref: /reactos/win32ss/gdi/eng/pdevobj.c (revision 01e5cb0c)
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 and reset video to its default mode */
526     if (!ppdev->pfn.AssertMode(ppdev->dhpdev, FALSE))
527     {
528         DPRINT1("DrvAssertMode(FALSE) 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 leave2;
539     }
540 
541     /* 3. Create a new surface */
542     pSurface = PDEVOBJ_pSurface(ppdevTmp);
543     if (!pSurface)
544     {
545         DPRINT1("PDEVOBJ_pSurface failed\n");
546         PDEVOBJ_vRelease(ppdevTmp);
547         goto leave2;
548     }
549 
550     /* 4. Get DirectDraw information */
551     /* 5. Enable DirectDraw Not traced */
552     /* 6. Copy old PDEV state to new PDEV instance */
553 
554     /* 7. Switch the PDEVs */
555     PDEVOBJ_vSwitchPdev(ppdev, ppdevTmp);
556 
557     /* 8. Disable DirectDraw */
558 
559     PDEVOBJ_vRelease(ppdevTmp);
560 
561     /* Update primary display capabilities */
562     if(ppdev == gppdevPrimary)
563     {
564         PDEVOBJ_vGetDeviceCaps(ppdev, &GdiHandleTable->DevCaps);
565     }
566 
567     /* Success! */
568     retval = TRUE;
569 
570 leave2:
571     /* Set the new video mode, or restore the original one in case of failure */
572     if (!ppdev->pfn.AssertMode(ppdev->dhpdev, TRUE))
573     {
574         DPRINT1("DrvAssertMode(TRUE) failed\n");
575     }
576 
577 leave:
578     /* Unlock everything else */
579     EngReleaseSemaphore(ghsemPDEV);
580     /* Unlock the PDEV */
581     EngReleaseSemaphore(ppdev->hsemDevLock);
582 
583     DPRINT1("leave, ppdev = %p, pSurface = %p\n", ppdev, ppdev->pSurface);
584 
585     return retval;
586 }
587 
588 
589 PPDEVOBJ
590 NTAPI
591 EngpGetPDEV(
592     _In_opt_ PUNICODE_STRING pustrDeviceName)
593 {
594     UNICODE_STRING ustrCurrent;
595     PPDEVOBJ ppdev;
596     PGRAPHICS_DEVICE pGraphicsDevice;
597 
598     /* Acquire PDEV lock */
599     EngAcquireSemaphore(ghsemPDEV);
600 
601     /* Did the caller pass a device name? */
602     if (pustrDeviceName)
603     {
604         /* Loop all present PDEVs */
605         for (ppdev = gppdevList; ppdev; ppdev = ppdev->ppdevNext)
606         {
607             /* Get a pointer to the GRAPHICS_DEVICE */
608             pGraphicsDevice = ppdev->pGraphicsDevice;
609 
610             /* Compare the name */
611             RtlInitUnicodeString(&ustrCurrent, pGraphicsDevice->szWinDeviceName);
612             if (RtlEqualUnicodeString(pustrDeviceName, &ustrCurrent, FALSE))
613             {
614                 /* Found! */
615                 break;
616             }
617         }
618     }
619     else
620     {
621         /* Otherwise use the primary PDEV */
622         ppdev = gppdevPrimary;
623     }
624 
625     /* Did we find one? */
626     if (ppdev)
627     {
628         /* Yes, reference the PDEV */
629         InterlockedIncrement(&ppdev->cPdevRefs);
630     }
631     else
632     {
633         /* No, create a new PDEV for the given device */
634         ppdev = EngpCreatePDEV(pustrDeviceName, NULL);
635         if (ppdev)
636         {
637             /* Insert the PDEV into the list */
638             ppdev->ppdevNext = gppdevList;
639             gppdevList = ppdev;
640 
641             /* Set as primary PDEV, if we don't have one yet */
642             if (!gppdevPrimary)
643             {
644                 gppdevPrimary = ppdev;
645                 ppdev->pGraphicsDevice->StateFlags |= DISPLAY_DEVICE_PRIMARY_DEVICE;
646             }
647         }
648     }
649 
650     /* Release PDEV lock */
651     EngReleaseSemaphore(ghsemPDEV);
652 
653     return ppdev;
654 }
655 
656 INT
657 NTAPI
658 PDEVOBJ_iGetColorManagementCaps(PPDEVOBJ ppdev)
659 {
660     INT ret = CM_NONE;
661 
662     if (ppdev->flFlags & PDEV_DISPLAY)
663     {
664         if (ppdev->devinfo.iDitherFormat == BMF_8BPP ||
665             ppdev->devinfo.flGraphicsCaps2 & GCAPS2_CHANGEGAMMARAMP)
666             ret = CM_GAMMA_RAMP;
667     }
668 
669     if (ppdev->devinfo.flGraphicsCaps & GCAPS_CMYKCOLOR)
670         ret |= CM_CMYK_COLOR;
671     if (ppdev->devinfo.flGraphicsCaps & GCAPS_ICM)
672         ret |= CM_DEVICE_ICM;
673 
674     return ret;
675 }
676 
677 VOID
678 NTAPI
679 PDEVOBJ_vGetDeviceCaps(
680     IN PPDEVOBJ ppdev,
681     OUT PDEVCAPS pDevCaps)
682 {
683     PGDIINFO pGdiInfo = &ppdev->gdiinfo;
684 
685     pDevCaps->ulVersion = pGdiInfo->ulVersion;
686     pDevCaps->ulTechnology = pGdiInfo->ulTechnology;
687     pDevCaps->ulHorzSizeM = (pGdiInfo->ulHorzSize + 500) / 1000;
688     pDevCaps->ulVertSizeM = (pGdiInfo->ulVertSize + 500) / 1000;
689     pDevCaps->ulHorzSize = pGdiInfo->ulHorzSize;
690     pDevCaps->ulVertSize = pGdiInfo->ulVertSize;
691     pDevCaps->ulHorzRes = pGdiInfo->ulHorzRes;
692     pDevCaps->ulVertRes = pGdiInfo->ulVertRes;
693     pDevCaps->ulBitsPixel = pGdiInfo->cBitsPixel;
694     if (pDevCaps->ulBitsPixel == 15) pDevCaps->ulBitsPixel = 16;
695     pDevCaps->ulPlanes = pGdiInfo->cPlanes;
696     pDevCaps->ulNumPens = pGdiInfo->ulNumColors;
697     if (pDevCaps->ulNumPens != -1) pDevCaps->ulNumPens *= 5;
698     pDevCaps->ulNumFonts = 0; // PDEVOBJ_cFonts(ppdev);
699     pDevCaps->ulNumColors = pGdiInfo->ulNumColors;
700     pDevCaps->ulRasterCaps = pGdiInfo->flRaster;
701     pDevCaps->ulAspectX = pGdiInfo->ulAspectX;
702     pDevCaps->ulAspectY = pGdiInfo->ulAspectY;
703     pDevCaps->ulAspectXY = pGdiInfo->ulAspectXY;
704     pDevCaps->ulLogPixelsX = pGdiInfo->ulLogPixelsX;
705     pDevCaps->ulLogPixelsY = pGdiInfo->ulLogPixelsY;
706     pDevCaps->ulSizePalette = pGdiInfo->ulNumPalReg;
707     pDevCaps->ulColorRes = pGdiInfo->ulDACRed +
708                            pGdiInfo->ulDACGreen +
709                            pGdiInfo->ulDACBlue;
710     pDevCaps->ulPhysicalWidth = pGdiInfo->szlPhysSize.cx;
711     pDevCaps->ulPhysicalHeight = pGdiInfo->szlPhysSize.cy;
712     pDevCaps->ulPhysicalOffsetX = pGdiInfo->ptlPhysOffset.x;
713     pDevCaps->ulPhysicalOffsetY = pGdiInfo->ptlPhysOffset.y;
714     pDevCaps->ulTextCaps = pGdiInfo->flTextCaps;
715     pDevCaps->ulTextCaps |= (TC_SO_ABLE|TC_UA_ABLE|TC_CP_STROKE|TC_OP_STROKE|TC_OP_CHARACTER);
716     if (pGdiInfo->ulTechnology != DT_PLOTTER)
717         pDevCaps->ulTextCaps |= TC_VA_ABLE;
718     pDevCaps->ulVRefresh = pGdiInfo->ulVRefresh;
719     pDevCaps->ulDesktopHorzRes = pGdiInfo->ulHorzRes;
720     pDevCaps->ulDesktopVertRes = pGdiInfo->ulVertRes;
721     pDevCaps->ulBltAlignment = pGdiInfo->ulBltAlignment;
722     pDevCaps->ulPanningHorzRes = pGdiInfo->ulPanningHorzRes;
723     pDevCaps->ulPanningVertRes = pGdiInfo->ulPanningVertRes;
724     pDevCaps->xPanningAlignment = pGdiInfo->xPanningAlignment;
725     pDevCaps->yPanningAlignment = pGdiInfo->yPanningAlignment;
726     pDevCaps->ulShadeBlend = pGdiInfo->flShadeBlend;
727     pDevCaps->ulColorMgmtCaps = PDEVOBJ_iGetColorManagementCaps(ppdev);
728 }
729 
730 
731 /** Exported functions ********************************************************/
732 
733 _Must_inspect_result_ _Ret_z_
734 LPWSTR
735 APIENTRY
736 EngGetDriverName(_In_ HDEV hdev)
737 {
738     PPDEVOBJ ppdev = (PPDEVOBJ)hdev;
739 
740     ASSERT(ppdev);
741     ASSERT(ppdev->pldev);
742     ASSERT(ppdev->pldev->pGdiDriverInfo);
743     ASSERT(ppdev->pldev->pGdiDriverInfo->DriverName.Buffer);
744 
745     return ppdev->pldev->pGdiDriverInfo->DriverName.Buffer;
746 }
747 
748 
749 INT
750 APIENTRY
751 NtGdiGetDeviceCaps(
752     HDC hdc,
753     INT Index)
754 {
755     PDC pdc;
756     DEVCAPS devcaps;
757 
758     /* Lock the given DC */
759     pdc = DC_LockDc(hdc);
760     if (!pdc)
761     {
762         EngSetLastError(ERROR_INVALID_HANDLE);
763         return 0;
764     }
765 
766     /* Get the data */
767     PDEVOBJ_vGetDeviceCaps(pdc->ppdev, &devcaps);
768 
769     /* Unlock the DC */
770     DC_UnlockDc(pdc);
771 
772     /* Return capability */
773     switch (Index)
774     {
775         case DRIVERVERSION:
776             return devcaps.ulVersion;
777 
778         case TECHNOLOGY:
779             return devcaps.ulTechnology;
780 
781         case HORZSIZE:
782             return devcaps.ulHorzSize;
783 
784         case VERTSIZE:
785             return devcaps.ulVertSize;
786 
787         case HORZRES:
788             return devcaps.ulHorzRes;
789 
790         case VERTRES:
791             return devcaps.ulVertRes;
792 
793         case LOGPIXELSX:
794             return devcaps.ulLogPixelsX;
795 
796         case LOGPIXELSY:
797             return devcaps.ulLogPixelsY;
798 
799         case BITSPIXEL:
800             return devcaps.ulBitsPixel;
801 
802         case PLANES:
803             return devcaps.ulPlanes;
804 
805         case NUMBRUSHES:
806             return -1;
807 
808         case NUMPENS:
809             return devcaps.ulNumPens;
810 
811         case NUMFONTS:
812             return devcaps.ulNumFonts;
813 
814         case NUMCOLORS:
815             return devcaps.ulNumColors;
816 
817         case ASPECTX:
818             return devcaps.ulAspectX;
819 
820         case ASPECTY:
821             return devcaps.ulAspectY;
822 
823         case ASPECTXY:
824             return devcaps.ulAspectXY;
825 
826         case CLIPCAPS:
827             return CP_RECTANGLE;
828 
829         case SIZEPALETTE:
830             return devcaps.ulSizePalette;
831 
832         case NUMRESERVED:
833             return 20;
834 
835         case COLORRES:
836             return devcaps.ulColorRes;
837 
838         case DESKTOPVERTRES:
839             return devcaps.ulVertRes;
840 
841         case DESKTOPHORZRES:
842             return devcaps.ulHorzRes;
843 
844         case BLTALIGNMENT:
845             return devcaps.ulBltAlignment;
846 
847         case SHADEBLENDCAPS:
848             return devcaps.ulShadeBlend;
849 
850         case COLORMGMTCAPS:
851             return devcaps.ulColorMgmtCaps;
852 
853         case PHYSICALWIDTH:
854             return devcaps.ulPhysicalWidth;
855 
856         case PHYSICALHEIGHT:
857             return devcaps.ulPhysicalHeight;
858 
859         case PHYSICALOFFSETX:
860             return devcaps.ulPhysicalOffsetX;
861 
862         case PHYSICALOFFSETY:
863             return devcaps.ulPhysicalOffsetY;
864 
865         case VREFRESH:
866             return devcaps.ulVRefresh;
867 
868         case RASTERCAPS:
869             return devcaps.ulRasterCaps;
870 
871         case CURVECAPS:
872             return (CC_CIRCLES | CC_PIE | CC_CHORD | CC_ELLIPSES | CC_WIDE |
873                     CC_STYLED | CC_WIDESTYLED | CC_INTERIORS | CC_ROUNDRECT);
874 
875         case LINECAPS:
876             return (LC_POLYLINE | LC_MARKER | LC_POLYMARKER | LC_WIDE |
877                     LC_STYLED | LC_WIDESTYLED | LC_INTERIORS);
878 
879         case POLYGONALCAPS:
880             return (PC_POLYGON | PC_RECTANGLE | PC_WINDPOLYGON | PC_SCANLINE |
881                     PC_WIDE | PC_STYLED | PC_WIDESTYLED | PC_INTERIORS);
882 
883         case TEXTCAPS:
884             return devcaps.ulTextCaps;
885 
886         case CAPS1:
887         case PDEVICESIZE:
888         case SCALINGFACTORX:
889         case SCALINGFACTORY:
890         default:
891             return 0;
892     }
893 
894     return 0;
895 }
896 
897 _Success_(return!=FALSE)
898 BOOL
899 APIENTRY
900 NtGdiGetDeviceCapsAll(
901     IN HDC hDC,
902     OUT PDEVCAPS pDevCaps)
903 {
904     PDC pdc;
905     DEVCAPS devcaps;
906     BOOL bResult = TRUE;
907 
908     /* Lock the given DC */
909     pdc = DC_LockDc(hDC);
910     if (!pdc)
911     {
912         EngSetLastError(ERROR_INVALID_HANDLE);
913         return FALSE;
914     }
915 
916     /* Get the data */
917     PDEVOBJ_vGetDeviceCaps(pdc->ppdev, &devcaps);
918 
919     /* Unlock the DC */
920     DC_UnlockDc(pdc);
921 
922     /* Copy data to caller */
923     _SEH2_TRY
924     {
925         ProbeForWrite(pDevCaps, sizeof(DEVCAPS), 1);
926         RtlCopyMemory(pDevCaps, &devcaps, sizeof(DEVCAPS));
927     }
928     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
929     {
930         SetLastNtError(_SEH2_GetExceptionCode());
931         bResult = FALSE;
932     }
933     _SEH2_END;
934 
935     return bResult;
936 }
937 
938 DHPDEV
939 APIENTRY
940 NtGdiGetDhpdev(
941     IN HDEV hdev)
942 {
943     PPDEVOBJ ppdev;
944     DHPDEV dhpdev = NULL;
945 
946     /* Check parameter */
947     if (!hdev || (PCHAR)hdev < (PCHAR)MmSystemRangeStart)
948         return NULL;
949 
950     /* Lock PDEV list */
951     EngAcquireSemaphoreShared(ghsemPDEV);
952 
953     /* Walk through the list of PDEVs */
954     for (ppdev = gppdevList;  ppdev; ppdev = ppdev->ppdevNext)
955     {
956         /* Compare with the given HDEV */
957         if (ppdev == (PPDEVOBJ)hdev)
958         {
959             /* Found the PDEV! Get it's dhpdev and break */
960             dhpdev = ppdev->dhpdev;
961             break;
962         }
963     }
964 
965     /* Unlock PDEV list */
966     EngReleaseSemaphore(ghsemPDEV);
967 
968     return dhpdev;
969 }
970 
971 PSIZEL
972 FASTCALL
973 PDEVOBJ_sizl(PPDEVOBJ ppdev, PSIZEL psizl)
974 {
975     if (ppdev->flFlags & PDEV_META_DEVICE)
976     {
977         psizl->cx = ppdev->ulHorzRes;
978         psizl->cy = ppdev->ulVertRes;
979     }
980     else
981     {
982         psizl->cx = ppdev->gdiinfo.ulHorzRes;
983         psizl->cy = ppdev->gdiinfo.ulVertRes;
984     }
985     return psizl;
986 }
987