4 * Copyright 1993 Alexandre Julliard
5 * 1996,1997 Alex Korobka
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * Note: Visible regions of CS_OWNDC/CS_CLASSDC window DCs
23 * have to be updated dynamically.
27 * DCX_DCEEMPTY - dce is uninitialized
28 * DCX_DCEBUSY - dce is in use
29 * DCX_DCEDIRTY - ReleaseDC() should wipe instead of caching
30 * DCX_KEEPCLIPRGN - ReleaseDC() should not delete the clipping region
31 * DCX_WINDOWPAINT - BeginPaint() is in effect
38 #include "wine/debug.h"
42 #include "wine/winbase16.h"
43 #include "wine/winuser16.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(dc
);
48 static HDC16 defaultDCstate
;
50 static void DCE_DeleteClipRgn( DCE
* );
51 static INT
DCE_ReleaseDC( DCE
* );
54 /***********************************************************************
57 static void DCE_DumpCache(void)
67 DPRINTF("\t[0x%08x] hWnd %p, dcx %08x, %s %s\n",
68 (unsigned)dce
, dce
->hwndCurrent
, (unsigned)dce
->DCXflags
,
69 (dce
->DCXflags
& DCX_CACHE
) ? "Cache" : "Owned",
70 (dce
->DCXflags
& DCX_DCEBUSY
) ? "InUse" : "" );
77 /***********************************************************************
82 DCE
*DCE_AllocDCE( HWND hWnd
, DCE_TYPE type
)
84 static const WCHAR szDisplayW
[] = { 'D','I','S','P','L','A','Y','\0' };
87 TRACE("(%p,%d)\n", hWnd
, type
);
89 if (!(dce
= HeapAlloc( GetProcessHeap(), 0, sizeof(DCE
) ))) return NULL
;
90 if (!(dce
->hDC
= CreateDCW( szDisplayW
, NULL
, NULL
, NULL
)))
92 HeapFree( GetProcessHeap(), 0, dce
);
95 if (!defaultDCstate
) defaultDCstate
= GetDCState16( HDC_16(dce
->hDC
) );
97 /* store DCE handle in DC hook data field */
99 SetDCHook( dce
->hDC
, DCHook16
, (DWORD
)dce
);
101 dce
->hwndCurrent
= WIN_GetFullHandle( hWnd
);
104 if( type
!= DCE_CACHE_DC
) /* owned or class DC */
106 dce
->DCXflags
= DCX_DCEBUSY
;
109 LONG style
= GetWindowLongW( hWnd
, GWL_STYLE
);
110 if (style
& WS_CLIPCHILDREN
) dce
->DCXflags
|= DCX_CLIPCHILDREN
;
111 if (style
& WS_CLIPSIBLINGS
) dce
->DCXflags
|= DCX_CLIPSIBLINGS
;
113 SetHookFlags16( HDC_16(dce
->hDC
), DCHF_INVALIDATEVISRGN
);
115 else dce
->DCXflags
= DCX_CACHE
| DCX_DCEEMPTY
;
118 dce
->next
= firstDCE
;
125 /***********************************************************************
128 DCE
* DCE_FreeDCE( DCE
*dce
)
132 if (!dce
) return NULL
;
138 while (*ppDCE
&& (*ppDCE
!= dce
)) ppDCE
= &(*ppDCE
)->next
;
139 if (*ppDCE
== dce
) *ppDCE
= dce
->next
;
143 SetDCHook(dce
->hDC
, NULL
, 0L);
145 DeleteDC( dce
->hDC
);
146 if( dce
->hClipRgn
&& !(dce
->DCXflags
& DCX_KEEPCLIPRGN
) )
147 DeleteObject(dce
->hClipRgn
);
148 HeapFree( GetProcessHeap(), 0, dce
);
153 /***********************************************************************
156 * Remove owned DCE and reset unreleased cache DCEs.
158 void DCE_FreeWindowDCE( HWND hwnd
)
161 WND
*pWnd
= WIN_GetPtr( hwnd
);
166 if( pDCE
->hwndCurrent
== hwnd
)
168 if( pDCE
== pWnd
->dce
) /* owned or Class DCE*/
170 if (pWnd
->clsStyle
& CS_OWNDC
) /* owned DCE*/
172 pDCE
= DCE_FreeDCE( pDCE
);
176 else if( pDCE
->DCXflags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
) ) /* Class DCE*/
178 if (USER_Driver
.pReleaseDC
)
179 USER_Driver
.pReleaseDC( pDCE
->hwndCurrent
, pDCE
->hDC
);
180 DCE_DeleteClipRgn( pDCE
);
181 pDCE
->hwndCurrent
= 0;
186 if( pDCE
->DCXflags
& DCX_DCEBUSY
) /* shared cache DCE */
188 /* FIXME: AFAICS we are doing the right thing here so
189 * this should be a WARN. But this is best left as an ERR
190 * because the 'application error' is likely to come from
191 * another part of Wine (i.e. it's our fault after all).
192 * We should change this to WARN when Wine is more stable
195 ERR("[%p] GetDC() without ReleaseDC()!\n",hwnd
);
196 DCE_ReleaseDC( pDCE
);
199 if (pDCE
->hwndCurrent
&& USER_Driver
.pReleaseDC
)
200 USER_Driver
.pReleaseDC( pDCE
->hwndCurrent
, pDCE
->hDC
);
201 pDCE
->DCXflags
&= DCX_CACHE
;
202 pDCE
->DCXflags
|= DCX_DCEEMPTY
;
203 pDCE
->hwndCurrent
= 0;
208 WIN_ReleasePtr( pWnd
);
212 /***********************************************************************
215 static void DCE_DeleteClipRgn( DCE
* dce
)
217 dce
->DCXflags
&= ~(DCX_EXCLUDERGN
| DCX_INTERSECTRGN
| DCX_WINDOWPAINT
);
219 if( dce
->DCXflags
& DCX_KEEPCLIPRGN
)
220 dce
->DCXflags
&= ~DCX_KEEPCLIPRGN
;
222 if( dce
->hClipRgn
> (HRGN
)1 )
223 DeleteObject( dce
->hClipRgn
);
227 /* make it dirty so that the vis rgn gets recomputed next time */
228 dce
->DCXflags
|= DCX_DCEDIRTY
;
229 SetHookFlags16( HDC_16(dce
->hDC
), DCHF_INVALIDATEVISRGN
);
233 /***********************************************************************
236 static INT
DCE_ReleaseDC( DCE
* dce
)
238 if ((dce
->DCXflags
& (DCX_DCEEMPTY
| DCX_DCEBUSY
)) != DCX_DCEBUSY
) return 0;
240 /* restore previous visible region */
242 if ((dce
->DCXflags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
)) &&
243 (dce
->DCXflags
& (DCX_CACHE
| DCX_WINDOWPAINT
)) )
244 DCE_DeleteClipRgn( dce
);
246 if (dce
->DCXflags
& DCX_CACHE
)
248 /* make the DC clean so that SetDCState doesn't try to update the vis rgn */
249 SetHookFlags16( HDC_16(dce
->hDC
), DCHF_VALIDATEVISRGN
);
250 SetDCState16( HDC_16(dce
->hDC
), defaultDCstate
);
251 dce
->DCXflags
&= ~DCX_DCEBUSY
;
252 if (dce
->DCXflags
& DCX_DCEDIRTY
)
254 /* don't keep around invalidated entries
255 * because SetDCState() disables hVisRgn updates
256 * by removing dirty bit. */
257 if (dce
->hwndCurrent
&& USER_Driver
.pReleaseDC
)
258 USER_Driver
.pReleaseDC( dce
->hwndCurrent
, dce
->hDC
);
259 dce
->hwndCurrent
= 0;
260 dce
->DCXflags
&= DCX_CACHE
;
261 dce
->DCXflags
|= DCX_DCEEMPTY
;
268 /***********************************************************************
271 * It is called from SetWindowPos() and EVENT_MapNotify - we have to
272 * mark as dirty all busy DCEs for windows that have pWnd->parent as
273 * an ancestor and whose client rect intersects with specified update
274 * rectangle. In addition, pWnd->parent DCEs may need to be updated if
275 * DCX_CLIPCHILDREN flag is set. */
276 BOOL
DCE_InvalidateDCE(HWND hwnd
, const RECT
* pRectUpdate
)
278 HWND hwndScope
= GetAncestor( hwnd
, GA_PARENT
);
285 TRACE("scope hwnd = %p, (%ld,%ld - %ld,%ld)\n",
286 hwndScope
, pRectUpdate
->left
,pRectUpdate
->top
,
287 pRectUpdate
->right
,pRectUpdate
->bottom
);
291 /* walk all DCEs and fixup non-empty entries */
293 for (dce
= firstDCE
; (dce
); dce
= dce
->next
)
295 if (dce
->DCXflags
& DCX_DCEEMPTY
) continue;
296 if ((dce
->hwndCurrent
== hwndScope
) && !(dce
->DCXflags
& DCX_CLIPCHILDREN
))
297 continue; /* child window positions don't bother us */
299 /* check if DCE window is within the z-order scope */
301 if (hwndScope
== dce
->hwndCurrent
|| IsChild( hwndScope
, dce
->hwndCurrent
))
303 if (hwnd
!= dce
->hwndCurrent
)
305 /* check if the window rectangle intersects this DCE window */
307 GetWindowRect( dce
->hwndCurrent
, &rect
);
308 MapWindowPoints( 0, hwndScope
, (POINT
*)&rect
, 2 );
309 if (!IntersectRect( &rect
, &rect
, pRectUpdate
)) continue;
312 if( !(dce
->DCXflags
& DCX_DCEBUSY
) )
314 /* Don't bother with visible regions of unused DCEs */
316 TRACE("\tpurged %p dce [%p]\n", dce
, dce
->hwndCurrent
);
317 if (dce
->hwndCurrent
&& USER_Driver
.pReleaseDC
)
318 USER_Driver
.pReleaseDC( dce
->hwndCurrent
, dce
->hDC
);
319 dce
->hwndCurrent
= 0;
320 dce
->DCXflags
&= DCX_CACHE
;
321 dce
->DCXflags
|= DCX_DCEEMPTY
;
325 /* Set dirty bits in the hDC and DCE structs */
327 TRACE("\tfixed up %p dce [%p]\n", dce
, dce
->hwndCurrent
);
328 dce
->DCXflags
|= DCX_DCEDIRTY
;
329 SetHookFlags16( HDC_16(dce
->hDC
), DCHF_INVALIDATEVISRGN
);
339 /***********************************************************************
342 * Translate given region from the wnd client to the DC coordinates
343 * and add it to the clipping region.
345 INT
DCE_ExcludeRgn( HDC hDC
, HWND hwnd
, HRGN hRgn
)
350 while (dce
&& (dce
->hDC
!= hDC
)) dce
= dce
->next
;
351 if (!dce
) return ERROR
;
353 MapWindowPoints( hwnd
, dce
->hwndCurrent
, &pt
, 1);
354 if( dce
->DCXflags
& DCX_WINDOW
)
356 WND
*wnd
= WIN_FindWndPtr(dce
->hwndCurrent
);
357 pt
.x
+= wnd
->rectClient
.left
- wnd
->rectWindow
.left
;
358 pt
.y
+= wnd
->rectClient
.top
- wnd
->rectWindow
.top
;
359 WIN_ReleaseWndPtr(wnd
);
361 OffsetRgn(hRgn
, pt
.x
, pt
.y
);
363 return ExtSelectClipRgn( hDC
, hRgn
, RGN_DIFF
);
367 /***********************************************************************
370 * Unimplemented flags: DCX_LOCKWINDOWUPDATE
372 * FIXME: Full support for hrgnClip == 1 (alias for entire window).
374 HDC WINAPI
GetDCEx( HWND hwnd
, HRGN hrgnClip
, DWORD flags
)
380 BOOL bUpdateVisRgn
= TRUE
;
381 BOOL bUpdateClipOrigin
= FALSE
;
384 TRACE("hwnd %p, hrgnClip %p, flags %08lx\n", hwnd
, hrgnClip
, flags
);
386 if (flags
& (DCX_LOCKWINDOWUPDATE
)) {
387 FIXME("not yet supported - see source\n");
388 /* See the comment in LockWindowUpdate for more explanation. This flag is not implemented
389 * by that patch, but we need LockWindowUpdate implemented correctly before this can be done.
393 if (!hwnd
) hwnd
= GetDesktopWindow();
394 if (!(full
= WIN_IsCurrentProcess( hwnd
)))
396 FIXME( "not supported yet on other process window %p\n", hwnd
);
400 if (!(wndPtr
= WIN_GetPtr( hwnd
))) return 0;
404 if (flags
& (DCX_WINDOW
| DCX_PARENTCLIP
)) flags
|= DCX_CACHE
;
406 if (flags
& DCX_USESTYLE
)
408 flags
&= ~( DCX_CLIPCHILDREN
| DCX_CLIPSIBLINGS
| DCX_PARENTCLIP
);
410 if( wndPtr
->dwStyle
& WS_CLIPSIBLINGS
)
411 flags
|= DCX_CLIPSIBLINGS
;
413 if ( !(flags
& DCX_WINDOW
) )
415 if (wndPtr
->clsStyle
& CS_PARENTDC
) flags
|= DCX_PARENTCLIP
;
417 if (wndPtr
->dwStyle
& WS_CLIPCHILDREN
&&
418 !(wndPtr
->dwStyle
& WS_MINIMIZE
) ) flags
|= DCX_CLIPCHILDREN
;
419 if (!wndPtr
->dce
) flags
|= DCX_CACHE
;
423 if (flags
& DCX_WINDOW
) flags
&= ~DCX_CLIPCHILDREN
;
425 parent
= GetAncestor( hwnd
, GA_PARENT
);
426 if (!parent
|| (parent
== GetDesktopWindow()))
427 flags
= (flags
& ~DCX_PARENTCLIP
) | DCX_CLIPSIBLINGS
;
429 /* it seems parent clip is ignored when clipping siblings or children */
430 if (flags
& (DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
)) flags
&= ~DCX_PARENTCLIP
;
432 if( flags
& DCX_PARENTCLIP
)
434 LONG parent_style
= GetWindowLongW( parent
, GWL_STYLE
);
435 if( (wndPtr
->dwStyle
& WS_VISIBLE
) && (parent_style
& WS_VISIBLE
) )
437 flags
&= ~DCX_CLIPCHILDREN
;
438 if (parent_style
& WS_CLIPSIBLINGS
) flags
|= DCX_CLIPSIBLINGS
;
442 /* find a suitable DCE */
444 dcxFlags
= flags
& (DCX_PARENTCLIP
| DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
|
445 DCX_CACHE
| DCX_WINDOW
);
447 if (flags
& DCX_CACHE
)
452 dceEmpty
= dceUnused
= NULL
;
454 /* Strategy: First, we attempt to find a non-empty but unused DCE with
455 * compatible flags. Next, we look for an empty entry. If the cache is
456 * full we have to purge one of the unused entries.
459 for (dce
= firstDCE
; (dce
); dce
= dce
->next
)
461 if ((dce
->DCXflags
& (DCX_CACHE
| DCX_DCEBUSY
)) == DCX_CACHE
)
465 if (dce
->DCXflags
& DCX_DCEEMPTY
)
468 if ((dce
->hwndCurrent
== hwnd
) &&
469 ((dce
->DCXflags
& (DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
|
470 DCX_CACHE
| DCX_WINDOW
| DCX_PARENTCLIP
)) == dcxFlags
))
472 TRACE("\tfound valid %p dce [%p], flags %08lx\n",
473 dce
, hwnd
, dcxFlags
);
474 bUpdateVisRgn
= FALSE
;
475 bUpdateClipOrigin
= TRUE
;
481 if (!dce
) dce
= (dceEmpty
) ? dceEmpty
: dceUnused
;
483 /* if there's no dce empty or unused, allocate a new one */
486 dce
= DCE_AllocDCE( 0, DCE_CACHE_DC
);
492 if (dce
&& dce
->hwndCurrent
== hwnd
)
494 TRACE("\tskipping hVisRgn update\n");
495 bUpdateVisRgn
= FALSE
; /* updated automatically, via DCHook() */
504 if (!(flags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
))) hrgnClip
= 0;
506 if (((flags
^ dce
->DCXflags
) & (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
)) &&
507 (dce
->hClipRgn
!= hrgnClip
))
509 /* if the extra clip region has changed, get rid of the old one */
510 DCE_DeleteClipRgn( dce
);
513 dce
->hwndCurrent
= hwnd
;
514 dce
->hClipRgn
= hrgnClip
;
515 dce
->DCXflags
= flags
& (DCX_PARENTCLIP
| DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
|
516 DCX_CACHE
| DCX_WINDOW
| DCX_WINDOWPAINT
|
517 DCX_KEEPCLIPRGN
| DCX_INTERSECTRGN
| DCX_EXCLUDERGN
);
518 dce
->DCXflags
|= DCX_DCEBUSY
;
519 dce
->DCXflags
&= ~DCX_DCEDIRTY
;
522 if (bUpdateVisRgn
) SetHookFlags16( HDC_16(hdc
), DCHF_INVALIDATEVISRGN
); /* force update */
524 if (!USER_Driver
.pGetDC
|| !USER_Driver
.pGetDC( hwnd
, hdc
, hrgnClip
, flags
))
527 TRACE("(%p,%p,0x%lx): returning %p\n", hwnd
, hrgnClip
, flags
, hdc
);
529 WIN_ReleasePtr(wndPtr
);
534 /***********************************************************************
537 * Get a device context.
540 * Success: Handle to the device context
544 HWND hwnd
/* [in] handle of window - may be NULL */
547 return GetDCEx( 0, 0, DCX_CACHE
| DCX_WINDOW
);
548 return GetDCEx( hwnd
, 0, DCX_USESTYLE
);
552 /***********************************************************************
553 * GetWindowDC (USER32.@)
555 HDC WINAPI
GetWindowDC( HWND hwnd
)
557 return GetDCEx( hwnd
, 0, DCX_USESTYLE
| DCX_WINDOW
);
561 /***********************************************************************
562 * ReleaseDC (USER32.@)
564 * Release a device context.
567 * Success: Non-zero. Resources used by hdc are released.
570 INT WINAPI
ReleaseDC(
571 HWND hwnd
, /* [in] Handle of window - ignored */
572 HDC hdc
/* [in] Handle of device context */
580 TRACE("%p %p\n", hwnd
, hdc
);
582 while (dce
&& (dce
->hDC
!= hdc
)) dce
= dce
->next
;
585 if ( dce
->DCXflags
& DCX_DCEBUSY
)
586 nRet
= DCE_ReleaseDC( dce
);
593 /***********************************************************************
596 * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..
598 BOOL16 WINAPI
DCHook16( HDC16 hDC
, WORD code
, DWORD data
, LPARAM lParam
)
601 DCE
*dce
= (DCE
*)data
;
603 TRACE("hDC = %04x, %i\n", hDC
, code
);
606 assert( HDC_16(dce
->hDC
) == hDC
);
608 /* Grab the windows lock before doing anything else */
613 case DCHC_INVALIDVISRGN
:
614 /* GDI code calls this when it detects that the
615 * DC is dirty (usually after SetHookFlags()). This
616 * means that we have to recompute the visible region.
618 if( dce
->DCXflags
& DCX_DCEBUSY
)
620 /* Dirty bit has been cleared by caller, set it again so that
621 * pGetDC recomputes the visible region. */
622 SetHookFlags16( hDC
, DCHF_INVALIDATEVISRGN
);
623 if (USER_Driver
.pGetDC
)
624 USER_Driver
.pGetDC( dce
->hwndCurrent
, dce
->hDC
, dce
->hClipRgn
, dce
->DCXflags
);
626 else /* non-fatal but shouldn't happen */
627 WARN("DC is not in use!\n");
632 * Windows will not let you delete a DC that is busy
633 * (between GetDC and ReleaseDC)
636 if ( dce
->DCXflags
& DCX_DCEBUSY
)
638 WARN("Application trying to delete a busy DC\n");
641 else DCE_FreeDCE( dce
);
645 FIXME("unknown code\n");
648 USER_Unlock(); /* Release the wnd lock */
653 /**********************************************************************
654 * WindowFromDC (USER32.@)
656 HWND WINAPI
WindowFromDC( HDC hDC
)
664 while (dce
&& (dce
->hDC
!= hDC
)) dce
= dce
->next
;
666 hwnd
= dce
? dce
->hwndCurrent
: 0;
673 /***********************************************************************
674 * LockWindowUpdate (USER32.@)
676 BOOL WINAPI
LockWindowUpdate( HWND hwnd
)
678 static HWND lockedWnd
;
680 /* This function is fully implemented by the following patch:
682 * http://www.winehq.org/hypermail/wine-patches/2004/01/0142.html
684 * but in order to work properly, it needs the ability to invalidate
685 * DCEs in other processes when the lock window is changed, which
686 * isn't possible yet.
690 FIXME("(%p), partial stub!\n",hwnd
);
697 /* Unlock lockedWnd */
698 /* FIXME: Do something */
702 /* Attempted to lock a second window */
703 /* Return FALSE and do nothing */