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
)
75 if (!(dce
= HeapAlloc( GetProcessHeap(), 0, sizeof(DCE
) ))) return NULL
;
76 if (!(dce
->hDC
= CreateDCA( "DISPLAY", NULL
, NULL
, NULL
)))
78 HeapFree( GetProcessHeap(), 0, dce
);
81 if (!defaultDCstate
) defaultDCstate
= GetDCState16( dce
->hDC
);
83 wnd
= WIN_FindWndPtr(hWnd
);
85 /* store DCE handle in DC hook data field */
87 hookProc
= GetProcAddress16( GetModuleHandle16("USER"), (LPCSTR
)362 );
88 SetDCHook( dce
->hDC
, hookProc
, (DWORD
)dce
);
90 dce
->hwndCurrent
= hWnd
;
95 if( type
!= DCE_CACHE_DC
) /* owned or class DC */
97 dce
->DCXflags
= DCX_DCEBUSY
;
100 if( wnd
->dwStyle
& WS_CLIPCHILDREN
) dce
->DCXflags
|= DCX_CLIPCHILDREN
;
101 if( wnd
->dwStyle
& WS_CLIPSIBLINGS
) dce
->DCXflags
|= DCX_CLIPSIBLINGS
;
103 SetHookFlags16(dce
->hDC
,DCHF_INVALIDATEVISRGN
);
105 else dce
->DCXflags
= DCX_CACHE
| DCX_DCEEMPTY
;
107 WIN_ReleaseWndPtr(wnd
);
113 /***********************************************************************
116 DCE
* DCE_FreeDCE( DCE
*dce
)
120 if (!dce
) return NULL
;
126 while (*ppDCE
&& (*ppDCE
!= dce
)) ppDCE
= &(*ppDCE
)->next
;
127 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
);
141 /***********************************************************************
144 * Remove owned DCE and reset unreleased cache DCEs.
146 void DCE_FreeWindowDCE( WND
* pWnd
)
155 if( pDCE
->hwndCurrent
== pWnd
->hwndSelf
)
157 if( pDCE
== pWnd
->dce
) /* owned or Class DCE*/
159 if (pWnd
->clsStyle
& CS_OWNDC
) /* owned DCE*/
161 pDCE
= DCE_FreeDCE( pDCE
);
165 else if( pDCE
->DCXflags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
) ) /* Class DCE*/
167 DCE_DeleteClipRgn( pDCE
);
168 pDCE
->hwndCurrent
= 0;
173 if( pDCE
->DCXflags
& DCX_DCEBUSY
) /* shared cache DCE */
175 /* FIXME: AFAICS we are doing the right thing here so
176 * this should be a WARN. But this is best left as an ERR
177 * because the 'application error' is likely to come from
178 * another part of Wine (i.e. it's our fault after all).
179 * We should change this to WARN when Wine is more stable
182 ERR("[%04x] GetDC() without ReleaseDC()!\n",
184 DCE_ReleaseDC( pDCE
);
187 pDCE
->DCXflags
&= DCX_CACHE
;
188 pDCE
->DCXflags
|= DCX_DCEEMPTY
;
189 pDCE
->hwndCurrent
= 0;
199 /***********************************************************************
202 static void DCE_DeleteClipRgn( DCE
* dce
)
204 dce
->DCXflags
&= ~(DCX_EXCLUDERGN
| DCX_INTERSECTRGN
| DCX_WINDOWPAINT
);
206 if( dce
->DCXflags
& DCX_KEEPCLIPRGN
)
207 dce
->DCXflags
&= ~DCX_KEEPCLIPRGN
;
209 if( dce
->hClipRgn
> 1 )
210 DeleteObject( dce
->hClipRgn
);
214 TRACE("\trestoring VisRgn\n");
216 RestoreVisRgn16(dce
->hDC
);
220 /***********************************************************************
223 static INT
DCE_ReleaseDC( DCE
* dce
)
225 if ((dce
->DCXflags
& (DCX_DCEEMPTY
| DCX_DCEBUSY
)) != DCX_DCEBUSY
) return 0;
227 /* restore previous visible region */
229 if ((dce
->DCXflags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
)) &&
230 (dce
->DCXflags
& (DCX_CACHE
| DCX_WINDOWPAINT
)) )
231 DCE_DeleteClipRgn( dce
);
233 if (dce
->DCXflags
& DCX_CACHE
)
235 SetDCState16( dce
->hDC
, defaultDCstate
);
236 dce
->DCXflags
&= ~DCX_DCEBUSY
;
237 if (dce
->DCXflags
& DCX_DCEDIRTY
)
239 /* don't keep around invalidated entries
240 * because SetDCState() disables hVisRgn updates
241 * by removing dirty bit. */
243 dce
->hwndCurrent
= 0;
244 dce
->DCXflags
&= DCX_CACHE
;
245 dce
->DCXflags
|= DCX_DCEEMPTY
;
252 /***********************************************************************
255 * It is called from SetWindowPos() and EVENT_MapNotify - we have to
256 * mark as dirty all busy DCEs for windows that have pWnd->parent as
257 * an ancestor and whose client rect intersects with specified update
258 * rectangle. In addition, pWnd->parent DCEs may need to be updated if
259 * DCX_CLIPCHILDREN flag is set. */
260 BOOL
DCE_InvalidateDCE(WND
* pWnd
, const RECT
* pRectUpdate
)
262 WND
* wndScope
= WIN_LockWndPtr(pWnd
->parent
);
263 WND
*pDesktop
= WIN_GetDesktop();
270 TRACE("scope hwnd = %04x, (%i,%i - %i,%i)\n",
271 wndScope
->hwndSelf
, pRectUpdate
->left
,pRectUpdate
->top
,
272 pRectUpdate
->right
,pRectUpdate
->bottom
);
276 /* walk all DCEs and fixup non-empty entries */
278 for (dce
= firstDCE
; (dce
); dce
= dce
->next
)
280 if( !(dce
->DCXflags
& DCX_DCEEMPTY
) )
282 WND
* wndCurrent
= WIN_FindWndPtr(dce
->hwndCurrent
);
287 INT xoffset
= 0, yoffset
= 0;
289 if( (wndCurrent
== wndScope
) && !(dce
->DCXflags
& DCX_CLIPCHILDREN
) )
291 /* child window positions don't bother us */
292 WIN_ReleaseWndPtr(wndCurrent
);
296 if (wndCurrent
== pDesktop
&& !(wndCurrent
->flags
& WIN_NATIVE
))
298 /* don't bother with fake desktop */
299 WIN_ReleaseWndPtr(wndCurrent
);
303 /* check if DCE window is within the z-order scope */
305 for( wnd
= WIN_LockWndPtr(wndCurrent
); wnd
; WIN_UpdateWndPtr(&wnd
,wnd
->parent
))
307 if( wnd
== wndScope
)
311 wndRect
= wndCurrent
->rectWindow
;
313 OffsetRect( &wndRect
, xoffset
- wndCurrent
->rectClient
.left
,
314 yoffset
- wndCurrent
->rectClient
.top
);
316 if (pWnd
== wndCurrent
||
317 IntersectRect( &wndRect
, &wndRect
, pRectUpdate
))
319 if( !(dce
->DCXflags
& DCX_DCEBUSY
) )
321 /* Don't bother with visible regions of unused DCEs */
323 TRACE("\tpurged %08x dce [%04x]\n",
324 (unsigned)dce
, wndCurrent
->hwndSelf
);
326 dce
->hwndCurrent
= 0;
327 dce
->DCXflags
&= DCX_CACHE
;
328 dce
->DCXflags
|= DCX_DCEEMPTY
;
332 /* Set dirty bits in the hDC and DCE structs */
334 TRACE("\tfixed up %08x dce [%04x]\n",
335 (unsigned)dce
, wndCurrent
->hwndSelf
);
337 dce
->DCXflags
|= DCX_DCEDIRTY
;
338 SetHookFlags16(dce
->hDC
, DCHF_INVALIDATEVISRGN
);
342 WIN_ReleaseWndPtr(wnd
);
345 xoffset
+= wnd
->rectClient
.left
;
346 yoffset
+= wnd
->rectClient
.top
;
349 WIN_ReleaseWndPtr(wndCurrent
);
352 WIN_ReleaseWndPtr(wndScope
);
354 WIN_ReleaseDesktop();
359 /***********************************************************************
362 * Translate given region from the wnd client to the DC coordinates
363 * and add it to the clipping region.
365 INT
DCE_ExcludeRgn( HDC hDC
, WND
* wnd
, HRGN hRgn
)
370 while (dce
&& (dce
->hDC
!= hDC
)) dce
= dce
->next
;
373 MapWindowPoints( wnd
->hwndSelf
, dce
->hwndCurrent
, &pt
, 1);
374 if( dce
->DCXflags
& DCX_WINDOW
)
376 wnd
= WIN_FindWndPtr(dce
->hwndCurrent
);
377 pt
.x
+= wnd
->rectClient
.left
- wnd
->rectWindow
.left
;
378 pt
.y
+= wnd
->rectClient
.top
- wnd
->rectWindow
.top
;
379 WIN_ReleaseWndPtr(wnd
);
383 OffsetRgn(hRgn
, pt
.x
, pt
.y
);
385 return ExtSelectClipRgn( hDC
, hRgn
, RGN_DIFF
);
389 /***********************************************************************
392 HDC16 WINAPI
GetDCEx16( HWND16 hwnd
, HRGN16 hrgnClip
, DWORD flags
)
394 return (HDC16
)GetDCEx( hwnd
, hrgnClip
, flags
);
398 /***********************************************************************
401 * Unimplemented flags: DCX_LOCKWINDOWUPDATE
403 * FIXME: Full support for hrgnClip == 1 (alias for entire window).
405 HDC WINAPI
GetDCEx( HWND hwnd
, HRGN hrgnClip
, DWORD flags
)
411 BOOL bUpdateVisRgn
= TRUE
;
412 BOOL bUpdateClipOrigin
= FALSE
;
414 TRACE("hwnd %04x, hrgnClip %04x, flags %08x\n",
415 hwnd
, hrgnClip
, (unsigned)flags
);
417 if (!(wndPtr
= WIN_FindWndPtr( hwnd
))) return 0;
421 if (!wndPtr
->dce
) flags
|= DCX_CACHE
;
423 if (flags
& DCX_USESTYLE
)
425 flags
&= ~( DCX_CLIPCHILDREN
| DCX_CLIPSIBLINGS
| DCX_PARENTCLIP
);
427 if( wndPtr
->dwStyle
& WS_CLIPSIBLINGS
)
428 flags
|= DCX_CLIPSIBLINGS
;
430 if ( !(flags
& DCX_WINDOW
) )
432 if (wndPtr
->clsStyle
& CS_PARENTDC
) flags
|= DCX_PARENTCLIP
;
434 if (wndPtr
->dwStyle
& WS_CLIPCHILDREN
&&
435 !(wndPtr
->dwStyle
& WS_MINIMIZE
) ) flags
|= DCX_CLIPCHILDREN
;
437 else flags
|= DCX_CACHE
;
440 if( flags
& DCX_NOCLIPCHILDREN
)
443 flags
&= ~(DCX_PARENTCLIP
| DCX_CLIPCHILDREN
);
446 if (flags
& DCX_WINDOW
)
447 flags
= (flags
& ~DCX_CLIPCHILDREN
) | DCX_CACHE
;
449 if (!(wndPtr
->dwStyle
& WS_CHILD
) || !wndPtr
->parent
)
450 flags
&= ~DCX_PARENTCLIP
;
451 else if( flags
& DCX_PARENTCLIP
)
454 if( !(flags
& (DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
)) )
455 if( (wndPtr
->dwStyle
& WS_VISIBLE
) && (wndPtr
->parent
->dwStyle
& WS_VISIBLE
) )
457 flags
&= ~DCX_CLIPCHILDREN
;
458 if( wndPtr
->parent
->dwStyle
& WS_CLIPSIBLINGS
)
459 flags
|= DCX_CLIPSIBLINGS
;
463 /* find a suitable DCE */
465 dcxFlags
= flags
& (DCX_PARENTCLIP
| DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
|
466 DCX_CACHE
| DCX_WINDOW
);
468 if (flags
& DCX_CACHE
)
473 dceEmpty
= dceUnused
= NULL
;
475 /* Strategy: First, we attempt to find a non-empty but unused DCE with
476 * compatible flags. Next, we look for an empty entry. If the cache is
477 * full we have to purge one of the unused entries.
480 for (dce
= firstDCE
; (dce
); dce
= dce
->next
)
482 if ((dce
->DCXflags
& (DCX_CACHE
| DCX_DCEBUSY
)) == DCX_CACHE
)
486 if (dce
->DCXflags
& DCX_DCEEMPTY
)
489 if ((dce
->hwndCurrent
== hwnd
) &&
490 ((dce
->DCXflags
& (DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
|
491 DCX_CACHE
| DCX_WINDOW
| DCX_PARENTCLIP
)) == dcxFlags
))
493 TRACE("\tfound valid %08x dce [%04x], flags %08x\n",
494 (unsigned)dce
, hwnd
, (unsigned)dcxFlags
);
495 bUpdateVisRgn
= FALSE
;
496 bUpdateClipOrigin
= TRUE
;
502 if (!dce
) dce
= (dceEmpty
) ? dceEmpty
: dceUnused
;
504 /* if there's no dce empty or unused, allocate a new one */
507 dce
= DCE_AllocDCE( 0, DCE_CACHE_DC
);
513 if( dce
->hwndCurrent
== hwnd
)
515 TRACE("\tskipping hVisRgn update\n");
516 bUpdateVisRgn
= FALSE
; /* updated automatically, via DCHook() */
518 /* Abey - 16Jul99. to take care of the nested GetDC. first one
519 with DCX_EXCLUDERGN or DCX_INTERSECTRGN flags and the next
520 one with or without these flags. */
522 if(dce
->DCXflags
& (DCX_EXCLUDERGN
| DCX_INTERSECTRGN
))
524 /* This is likely to be a nested BeginPaint().
525 or a BeginPaint() followed by a GetDC()*/
527 if( dce
->hClipRgn
!= hrgnClip
)
529 FIXME("new hrgnClip[%04x] smashes the previous[%04x]\n",
530 hrgnClip
, dce
->hClipRgn
);
531 DCE_DeleteClipRgn( dce
);
534 RestoreVisRgn16(dce
->hDC
);
544 if (!(flags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
))) hrgnClip
= 0;
545 dce
->hwndCurrent
= hwnd
;
546 dce
->hClipRgn
= hrgnClip
;
547 dce
->DCXflags
= flags
& (DCX_PARENTCLIP
| DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
|
548 DCX_CACHE
| DCX_WINDOW
| DCX_WINDOWPAINT
|
549 DCX_KEEPCLIPRGN
| DCX_INTERSECTRGN
| DCX_EXCLUDERGN
);
550 dce
->DCXflags
|= DCX_DCEBUSY
;
551 dce
->DCXflags
&= ~DCX_DCEDIRTY
;
554 if (bUpdateVisRgn
) SetHookFlags16( hdc
, DCHF_INVALIDATEVISRGN
); /* force update */
556 if (!USER_Driver
.pGetDC( hwnd
, hdc
, hrgnClip
, flags
)) hdc
= 0;
558 TRACE("(%04x,%04x,0x%lx): returning %04x\n", hwnd
, hrgnClip
, flags
, hdc
);
560 WIN_ReleaseWndPtr(wndPtr
);
565 /***********************************************************************
568 HDC16 WINAPI
GetDC16( HWND16 hwnd
)
570 return (HDC16
)GetDC( hwnd
);
574 /***********************************************************************
581 HWND hwnd
/* [in] handle of window */
584 return GetDCEx( GetDesktopWindow(), 0, DCX_CACHE
| DCX_WINDOW
);
585 return GetDCEx( hwnd
, 0, DCX_USESTYLE
);
589 /***********************************************************************
590 * GetWindowDC (USER.67)
592 HDC16 WINAPI
GetWindowDC16( HWND16 hwnd
)
594 if (!hwnd
) hwnd
= GetDesktopWindow16();
595 return GetDCEx16( hwnd
, 0, DCX_USESTYLE
| DCX_WINDOW
);
599 /***********************************************************************
600 * GetWindowDC (USER32.@)
602 HDC WINAPI
GetWindowDC( HWND hwnd
)
604 if (!hwnd
) hwnd
= GetDesktopWindow();
605 return GetDCEx( hwnd
, 0, DCX_USESTYLE
| DCX_WINDOW
);
609 /***********************************************************************
610 * ReleaseDC (USER.68)
612 INT16 WINAPI
ReleaseDC16( HWND16 hwnd
, HDC16 hdc
)
614 return (INT16
)ReleaseDC( hwnd
, hdc
);
618 /***********************************************************************
619 * ReleaseDC (USER32.@)
625 INT WINAPI
ReleaseDC(
626 HWND hwnd
/* [in] Handle of window - ignored */,
627 HDC hdc
/* [in] Handle of device context */
635 TRACE("%04x %04x\n", hwnd
, hdc
);
637 while (dce
&& (dce
->hDC
!= hdc
)) dce
= dce
->next
;
640 if ( dce
->DCXflags
& DCX_DCEBUSY
)
641 nRet
= DCE_ReleaseDC( dce
);
648 /***********************************************************************
651 * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..
653 BOOL16 WINAPI
DCHook16( HDC16 hDC
, WORD code
, DWORD data
, LPARAM lParam
)
656 DCE
*dce
= (DCE
*)data
;
658 TRACE("hDC = %04x, %i\n", hDC
, code
);
661 assert(dce
->hDC
== hDC
);
663 /* Grab the windows lock before doing anything else */
668 case DCHC_INVALIDVISRGN
:
669 /* GDI code calls this when it detects that the
670 * DC is dirty (usually after SetHookFlags()). This
671 * means that we have to recompute the visible region.
673 if( dce
->DCXflags
& DCX_DCEBUSY
)
674 USER_Driver
.pGetDC( dce
->hwndCurrent
, dce
->hDC
, dce
->hClipRgn
, dce
->DCXflags
);
675 else /* non-fatal but shouldn't happen */
676 WARN("DC is not in use!\n");
681 * Windows will not let you delete a DC that is busy
682 * (between GetDC and ReleaseDC)
685 if ( dce
->DCXflags
& DCX_DCEBUSY
)
687 WARN("Application trying to delete a busy DC\n");
693 FIXME("unknown code\n");
696 WIN_UnlockWnds(); /* Release the wnd lock */
701 /**********************************************************************
702 * WindowFromDC (USER.117)
704 HWND16 WINAPI
WindowFromDC16( HDC16 hDC
)
706 return (HWND16
)WindowFromDC( hDC
);
710 /**********************************************************************
711 * WindowFromDC (USER32.@)
713 HWND WINAPI
WindowFromDC( HDC hDC
)
721 while (dce
&& (dce
->hDC
!= hDC
)) dce
= dce
->next
;
723 hwnd
= dce
? dce
->hwndCurrent
: 0;
730 /***********************************************************************
731 * LockWindowUpdate (USER.294)
733 BOOL16 WINAPI
LockWindowUpdate16( HWND16 hwnd
)
735 return LockWindowUpdate( hwnd
);
739 /***********************************************************************
740 * LockWindowUpdate (USER32.@)
742 BOOL WINAPI
LockWindowUpdate( HWND hwnd
)
744 FIXME("DCX_LOCKWINDOWUPDATE is unimplemented\n");