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