xref: /reactos/win32ss/user/ntuser/windc.c (revision d6eebaa4)
1 /*
2  * PROJECT:         ReactOS Win32k subsystem
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            win32ss/user/ntuser/windc.c
5  * PURPOSE:         Window DC management
6  * COPYRIGHT:       Copyright 2007 ReactOS Team
7  */
8 
9 #include <win32k.h>
10 DBG_DEFAULT_CHANNEL(UserDce);
11 
12 /* GLOBALS *******************************************************************/
13 
14 /* NOTE: I think we should store this per window station (including GDI objects) */
15 /* Answer: No, use the DCE pMonitor to compare with! */
16 
17 static LIST_ENTRY LEDce;
18 static INT DCECount = 0; // Count of DCE in system.
19 
20 #define DCX_CACHECOMPAREMASK (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | \
21                               DCX_NORESETATTRS | DCX_LOCKWINDOWUPDATE | \
22                               DCX_LAYEREDWIN | DCX_CACHE | DCX_WINDOW | \
23                               DCX_PARENTCLIP)
24 
25 /* FUNCTIONS *****************************************************************/
26 
27 CODE_SEG("INIT")
28 NTSTATUS
29 NTAPI
30 InitDCEImpl(VOID)
31 {
32     InitializeListHead(&LEDce);
33     return STATUS_SUCCESS;
34 }
35 
36 //
37 // This should be moved to dc.c or dcutil.c.
38 //
39 HDC FASTCALL
40 DceCreateDisplayDC(VOID)
41 {
42   UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"DISPLAY");
43 
44   if (!co_IntGraphicsCheck(TRUE))
45     KeBugCheckEx(VIDEO_DRIVER_INIT_FAILURE, 0, 0, 0, USER_VERSION);
46 
47   return IntGdiCreateDC(&DriverName, NULL, NULL, NULL, FALSE);
48 }
49 
50 /* Returns the DCE pointer from the HDC handle */
51 DCE*
52 FASTCALL
53 DceGetDceFromDC(HDC hdc)
54 {
55     PLIST_ENTRY ListEntry;
56     DCE* dce;
57 
58     ListEntry = LEDce.Flink;
59     while (ListEntry != &LEDce)
60     {
61         dce = CONTAINING_RECORD(ListEntry, DCE, List);
62         ListEntry = ListEntry->Flink;
63         if (dce->hDC == hdc)
64             return dce;
65     }
66 
67     return NULL;
68 }
69 
70 static
71 PREGION FASTCALL
72 DceGetVisRgn(PWND Window, ULONG Flags, HWND hWndChild, ULONG CFlags)
73 {
74     PREGION Rgn;
75     Rgn = VIS_ComputeVisibleRegion( Window,
76                                     0 == (Flags & DCX_WINDOW),
77                                     0 != (Flags & DCX_CLIPCHILDREN),
78                                     0 != (Flags & DCX_CLIPSIBLINGS));
79     /* Caller expects a non-null region */
80     if (!Rgn)
81         Rgn = IntSysCreateRectpRgn(0, 0, 0, 0);
82     return Rgn;
83 }
84 
85 PDCE FASTCALL
86 DceAllocDCE(PWND Window OPTIONAL, DCE_TYPE Type)
87 {
88   PDCE pDce;
89 
90   pDce = ExAllocatePoolWithTag(PagedPool, sizeof(DCE), USERTAG_DCE);
91   if(!pDce)
92         return NULL;
93 
94   pDce->hDC = DceCreateDisplayDC();
95   if (!pDce->hDC)
96   {
97       ExFreePoolWithTag(pDce, USERTAG_DCE);
98       return NULL;
99   }
100   DCECount++;
101   TRACE("Alloc DCE's! %d\n",DCECount);
102   pDce->hwndCurrent = (Window ? UserHMGetHandle(Window) : NULL);
103   pDce->pwndOrg  = Window;
104   pDce->pwndClip = Window;
105   pDce->hrgnClip = NULL;
106   pDce->hrgnClipPublic = NULL;
107   pDce->hrgnSavedVis = NULL;
108   pDce->ppiOwner = NULL;
109 
110   InsertTailList(&LEDce, &pDce->List);
111 
112   DCU_SetDcUndeletable(pDce->hDC);
113 
114   if (Type == DCE_WINDOW_DC || Type == DCE_CLASS_DC) // Window DCE have ownership.
115   {
116      pDce->ptiOwner = GetW32ThreadInfo();
117   }
118   else
119   {
120      TRACE("FREE DCATTR!!!! NOT DCE_WINDOW_DC!!!!! hDC-> %p\n", pDce->hDC);
121      GreSetDCOwner(pDce->hDC, GDI_OBJ_HMGR_NONE);
122      pDce->ptiOwner = NULL;
123   }
124 
125   if (Type == DCE_CACHE_DC)
126   {
127      pDce->DCXFlags = DCX_CACHE | DCX_DCEEMPTY;
128   }
129   else
130   {
131      pDce->DCXFlags = DCX_DCEBUSY;
132      if (Window)
133      {
134         if (Type == DCE_WINDOW_DC)
135         {
136           if (Window->style & WS_CLIPCHILDREN) pDce->DCXFlags |= DCX_CLIPCHILDREN;
137           if (Window->style & WS_CLIPSIBLINGS) pDce->DCXFlags |= DCX_CLIPSIBLINGS;
138         }
139      }
140   }
141   return(pDce);
142 }
143 
144 static VOID APIENTRY
145 DceSetDrawable( PWND Window OPTIONAL,
146                 HDC hDC,
147                 ULONG Flags,
148                 BOOL SetClipOrigin)
149 {
150   RECTL rect = {0,0,0,0};
151 
152   if (Window)
153   {
154       if (Flags & DCX_WINDOW)
155       {
156          rect = Window->rcWindow;
157       }
158       else
159       {
160          rect = Window->rcClient;
161       }
162   }
163 
164   /* Set DC Origin and Window Rectangle */
165   GreSetDCOrg( hDC, rect.left, rect.top, &rect);
166 }
167 
168 
169 static VOID FASTCALL
170 DceDeleteClipRgn(DCE* Dce)
171 {
172    Dce->DCXFlags &= ~(DCX_EXCLUDERGN | DCX_INTERSECTRGN);
173 
174    if (Dce->DCXFlags & DCX_KEEPCLIPRGN )
175    {
176       Dce->DCXFlags &= ~DCX_KEEPCLIPRGN;
177    }
178    else if (Dce->hrgnClip != NULL)
179    {
180       GreDeleteObject(Dce->hrgnClip);
181    }
182 
183    Dce->hrgnClip = NULL;
184 
185    /* Make it dirty so that the vis rgn gets recomputed next time */
186    Dce->DCXFlags |= DCX_DCEDIRTY;
187    IntGdiSetHookFlags(Dce->hDC, DCHF_INVALIDATEVISRGN);
188 }
189 
190 VOID
191 FASTCALL
192 DceUpdateVisRgn(DCE *Dce, PWND Window, ULONG Flags)
193 {
194    PREGION RgnVisible = NULL;
195    ULONG DcxFlags;
196    PWND DesktopWindow;
197 
198    if (Flags & DCX_PARENTCLIP)
199    {
200       PWND Parent;
201 
202       Parent = Window->spwndParent;
203       if (!Parent)
204       {
205          RgnVisible = NULL;
206          goto noparent;
207       }
208 
209       if (Parent->style & WS_CLIPSIBLINGS)
210       {
211          DcxFlags = DCX_CLIPSIBLINGS |
212                     (Flags & ~(DCX_CLIPCHILDREN | DCX_WINDOW));
213       }
214       else
215       {
216          DcxFlags = Flags & ~(DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_WINDOW);
217       }
218       RgnVisible = DceGetVisRgn(Parent, DcxFlags, UserHMGetHandle(Window), Flags);
219    }
220    else if (Window == NULL)
221    {
222       DesktopWindow = UserGetWindowObject(IntGetDesktopWindow());
223       if (NULL != DesktopWindow)
224       {
225          RgnVisible = IntSysCreateRectpRgnIndirect(&DesktopWindow->rcWindow);
226       }
227       else
228       {
229          RgnVisible = NULL;
230       }
231    }
232    else
233    {
234       RgnVisible = DceGetVisRgn(Window, Flags, 0, 0);
235    }
236 
237 noparent:
238    if (Flags & DCX_INTERSECTRGN)
239    {
240       PREGION RgnClip = NULL;
241 
242       if (Dce->hrgnClip != NULL)
243           RgnClip = REGION_LockRgn(Dce->hrgnClip);
244 
245       if (RgnClip)
246       {
247          IntGdiCombineRgn(RgnVisible, RgnVisible, RgnClip, RGN_AND);
248          REGION_UnlockRgn(RgnClip);
249       }
250       else
251       {
252          if (RgnVisible != NULL)
253          {
254             REGION_Delete(RgnVisible);
255          }
256          RgnVisible = IntSysCreateRectpRgn(0, 0, 0, 0);
257       }
258    }
259    else if ((Flags & DCX_EXCLUDERGN) && Dce->hrgnClip != NULL)
260    {
261        PREGION RgnClip = REGION_LockRgn(Dce->hrgnClip);
262        IntGdiCombineRgn(RgnVisible, RgnVisible, RgnClip, RGN_DIFF);
263        REGION_UnlockRgn(RgnClip);
264    }
265 
266    Dce->DCXFlags &= ~DCX_DCEDIRTY;
267    GdiSelectVisRgn(Dce->hDC, RgnVisible);
268    /* Tell GDI driver */
269    if (Window)
270        IntEngWindowChanged(Window, WOC_RGN_CLIENT);
271 
272    if (RgnVisible != NULL)
273    {
274       REGION_Delete(RgnVisible);
275    }
276 }
277 
278 static INT FASTCALL
279 DceReleaseDC(DCE* dce, BOOL EndPaint)
280 {
281    if (DCX_DCEBUSY != (dce->DCXFlags & (DCX_INDESTROY | DCX_DCEEMPTY | DCX_DCEBUSY)))
282    {
283       return 0;
284    }
285 
286    /* Restore previous visible region */
287    if (EndPaint)
288    {
289       DceUpdateVisRgn(dce, dce->pwndOrg, dce->DCXFlags);
290    }
291 
292    if ((dce->DCXFlags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN)) &&
293          ((dce->DCXFlags & DCX_CACHE) || EndPaint))
294    {
295       DceDeleteClipRgn(dce);
296    }
297 
298    if (dce->DCXFlags & DCX_CACHE)
299    {
300       if (!(dce->DCXFlags & DCX_NORESETATTRS))
301       {
302          // Clean the DC
303          if (!IntGdiCleanDC(dce->hDC)) return 0;
304 
305          if (dce->DCXFlags & DCX_DCEDIRTY)
306          {
307            /* Don't keep around invalidated entries
308             * because SetDCState() disables hVisRgn updates
309             * by removing dirty bit. */
310            dce->hwndCurrent = 0;
311            dce->pwndOrg  = NULL;
312            dce->pwndClip = NULL;
313            dce->DCXFlags &= DCX_CACHE;
314            dce->DCXFlags |= DCX_DCEEMPTY;
315          }
316       }
317       dce->DCXFlags &= ~DCX_DCEBUSY;
318       TRACE("Exit!!!!! DCX_CACHE!!!!!!   hDC-> %p \n", dce->hDC);
319       if (!GreSetDCOwner(dce->hDC, GDI_OBJ_HMGR_NONE))
320          return 0;
321       dce->ptiOwner = NULL; // Reset ownership.
322       dce->ppiOwner = NULL;
323 
324 #if 0 // Need to research and fix before this is a "growing" issue.
325       if (++DCECache > 32)
326       {
327          ListEntry = LEDce.Flink;
328          while (ListEntry != &LEDce)
329          {
330             pDCE = CONTAINING_RECORD(ListEntry, DCE, List);
331             ListEntry = ListEntry->Flink;
332             if (!(pDCE->DCXFlags & DCX_DCEBUSY))
333             {  /* Free the unused cache DCEs. */
334                DceFreeDCE(pDCE, TRUE);
335             }
336          }
337       }
338 #endif
339    }
340    return 1; // Released!
341 }
342 
343 
344 HDC FASTCALL
345 UserGetDCEx(PWND Wnd OPTIONAL, HANDLE ClipRegion, ULONG Flags)
346 {
347    PWND Parent;
348    ULONG DcxFlags;
349    DCE* Dce = NULL;
350    BOOL UpdateClipOrigin = FALSE;
351    BOOL bUpdateVisRgn = TRUE;
352    HDC hDC = NULL;
353    PPROCESSINFO ppi;
354    PLIST_ENTRY ListEntry;
355 
356    if (NULL == Wnd)
357    {
358       Flags &= ~DCX_USESTYLE;
359       Flags |= DCX_CACHE;
360    }
361 
362    if (Flags & DCX_PARENTCLIP) Flags |= DCX_CACHE;
363 
364    // When GetDC is called with hWnd nz, DCX_CACHE & _WINDOW are clear w _USESTYLE set.
365    if (Flags & DCX_USESTYLE)
366    {
367       Flags &= ~(DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS | DCX_PARENTCLIP);
368       if (!(Flags & DCX_WINDOW)) // Not window rectangle
369       {
370          if (Wnd->pcls->style & CS_PARENTDC)
371          {
372             Flags |= DCX_PARENTCLIP;
373          }
374 
375          if (!(Flags & DCX_CACHE) && // Not on the cheap wine list.
376              !(Wnd->pcls->style & CS_OWNDC) )
377          {
378             if (!(Wnd->pcls->style & CS_CLASSDC))
379             // The window is not POWNED or has any CLASS, so we are looking for cheap wine.
380                Flags |= DCX_CACHE;
381             else
382             {
383                if (Wnd->pcls->pdce) hDC = ((PDCE)Wnd->pcls->pdce)->hDC;
384                TRACE("We have CLASS!!\n");
385             }
386          }
387 
388          if (Wnd->style & WS_CLIPSIBLINGS)
389          {
390             Flags |= DCX_CLIPSIBLINGS;
391          }
392 
393          if (Wnd->style & WS_CLIPCHILDREN &&
394              !(Wnd->style & WS_MINIMIZE))
395          {
396             Flags |= DCX_CLIPCHILDREN;
397          }
398          /* If minized with icon in the set, we are forced to be cheap! */
399          if (Wnd->style & WS_MINIMIZE && Wnd->pcls->spicn)
400          {
401             Flags |= DCX_CACHE;
402          }
403       }
404       else
405       {
406          if (Wnd->style & WS_CLIPSIBLINGS) Flags |= DCX_CLIPSIBLINGS;
407          Flags |= DCX_CACHE;
408       }
409    }
410 
411    if (Flags & DCX_WINDOW) Flags &= ~DCX_CLIPCHILDREN;
412 
413    if (Flags & DCX_NOCLIPCHILDREN)
414    {
415       Flags |= DCX_CACHE;
416       Flags &= ~(DCX_PARENTCLIP | DCX_CLIPCHILDREN);
417    }
418 
419    Parent = (Wnd ? Wnd->spwndParent : NULL);
420 
421    if (NULL == Wnd || !(Wnd->style & WS_CHILD) || NULL == Parent)
422    {
423       Flags &= ~DCX_PARENTCLIP;
424       Flags |= DCX_CLIPSIBLINGS;
425    }
426 
427    /* It seems parent clip is ignored when clipping siblings or children */
428    if (Flags & (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN)) Flags &= ~DCX_PARENTCLIP;
429 
430    if (Flags & DCX_PARENTCLIP)
431    {
432       if ((Wnd->style & WS_VISIBLE) &&
433           (Parent->style & WS_VISIBLE))
434       {
435          Flags &= ~DCX_CLIPCHILDREN;
436          if (Parent->style & WS_CLIPSIBLINGS)
437          {
438             Flags |= DCX_CLIPSIBLINGS;
439          }
440       }
441    }
442 
443    // Window nz, check to see if we still own this or it is just cheap wine tonight.
444    if (!(Flags & DCX_CACHE))
445    {
446       if ( Wnd->head.pti != GetW32ThreadInfo())
447          Flags |= DCX_CACHE; // Ah~ Not Powned! Forced to be cheap~
448    }
449 
450    DcxFlags = Flags & DCX_CACHECOMPAREMASK;
451 
452    if (Flags & DCX_CACHE)
453    { // Scan the cheap wine list for our match.
454       DCE* DceEmpty = NULL;
455       DCE* DceUnused = NULL;
456       KeEnterCriticalRegion();
457       ListEntry = LEDce.Flink;
458       while (ListEntry != &LEDce)
459       {
460          Dce = CONTAINING_RECORD(ListEntry, DCE, List);
461          ListEntry = ListEntry->Flink;
462 //
463 // The way I understand this, you can have more than one DC per window.
464 // Only one Owned if one was requested and saved and one Cached.
465 //
466          if ((Dce->DCXFlags & (DCX_CACHE | DCX_DCEBUSY)) == DCX_CACHE)
467          {
468             DceUnused = Dce;
469             if (Dce->DCXFlags & DCX_DCEEMPTY)
470             {
471                DceEmpty = Dce;
472             }
473             else if (Dce->hwndCurrent == (Wnd ? UserHMGetHandle(Wnd) : NULL) &&
474                      ((Dce->DCXFlags & DCX_CACHECOMPAREMASK) == DcxFlags))
475             {
476                UpdateClipOrigin = TRUE;
477                break;
478             }
479          }
480          Dce = NULL; // Loop issue?
481       }
482       KeLeaveCriticalRegion();
483 
484       Dce = (DceEmpty == NULL) ? DceUnused : DceEmpty;
485 
486       if (Dce == NULL)
487       {
488          Dce = DceAllocDCE(NULL, DCE_CACHE_DC);
489       }
490       if (Dce == NULL) return NULL;
491 
492       Dce->hwndCurrent = (Wnd ? UserHMGetHandle(Wnd) : NULL);
493       Dce->pwndOrg = Dce->pwndClip = Wnd;
494    }
495    else // If we are here, we are POWNED or having CLASS.
496    {
497       KeEnterCriticalRegion();
498       ListEntry = LEDce.Flink;
499       while (ListEntry != &LEDce)
500       {
501           Dce = CONTAINING_RECORD(ListEntry, DCE, List);
502           ListEntry = ListEntry->Flink;
503 
504           // Skip Cache DCE entries.
505           if (!(Dce->DCXFlags & DCX_CACHE))
506           {
507              // Check for Window handle than HDC match for CLASS.
508              if (Dce->hwndCurrent == UserHMGetHandle(Wnd))
509              {
510                 bUpdateVisRgn = FALSE;
511                 break;
512              }
513              else if (Dce->hDC == hDC) break;
514           }
515           Dce = NULL; // Loop issue?
516       }
517       KeLeaveCriticalRegion();
518 
519       if (Dce == NULL)
520       {
521          return(NULL);
522       }
523 
524       if ( (Flags & (DCX_INTERSECTRGN|DCX_EXCLUDERGN)) &&
525            (Dce->DCXFlags & (DCX_INTERSECTRGN|DCX_EXCLUDERGN)) )
526       {
527          DceDeleteClipRgn(Dce);
528       }
529    }
530 // First time use hax, need to use DceAllocDCE during window display init.
531    if (NULL == Dce)
532    {
533       return(NULL);
534    }
535 
536    if (!GreIsHandleValid(Dce->hDC))
537    {
538       ERR("FIXME: Got DCE with invalid hDC! %p\n", Dce->hDC);
539       Dce->hDC = DceCreateDisplayDC();
540       /* FIXME: Handle error */
541    }
542 
543    Dce->DCXFlags = Flags | DCX_DCEBUSY;
544 
545    /*
546     * Bump it up! This prevents the random errors in wine dce tests and with
547     * proper bits set in DCX_CACHECOMPAREMASK.
548     * Reference:
549     *   https://reactos.org/archives/public/ros-dev/2008-July/010498.html
550     *   https://reactos.org/archives/public/ros-dev/2008-July/010499.html
551     */
552    RemoveEntryList(&Dce->List);
553    InsertHeadList(&LEDce, &Dce->List);
554 
555    /* Introduced in rev 6691 and modified later. */
556    if ( (Flags & DCX_INTERSECTUPDATE) && !ClipRegion )
557    {
558       Flags |= DCX_INTERSECTRGN | DCX_KEEPCLIPRGN;
559       Dce->DCXFlags |= DCX_INTERSECTRGN | DCX_KEEPCLIPRGN;
560       ClipRegion = Wnd->hrgnUpdate;
561       bUpdateVisRgn = TRUE;
562    }
563 
564    if (ClipRegion == HRGN_WINDOW)
565    {
566       if (!(Flags & DCX_WINDOW))
567       {
568          Dce->hrgnClip = NtGdiCreateRectRgn(
569              Wnd->rcClient.left,
570              Wnd->rcClient.top,
571              Wnd->rcClient.right,
572              Wnd->rcClient.bottom);
573       }
574       else
575       {
576           Dce->hrgnClip = NtGdiCreateRectRgn(
577               Wnd->rcWindow.left,
578               Wnd->rcWindow.top,
579               Wnd->rcWindow.right,
580               Wnd->rcWindow.bottom);
581       }
582       Dce->DCXFlags &= ~DCX_KEEPCLIPRGN;
583       bUpdateVisRgn = TRUE;
584    }
585    else if (ClipRegion != NULL)
586    {
587       if (Dce->hrgnClip != NULL)
588       {
589          ERR("Should not be called!!\n");
590          GreDeleteObject(Dce->hrgnClip);
591          Dce->hrgnClip = NULL;
592       }
593       Dce->hrgnClip = ClipRegion;
594       bUpdateVisRgn = TRUE;
595    }
596 
597    if (IntGdiSetHookFlags(Dce->hDC, DCHF_VALIDATEVISRGN)) bUpdateVisRgn = TRUE;
598 
599    DceSetDrawable(Wnd, Dce->hDC, Flags, UpdateClipOrigin);
600 
601    if (bUpdateVisRgn) DceUpdateVisRgn(Dce, Wnd, Flags);
602 
603    if (Dce->DCXFlags & DCX_CACHE)
604    {
605       TRACE("ENTER!!!!!! DCX_CACHE!!!!!!   hDC-> %p\n", Dce->hDC);
606       // Need to set ownership so Sync dcattr will work.
607       GreSetDCOwner(Dce->hDC, GDI_OBJ_HMGR_POWNED);
608       Dce->ptiOwner = GetW32ThreadInfo(); // Set the temp owning
609    }
610 
611    if ( Wnd &&
612         Wnd->ExStyle & WS_EX_LAYOUTRTL &&
613        !(Flags & DCX_KEEPLAYOUT) )
614    {
615       NtGdiSetLayout(Dce->hDC, -1, LAYOUT_RTL);
616    }
617 
618    if (Dce->DCXFlags & DCX_PROCESSOWNED)
619    {
620       ppi = PsGetCurrentProcessWin32Process();
621       ppi->W32PF_flags |= W32PF_OWNDCCLEANUP;
622       Dce->ptiOwner = NULL;
623       Dce->ppiOwner = ppi;
624    }
625 
626    return(Dce->hDC);
627 }
628 
629 /***********************************************************************
630  *           DceFreeDCE
631  */
632 void FASTCALL
633 DceFreeDCE(PDCE pdce, BOOLEAN Force)
634 {
635   BOOL Hit = FALSE;
636 
637   ASSERT(pdce != NULL);
638   if (NULL == pdce) return;
639 
640   pdce->DCXFlags |= DCX_INDESTROY;
641 
642   if (Force &&
643       GreGetObjectOwner(pdce->hDC) != GDI_OBJ_HMGR_POWNED)
644   {
645      TRACE("Change ownership for DCE! -> %p\n" , pdce);
646      // NOTE: Windows sets W32PF_OWNDCCLEANUP and moves on.
647      if (GreIsHandleValid(pdce->hDC))
648      {
649          GreSetDCOwner(pdce->hDC, GDI_OBJ_HMGR_POWNED);
650      }
651      else
652      {
653          ERR("Attempted to change ownership of an DCEhDC %p currently being destroyed!!!\n",
654              pdce->hDC);
655          Hit = TRUE;
656      }
657   }
658   else
659   {
660      if (GreGetObjectOwner(pdce->hDC) == GDI_OBJ_HMGR_PUBLIC)
661         GreSetDCOwner(pdce->hDC, GDI_OBJ_HMGR_POWNED);
662   }
663 
664   if (!Hit) IntGdiDeleteDC(pdce->hDC, TRUE);
665 
666   if (pdce->hrgnClip && !(pdce->DCXFlags & DCX_KEEPCLIPRGN))
667   {
668       GreDeleteObject(pdce->hrgnClip);
669       pdce->hrgnClip = NULL;
670   }
671 
672   RemoveEntryList(&pdce->List);
673 
674   ExFreePoolWithTag(pdce, USERTAG_DCE);
675 
676   DCECount--;
677   TRACE("Freed DCE's! %d \n", DCECount);
678 }
679 
680 /***********************************************************************
681  *           DceFreeWindowDCE
682  *
683  * Remove owned DCE and reset unreleased cache DCEs.
684  */
685 void FASTCALL
686 DceFreeWindowDCE(PWND Window)
687 {
688   PDCE pDCE;
689   PLIST_ENTRY ListEntry;
690 
691   if (DCECount <= 0)
692   {
693      ERR("FreeWindowDCE No Entry! %d\n",DCECount);
694      return;
695   }
696 
697   ListEntry = LEDce.Flink;
698   while (ListEntry != &LEDce)
699   {
700      pDCE = CONTAINING_RECORD(ListEntry, DCE, List);
701      ListEntry = ListEntry->Flink;
702      if ( pDCE->hwndCurrent == UserHMGetHandle(Window) &&
703           !(pDCE->DCXFlags & DCX_DCEEMPTY) )
704      {
705         if (!(pDCE->DCXFlags & DCX_CACHE)) /* Owned or Class DCE */
706         {
707            if (Window->pcls->style & CS_CLASSDC) /* Test Class first */
708            {
709               if (pDCE->DCXFlags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN)) /* Class DCE */
710                  DceDeleteClipRgn(pDCE);
711               // Update and reset Vis Rgn and clear the dirty bit.
712               // Should release VisRgn than reset it to default.
713               DceUpdateVisRgn(pDCE, Window, pDCE->DCXFlags);
714               pDCE->DCXFlags = DCX_DCEEMPTY|DCX_CACHE;
715               pDCE->hwndCurrent = 0;
716               pDCE->pwndOrg = pDCE->pwndClip = NULL;
717 
718               TRACE("POWNED DCE going Cheap!! DCX_CACHE!! hDC-> %p \n",
719                     pDCE->hDC);
720               if (!GreSetDCOwner( pDCE->hDC, GDI_OBJ_HMGR_NONE))
721               {
722                   ERR("Fail Owner Switch hDC-> %p \n", pDCE->hDC);
723                   break;
724               }
725               /* Do not change owner so thread can clean up! */
726            }
727            else if (Window->pcls->style & CS_OWNDC) /* Owned DCE */
728            {
729               DceFreeDCE(pDCE, FALSE);
730               continue;
731            }
732            else
733            {
734               ERR("Not POWNED or CLASSDC hwndCurrent -> %p \n",
735                   pDCE->hwndCurrent);
736               // ASSERT(FALSE); /* bug 5320 */
737            }
738         }
739         else
740         {
741            if (pDCE->DCXFlags & DCX_DCEBUSY) /* Shared cache DCE */
742            {
743               /* FIXME: AFAICS we are doing the right thing here so
744                * this should be a TRACE. But this is best left as an ERR
745                * because the 'application error' is likely to come from
746                * another part of Wine (i.e. it's our fault after all).
747                * We should change this to TRACE when ReactOS is more stable
748                * (for 1.0?).
749                */
750               ERR("[%p] GetDC() without ReleaseDC()!\n", UserHMGetHandle(Window));
751               DceReleaseDC(pDCE, FALSE);
752            }
753            pDCE->DCXFlags |= DCX_DCEEMPTY;
754            pDCE->hwndCurrent = 0;
755            pDCE->pwndOrg = pDCE->pwndClip = NULL;
756         }
757      }
758   }
759 }
760 
761 void FASTCALL
762 DceFreeClassDCE(PDCE pdceClass)
763 {
764    PDCE pDCE;
765    PLIST_ENTRY ListEntry;
766 
767    ListEntry = LEDce.Flink;
768    while (ListEntry != &LEDce)
769    {
770        pDCE = CONTAINING_RECORD(ListEntry, DCE, List);
771        ListEntry = ListEntry->Flink;
772        if (pDCE == pdceClass)
773        {
774           DceFreeDCE(pDCE, TRUE); // Might have gone cheap!
775        }
776    }
777 }
778 
779 void FASTCALL
780 DceFreeThreadDCE(PTHREADINFO pti)
781 {
782    PDCE pDCE;
783    PLIST_ENTRY ListEntry;
784 
785    ListEntry = LEDce.Flink;
786    while (ListEntry != &LEDce)
787    {
788        pDCE = CONTAINING_RECORD(ListEntry, DCE, List);
789        ListEntry = ListEntry->Flink;
790        if (pDCE->ptiOwner == pti)
791        {
792           if (pDCE->DCXFlags & DCX_CACHE)
793           {
794              DceFreeDCE(pDCE, TRUE);
795           }
796        }
797    }
798 }
799 
800 VOID FASTCALL
801 DceEmptyCache(VOID)
802 {
803    PDCE pDCE;
804    PLIST_ENTRY ListEntry;
805 
806    ListEntry = LEDce.Flink;
807    while (ListEntry != &LEDce)
808    {
809       pDCE = CONTAINING_RECORD(ListEntry, DCE, List);
810       ListEntry = ListEntry->Flink;
811       DceFreeDCE(pDCE, TRUE);
812    }
813 }
814 
815 VOID FASTCALL
816 DceResetActiveDCEs(PWND Window)
817 {
818    DCE *pDCE;
819    PDC dc;
820    PWND CurrentWindow;
821    INT DeltaX;
822    INT DeltaY;
823    PLIST_ENTRY ListEntry;
824 
825    if (NULL == Window)
826    {
827       return;
828    }
829 
830    ListEntry = LEDce.Flink;
831    while (ListEntry != &LEDce)
832    {
833       pDCE = CONTAINING_RECORD(ListEntry, DCE, List);
834       ListEntry = ListEntry->Flink;
835       if (0 == (pDCE->DCXFlags & (DCX_DCEEMPTY|DCX_INDESTROY)))
836       {
837          if (UserHMGetHandle(Window) == pDCE->hwndCurrent)
838          {
839             CurrentWindow = Window;
840          }
841          else
842          {
843             if (!pDCE->hwndCurrent)
844                CurrentWindow = NULL;
845             else
846                CurrentWindow = UserGetWindowObject(pDCE->hwndCurrent);
847             if (NULL == CurrentWindow)
848             {
849                continue;
850             }
851          }
852 
853          if (!GreIsHandleValid(pDCE->hDC) ||
854              (dc = DC_LockDc(pDCE->hDC)) == NULL)
855          {
856             continue;
857          }
858          if (Window == CurrentWindow || IntIsChildWindow(Window, CurrentWindow))
859          {
860             if (pDCE->DCXFlags & DCX_WINDOW)
861             {
862                DeltaX = CurrentWindow->rcWindow.left - dc->ptlDCOrig.x;
863                DeltaY = CurrentWindow->rcWindow.top - dc->ptlDCOrig.y;
864                dc->ptlDCOrig.x = CurrentWindow->rcWindow.left;
865                dc->ptlDCOrig.y = CurrentWindow->rcWindow.top;
866             }
867             else
868             {
869                DeltaX = CurrentWindow->rcClient.left - dc->ptlDCOrig.x;
870                DeltaY = CurrentWindow->rcClient.top - dc->ptlDCOrig.y;
871                dc->ptlDCOrig.x = CurrentWindow->rcClient.left;
872                dc->ptlDCOrig.y = CurrentWindow->rcClient.top;
873             }
874 
875             if (NULL != dc->dclevel.prgnClip)
876             {
877                REGION_bOffsetRgn(dc->dclevel.prgnClip, DeltaX, DeltaY);
878                dc->fs |= DC_DIRTY_RAO;
879             }
880             if (NULL != pDCE->hrgnClip)
881             {
882                NtGdiOffsetRgn(pDCE->hrgnClip, DeltaX, DeltaY);
883             }
884          }
885          DC_UnlockDc(dc);
886 
887          DceUpdateVisRgn(pDCE, CurrentWindow, pDCE->DCXFlags);
888          IntGdiSetHookFlags(pDCE->hDC, DCHF_VALIDATEVISRGN);
889       }
890    }
891 }
892 
893 HWND FASTCALL
894 IntWindowFromDC(HDC hDc)
895 {
896   DCE *Dce;
897   PLIST_ENTRY ListEntry;
898   HWND Ret = NULL;
899 
900   ListEntry = LEDce.Flink;
901   while (ListEntry != &LEDce)
902   {
903       Dce = CONTAINING_RECORD(ListEntry, DCE, List);
904       ListEntry = ListEntry->Flink;
905       if (Dce->hDC == hDc)
906       {
907          if (Dce->DCXFlags & DCX_INDESTROY)
908             Ret = NULL;
909          else
910             Ret = Dce->hwndCurrent;
911          break;
912       }
913   }
914   return Ret;
915 }
916 
917 INT FASTCALL
918 UserReleaseDC(PWND Window, HDC hDc, BOOL EndPaint)
919 {
920   PDCE dce;
921   PLIST_ENTRY ListEntry;
922   INT nRet = 0;
923   BOOL Hit = FALSE;
924 
925   TRACE("%p %p\n", Window, hDc);
926   ListEntry = LEDce.Flink;
927   while (ListEntry != &LEDce)
928   {
929      dce = CONTAINING_RECORD(ListEntry, DCE, List);
930      ListEntry = ListEntry->Flink;
931      if (dce->hDC == hDc)
932      {
933         Hit = TRUE;
934         break;
935      }
936   }
937 
938   if ( Hit && (dce->DCXFlags & DCX_DCEBUSY))
939   {
940      nRet = DceReleaseDC(dce, EndPaint);
941   }
942 
943   return nRet;
944 }
945 
946 HDC FASTCALL
947 UserGetWindowDC(PWND Wnd)
948 {
949   return UserGetDCEx(Wnd, 0, DCX_USESTYLE | DCX_WINDOW);
950 }
951 
952 HWND FASTCALL
953 UserGethWnd( HDC hdc, PWNDOBJ *pwndo)
954 {
955   EWNDOBJ* Clip;
956   PWND Wnd;
957   HWND hWnd;
958 
959   hWnd = IntWindowFromDC(hdc);
960 
961   if (hWnd && (Wnd = UserGetWindowObject(hWnd)))
962   {
963      Clip = (EWNDOBJ*)UserGetProp(Wnd, AtomWndObj, TRUE);
964 
965      if ( Clip && Clip->Hwnd == hWnd )
966      {
967         if (pwndo) *pwndo = (PWNDOBJ)Clip;
968      }
969   }
970   return hWnd;
971 }
972 
973 HDC APIENTRY
974 NtUserGetDCEx(HWND hWnd OPTIONAL, HANDLE ClipRegion, ULONG Flags)
975 {
976   PWND Wnd=NULL;
977   HDC Ret = NULL;
978 
979   TRACE("Enter NtUserGetDCEx: hWnd %p, ClipRegion %p, Flags %x.\n",
980       hWnd, ClipRegion, Flags);
981   UserEnterExclusive();
982 
983   if (hWnd && !(Wnd = UserGetWindowObject(hWnd)))
984   {
985       goto Exit; // Return NULL
986   }
987   Ret = UserGetDCEx(Wnd, ClipRegion, Flags);
988 
989 Exit:
990   TRACE("Leave NtUserGetDCEx, ret=%p\n", Ret);
991   UserLeave();
992   return Ret;
993 }
994 
995 /*
996  * NtUserGetWindowDC
997  *
998  * The NtUserGetWindowDC function retrieves the device context (DC) for the
999  * entire window, including title bar, menus, and scroll bars. A window device
1000  * context permits painting anywhere in a window, because the origin of the
1001  * device context is the upper-left corner of the window instead of the client
1002  * area.
1003  *
1004  * Status
1005  *    @implemented
1006  */
1007 HDC APIENTRY
1008 NtUserGetWindowDC(HWND hWnd)
1009 {
1010   return NtUserGetDCEx(hWnd, 0, DCX_USESTYLE | DCX_WINDOW);
1011 }
1012 
1013 HDC APIENTRY
1014 NtUserGetDC(HWND hWnd)
1015 {
1016  TRACE("NtUGetDC -> %p:%x\n", hWnd, !hWnd ? DCX_CACHE | DCX_WINDOW : DCX_USESTYLE);
1017 
1018   return NtUserGetDCEx(hWnd, NULL, NULL == hWnd ? DCX_CACHE | DCX_WINDOW : DCX_USESTYLE);
1019 }
1020 
1021 /*!
1022  * Select logical palette into device context.
1023  * \param	hDC 				handle to the device context
1024  * \param	hpal				handle to the palette
1025  * \param	ForceBackground 	If this value is FALSE the logical palette will be copied to the device palette only when the application
1026  * 								is in the foreground. If this value is TRUE then map the colors in the logical palette to the device
1027  * 								palette colors in the best way.
1028  * \return	old palette
1029  *
1030  * \todo	implement ForceBackground == TRUE
1031 */
1032 HPALETTE
1033 APIENTRY
1034 NtUserSelectPalette(HDC  hDC,
1035               HPALETTE  hpal,
1036        BOOL  ForceBackground)
1037 {
1038     HPALETTE oldPal;
1039     UserEnterExclusive();
1040     // Implement window checks
1041     oldPal = GdiSelectPalette( hDC, hpal, ForceBackground);
1042     UserLeave();
1043     return oldPal;
1044 }
1045 
1046 /* EOF */
1047