4 * Copyright 1993 Alexandre Julliard
5 * 1996,1997 Alex Korobka
8 * Note: Visible regions of CS_OWNDC/CS_CLASSDC window DCs
9 * have to be updated dynamically.
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
26 #include "debugtools.h"
29 #include "wine/winbase16.h"
30 #include "wine/winuser16.h"
32 DEFAULT_DEBUG_CHANNEL(dc
);
35 static HDC defaultDCstate
;
37 static void DCE_DeleteClipRgn( DCE
* );
38 static INT
DCE_ReleaseDC( DCE
* );
41 /***********************************************************************
44 static void DCE_DumpCache(void)
54 DPRINTF("\t[0x%08x] hWnd 0x%04x, dcx %08x, %s %s\n",
55 (unsigned)dce
, dce
->hwndCurrent
, (unsigned)dce
->DCXflags
,
56 (dce
->DCXflags
& DCX_CACHE
) ? "Cache" : "Owned",
57 (dce
->DCXflags
& DCX_DCEBUSY
) ? "InUse" : "" );
64 /***********************************************************************
69 DCE
*DCE_AllocDCE( HWND hWnd
, DCE_TYPE type
)
74 if (!(dce
= HeapAlloc( GetProcessHeap(), 0, sizeof(DCE
) ))) return NULL
;
75 if (!(dce
->hDC
= CreateDCA( "DISPLAY", NULL
, NULL
, NULL
)))
77 HeapFree( GetProcessHeap(), 0, dce
);
80 if (!defaultDCstate
) defaultDCstate
= GetDCState16( dce
->hDC
);
82 /* store DCE handle in DC hook data field */
84 hookProc
= GetProcAddress16( GetModuleHandle16("USER"), (LPCSTR
)362 );
85 SetDCHook( dce
->hDC
, hookProc
, (DWORD
)dce
);
87 dce
->hwndCurrent
= WIN_GetFullHandle( hWnd
);
90 if( type
!= DCE_CACHE_DC
) /* owned or class DC */
92 dce
->DCXflags
= DCX_DCEBUSY
;
95 LONG style
= GetWindowLongW( hWnd
, GWL_STYLE
);
96 if (style
& WS_CLIPCHILDREN
) dce
->DCXflags
|= DCX_CLIPCHILDREN
;
97 if (style
& WS_CLIPSIBLINGS
) dce
->DCXflags
|= DCX_CLIPSIBLINGS
;
99 SetHookFlags16(dce
->hDC
,DCHF_INVALIDATEVISRGN
);
101 else dce
->DCXflags
= DCX_CACHE
| DCX_DCEEMPTY
;
104 dce
->next
= firstDCE
;
111 /***********************************************************************
114 DCE
* DCE_FreeDCE( DCE
*dce
)
118 if (!dce
) return NULL
;
124 while (*ppDCE
&& (*ppDCE
!= dce
)) ppDCE
= &(*ppDCE
)->next
;
125 if (*ppDCE
== dce
) *ppDCE
= dce
->next
;
129 SetDCHook(dce
->hDC
, NULL
, 0L);
131 DeleteDC( dce
->hDC
);
132 if( dce
->hClipRgn
&& !(dce
->DCXflags
& DCX_KEEPCLIPRGN
) )
133 DeleteObject(dce
->hClipRgn
);
134 HeapFree( GetProcessHeap(), 0, dce
);
139 /***********************************************************************
142 * Remove owned DCE and reset unreleased cache DCEs.
144 void DCE_FreeWindowDCE( HWND hwnd
)
147 WND
*pWnd
= WIN_FindWndPtr( hwnd
);
150 hwnd
= pWnd
->hwndSelf
; /* make it a full handle */
154 if( pDCE
->hwndCurrent
== hwnd
)
156 if( pDCE
== pWnd
->dce
) /* owned or Class DCE*/
158 if (pWnd
->clsStyle
& CS_OWNDC
) /* owned DCE*/
160 pDCE
= DCE_FreeDCE( pDCE
);
164 else if( pDCE
->DCXflags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
) ) /* Class DCE*/
166 DCE_DeleteClipRgn( pDCE
);
167 pDCE
->hwndCurrent
= 0;
172 if( pDCE
->DCXflags
& DCX_DCEBUSY
) /* shared cache DCE */
174 /* FIXME: AFAICS we are doing the right thing here so
175 * this should be a WARN. But this is best left as an ERR
176 * because the 'application error' is likely to come from
177 * another part of Wine (i.e. it's our fault after all).
178 * We should change this to WARN when Wine is more stable
181 ERR("[%08x] GetDC() without ReleaseDC()!\n",hwnd
);
182 DCE_ReleaseDC( pDCE
);
185 pDCE
->DCXflags
&= DCX_CACHE
;
186 pDCE
->DCXflags
|= DCX_DCEEMPTY
;
187 pDCE
->hwndCurrent
= 0;
193 WIN_ReleaseWndPtr( pWnd
);
197 /***********************************************************************
200 static void DCE_DeleteClipRgn( DCE
* dce
)
202 dce
->DCXflags
&= ~(DCX_EXCLUDERGN
| DCX_INTERSECTRGN
| DCX_WINDOWPAINT
);
204 if( dce
->DCXflags
& DCX_KEEPCLIPRGN
)
205 dce
->DCXflags
&= ~DCX_KEEPCLIPRGN
;
207 if( dce
->hClipRgn
> 1 )
208 DeleteObject( dce
->hClipRgn
);
212 /* make it dirty so that the vis rgn gets recomputed next time */
213 dce
->DCXflags
|= DCX_DCEDIRTY
;
214 SetHookFlags16( dce
->hDC
, DCHF_INVALIDATEVISRGN
);
218 /***********************************************************************
221 static INT
DCE_ReleaseDC( DCE
* dce
)
223 if ((dce
->DCXflags
& (DCX_DCEEMPTY
| DCX_DCEBUSY
)) != DCX_DCEBUSY
) return 0;
225 /* restore previous visible region */
227 if ((dce
->DCXflags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
)) &&
228 (dce
->DCXflags
& (DCX_CACHE
| DCX_WINDOWPAINT
)) )
229 DCE_DeleteClipRgn( dce
);
231 if (dce
->DCXflags
& DCX_CACHE
)
233 SetDCState16( dce
->hDC
, defaultDCstate
);
234 dce
->DCXflags
&= ~DCX_DCEBUSY
;
235 if (dce
->DCXflags
& DCX_DCEDIRTY
)
237 /* don't keep around invalidated entries
238 * because SetDCState() disables hVisRgn updates
239 * by removing dirty bit. */
241 dce
->hwndCurrent
= 0;
242 dce
->DCXflags
&= DCX_CACHE
;
243 dce
->DCXflags
|= DCX_DCEEMPTY
;
250 /***********************************************************************
253 * It is called from SetWindowPos() and EVENT_MapNotify - we have to
254 * mark as dirty all busy DCEs for windows that have pWnd->parent as
255 * an ancestor and whose client rect intersects with specified update
256 * rectangle. In addition, pWnd->parent DCEs may need to be updated if
257 * DCX_CLIPCHILDREN flag is set. */
258 BOOL
DCE_InvalidateDCE(HWND hwnd
, const RECT
* pRectUpdate
)
260 HWND hwndScope
= GetAncestor( hwnd
, GA_PARENT
);
267 TRACE("scope hwnd = %04x, (%i,%i - %i,%i)\n",
268 hwndScope
, pRectUpdate
->left
,pRectUpdate
->top
,
269 pRectUpdate
->right
,pRectUpdate
->bottom
);
273 /* walk all DCEs and fixup non-empty entries */
275 for (dce
= firstDCE
; (dce
); dce
= dce
->next
)
279 INT xoffset
= 0, yoffset
= 0;
281 if (dce
->DCXflags
& DCX_DCEEMPTY
) continue;
282 if ((dce
->hwndCurrent
== hwndScope
) && !(dce
->DCXflags
& DCX_CLIPCHILDREN
))
283 continue; /* child window positions don't bother us */
284 if (!(wndCurrent
= WIN_FindWndPtr(dce
->hwndCurrent
))) continue;
286 /* check if DCE window is within the z-order scope */
288 for (tmp
= dce
->hwndCurrent
; tmp
; tmp
= GetAncestor( tmp
, GA_PARENT
))
290 if (tmp
== hwndScope
)
294 wndRect
= wndCurrent
->rectWindow
;
296 OffsetRect( &wndRect
, xoffset
- wndCurrent
->rectClient
.left
,
297 yoffset
- wndCurrent
->rectClient
.top
);
299 if (hwnd
== wndCurrent
->hwndSelf
||
300 IntersectRect( &wndRect
, &wndRect
, pRectUpdate
))
302 if( !(dce
->DCXflags
& DCX_DCEBUSY
) )
304 /* Don't bother with visible regions of unused DCEs */
306 TRACE("\tpurged %08x dce [%04x]\n",
307 (unsigned)dce
, wndCurrent
->hwndSelf
);
309 dce
->hwndCurrent
= 0;
310 dce
->DCXflags
&= DCX_CACHE
;
311 dce
->DCXflags
|= DCX_DCEEMPTY
;
315 /* Set dirty bits in the hDC and DCE structs */
317 TRACE("\tfixed up %08x dce [%04x]\n",
318 (unsigned)dce
, wndCurrent
->hwndSelf
);
320 dce
->DCXflags
|= DCX_DCEDIRTY
;
321 SetHookFlags16(dce
->hDC
, DCHF_INVALIDATEVISRGN
);
329 WND
* wnd
= WIN_FindWndPtr( tmp
);
330 xoffset
+= wnd
->rectClient
.left
;
331 yoffset
+= wnd
->rectClient
.top
;
332 WIN_ReleaseWndPtr( wnd
);
335 WIN_ReleaseWndPtr(wndCurrent
);
342 /***********************************************************************
345 * Translate given region from the wnd client to the DC coordinates
346 * and add it to the clipping region.
348 INT
DCE_ExcludeRgn( HDC hDC
, HWND hwnd
, HRGN hRgn
)
353 while (dce
&& (dce
->hDC
!= hDC
)) dce
= dce
->next
;
354 if (!dce
) return ERROR
;
356 MapWindowPoints( hwnd
, dce
->hwndCurrent
, &pt
, 1);
357 if( dce
->DCXflags
& DCX_WINDOW
)
359 WND
*wnd
= WIN_FindWndPtr(dce
->hwndCurrent
);
360 pt
.x
+= wnd
->rectClient
.left
- wnd
->rectWindow
.left
;
361 pt
.y
+= wnd
->rectClient
.top
- wnd
->rectWindow
.top
;
362 WIN_ReleaseWndPtr(wnd
);
364 OffsetRgn(hRgn
, pt
.x
, pt
.y
);
366 return ExtSelectClipRgn( hDC
, hRgn
, RGN_DIFF
);
370 /***********************************************************************
373 * Unimplemented flags: DCX_LOCKWINDOWUPDATE
375 * FIXME: Full support for hrgnClip == 1 (alias for entire window).
377 HDC WINAPI
GetDCEx( HWND hwnd
, HRGN hrgnClip
, DWORD flags
)
383 BOOL bUpdateVisRgn
= TRUE
;
384 BOOL bUpdateClipOrigin
= FALSE
;
387 TRACE("hwnd %04x, hrgnClip %04x, flags %08x\n",
388 hwnd
, hrgnClip
, (unsigned)flags
);
390 if (!hwnd
) hwnd
= GetDesktopWindow();
391 if (!(wndPtr
= WIN_FindWndPtr( hwnd
))) return 0;
392 hwnd
= wndPtr
->hwndSelf
; /* make it a full handle */
396 if (flags
& (DCX_WINDOW
| DCX_PARENTCLIP
)) flags
|= DCX_CACHE
;
398 if (flags
& DCX_USESTYLE
)
400 flags
&= ~( DCX_CLIPCHILDREN
| DCX_CLIPSIBLINGS
| DCX_PARENTCLIP
);
402 if( wndPtr
->dwStyle
& WS_CLIPSIBLINGS
)
403 flags
|= DCX_CLIPSIBLINGS
;
405 if ( !(flags
& DCX_WINDOW
) )
407 if (wndPtr
->clsStyle
& CS_PARENTDC
) flags
|= DCX_PARENTCLIP
;
409 if (wndPtr
->dwStyle
& WS_CLIPCHILDREN
&&
410 !(wndPtr
->dwStyle
& WS_MINIMIZE
) ) flags
|= DCX_CLIPCHILDREN
;
411 if (!wndPtr
->dce
) flags
|= DCX_CACHE
;
415 if (flags
& DCX_WINDOW
) flags
&= ~DCX_CLIPCHILDREN
;
417 parent
= GetAncestor( hwnd
, GA_PARENT
);
418 if (!parent
|| (parent
== GetDesktopWindow()))
419 flags
= (flags
& ~DCX_PARENTCLIP
) | DCX_CLIPSIBLINGS
;
421 /* it seems parent clip is ignored when clipping siblings or children */
422 if (flags
& (DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
)) flags
&= ~DCX_PARENTCLIP
;
424 if( flags
& DCX_PARENTCLIP
)
426 LONG parent_style
= GetWindowLongW( parent
, GWL_STYLE
);
427 if( (wndPtr
->dwStyle
& WS_VISIBLE
) && (parent_style
& WS_VISIBLE
) )
429 flags
&= ~DCX_CLIPCHILDREN
;
430 if (parent_style
& WS_CLIPSIBLINGS
) flags
|= DCX_CLIPSIBLINGS
;
434 /* find a suitable DCE */
436 dcxFlags
= flags
& (DCX_PARENTCLIP
| DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
|
437 DCX_CACHE
| DCX_WINDOW
);
439 if (flags
& DCX_CACHE
)
444 dceEmpty
= dceUnused
= NULL
;
446 /* Strategy: First, we attempt to find a non-empty but unused DCE with
447 * compatible flags. Next, we look for an empty entry. If the cache is
448 * full we have to purge one of the unused entries.
451 for (dce
= firstDCE
; (dce
); dce
= dce
->next
)
453 if ((dce
->DCXflags
& (DCX_CACHE
| DCX_DCEBUSY
)) == DCX_CACHE
)
457 if (dce
->DCXflags
& DCX_DCEEMPTY
)
460 if ((dce
->hwndCurrent
== hwnd
) &&
461 ((dce
->DCXflags
& (DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
|
462 DCX_CACHE
| DCX_WINDOW
| DCX_PARENTCLIP
)) == dcxFlags
))
464 TRACE("\tfound valid %08x dce [%04x], flags %08x\n",
465 (unsigned)dce
, hwnd
, (unsigned)dcxFlags
);
466 bUpdateVisRgn
= FALSE
;
467 bUpdateClipOrigin
= TRUE
;
473 if (!dce
) dce
= (dceEmpty
) ? dceEmpty
: dceUnused
;
475 /* if there's no dce empty or unused, allocate a new one */
478 dce
= DCE_AllocDCE( 0, DCE_CACHE_DC
);
484 if (dce
&& dce
->hwndCurrent
== hwnd
)
486 TRACE("\tskipping hVisRgn update\n");
487 bUpdateVisRgn
= FALSE
; /* updated automatically, via DCHook() */
496 if (!(flags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
))) hrgnClip
= 0;
498 if (((flags
^ dce
->DCXflags
) & (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
)) &&
499 (dce
->hClipRgn
!= hrgnClip
))
501 /* if the extra clip region has changed, get rid of the old one */
502 DCE_DeleteClipRgn( dce
);
505 dce
->hwndCurrent
= hwnd
;
506 dce
->hClipRgn
= hrgnClip
;
507 dce
->DCXflags
= flags
& (DCX_PARENTCLIP
| DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
|
508 DCX_CACHE
| DCX_WINDOW
| DCX_WINDOWPAINT
|
509 DCX_KEEPCLIPRGN
| DCX_INTERSECTRGN
| DCX_EXCLUDERGN
);
510 dce
->DCXflags
|= DCX_DCEBUSY
;
511 dce
->DCXflags
&= ~DCX_DCEDIRTY
;
514 if (bUpdateVisRgn
) SetHookFlags16( hdc
, DCHF_INVALIDATEVISRGN
); /* force update */
516 if (!USER_Driver
.pGetDC( hwnd
, hdc
, hrgnClip
, flags
)) hdc
= 0;
518 TRACE("(%04x,%04x,0x%lx): returning %04x\n", hwnd
, hrgnClip
, flags
, hdc
);
520 WIN_ReleaseWndPtr(wndPtr
);
525 /***********************************************************************
532 HWND hwnd
/* [in] handle of window */
535 return GetDCEx( 0, 0, DCX_CACHE
| DCX_WINDOW
);
536 return GetDCEx( hwnd
, 0, DCX_USESTYLE
);
540 /***********************************************************************
541 * GetWindowDC (USER32.@)
543 HDC WINAPI
GetWindowDC( HWND hwnd
)
545 return GetDCEx( hwnd
, 0, DCX_USESTYLE
| DCX_WINDOW
);
549 /***********************************************************************
550 * ReleaseDC (USER32.@)
556 INT WINAPI
ReleaseDC(
557 HWND hwnd
/* [in] Handle of window - ignored */,
558 HDC hdc
/* [in] Handle of device context */
566 TRACE("%04x %04x\n", hwnd
, hdc
);
568 while (dce
&& (dce
->hDC
!= hdc
)) dce
= dce
->next
;
571 if ( dce
->DCXflags
& DCX_DCEBUSY
)
572 nRet
= DCE_ReleaseDC( dce
);
579 /***********************************************************************
582 * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..
584 BOOL16 WINAPI
DCHook16( HDC16 hDC
, WORD code
, DWORD data
, LPARAM lParam
)
587 DCE
*dce
= (DCE
*)data
;
589 TRACE("hDC = %04x, %i\n", hDC
, code
);
592 assert(dce
->hDC
== hDC
);
594 /* Grab the windows lock before doing anything else */
599 case DCHC_INVALIDVISRGN
:
600 /* GDI code calls this when it detects that the
601 * DC is dirty (usually after SetHookFlags()). This
602 * means that we have to recompute the visible region.
604 if( dce
->DCXflags
& DCX_DCEBUSY
)
606 /* Dirty bit has been cleared by caller, set it again so that
607 * pGetDC recomputes the visible region. */
608 SetHookFlags16( dce
->hDC
, DCHF_INVALIDATEVISRGN
);
609 USER_Driver
.pGetDC( dce
->hwndCurrent
, dce
->hDC
, dce
->hClipRgn
, dce
->DCXflags
);
611 else /* non-fatal but shouldn't happen */
612 WARN("DC is not in use!\n");
617 * Windows will not let you delete a DC that is busy
618 * (between GetDC and ReleaseDC)
621 if ( dce
->DCXflags
& DCX_DCEBUSY
)
623 WARN("Application trying to delete a busy DC\n");
629 FIXME("unknown code\n");
632 USER_Unlock(); /* Release the wnd lock */
637 /**********************************************************************
638 * WindowFromDC (USER32.@)
640 HWND WINAPI
WindowFromDC( HDC hDC
)
648 while (dce
&& (dce
->hDC
!= hDC
)) dce
= dce
->next
;
650 hwnd
= dce
? dce
->hwndCurrent
: 0;
657 /***********************************************************************
658 * LockWindowUpdate (USER32.@)
660 BOOL WINAPI
LockWindowUpdate( HWND hwnd
)
662 FIXME("(%x), stub!\n",hwnd
);