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