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