Added a first-cut version of MapVirtualKeyExW() that has the same
[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 <assert.h>
21 #include "options.h"
22 #include "dce.h"
23 #include "class.h"
24 #include "win.h"
25 #include "gdi.h"
26 #include "region.h"
27 #include "heap.h"
28 #include "local.h"
29 #include "user.h"
30 #include "debugtools.h"
31 #include "windef.h"
32 #include "wingdi.h"
33 #include "wine/winbase16.h"
34 #include "wine/winuser16.h"
35
36 DEFAULT_DEBUG_CHANNEL(dc);
37
38 #define NB_DCE    5  /* Number of DCEs created at startup */
39
40 static DCE *firstDCE = 0;
41 static HDC defaultDCstate = 0;
42
43 static void DCE_DeleteClipRgn( DCE* );
44 static INT DCE_ReleaseDC( DCE* );
45
46
47 /***********************************************************************
48  *           DCE_DumpCache
49  */
50 static void DCE_DumpCache(void)
51 {
52     DCE *dce;
53     
54     WIN_LockWnds();
55     dce = firstDCE;
56     
57     DPRINTF("DCE:\n");
58     while( dce )
59     {
60         DPRINTF("\t[0x%08x] hWnd 0x%04x, dcx %08x, %s %s\n",
61              (unsigned)dce, dce->hwndCurrent, (unsigned)dce->DCXflags, 
62              (dce->DCXflags & DCX_CACHE) ? "Cache" : "Owned", 
63              (dce->DCXflags & DCX_DCEBUSY) ? "InUse" : "" );
64         dce = dce->next;
65     }
66
67     WIN_UnlockWnds();
68 }
69
70 /***********************************************************************
71  *           DCE_AllocDCE
72  *
73  * Allocate a new DCE.
74  */
75 DCE *DCE_AllocDCE( HWND hWnd, DCE_TYPE type )
76 {
77     FARPROC16 hookProc;
78     DCE * dce;
79     WND* wnd;
80     
81     if (!(dce = HeapAlloc( SystemHeap, 0, sizeof(DCE) ))) return NULL;
82     if (!(dce->hDC = CreateDCA( "DISPLAY", NULL, NULL, NULL )))
83     {
84         HeapFree( SystemHeap, 0, dce );
85         return 0;
86     }
87
88     wnd = WIN_FindWndPtr(hWnd);
89     
90     /* store DCE handle in DC hook data field */
91
92     hookProc = GetProcAddress16( GetModuleHandle16("USER"), (LPCSTR)362 );
93     SetDCHook( dce->hDC, hookProc, (DWORD)dce );
94
95     dce->hwndCurrent = hWnd;
96     dce->hClipRgn    = 0;
97     dce->next        = firstDCE;
98     firstDCE = dce;
99
100     if( type != DCE_CACHE_DC ) /* owned or class DC */
101     {
102         dce->DCXflags = DCX_DCEBUSY;
103         if( hWnd )
104         {
105             if( wnd->dwStyle & WS_CLIPCHILDREN ) dce->DCXflags |= DCX_CLIPCHILDREN;
106             if( wnd->dwStyle & WS_CLIPSIBLINGS ) dce->DCXflags |= DCX_CLIPSIBLINGS;
107         }
108         SetHookFlags16(dce->hDC,DCHF_INVALIDATEVISRGN);
109     }
110     else dce->DCXflags = DCX_CACHE | DCX_DCEEMPTY;
111
112     WIN_ReleaseWndPtr(wnd);
113     
114     return dce;
115 }
116
117
118 /***********************************************************************
119  *           DCE_FreeDCE
120  */
121 DCE* DCE_FreeDCE( DCE *dce )
122 {
123     DCE **ppDCE;
124
125     if (!dce) return NULL;
126
127     WIN_LockWnds();
128
129     ppDCE = &firstDCE;
130
131     while (*ppDCE && (*ppDCE != dce)) ppDCE = &(*ppDCE)->next;
132     if (*ppDCE == dce) *ppDCE = dce->next;
133
134     SetDCHook(dce->hDC, NULL, 0L);
135
136     DeleteDC( dce->hDC );
137     if( dce->hClipRgn && !(dce->DCXflags & DCX_KEEPCLIPRGN) )
138         DeleteObject(dce->hClipRgn);
139     HeapFree( SystemHeap, 0, dce );
140
141     WIN_UnlockWnds();
142     
143     return *ppDCE;
144 }
145
146 /***********************************************************************
147  *           DCE_FreeWindowDCE
148  *
149  * Remove owned DCE and reset unreleased cache DCEs.
150  */
151 void DCE_FreeWindowDCE( WND* pWnd )
152 {
153     DCE *pDCE;
154
155     WIN_LockWnds();
156     pDCE = firstDCE;
157
158     while( pDCE )
159     {
160         if( pDCE->hwndCurrent == pWnd->hwndSelf )
161         {
162             if( pDCE == pWnd->dce ) /* owned or Class DCE*/
163             {
164                 if (pWnd->class->style & CS_OWNDC)      /* owned DCE*/
165                 {
166                     pDCE = DCE_FreeDCE( pDCE );
167                     pWnd->dce = NULL;
168                     continue;
169                 }
170                 else if( pDCE->DCXflags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN) ) /* Class DCE*/
171                 {
172                     DCE_DeleteClipRgn( pDCE );
173                     pDCE->hwndCurrent = 0;
174                 }
175             }
176             else
177             {
178                 if( pDCE->DCXflags & DCX_DCEBUSY ) /* shared cache DCE */
179                 {
180                     /* FIXME: AFAICS we are doing the right thing here so 
181                      * this should be a WARN. But this is best left as an ERR 
182                      * because the 'application error' is likely to come from 
183                      * another part of Wine (i.e. it's our fault after all). 
184                      * We should change this to WARN when Wine is more stable
185                      * (for 1.0?).
186                      */
187                     ERR("[%04x] GetDC() without ReleaseDC()!\n", 
188                         pWnd->hwndSelf);
189                     DCE_ReleaseDC( pDCE );
190                 }
191
192                 pDCE->DCXflags &= DCX_CACHE;
193                 pDCE->DCXflags |= DCX_DCEEMPTY;
194                 pDCE->hwndCurrent = 0;
195             }
196         }
197         pDCE = pDCE->next;
198     }
199     
200     WIN_UnlockWnds();
201 }
202
203
204 /***********************************************************************
205  *   DCE_DeleteClipRgn
206  */
207 static void DCE_DeleteClipRgn( DCE* dce )
208 {
209     dce->DCXflags &= ~(DCX_EXCLUDERGN | DCX_INTERSECTRGN | DCX_WINDOWPAINT);
210
211     if( dce->DCXflags & DCX_KEEPCLIPRGN )
212         dce->DCXflags &= ~DCX_KEEPCLIPRGN;
213     else
214         if( dce->hClipRgn > 1 )
215             DeleteObject( dce->hClipRgn );
216
217     dce->hClipRgn = 0;
218
219     TRACE("\trestoring VisRgn\n");
220
221     RestoreVisRgn16(dce->hDC);
222 }
223
224
225 /***********************************************************************
226  *   DCE_ReleaseDC
227  */
228 static INT DCE_ReleaseDC( DCE* dce )
229 {
230     if ((dce->DCXflags & (DCX_DCEEMPTY | DCX_DCEBUSY)) != DCX_DCEBUSY) return 0;
231
232     /* restore previous visible region */
233
234     if ((dce->DCXflags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN)) &&
235         (dce->DCXflags & (DCX_CACHE | DCX_WINDOWPAINT)) )
236         DCE_DeleteClipRgn( dce );
237
238     if (dce->DCXflags & DCX_CACHE)
239     {
240         SetDCState16( dce->hDC, defaultDCstate );
241         dce->DCXflags &= ~DCX_DCEBUSY;
242         if (dce->DCXflags & DCX_DCEDIRTY)
243         {
244             /* don't keep around invalidated entries 
245              * because SetDCState() disables hVisRgn updates
246              * by removing dirty bit. */
247
248             dce->hwndCurrent = 0;
249             dce->DCXflags &= DCX_CACHE;
250             dce->DCXflags |= DCX_DCEEMPTY;
251         }
252     }
253     return 1;
254 }
255
256
257 /***********************************************************************
258  *   DCE_InvalidateDCE
259  *
260  * It is called from SetWindowPos() and EVENT_MapNotify - we have to
261  * mark as dirty all busy DCEs for windows that have pWnd->parent as
262  * an ansector and whose client rect intersects with specified update
263  * rectangle. In addition, pWnd->parent DCEs may need to be updated if
264  * DCX_CLIPCHILDREN flag is set.  */
265 BOOL DCE_InvalidateDCE(WND* pWnd, const RECT* pRectUpdate)
266 {
267     WND* wndScope = WIN_LockWndPtr(pWnd->parent);
268     WND *pDesktop = WIN_GetDesktop();
269     BOOL bRet = FALSE;
270
271     if( wndScope )
272     {
273         DCE *dce;
274
275         TRACE("scope hwnd = %04x, (%i,%i - %i,%i)\n",
276                      wndScope->hwndSelf, pRectUpdate->left,pRectUpdate->top,
277                      pRectUpdate->right,pRectUpdate->bottom);
278         if(TRACE_ON(dc)) 
279           DCE_DumpCache();
280
281         /* walk all DCEs and fixup non-empty entries */
282
283         for (dce = firstDCE; (dce); dce = dce->next)
284         {
285             if( !(dce->DCXflags & DCX_DCEEMPTY) )
286             {
287                 WND* wndCurrent = WIN_FindWndPtr(dce->hwndCurrent);
288
289                 if( wndCurrent )
290                 {
291                     WND* wnd = NULL;
292                     INT xoffset = 0, yoffset = 0;
293
294                     if( (wndCurrent == wndScope) && !(dce->DCXflags & DCX_CLIPCHILDREN) )
295                     {
296                         /* child window positions don't bother us */
297                         WIN_ReleaseWndPtr(wndCurrent);
298                         continue;
299                     }
300
301                     if( !Options.desktopGeometry && wndCurrent == pDesktop )
302                     {
303                         /* don't bother with fake desktop */
304                         WIN_ReleaseWndPtr(wndCurrent);
305                         continue;
306                     }
307
308                     /* check if DCE window is within the z-order scope */
309
310                     for( wnd = WIN_LockWndPtr(wndCurrent); wnd; WIN_UpdateWndPtr(&wnd,wnd->parent))
311                     {
312                         if( wnd == wndScope )
313                         {
314                             RECT wndRect;
315
316                             wndRect = wndCurrent->rectWindow;
317
318                             OffsetRect( &wndRect, xoffset - wndCurrent->rectClient.left, 
319                                                     yoffset - wndCurrent->rectClient.top);
320
321                             if (pWnd == wndCurrent ||
322                                 IntersectRect( &wndRect, &wndRect, pRectUpdate ))
323                             { 
324                                 if( !(dce->DCXflags & DCX_DCEBUSY) )
325                                 {
326                                     /* Don't bother with visible regions of unused DCEs */
327
328                                     TRACE("\tpurged %08x dce [%04x]\n", 
329                                                 (unsigned)dce, wndCurrent->hwndSelf);
330
331                                     dce->hwndCurrent = 0;
332                                     dce->DCXflags &= DCX_CACHE;
333                                     dce->DCXflags |= DCX_DCEEMPTY;
334                                 }
335                                 else
336                                 {
337                                     /* Set dirty bits in the hDC and DCE structs */
338
339                                     TRACE("\tfixed up %08x dce [%04x]\n", 
340                                                 (unsigned)dce, wndCurrent->hwndSelf);
341
342                                     dce->DCXflags |= DCX_DCEDIRTY;
343                                     SetHookFlags16(dce->hDC, DCHF_INVALIDATEVISRGN);
344                                     bRet = TRUE;
345                                 }
346                             }
347                             WIN_ReleaseWndPtr(wnd);
348                             break;
349                         }
350                         xoffset += wnd->rectClient.left;
351                         yoffset += wnd->rectClient.top;
352                     }
353                 }
354                 WIN_ReleaseWndPtr(wndCurrent);
355             }
356         } /* dce list */
357         WIN_ReleaseWndPtr(wndScope);
358     }
359     WIN_ReleaseDesktop();
360     return bRet;
361 }
362
363 /***********************************************************************
364  *           DCE_Init
365  */
366 void DCE_Init(void)
367 {
368     int i;
369     DCE * dce;
370         
371     for (i = 0; i < NB_DCE; i++)
372     {
373         if (!(dce = DCE_AllocDCE( 0, DCE_CACHE_DC ))) return;
374         if (!defaultDCstate) defaultDCstate = GetDCState16( dce->hDC );
375     }
376 }
377
378
379 /***********************************************************************
380  *           DCE_GetVisRect
381  *
382  * Calculate the visible rectangle of a window (i.e. the client or
383  * window area clipped by the client area of all ancestors) in the
384  * corresponding coordinates. Return FALSE if the visible region is empty.
385  */
386 static BOOL DCE_GetVisRect( WND *wndPtr, BOOL clientArea, RECT *lprect )
387 {
388     *lprect = clientArea ? wndPtr->rectClient : wndPtr->rectWindow;
389
390     if (wndPtr->dwStyle & WS_VISIBLE)
391     {
392         INT xoffset = lprect->left;
393         INT yoffset = lprect->top;
394
395         while( !(wndPtr->flags & WIN_NATIVE) &&
396            ( wndPtr = WIN_LockWndPtr(wndPtr->parent)) )
397         {
398             if ( (wndPtr->dwStyle & (WS_ICONIC | WS_VISIBLE)) != WS_VISIBLE )
399             {
400                 WIN_ReleaseWndPtr(wndPtr);
401                 goto fail;
402             }
403
404             xoffset += wndPtr->rectClient.left;
405             yoffset += wndPtr->rectClient.top;
406             OffsetRect( lprect, wndPtr->rectClient.left,
407                                   wndPtr->rectClient.top );
408
409             if( (wndPtr->rectClient.left >= wndPtr->rectClient.right) ||
410                 (wndPtr->rectClient.top >= wndPtr->rectClient.bottom) ||
411                 (lprect->left >= wndPtr->rectClient.right) ||
412                 (lprect->right <= wndPtr->rectClient.left) ||
413                 (lprect->top >= wndPtr->rectClient.bottom) ||
414                 (lprect->bottom <= wndPtr->rectClient.top) )
415             {
416                 WIN_ReleaseWndPtr(wndPtr);
417                 goto fail;
418             }
419
420             lprect->left = max( lprect->left, wndPtr->rectClient.left );
421             lprect->right = min( lprect->right, wndPtr->rectClient.right );
422             lprect->top = max( lprect->top, wndPtr->rectClient.top );
423             lprect->bottom = min( lprect->bottom, wndPtr->rectClient.bottom );
424
425             WIN_ReleaseWndPtr(wndPtr);
426         }
427         OffsetRect( lprect, -xoffset, -yoffset );
428         return TRUE;
429     }
430
431 fail:
432     SetRectEmpty( lprect );
433     return FALSE;
434 }
435
436
437 /***********************************************************************
438  *           DCE_AddClipRects
439  *
440  * Go through the linked list of windows from pWndStart to pWndEnd,
441  * adding to the clip region the intersection of the target rectangle
442  * with an offset window rectangle.
443  */
444 static BOOL DCE_AddClipRects( WND *pWndStart, WND *pWndEnd, 
445                                 HRGN hrgnClip, LPRECT lpRect, int x, int y )
446 {
447     RECT rect;
448
449     if( pWndStart->pDriver->pIsSelfClipping( pWndStart ) )
450         return TRUE; /* The driver itself will do the clipping */
451
452     for (WIN_LockWndPtr(pWndStart); (pWndStart && (pWndStart != pWndEnd)); WIN_UpdateWndPtr(&pWndStart,pWndStart->next))
453     {
454         if( !(pWndStart->dwStyle & WS_VISIBLE) ) continue;
455             
456         rect.left = pWndStart->rectWindow.left + x;
457         rect.top = pWndStart->rectWindow.top + y;
458         rect.right = pWndStart->rectWindow.right + x;
459         rect.bottom = pWndStart->rectWindow.bottom + y;
460
461         if( IntersectRect( &rect, &rect, lpRect ))
462         {
463             if(!REGION_UnionRectWithRgn( hrgnClip, &rect )) break;
464         }
465     }
466     WIN_ReleaseWndPtr(pWndStart);
467     return (pWndStart == pWndEnd);
468 }
469
470
471 /***********************************************************************
472  *           DCE_GetVisRgn
473  *
474  * Return the visible region of a window, i.e. the client or window area
475  * clipped by the client area of all ancestors, and then optionally
476  * by siblings and children.
477  */
478 HRGN DCE_GetVisRgn( HWND hwnd, WORD flags, HWND hwndChild, WORD cflags )
479 {
480     HRGN hrgnVis = 0;
481     RECT rect;
482     WND *wndPtr = WIN_FindWndPtr( hwnd );
483     WND *childWnd = WIN_FindWndPtr( hwndChild );
484
485     /* Get visible rectangle and create a region with it. */
486
487     if (wndPtr && DCE_GetVisRect(wndPtr, !(flags & DCX_WINDOW), &rect))
488     {
489         if((hrgnVis = CreateRectRgnIndirect( &rect )))
490         {
491             HRGN hrgnClip = CreateRectRgn( 0, 0, 0, 0 );
492             INT xoffset, yoffset;
493
494             if( hrgnClip )
495             {
496                 /* Compute obscured region for the visible rectangle by 
497                  * clipping children, siblings, and ancestors. Note that
498                  * DCE_GetVisRect() returns a rectangle either in client
499                  * or in window coordinates (for DCX_WINDOW request). */
500
501                 if( (flags & DCX_CLIPCHILDREN) && wndPtr->child )
502                 {
503                     if( flags & DCX_WINDOW )
504                     {
505                         /* adjust offsets since child window rectangles are 
506                          * in client coordinates */
507
508                         xoffset = wndPtr->rectClient.left - wndPtr->rectWindow.left;
509                         yoffset = wndPtr->rectClient.top - wndPtr->rectWindow.top;
510                     }
511                     else 
512                         xoffset = yoffset = 0;
513
514                     DCE_AddClipRects( wndPtr->child, NULL, hrgnClip, 
515                                       &rect, xoffset, yoffset );
516                 }
517
518                 /* We may need to clip children of child window, if a window with PARENTDC
519                  * class style and CLIPCHILDREN window style (like in Free Agent 16
520                  * preference dialogs) gets here, we take the region for the parent window
521                  * but apparently still need to clip the children of the child window... */
522
523                 if( (cflags & DCX_CLIPCHILDREN) && childWnd && childWnd->child )
524                 {
525                     if( flags & DCX_WINDOW )
526                     {
527                         /* adjust offsets since child window rectangles are 
528                          * in client coordinates */
529
530                         xoffset = wndPtr->rectClient.left - wndPtr->rectWindow.left;
531                         yoffset = wndPtr->rectClient.top - wndPtr->rectWindow.top;
532                     }
533                     else 
534                         xoffset = yoffset = 0;
535
536                     /* client coordinates of child window */
537                     xoffset += childWnd->rectClient.left;
538                     yoffset += childWnd->rectClient.top;
539
540                     DCE_AddClipRects( childWnd->child, NULL, hrgnClip, 
541                                       &rect, xoffset, yoffset );
542                 }
543
544                 /* sibling window rectangles are in client 
545                  * coordinates of the parent window */
546
547                 if (flags & DCX_WINDOW)
548                 {
549                     xoffset = -wndPtr->rectWindow.left;
550                     yoffset = -wndPtr->rectWindow.top;
551                 }
552                 else
553                 {
554                     xoffset = -wndPtr->rectClient.left;
555                     yoffset = -wndPtr->rectClient.top;
556                 }
557
558                 if (flags & DCX_CLIPSIBLINGS && wndPtr->parent )
559                     DCE_AddClipRects( wndPtr->parent->child,
560                                       wndPtr, hrgnClip, &rect, xoffset, yoffset );
561
562                 /* Clip siblings of all ancestors that have the
563                  * WS_CLIPSIBLINGS style
564                  */
565
566                 while (wndPtr->dwStyle & WS_CHILD)
567                 {
568                     WIN_UpdateWndPtr(&wndPtr,wndPtr->parent);
569                     xoffset -= wndPtr->rectClient.left;
570                     yoffset -= wndPtr->rectClient.top;
571                     if(wndPtr->dwStyle & WS_CLIPSIBLINGS && wndPtr->parent)
572                     {
573                         DCE_AddClipRects( wndPtr->parent->child, wndPtr,
574                                           hrgnClip, &rect, xoffset, yoffset );
575                     }
576                 }
577
578                 /* Now once we've got a jumbo clip region we have
579                  * to substract it from the visible rectangle.
580                  */
581
582                 CombineRgn( hrgnVis, hrgnVis, hrgnClip, RGN_DIFF );
583                 DeleteObject( hrgnClip );
584             }
585             else
586             {
587                 DeleteObject( hrgnVis );
588                 hrgnVis = 0;
589             }
590         }
591     }
592     else
593         hrgnVis = CreateRectRgn(0, 0, 0, 0); /* empty */
594     WIN_ReleaseWndPtr(wndPtr);
595     WIN_ReleaseWndPtr(childWnd);
596     return hrgnVis;
597 }
598
599 /***********************************************************************
600  *           DCE_OffsetVisRgn
601  *
602  * Change region from DC-origin relative coordinates to screen coords.
603  */
604
605 static void DCE_OffsetVisRgn( HDC hDC, HRGN hVisRgn )
606 {
607     DC *dc;
608     if (!(dc = DC_GetDCPtr( hDC ))) return;
609
610     OffsetRgn( hVisRgn, dc->DCOrgX, dc->DCOrgY );
611
612     GDI_ReleaseObj( hDC );
613 }
614
615 /***********************************************************************
616  *           DCE_ExcludeRgn
617  * 
618  *  Translate given region from the wnd client to the DC coordinates
619  *  and add it to the clipping region.
620  */
621 INT DCE_ExcludeRgn( HDC hDC, WND* wnd, HRGN hRgn )
622 {
623   POINT  pt = {0, 0};
624   DCE     *dce = firstDCE;
625
626   while (dce && (dce->hDC != hDC)) dce = dce->next;
627   if( dce )
628   {
629       MapWindowPoints( wnd->hwndSelf, dce->hwndCurrent, &pt, 1);
630       if( dce->DCXflags & DCX_WINDOW )
631       { 
632           wnd = WIN_FindWndPtr(dce->hwndCurrent);
633           pt.x += wnd->rectClient.left - wnd->rectWindow.left;
634           pt.y += wnd->rectClient.top - wnd->rectWindow.top;
635           WIN_ReleaseWndPtr(wnd);
636       }
637   }
638   else return ERROR;
639   OffsetRgn(hRgn, pt.x, pt.y);
640
641   return ExtSelectClipRgn( hDC, hRgn, RGN_DIFF );
642 }
643
644
645 /***********************************************************************
646  *           GetDCEx16    (USER.359)
647  */
648 HDC16 WINAPI GetDCEx16( HWND16 hwnd, HRGN16 hrgnClip, DWORD flags )
649 {
650     return (HDC16)GetDCEx( hwnd, hrgnClip, flags );
651 }
652
653
654 /***********************************************************************
655  *           GetDCEx    (USER32.231)
656  *
657  * Unimplemented flags: DCX_LOCKWINDOWUPDATE
658  *
659  * FIXME: Full support for hrgnClip == 1 (alias for entire window).
660  */
661 HDC WINAPI GetDCEx( HWND hwnd, HRGN hrgnClip, DWORD flags )
662 {
663     HRGN        hrgnVisible = 0;
664     HDC         hdc = 0;
665     DCE *       dce;
666     DC *        dc;
667     WND *       wndPtr;
668     DWORD       dcxFlags = 0;
669     BOOL        bUpdateVisRgn = TRUE;
670     BOOL        bUpdateClipOrigin = FALSE;
671
672     TRACE("hwnd %04x, hrgnClip %04x, flags %08x\n", 
673                                 hwnd, hrgnClip, (unsigned)flags);
674     
675     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return 0;
676
677     /* fixup flags */
678
679     if (!(wndPtr->class->style & (CS_OWNDC | CS_CLASSDC))) flags |= DCX_CACHE;
680
681     if (flags & DCX_USESTYLE)
682     {
683         flags &= ~( DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS | DCX_PARENTCLIP);
684
685         if( wndPtr->dwStyle & WS_CLIPSIBLINGS )
686             flags |= DCX_CLIPSIBLINGS;
687
688         if ( !(flags & DCX_WINDOW) )
689         {
690             if (wndPtr->class->style & CS_PARENTDC) flags |= DCX_PARENTCLIP;
691
692             if (wndPtr->dwStyle & WS_CLIPCHILDREN &&
693                      !(wndPtr->dwStyle & WS_MINIMIZE) ) flags |= DCX_CLIPCHILDREN;
694         }
695         else flags |= DCX_CACHE;
696     }
697
698     if( flags & DCX_NOCLIPCHILDREN )
699     {
700         flags |= DCX_CACHE;
701         flags &= ~(DCX_PARENTCLIP | DCX_CLIPCHILDREN);
702     }
703
704     if (flags & DCX_WINDOW) 
705         flags = (flags & ~DCX_CLIPCHILDREN) | DCX_CACHE;
706
707     if (!(wndPtr->dwStyle & WS_CHILD) || !wndPtr->parent ) 
708         flags &= ~DCX_PARENTCLIP;
709     else if( flags & DCX_PARENTCLIP )
710     {
711         flags |= DCX_CACHE;
712         if( !(flags & (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN)) )
713             if( (wndPtr->dwStyle & WS_VISIBLE) && (wndPtr->parent->dwStyle & WS_VISIBLE) )
714             {
715                 flags &= ~DCX_CLIPCHILDREN;
716                 if( wndPtr->parent->dwStyle & WS_CLIPSIBLINGS )
717                     flags |= DCX_CLIPSIBLINGS;
718             }
719     }
720
721     /* find a suitable DCE */
722
723     dcxFlags = flags & (DCX_PARENTCLIP | DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | 
724                         DCX_CACHE | DCX_WINDOW);
725
726     if (flags & DCX_CACHE)
727     {
728         DCE*    dceEmpty;
729         DCE*    dceUnused;
730
731         dceEmpty = dceUnused = NULL;
732
733         /* Strategy: First, we attempt to find a non-empty but unused DCE with
734          * compatible flags. Next, we look for an empty entry. If the cache is
735          * full we have to purge one of the unused entries.
736          */
737
738         for (dce = firstDCE; (dce); dce = dce->next)
739         {
740             if ((dce->DCXflags & (DCX_CACHE | DCX_DCEBUSY)) == DCX_CACHE )
741             {
742                 dceUnused = dce;
743
744                 if (dce->DCXflags & DCX_DCEEMPTY)
745                     dceEmpty = dce;
746                 else
747                 if ((dce->hwndCurrent == hwnd) &&
748                    ((dce->DCXflags & (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN |
749                                       DCX_CACHE | DCX_WINDOW | DCX_PARENTCLIP)) == dcxFlags))
750                 {
751                     TRACE("\tfound valid %08x dce [%04x], flags %08x\n", 
752                                         (unsigned)dce, hwnd, (unsigned)dcxFlags );
753                     bUpdateVisRgn = FALSE; 
754                     bUpdateClipOrigin = TRUE;
755                     break;
756                 }
757             }
758         }
759
760         if (!dce) dce = (dceEmpty) ? dceEmpty : dceUnused;
761         
762         /* if there's no dce empty or unused, allocate a new one */
763         if (!dce)
764         {
765             dce = DCE_AllocDCE( 0, DCE_CACHE_DC );
766         }
767     }
768     else 
769     {
770         dce = (wndPtr->class->style & CS_OWNDC) ? wndPtr->dce : wndPtr->class->dce;
771         if( dce->hwndCurrent == hwnd )
772         {
773             TRACE("\tskipping hVisRgn update\n");
774             bUpdateVisRgn = FALSE; /* updated automatically, via DCHook() */
775
776             /* Abey - 16Jul99. to take care of the nested GetDC. first one
777                with DCX_EXCLUDERGN or DCX_INTERSECTRGN flags and the next
778                one with or without these flags. */
779
780             if(dce->DCXflags & (DCX_EXCLUDERGN | DCX_INTERSECTRGN))
781             {
782                 /* This is likely to be a nested BeginPaint().
783                    or a BeginPaint() followed by a GetDC()*/
784
785                 if( dce->hClipRgn != hrgnClip )
786                 {
787                     FIXME("new hrgnClip[%04x] smashes the previous[%04x]\n",
788                           hrgnClip, dce->hClipRgn );
789                     DCE_DeleteClipRgn( dce );
790                 }
791                 else 
792                     RestoreVisRgn16(dce->hDC);
793             }
794         }
795     }
796     if (!dce)
797     {
798         hdc = 0;
799         goto END;
800     }
801
802     dce->hwndCurrent = hwnd;
803     dce->hClipRgn = 0;
804     dce->DCXflags = dcxFlags | (flags & DCX_WINDOWPAINT) | DCX_DCEBUSY;
805     hdc = dce->hDC;
806     
807     if (!(dc = DC_GetDCPtr( hdc )))
808     {
809         hdc = 0;
810         goto END;
811     }
812     bUpdateVisRgn = bUpdateVisRgn || (dc->flags & DC_DIRTY);
813
814     /* recompute visible region */
815     wndPtr->pDriver->pSetDrawable( wndPtr, hdc, flags, bUpdateClipOrigin );
816     dc->flags &= ~DC_DIRTY;
817     GDI_ReleaseObj( hdc );
818
819     if( bUpdateVisRgn )
820     {
821         TRACE("updating visrgn for %08x dce, hwnd [%04x]\n", (unsigned)dce, hwnd);
822
823         if (flags & DCX_PARENTCLIP)
824         {
825             WND *parentPtr = WIN_LockWndPtr(wndPtr->parent);
826
827             if( wndPtr->dwStyle & WS_VISIBLE && !(parentPtr->dwStyle & WS_MINIMIZE) )
828             {
829                 if( parentPtr->dwStyle & WS_CLIPSIBLINGS ) 
830                     dcxFlags = DCX_CLIPSIBLINGS | (flags & ~(DCX_CLIPCHILDREN | DCX_WINDOW));
831                 else
832                     dcxFlags = flags & ~(DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_WINDOW);
833
834                 hrgnVisible = DCE_GetVisRgn( parentPtr->hwndSelf, dcxFlags,
835                                              wndPtr->hwndSelf, flags );
836                 if( flags & DCX_WINDOW )
837                     OffsetRgn( hrgnVisible, -wndPtr->rectWindow.left,
838                                               -wndPtr->rectWindow.top );
839                 else
840                     OffsetRgn( hrgnVisible, -wndPtr->rectClient.left,
841                                               -wndPtr->rectClient.top );
842                 DCE_OffsetVisRgn( hdc, hrgnVisible );
843             }
844             else
845                 hrgnVisible = CreateRectRgn( 0, 0, 0, 0 );
846             WIN_ReleaseWndPtr(parentPtr);
847         }
848         else
849             if ((hwnd == GetDesktopWindow()) && !USER_Driver.pIsSingleWindow())
850                  hrgnVisible = CreateRectRgn( 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN) );
851             else 
852             {
853                 hrgnVisible = DCE_GetVisRgn( hwnd, flags, 0, 0 );
854                 DCE_OffsetVisRgn( hdc, hrgnVisible );
855             }
856
857         dce->DCXflags &= ~DCX_DCEDIRTY;
858         SelectVisRgn16( hdc, hrgnVisible );
859     }
860     else
861         TRACE("no visrgn update %08x dce, hwnd [%04x]\n", (unsigned)dce, hwnd);
862
863     /* apply additional region operation (if any) */
864
865     if( flags & (DCX_EXCLUDERGN | DCX_INTERSECTRGN) )
866     {
867         if( !hrgnVisible ) hrgnVisible = CreateRectRgn( 0, 0, 0, 0 );
868
869         dce->DCXflags |= flags & (DCX_KEEPCLIPRGN | DCX_INTERSECTRGN | DCX_EXCLUDERGN);
870         dce->hClipRgn = hrgnClip;
871
872         TRACE("\tsaved VisRgn, clipRgn = %04x\n", hrgnClip);
873
874         SaveVisRgn16( hdc );
875         CombineRgn( hrgnVisible, hrgnClip, 0, RGN_COPY );
876         DCE_OffsetVisRgn( hdc, hrgnVisible );
877         CombineRgn( hrgnVisible, InquireVisRgn16( hdc ), hrgnVisible,
878                       (flags & DCX_INTERSECTRGN) ? RGN_AND : RGN_DIFF );
879         SelectVisRgn16( hdc, hrgnVisible );
880     }
881
882     if( hrgnVisible ) DeleteObject( hrgnVisible );
883
884     TRACE("(%04x,%04x,0x%lx): returning %04x\n", 
885                hwnd, hrgnClip, flags, hdc);
886 END:
887     WIN_ReleaseWndPtr(wndPtr);
888     return hdc;
889 }
890
891
892 /***********************************************************************
893  *           GetDC16    (USER.66)
894  */
895 HDC16 WINAPI GetDC16( HWND16 hwnd )
896 {
897     return (HDC16)GetDC( hwnd );
898 }
899
900
901 /***********************************************************************
902  *           GetDC    (USER32.230)
903  * RETURNS
904  *      :Handle to DC
905  *      NULL: Failure
906  */
907 HDC WINAPI GetDC(
908              HWND hwnd /* [in] handle of window */
909 ) {
910     if (!hwnd)
911         return GetDCEx( GetDesktopWindow(), 0, DCX_CACHE | DCX_WINDOW );
912     return GetDCEx( hwnd, 0, DCX_USESTYLE );
913 }
914
915
916 /***********************************************************************
917  *           GetWindowDC16    (USER.67)
918  */
919 HDC16 WINAPI GetWindowDC16( HWND16 hwnd )
920 {
921     if (!hwnd) hwnd = GetDesktopWindow16();
922     return GetDCEx16( hwnd, 0, DCX_USESTYLE | DCX_WINDOW );
923 }
924
925
926 /***********************************************************************
927  *           GetWindowDC    (USER32.304)
928  */
929 HDC WINAPI GetWindowDC( HWND hwnd )
930 {
931     if (!hwnd) hwnd = GetDesktopWindow();
932     return GetDCEx( hwnd, 0, DCX_USESTYLE | DCX_WINDOW );
933 }
934
935
936 /***********************************************************************
937  *           ReleaseDC16    (USER.68)
938  */
939 INT16 WINAPI ReleaseDC16( HWND16 hwnd, HDC16 hdc )
940 {
941     return (INT16)ReleaseDC( hwnd, hdc );
942 }
943
944
945 /***********************************************************************
946  *           ReleaseDC    (USER32.440)
947  *
948  * RETURNS
949  *      1: Success
950  *      0: Failure
951  */
952 INT WINAPI ReleaseDC( 
953              HWND hwnd /* [in] Handle of window - ignored */, 
954              HDC hdc   /* [in] Handle of device context */
955 ) {
956     DCE * dce;
957     INT nRet = 0;
958
959     WIN_LockWnds();
960     dce = firstDCE;
961     
962     TRACE("%04x %04x\n", hwnd, hdc );
963         
964     while (dce && (dce->hDC != hdc)) dce = dce->next;
965
966     if ( dce ) 
967         if ( dce->DCXflags & DCX_DCEBUSY )
968             nRet = DCE_ReleaseDC( dce );
969
970     WIN_UnlockWnds();
971
972     return nRet;
973 }
974
975 /***********************************************************************
976  *           DCHook    (USER.362)
977  *
978  * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..  
979  */
980 BOOL16 WINAPI DCHook16( HDC16 hDC, WORD code, DWORD data, LPARAM lParam )
981 {
982     BOOL retv = TRUE;
983     HRGN hVisRgn;
984     DCE *dce = (DCE *)data;
985     WND *wndPtr;
986
987     TRACE("hDC = %04x, %i\n", hDC, code);
988
989     if (!dce) return 0;
990     assert(dce->hDC == hDC);
991
992     /* Grab the windows lock before doing anything else  */
993     WIN_LockWnds();
994
995     switch( code )
996     {
997       case DCHC_INVALIDVISRGN:
998
999            /* GDI code calls this when it detects that the
1000             * DC is dirty (usually after SetHookFlags()). This
1001             * means that we have to recompute the visible region.
1002             */
1003
1004            if( dce->DCXflags & DCX_DCEBUSY )
1005            {
1006
1007                /* Update stale DC in DCX */
1008                wndPtr = WIN_FindWndPtr( dce->hwndCurrent);
1009                if (wndPtr) wndPtr->pDriver->pSetDrawable( wndPtr, dce->hDC, dce->DCXflags, TRUE);
1010
1011                SetHookFlags16(hDC, DCHF_VALIDATEVISRGN);
1012                hVisRgn = DCE_GetVisRgn(dce->hwndCurrent, dce->DCXflags, 0, 0);
1013
1014                TRACE("\tapplying saved clipRgn\n");
1015   
1016                /* clip this region with saved clipping region */
1017
1018                if ( (dce->DCXflags & DCX_INTERSECTRGN && dce->hClipRgn != 1) ||
1019                   (  dce->DCXflags & DCX_EXCLUDERGN && dce->hClipRgn) )
1020                {
1021
1022                     if( (!dce->hClipRgn && dce->DCXflags & DCX_INTERSECTRGN) ||
1023                          (dce->hClipRgn == 1 && dce->DCXflags & DCX_EXCLUDERGN) )            
1024                          SetRectRgn(hVisRgn,0,0,0,0);
1025                     else
1026                          CombineRgn(hVisRgn, hVisRgn, dce->hClipRgn, 
1027                                       (dce->DCXflags & DCX_EXCLUDERGN)? RGN_DIFF:RGN_AND);
1028                }
1029                dce->DCXflags &= ~DCX_DCEDIRTY;
1030                DCE_OffsetVisRgn( hDC, hVisRgn );
1031                SelectVisRgn16(hDC, hVisRgn);
1032                DeleteObject( hVisRgn );
1033               WIN_ReleaseWndPtr( wndPtr );  /* Release WIN_FindWndPtr lock */
1034            }
1035            else /* non-fatal but shouldn't happen */
1036              WARN("DC is not in use!\n");
1037            break;
1038
1039       case DCHC_DELETEDC:
1040            /*
1041             * Windows will not let you delete a DC that is busy
1042             * (between GetDC and ReleaseDC)
1043             */
1044
1045            if ( dce->DCXflags & DCX_DCEBUSY )
1046            {
1047                WARN("Application trying to delete a busy DC\n");
1048                retv = FALSE;
1049            }
1050            break;
1051
1052       default:
1053            FIXME("unknown code\n");
1054     }
1055
1056   WIN_UnlockWnds();  /* Release the wnd lock */
1057   return retv;
1058 }
1059
1060
1061 /**********************************************************************
1062  *          WindowFromDC16   (USER.117)
1063  */
1064 HWND16 WINAPI WindowFromDC16( HDC16 hDC )
1065 {
1066     return (HWND16)WindowFromDC( hDC );
1067 }
1068
1069
1070 /**********************************************************************
1071  *          WindowFromDC   (USER32.581)
1072  */
1073 HWND WINAPI WindowFromDC( HDC hDC )
1074 {
1075     DCE *dce;
1076     HWND hwnd;
1077
1078     WIN_LockWnds();
1079     dce = firstDCE;
1080     
1081     while (dce && (dce->hDC != hDC)) dce = dce->next;
1082
1083     hwnd = dce ? dce->hwndCurrent : 0;
1084     WIN_UnlockWnds();
1085     
1086     return hwnd;
1087 }
1088
1089
1090 /***********************************************************************
1091  *           LockWindowUpdate16   (USER.294)
1092  */
1093 BOOL16 WINAPI LockWindowUpdate16( HWND16 hwnd )
1094 {
1095     return LockWindowUpdate( hwnd );
1096 }
1097
1098
1099 /***********************************************************************
1100  *           LockWindowUpdate   (USER32.378)
1101  */
1102 BOOL WINAPI LockWindowUpdate( HWND hwnd )
1103 {
1104     /* FIXME? DCX_LOCKWINDOWUPDATE is unimplemented */
1105     return TRUE;
1106 }