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