Delay linking newly created window in linked list until after the
[wine] / windows / dce.c
1 /*
2  * USER DCE functions
3  *
4  * Copyright 1993 Alexandre Julliard
5  *           1996,1997 Alex Korobka
6  *
7  *
8  * Note: Visible regions of CS_OWNDC/CS_CLASSDC window DCs 
9  * have to be updated dynamically. 
10  * 
11  * Internal DCX flags:
12  *
13  * DCX_DCEEMPTY    - dce is uninitialized
14  * DCX_DCEBUSY     - dce is in use
15  * DCX_DCEDIRTY    - ReleaseDC() should wipe instead of caching
16  * DCX_KEEPCLIPRGN - ReleaseDC() should not delete the clipping region
17  * DCX_WINDOWPAINT - BeginPaint() is in effect
18  */
19
20 #include "options.h"
21 #include "dce.h"
22 #include "class.h"
23 #include "win.h"
24 #include "gdi.h"
25 #include "region.h"
26 #include "heap.h"
27 #include "sysmetrics.h"
28 #include "debug.h"
29
30 #define NB_DCE    5  /* Number of DCEs created at startup */
31
32 static DCE *firstDCE = 0;
33 static HDC32 defaultDCstate = 0;
34
35 static void DCE_DeleteClipRgn( DCE* );
36 static INT32 DCE_ReleaseDC( DCE* );
37
38
39 /***********************************************************************
40  *           DCE_DumpCache
41  */
42 static void DCE_DumpCache(void)
43 {
44     DCE* dce = firstDCE;
45     
46     DUMP("DCE:\n");
47     while( dce )
48     {
49         DUMP("\t[0x%08x] hWnd 0x%04x, dcx %08x, %s %s\n",
50              (unsigned)dce, dce->hwndCurrent, (unsigned)dce->DCXflags, 
51              (dce->DCXflags & DCX_CACHE) ? "Cache" : "Owned", 
52              (dce->DCXflags & DCX_DCEBUSY) ? "InUse" : "" );
53         dce = dce->next;
54     }
55 }
56
57 /***********************************************************************
58  *           DCE_AllocDCE
59  *
60  * Allocate a new DCE.
61  */
62 DCE *DCE_AllocDCE( HWND32 hWnd, DCE_TYPE type )
63 {
64     DCE * dce;
65     if (!(dce = HeapAlloc( SystemHeap, 0, sizeof(DCE) ))) return NULL;
66     if (!(dce->hDC = CreateDC16( "DISPLAY", NULL, NULL, NULL )))
67     {
68         HeapFree( SystemHeap, 0, dce );
69         return 0;
70     }
71
72     /* store DCE handle in DC hook data field */
73
74     SetDCHook( dce->hDC, (FARPROC16)DCHook, (DWORD)dce );
75
76     dce->hwndCurrent = hWnd;
77     dce->hClipRgn    = 0;
78     dce->next        = firstDCE;
79     firstDCE = dce;
80
81     if( type != DCE_CACHE_DC ) /* owned or class DC */
82     {
83         dce->DCXflags = DCX_DCEBUSY;
84         if( hWnd )
85         {
86             WND* wnd = WIN_FindWndPtr(hWnd);
87         
88             if( wnd->dwStyle & WS_CLIPCHILDREN ) dce->DCXflags |= DCX_CLIPCHILDREN;
89             if( wnd->dwStyle & WS_CLIPSIBLINGS ) dce->DCXflags |= DCX_CLIPSIBLINGS;
90         }
91         SetHookFlags(dce->hDC,DCHF_INVALIDATEVISRGN);
92     }
93     else dce->DCXflags = DCX_CACHE | DCX_DCEEMPTY;
94
95     return dce;
96 }
97
98
99 /***********************************************************************
100  *           DCE_FreeDCE
101  */
102 DCE* DCE_FreeDCE( DCE *dce )
103 {
104     DCE **ppDCE = &firstDCE;
105
106     if (!dce) return NULL;
107     while (*ppDCE && (*ppDCE != dce)) ppDCE = &(*ppDCE)->next;
108     if (*ppDCE == dce) *ppDCE = dce->next;
109
110     SetDCHook(dce->hDC, NULL, 0L);
111
112     DeleteDC32( dce->hDC );
113     if( dce->hClipRgn && !(dce->DCXflags & DCX_KEEPCLIPRGN) )
114         DeleteObject32(dce->hClipRgn);
115     HeapFree( SystemHeap, 0, dce );
116     return *ppDCE;
117 }
118
119 /***********************************************************************
120  *           DCE_FreeWindowDCE
121  *
122  * Remove owned DCE and reset unreleased cache DCEs.
123  */
124 void DCE_FreeWindowDCE( WND* pWnd )
125 {
126     DCE *pDCE = firstDCE;
127
128     while( pDCE )
129     {
130         if( pDCE->hwndCurrent == pWnd->hwndSelf )
131         {
132             if( pDCE == pWnd->dce ) /* owned DCE */
133             {
134                 pDCE = DCE_FreeDCE( pDCE );
135                 pWnd->dce = NULL;
136                 continue;
137             }
138             else
139             {
140                 if(!(pDCE->DCXflags & DCX_CACHE) ) /* class DCE */
141                 {
142                     if( pDCE->DCXflags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN) )
143                         DCE_DeleteClipRgn( pDCE );
144                 }
145                 else if( pDCE->DCXflags & DCX_DCEBUSY ) /* shared cache DCE */
146                 {
147                     ERR(dc,"[%04x] GetDC() without ReleaseDC()!\n", 
148                         pWnd->hwndSelf);
149                     DCE_ReleaseDC( pDCE );
150                 }
151
152                 pDCE->DCXflags &= DCX_CACHE;
153                 pDCE->DCXflags |= DCX_DCEEMPTY;
154                 pDCE->hwndCurrent = 0;
155             }
156         }
157         pDCE = pDCE->next;
158     }
159 }
160
161
162 /***********************************************************************
163  *   DCE_DeleteClipRgn
164  */
165 static void DCE_DeleteClipRgn( DCE* dce )
166 {
167     dce->DCXflags &= ~(DCX_EXCLUDERGN | DCX_INTERSECTRGN | DCX_WINDOWPAINT);
168
169     if( dce->DCXflags & DCX_KEEPCLIPRGN )
170         dce->DCXflags &= ~DCX_KEEPCLIPRGN;
171     else
172         if( dce->hClipRgn > 1 )
173             DeleteObject32( dce->hClipRgn );
174
175     dce->hClipRgn = 0;
176
177     TRACE(dc,"\trestoring VisRgn\n");
178
179     RestoreVisRgn(dce->hDC);
180 }
181
182
183 /***********************************************************************
184  *   DCE_ReleaseDC
185  */
186 static INT32 DCE_ReleaseDC( DCE* dce )
187 {
188     if ((dce->DCXflags & (DCX_DCEEMPTY | DCX_DCEBUSY)) != DCX_DCEBUSY) return 0;
189
190     /* restore previous visible region */
191
192     if ((dce->DCXflags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN)) &&
193         (dce->DCXflags & (DCX_CACHE | DCX_WINDOWPAINT)) )
194         DCE_DeleteClipRgn( dce );
195
196     if (dce->DCXflags & DCX_CACHE)
197     {
198         SetDCState( dce->hDC, defaultDCstate );
199         dce->DCXflags &= ~DCX_DCEBUSY;
200         if (dce->DCXflags & DCX_DCEDIRTY)
201         {
202             /* don't keep around invalidated entries 
203              * because SetDCState() disables hVisRgn updates
204              * by removing dirty bit. */
205
206             dce->hwndCurrent = 0;
207             dce->DCXflags &= DCX_CACHE;
208             dce->DCXflags |= DCX_DCEEMPTY;
209         }
210     }
211     return 1;
212 }
213
214
215 /***********************************************************************
216  *   DCE_InvalidateDCE
217  *
218  * It is called from SetWindowPos() - we have to mark as dirty all busy
219  * DCE's for windows that have pWnd->parent as an ansector and whose client 
220  * rect intersects with specified update rectangle. 
221  */
222 BOOL32 DCE_InvalidateDCE(WND* pWnd, const RECT32* pRectUpdate)
223 {
224     WND* wndScope = pWnd->parent;
225     BOOL32 bRet = FALSE;
226
227     if( wndScope )
228     {
229         DCE *dce;
230
231         TRACE(dc,"scope hwnd = %04x, (%i,%i - %i,%i)\n",
232                      wndScope->hwndSelf, pRectUpdate->left,pRectUpdate->top,
233                      pRectUpdate->right,pRectUpdate->bottom);
234         if(TRACE_ON(dc)) 
235           DCE_DumpCache();
236
237         /* walk all DCEs and fixup non-empty entries */
238
239         for (dce = firstDCE; (dce); dce = dce->next)
240         {
241             if( !(dce->DCXflags & DCX_DCEEMPTY) )
242             {
243                 WND* wndCurrent = WIN_FindWndPtr(dce->hwndCurrent);
244
245                 if( wndCurrent && wndCurrent != WIN_GetDesktop() )
246                 {
247                     WND* wnd = wndCurrent;
248                     INT32 xoffset = 0, yoffset = 0;
249
250                     if( (wndCurrent == wndScope) && !(dce->DCXflags & DCX_CLIPCHILDREN) ) continue;
251
252                     /* check if DCE window is within the z-order scope */
253
254                     for( wnd = wndCurrent; wnd; wnd = wnd->parent )
255                     {
256                         if( wnd == wndScope )
257                         {
258                             RECT32 wndRect;
259
260                             wndRect = wndCurrent->rectWindow;
261
262                             OffsetRect32( &wndRect, xoffset - wndCurrent->rectClient.left, 
263                                                     yoffset - wndCurrent->rectClient.top);
264
265                             if (pWnd == wndCurrent ||
266                                 IntersectRect32( &wndRect, &wndRect, pRectUpdate ))
267                             { 
268                                 if( !(dce->DCXflags & DCX_DCEBUSY) )
269                                 {
270                                     /* Don't bother with visible regions of unused DCEs */
271
272                                     TRACE(dc,"\tpurged %08x dce [%04x]\n", 
273                                                 (unsigned)dce, wndCurrent->hwndSelf);
274
275                                     dce->hwndCurrent = 0;
276                                     dce->DCXflags &= DCX_CACHE;
277                                     dce->DCXflags |= DCX_DCEEMPTY;
278                                 }
279                                 else
280                                 {
281                                     /* Set dirty bits in the hDC and DCE structs */
282
283                                     TRACE(dc,"\tfixed up %08x dce [%04x]\n", 
284                                                 (unsigned)dce, wndCurrent->hwndSelf);
285
286                                     dce->DCXflags |= DCX_DCEDIRTY;
287                                     SetHookFlags(dce->hDC, DCHF_INVALIDATEVISRGN);
288                                     bRet = TRUE;
289                                 }
290                             }
291                             break;
292                         }
293                         xoffset += wnd->rectClient.left;
294                         yoffset += wnd->rectClient.top;
295                     }
296                 }
297             }
298         } /* dce list */
299     }
300     return bRet;
301 }
302
303 /***********************************************************************
304  *           DCE_Init
305  */
306 void DCE_Init(void)
307 {
308     int i;
309     DCE * dce;
310         
311     for (i = 0; i < NB_DCE; i++)
312     {
313         if (!(dce = DCE_AllocDCE( 0, DCE_CACHE_DC ))) return;
314         if (!defaultDCstate) defaultDCstate = GetDCState( dce->hDC );
315     }
316 }
317
318
319 /***********************************************************************
320  *           DCE_GetVisRect
321  *
322  * Calculate the visible rectangle of a window (i.e. the client or
323  * window area clipped by the client area of all ancestors) in the
324  * corresponding coordinates. Return FALSE if the visible region is empty.
325  */
326 static BOOL32 DCE_GetVisRect( WND *wndPtr, BOOL32 clientArea, RECT32 *lprect )
327 {
328     *lprect = clientArea ? wndPtr->rectClient : wndPtr->rectWindow;
329
330     if (wndPtr->dwStyle & WS_VISIBLE)
331     {
332         INT32 xoffset = lprect->left;
333         INT32 yoffset = lprect->top;
334
335         while (wndPtr->parent)
336         {
337             wndPtr = wndPtr->parent;
338
339             if ( (wndPtr->dwStyle & (WS_ICONIC | WS_VISIBLE)) != WS_VISIBLE )
340                 goto fail;
341
342             xoffset += wndPtr->rectClient.left;
343             yoffset += wndPtr->rectClient.top;
344             OffsetRect32( lprect, wndPtr->rectClient.left,
345                                   wndPtr->rectClient.top );
346
347             if( (wndPtr->rectClient.left >= wndPtr->rectClient.right) ||
348                 (wndPtr->rectClient.top >= wndPtr->rectClient.bottom) ||
349                 (lprect->left >= wndPtr->rectClient.right) ||
350                 (lprect->right <= wndPtr->rectClient.left) ||
351                 (lprect->top >= wndPtr->rectClient.bottom) ||
352                 (lprect->bottom <= wndPtr->rectClient.top) )
353                 goto fail;
354
355             lprect->left = MAX( lprect->left, wndPtr->rectClient.left );
356             lprect->right = MIN( lprect->right, wndPtr->rectClient.right );
357             lprect->top = MAX( lprect->top, wndPtr->rectClient.top );
358             lprect->bottom = MIN( lprect->bottom, wndPtr->rectClient.bottom );
359         }
360         OffsetRect32( lprect, -xoffset, -yoffset );
361         return TRUE;
362     }
363
364 fail:
365     SetRectEmpty32( lprect );
366     return FALSE;
367 }
368
369
370 /***********************************************************************
371  *           DCE_AddClipRects
372  *
373  * Go through the linked list of windows from pWndStart to pWndEnd,
374  * adding to the clip region the intersection of the target rectangle
375  * with an offset window rectangle.
376  */
377 static BOOL32 DCE_AddClipRects( WND *pWndStart, WND *pWndEnd, 
378                                 HRGN32 hrgnClip, LPRECT32 lpRect, int x, int y )
379 {
380     RECT32 rect;
381
382     if (pWndStart->window) return TRUE; /* X itself will do the clipping */
383
384     for (; pWndStart != pWndEnd; pWndStart = pWndStart->next)
385     {
386         if( !(pWndStart->dwStyle & WS_VISIBLE) ) continue;
387             
388         rect.left = pWndStart->rectWindow.left + x;
389         rect.top = pWndStart->rectWindow.top + y;
390         rect.right = pWndStart->rectWindow.right + x;
391         rect.bottom = pWndStart->rectWindow.bottom + y;
392
393         if( IntersectRect32( &rect, &rect, lpRect ))
394             if(!REGION_UnionRectWithRgn( hrgnClip, &rect ))
395                 break;
396     }
397     return (pWndStart == pWndEnd);
398 }
399
400
401 /***********************************************************************
402  *           DCE_GetVisRgn
403  *
404  * Return the visible region of a window, i.e. the client or window area
405  * clipped by the client area of all ancestors, and then optionally
406  * by siblings and children.
407  */
408 HRGN32 DCE_GetVisRgn( HWND32 hwnd, WORD flags )
409 {
410     HRGN32 hrgnVis = 0;
411     RECT32 rect;
412     WND *wndPtr = WIN_FindWndPtr( hwnd );
413
414     /* Get visible rectangle and create a region with it. */
415
416     if (wndPtr && DCE_GetVisRect(wndPtr, !(flags & DCX_WINDOW), &rect))
417     {
418         if((hrgnVis = CreateRectRgnIndirect32( &rect )))
419         {
420             HRGN32 hrgnClip = CreateRectRgn32( 0, 0, 0, 0 );
421             INT32 xoffset, yoffset;
422
423             if( hrgnClip )
424             {
425                 /* Compute obscured region for the visible rectangle by 
426                  * clipping children, siblings, and ancestors. Note that
427                  * DCE_GetVisRect() returns a rectangle either in client
428                  * or in window coordinates (for DCX_WINDOW request). */
429
430                 if( (flags & DCX_CLIPCHILDREN) && wndPtr->child )
431                 {
432                     if( flags & DCX_WINDOW )
433                     {
434                         /* adjust offsets since child window rectangles are 
435                          * in client coordinates */
436
437                         xoffset = wndPtr->rectClient.left - wndPtr->rectWindow.left;
438                         yoffset = wndPtr->rectClient.top - wndPtr->rectWindow.top;
439                     }
440                     else 
441                         xoffset = yoffset = 0;
442
443                     DCE_AddClipRects( wndPtr->child, NULL, hrgnClip, 
444                                       &rect, xoffset, yoffset );
445                 }
446
447                 /* sibling window rectangles are in client 
448                  * coordinates of the parent window */
449
450                 if (flags & DCX_WINDOW)
451                 {
452                     xoffset = -wndPtr->rectWindow.left;
453                     yoffset = -wndPtr->rectWindow.top;
454                 }
455                 else
456                 {
457                     xoffset = -wndPtr->rectClient.left;
458                     yoffset = -wndPtr->rectClient.top;
459                 }
460
461                 if (flags & DCX_CLIPSIBLINGS && wndPtr->parent )
462                     DCE_AddClipRects( wndPtr->parent->child,
463                                       wndPtr, hrgnClip, &rect, xoffset, yoffset );
464
465                 /* Clip siblings of all ancestors that have the
466                  * WS_CLIPSIBLINGS style
467                  */
468
469                 while (wndPtr->dwStyle & WS_CHILD)
470                 {
471                     wndPtr = wndPtr->parent;
472                     xoffset -= wndPtr->rectClient.left;
473                     yoffset -= wndPtr->rectClient.top;
474                     if(wndPtr->dwStyle & WS_CLIPSIBLINGS && wndPtr->parent)
475                     {
476                         DCE_AddClipRects( wndPtr->parent->child, wndPtr,
477                                           hrgnClip, &rect, xoffset, yoffset );
478                     }
479                 }
480
481                 /* Now once we've got a jumbo clip region we have
482                  * to substract it from the visible rectangle.
483                  */
484
485                 CombineRgn32( hrgnVis, hrgnVis, hrgnClip, RGN_DIFF );
486                 DeleteObject32( hrgnClip );
487             }
488             else
489             {
490                 DeleteObject32( hrgnVis );
491                 hrgnVis = 0;
492             }
493         }
494     }
495     else
496         hrgnVis = CreateRectRgn32(0, 0, 0, 0); /* empty */
497     return hrgnVis;
498 }
499
500 /***********************************************************************
501  *           DCE_OffsetVisRgn
502  *
503  * Change region from DC-origin relative coordinates to screen coords.
504  */
505
506 static void DCE_OffsetVisRgn( HDC32 hDC, HRGN32 hVisRgn )
507 {
508     DC *dc;
509     if (!(dc = (DC *) GDI_GetObjPtr( hDC, DC_MAGIC ))) return;
510
511     OffsetRgn32( hVisRgn, dc->w.DCOrgX, dc->w.DCOrgY );
512
513     GDI_HEAP_UNLOCK( hDC );
514 }
515
516
517 /***********************************************************************
518  *           DCE_SetDrawable
519  *
520  * Set the drawable, origin and dimensions for the DC associated to
521  * a given window.
522  */
523 static void DCE_SetDrawable( WND *wndPtr, DC *dc, WORD flags, BOOL32 bSetClipOrigin )
524 {
525     if (!wndPtr)  /* Get a DC for the whole screen */
526     {
527         dc->w.DCOrgX = 0;
528         dc->w.DCOrgY = 0;
529         dc->u.x.drawable = rootWindow;
530         TSXSetSubwindowMode( display, dc->u.x.gc, IncludeInferiors );
531     }
532     else
533     {
534         if (flags & DCX_WINDOW)
535         {
536             dc->w.DCOrgX  = wndPtr->rectWindow.left;
537             dc->w.DCOrgY  = wndPtr->rectWindow.top;
538         }
539         else
540         {
541             dc->w.DCOrgX  = wndPtr->rectClient.left;
542             dc->w.DCOrgY  = wndPtr->rectClient.top;
543         }
544         while (!wndPtr->window)
545         {
546             wndPtr = wndPtr->parent;
547             dc->w.DCOrgX += wndPtr->rectClient.left;
548             dc->w.DCOrgY += wndPtr->rectClient.top;
549         }
550         dc->w.DCOrgX -= wndPtr->rectWindow.left;
551         dc->w.DCOrgY -= wndPtr->rectWindow.top;
552         dc->u.x.drawable = wndPtr->window;
553
554 #if 0
555         /* This is needed when we reuse a cached DC because
556          * SetDCState() called by ReleaseDC() screws up DC
557          * origins for child windows.
558          */
559
560         if( bSetClipOrigin )
561             TSXSetClipOrigin( display, dc->u.x.gc, dc->w.DCOrgX, dc->w.DCOrgY );
562 #endif
563     }
564 }
565 /***********************************************************************
566  *           DCE_ExcludeRgn
567  * 
568  *  Translate given region from the wnd client to the DC coordinates
569  *  and add it to the clipping region.
570  */
571 INT16 DCE_ExcludeRgn( HDC32 hDC, WND* wnd, HRGN32 hRgn )
572 {
573   POINT32  pt = {0, 0};
574   DCE     *dce = firstDCE;
575
576   while (dce && (dce->hDC != hDC)) dce = dce->next;
577   if( dce )
578   {
579       MapWindowPoints32( wnd->hwndSelf, dce->hwndCurrent, &pt, 1);
580       if( dce->DCXflags & DCX_WINDOW )
581       { 
582           wnd = WIN_FindWndPtr(dce->hwndCurrent);
583           pt.x += wnd->rectClient.left - wnd->rectWindow.left;
584           pt.y += wnd->rectClient.top - wnd->rectWindow.top;
585       }
586   }
587   else return ERROR;
588   OffsetRgn32(hRgn, pt.x, pt.y);
589
590   return ExtSelectClipRgn( hDC, hRgn, RGN_DIFF );
591 }
592
593 /***********************************************************************
594  *           GetDCEx16    (USER.359)
595  */
596 HDC16 WINAPI GetDCEx16( HWND16 hwnd, HRGN16 hrgnClip, DWORD flags )
597 {
598     return (HDC16)GetDCEx32( hwnd, hrgnClip, flags );
599 }
600
601
602 /***********************************************************************
603  *           GetDCEx32    (USER32.231)
604  *
605  * Unimplemented flags: DCX_LOCKWINDOWUPDATE
606  *
607  * FIXME: Full support for hrgnClip == 1 (alias for entire window).
608  */
609 HDC32 WINAPI GetDCEx32( HWND32 hwnd, HRGN32 hrgnClip, DWORD flags )
610 {
611     HRGN32      hrgnVisible = 0;
612     HDC32       hdc = 0;
613     DCE *       dce;
614     DC *        dc;
615     WND *       wndPtr;
616     DWORD       dcxFlags = 0;
617     BOOL32      bUpdateVisRgn = TRUE;
618     BOOL32      bUpdateClipOrigin = FALSE;
619
620     TRACE(dc,"hwnd %04x, hrgnClip %04x, flags %08x\n", 
621                                 hwnd, hrgnClip, (unsigned)flags);
622     
623     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return 0;
624
625     /* fixup flags */
626
627     if (!(wndPtr->class->style & (CS_OWNDC | CS_CLASSDC))) flags |= DCX_CACHE;
628
629     if (flags & DCX_USESTYLE)
630     {
631         flags &= ~( DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS | DCX_PARENTCLIP);
632
633         if( wndPtr->dwStyle & WS_CLIPSIBLINGS )
634             flags |= DCX_CLIPSIBLINGS;
635
636         if ( !(flags & DCX_WINDOW) )
637         {
638             if (wndPtr->class->style & CS_PARENTDC) flags |= DCX_PARENTCLIP;
639
640             if (wndPtr->dwStyle & WS_CLIPCHILDREN &&
641                      !(wndPtr->dwStyle & WS_MINIMIZE) ) flags |= DCX_CLIPCHILDREN;
642         }
643         else flags |= DCX_CACHE;
644     }
645
646     if( flags & DCX_NOCLIPCHILDREN )
647     {
648         flags |= DCX_CACHE;
649         flags &= ~(DCX_PARENTCLIP | DCX_CLIPCHILDREN);
650     }
651
652     if (flags & DCX_WINDOW) 
653         flags = (flags & ~DCX_CLIPCHILDREN) | DCX_CACHE;
654
655     if (!(wndPtr->dwStyle & WS_CHILD) || !wndPtr->parent ) 
656         flags &= ~DCX_PARENTCLIP;
657     else if( flags & DCX_PARENTCLIP )
658     {
659         flags |= DCX_CACHE;
660         if( !(flags & (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN)) )
661             if( (wndPtr->dwStyle & WS_VISIBLE) && (wndPtr->parent->dwStyle & WS_VISIBLE) )
662             {
663                 flags &= ~DCX_CLIPCHILDREN;
664                 if( wndPtr->parent->dwStyle & WS_CLIPSIBLINGS )
665                     flags |= DCX_CLIPSIBLINGS;
666             }
667     }
668
669     /* find a suitable DCE */
670
671     dcxFlags = flags & (DCX_PARENTCLIP | DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | 
672                         DCX_CACHE | DCX_WINDOW);
673
674     if (flags & DCX_CACHE)
675     {
676         DCE*    dceEmpty;
677         DCE*    dceUnused;
678
679         dceEmpty = dceUnused = NULL;
680
681         /* Strategy: First, we attempt to find a non-empty but unused DCE with
682          * compatible flags. Next, we look for an empty entry. If the cache is
683          * full we have to purge one of the unused entries.
684          */
685
686         for (dce = firstDCE; (dce); dce = dce->next)
687         {
688             if ((dce->DCXflags & (DCX_CACHE | DCX_DCEBUSY)) == DCX_CACHE )
689             {
690                 dceUnused = dce;
691
692                 if (dce->DCXflags & DCX_DCEEMPTY)
693                     dceEmpty = dce;
694                 else
695                 if ((dce->hwndCurrent == hwnd) &&
696                    ((dce->DCXflags & (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN |
697                                       DCX_CACHE | DCX_WINDOW | DCX_PARENTCLIP)) == dcxFlags))
698                 {
699                     TRACE(dc,"\tfound valid %08x dce [%04x], flags %08x\n", 
700                                         (unsigned)dce, hwnd, (unsigned)dcxFlags );
701                     bUpdateVisRgn = FALSE; 
702                     bUpdateClipOrigin = TRUE;
703                     break;
704                 }
705             }
706         }
707         if (!dce) dce = (dceEmpty) ? dceEmpty : dceUnused;
708     }
709     else 
710     {
711         dce = (wndPtr->class->style & CS_OWNDC) ? wndPtr->dce : wndPtr->class->dce;
712         if( dce->hwndCurrent == hwnd )
713         {
714             TRACE(dc,"\tskipping hVisRgn update\n");
715             bUpdateVisRgn = FALSE; /* updated automatically, via DCHook() */
716
717             if( (dce->DCXflags & (DCX_EXCLUDERGN | DCX_INTERSECTRGN)) &&
718                 (flags & (DCX_EXCLUDERGN | DCX_INTERSECTRGN)) )
719             {
720                 /* This is likely to be a nested BeginPaint(). */
721
722                 if( dce->hClipRgn != hrgnClip )
723                 {
724                     FIXME(dc,"new hrgnClip[%04x] smashes the previous[%04x]\n",
725                           hrgnClip, dce->hClipRgn );
726                     DCE_DeleteClipRgn( dce );
727                 }
728                 else 
729                     RestoreVisRgn(dce->hDC);
730             }
731         }
732     }
733     if (!dce) return 0;
734
735     dce->hwndCurrent = hwnd;
736     dce->hClipRgn = 0;
737     dce->DCXflags = dcxFlags | (flags & DCX_WINDOWPAINT) | DCX_DCEBUSY;
738     hdc = dce->hDC;
739     
740     if (!(dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC ))) return 0;
741     bUpdateVisRgn = bUpdateVisRgn || (dc->w.flags & DC_DIRTY);
742
743     /* recompute visible region */
744
745     DCE_SetDrawable( wndPtr, dc, flags, bUpdateClipOrigin );
746     if( bUpdateVisRgn )
747     {
748         TRACE(dc,"updating visrgn for %08x dce, hwnd [%04x]\n", (unsigned)dce, hwnd);
749
750         if (flags & DCX_PARENTCLIP)
751         {
752             WND *parentPtr = wndPtr->parent;
753
754             if( wndPtr->dwStyle & WS_VISIBLE && !(parentPtr->dwStyle & WS_MINIMIZE) )
755             {
756                 if( parentPtr->dwStyle & WS_CLIPSIBLINGS ) 
757                     dcxFlags = DCX_CLIPSIBLINGS | (flags & ~(DCX_CLIPCHILDREN | DCX_WINDOW));
758                 else
759                     dcxFlags = flags & ~(DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_WINDOW);
760
761                 hrgnVisible = DCE_GetVisRgn( parentPtr->hwndSelf, dcxFlags );
762                 if( flags & DCX_WINDOW )
763                     OffsetRgn32( hrgnVisible, -wndPtr->rectWindow.left,
764                                               -wndPtr->rectWindow.top );
765                 else
766                     OffsetRgn32( hrgnVisible, -wndPtr->rectClient.left,
767                                               -wndPtr->rectClient.top );
768                 DCE_OffsetVisRgn( hdc, hrgnVisible );
769             }
770             else
771                 hrgnVisible = CreateRectRgn32( 0, 0, 0, 0 );
772         }
773         else 
774             if ((hwnd == GetDesktopWindow32()) &&
775                 (rootWindow == DefaultRootWindow(display)))
776                  hrgnVisible = CreateRectRgn32( 0, 0, SYSMETRICS_CXSCREEN,
777                                                       SYSMETRICS_CYSCREEN );
778             else 
779             {
780                 hrgnVisible = DCE_GetVisRgn( hwnd, flags );
781                 DCE_OffsetVisRgn( hdc, hrgnVisible );
782             }
783
784         dc->w.flags &= ~DC_DIRTY;
785         dce->DCXflags &= ~DCX_DCEDIRTY;
786         SelectVisRgn( hdc, hrgnVisible );
787     }
788     else
789         TRACE(dc,"no visrgn update %08x dce, hwnd [%04x]\n", (unsigned)dce, hwnd);
790
791     /* apply additional region operation (if any) */
792
793     if( flags & (DCX_EXCLUDERGN | DCX_INTERSECTRGN) )
794     {
795         if( !hrgnVisible ) hrgnVisible = CreateRectRgn32( 0, 0, 0, 0 );
796
797         dce->DCXflags |= flags & (DCX_KEEPCLIPRGN | DCX_INTERSECTRGN | DCX_EXCLUDERGN);
798         dce->hClipRgn = hrgnClip;
799
800         TRACE(dc, "\tsaved VisRgn, clipRgn = %04x\n", hrgnClip);
801
802         SaveVisRgn( hdc );
803         CombineRgn32( hrgnVisible, hrgnClip, 0, RGN_COPY );
804         DCE_OffsetVisRgn( hdc, hrgnVisible );
805         CombineRgn32( hrgnVisible, InquireVisRgn( hdc ), hrgnVisible,
806                       (flags & DCX_INTERSECTRGN) ? RGN_AND : RGN_DIFF );
807         SelectVisRgn( hdc, hrgnVisible );
808     }
809
810     if( hrgnVisible ) DeleteObject32( hrgnVisible );
811
812     TRACE(dc, "(%04x,%04x,0x%lx): returning %04x\n", 
813                hwnd, hrgnClip, flags, hdc);
814     return hdc;
815 }
816
817
818 /***********************************************************************
819  *           GetDC16    (USER.66)
820  */
821 HDC16 WINAPI GetDC16( HWND16 hwnd )
822 {
823     return (HDC16)GetDC32( hwnd );
824 }
825
826
827 /***********************************************************************
828  *           GetDC32    (USER32.230)
829  * RETURNS
830  *      :Handle to DC
831  *      NULL: Failure
832  */
833 HDC32 WINAPI GetDC32(
834              HWND32 hwnd /* handle of window */
835 ) {
836     if (!hwnd)
837         return GetDCEx32( GetDesktopWindow32(), 0, DCX_CACHE | DCX_WINDOW );
838     return GetDCEx32( hwnd, 0, DCX_USESTYLE );
839 }
840
841
842 /***********************************************************************
843  *           GetWindowDC16    (USER.67)
844  */
845 HDC16 WINAPI GetWindowDC16( HWND16 hwnd )
846 {
847     if (!hwnd) hwnd = GetDesktopWindow16();
848     return GetDCEx16( hwnd, 0, DCX_USESTYLE | DCX_WINDOW );
849 }
850
851
852 /***********************************************************************
853  *           GetWindowDC32    (USER32.304)
854  */
855 HDC32 WINAPI GetWindowDC32( HWND32 hwnd )
856 {
857     if (!hwnd) hwnd = GetDesktopWindow32();
858     return GetDCEx32( hwnd, 0, DCX_USESTYLE | DCX_WINDOW );
859 }
860
861
862 /***********************************************************************
863  *           ReleaseDC16    (USER.68)
864  */
865 INT16 WINAPI ReleaseDC16( HWND16 hwnd, HDC16 hdc )
866 {
867     return (INT16)ReleaseDC32( hwnd, hdc );
868 }
869
870
871 /***********************************************************************
872  *           ReleaseDC32    (USER32.440)
873  *
874  * RETURNS
875  *      1: Success
876  *      0: Failure
877  */
878 INT32 WINAPI ReleaseDC32( 
879              HWND32 hwnd /* Handle of window - ignored */, 
880              HDC32 hdc   /* Handle of device context */
881 ) {
882     DCE * dce = firstDCE;
883     
884     TRACE(dc, "%04x %04x\n", hwnd, hdc );
885         
886     while (dce && (dce->hDC != hdc)) dce = dce->next;
887
888     if ( dce ) 
889         if ( dce->DCXflags & DCX_DCEBUSY )
890              return DCE_ReleaseDC( dce );
891     return 0;
892 }
893
894 /***********************************************************************
895  *           DCHook    (USER.362)
896  *
897  * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..  
898  */
899 BOOL16 WINAPI DCHook( HDC16 hDC, WORD code, DWORD data, LPARAM lParam )
900 {
901     HRGN32 hVisRgn;
902     DCE *dce = firstDCE;;
903
904     TRACE(dc,"hDC = %04x, %i\n", hDC, code);
905
906     while (dce && (dce->hDC != hDC)) dce = dce->next;
907     if (!dce) return 0;
908
909     switch( code )
910     {
911       case DCHC_INVALIDVISRGN:
912
913            /* GDI code calls this when it detects that the
914             * DC is dirty (usually after SetHookFlags()). This
915             * means that we have to recompute the visible region.
916             */
917
918            if( dce->DCXflags & DCX_DCEBUSY )
919            {
920                SetHookFlags(hDC, DCHF_VALIDATEVISRGN);
921                hVisRgn = DCE_GetVisRgn(dce->hwndCurrent, dce->DCXflags);
922
923                TRACE(dc,"\tapplying saved clipRgn\n");
924   
925                /* clip this region with saved clipping region */
926
927                if ( (dce->DCXflags & DCX_INTERSECTRGN && dce->hClipRgn != 1) ||
928                   (  dce->DCXflags & DCX_EXCLUDERGN && dce->hClipRgn) )
929                {
930
931                     if( (!dce->hClipRgn && dce->DCXflags & DCX_INTERSECTRGN) ||
932                          (dce->hClipRgn == 1 && dce->DCXflags & DCX_EXCLUDERGN) )            
933                          SetRectRgn32(hVisRgn,0,0,0,0);
934                     else
935                          CombineRgn32(hVisRgn, hVisRgn, dce->hClipRgn, 
936                                       (dce->DCXflags & DCX_EXCLUDERGN)? RGN_DIFF:RGN_AND);
937                }
938                dce->DCXflags &= ~DCX_DCEDIRTY;
939                DCE_OffsetVisRgn( hDC, hVisRgn );
940                SelectVisRgn(hDC, hVisRgn);
941                DeleteObject32( hVisRgn );
942            }
943            else /* non-fatal but shouldn't happen */
944              WARN(dc, "DC is not in use!\n");
945            break;
946
947       case DCHC_DELETEDC: /* FIXME: ?? */
948            break;
949
950       default:
951            FIXME(dc,"unknown code\n");
952     }
953   return 0;
954 }
955
956
957 /**********************************************************************
958  *          WindowFromDC16   (USER.117)
959  */
960 HWND16 WINAPI WindowFromDC16( HDC16 hDC )
961 {
962     return (HWND16)WindowFromDC32( hDC );
963 }
964
965
966 /**********************************************************************
967  *          WindowFromDC32   (USER32.581)
968  */
969 HWND32 WINAPI WindowFromDC32( HDC32 hDC )
970 {
971     DCE *dce = firstDCE;
972     while (dce && (dce->hDC != hDC)) dce = dce->next;
973     return dce ? dce->hwndCurrent : 0;
974 }
975
976
977 /***********************************************************************
978  *           LockWindowUpdate16   (USER.294)
979  */
980 BOOL16 WINAPI LockWindowUpdate16( HWND16 hwnd )
981 {
982     return LockWindowUpdate32( hwnd );
983 }
984
985
986 /***********************************************************************
987  *           LockWindowUpdate32   (USER32.378)
988  */
989 BOOL32 WINAPI LockWindowUpdate32( HWND32 hwnd )
990 {
991     /* FIXME? DCX_LOCKWINDOWUPDATE is unimplemented */
992     return TRUE;
993 }