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_WINDOWPAINT - BeginPaint() is in effect
36 #include "user_private.h"
40 #include "wine/winbase16.h"
41 #include "wine/winuser16.h"
42 #include "wine/debug.h"
43 #include "wine/list.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(dc
);
57 static struct list dce_list
= LIST_INIT(dce_list
);
59 static void DCE_DeleteClipRgn( DCE
* );
60 static INT
DCE_ReleaseDC( DCE
* );
63 /***********************************************************************
66 static void DCE_DumpCache(void)
72 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, DCE
, entry
)
74 TRACE("\t[0x%08x] hWnd %p, dcx %08x, %s %s\n",
75 (unsigned)dce
, dce
->hwndCurrent
, (unsigned)dce
->DCXflags
,
76 (dce
->DCXflags
& DCX_CACHE
) ? "Cache" : "Owned",
77 (dce
->DCXflags
& DCX_DCEBUSY
) ? "InUse" : "" );
83 /***********************************************************************
88 DCE
*DCE_AllocDCE( HWND hWnd
, DCE_TYPE type
)
90 static const WCHAR szDisplayW
[] = { 'D','I','S','P','L','A','Y','\0' };
93 TRACE("(%p,%d)\n", hWnd
, type
);
95 if (!(dce
= HeapAlloc( GetProcessHeap(), 0, sizeof(DCE
) ))) return NULL
;
96 if (!(dce
->hDC
= CreateDCW( szDisplayW
, NULL
, NULL
, NULL
)))
98 HeapFree( GetProcessHeap(), 0, dce
);
103 /* store DCE handle in DC hook data field */
105 SetDCHook( dce
->hDC
, DCHook16
, (DWORD
)dce
);
107 dce
->hwndCurrent
= hWnd
;
111 if( type
!= DCE_CACHE_DC
) /* owned or class DC */
113 dce
->DCXflags
= DCX_DCEBUSY
;
116 LONG style
= GetWindowLongW( hWnd
, GWL_STYLE
);
117 if (style
& WS_CLIPCHILDREN
) dce
->DCXflags
|= DCX_CLIPCHILDREN
;
118 if (style
& WS_CLIPSIBLINGS
) dce
->DCXflags
|= DCX_CLIPSIBLINGS
;
120 SetHookFlags16( HDC_16(dce
->hDC
), DCHF_INVALIDATEVISRGN
);
122 else dce
->DCXflags
= DCX_CACHE
| DCX_DCEEMPTY
;
125 list_add_head( &dce_list
, &dce
->entry
);
131 /***********************************************************************
134 void DCE_FreeDCE( DCE
*dce
)
139 list_remove( &dce
->entry
);
142 SetDCHook(dce
->hDC
, NULL
, 0L);
144 DeleteDC( dce
->hDC
);
145 if (dce
->hClipRgn
) DeleteObject(dce
->hClipRgn
);
146 HeapFree( GetProcessHeap(), 0, dce
);
149 /***********************************************************************
152 * Remove owned DCE and reset unreleased cache DCEs.
154 void DCE_FreeWindowDCE( HWND hwnd
)
156 struct list
*ptr
, *next
;
157 WND
*pWnd
= WIN_GetPtr( hwnd
);
159 LIST_FOR_EACH_SAFE( ptr
, next
, &dce_list
)
161 DCE
*pDCE
= LIST_ENTRY( ptr
, DCE
, entry
);
163 if (pDCE
->hwndCurrent
!= hwnd
) continue;
172 if( pDCE
->DCXflags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
) )
174 if (USER_Driver
.pReleaseDC
)
175 USER_Driver
.pReleaseDC( pDCE
->hwndCurrent
, pDCE
->hDC
);
176 DCE_DeleteClipRgn( pDCE
);
177 pDCE
->hwndCurrent
= 0;
181 if( pDCE
->DCXflags
& DCX_DCEBUSY
) /* shared cache DCE */
183 WARN("[%p] GetDC() without ReleaseDC()!\n",hwnd
);
184 DCE_ReleaseDC( pDCE
);
186 if (pDCE
->hwndCurrent
&& USER_Driver
.pReleaseDC
)
187 USER_Driver
.pReleaseDC( pDCE
->hwndCurrent
, pDCE
->hDC
);
188 pDCE
->DCXflags
&= DCX_CACHE
;
189 pDCE
->DCXflags
|= DCX_DCEEMPTY
;
190 pDCE
->hwndCurrent
= 0;
194 WIN_ReleasePtr( pWnd
);
198 /***********************************************************************
201 static void DCE_DeleteClipRgn( DCE
* dce
)
203 dce
->DCXflags
&= ~(DCX_EXCLUDERGN
| DCX_INTERSECTRGN
| DCX_WINDOWPAINT
);
205 if (dce
->hClipRgn
) DeleteObject( dce
->hClipRgn
);
208 /* make it dirty so that the vis rgn gets recomputed next time */
209 dce
->DCXflags
|= DCX_DCEDIRTY
;
210 SetHookFlags16( HDC_16(dce
->hDC
), DCHF_INVALIDATEVISRGN
);
214 /***********************************************************************
217 static INT
DCE_ReleaseDC( DCE
* dce
)
219 if ((dce
->DCXflags
& (DCX_DCEEMPTY
| DCX_DCEBUSY
)) != DCX_DCEBUSY
) return 0;
221 /* restore previous visible region */
223 if ((dce
->DCXflags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
)) &&
224 (dce
->DCXflags
& (DCX_CACHE
| DCX_WINDOWPAINT
)) )
225 DCE_DeleteClipRgn( dce
);
227 if (dce
->DCXflags
& DCX_CACHE
)
229 /* make the DC clean so that RestoreDC doesn't try to update the vis rgn */
230 SetHookFlags16( HDC_16(dce
->hDC
), DCHF_VALIDATEVISRGN
);
231 RestoreDC( dce
->hDC
, 1 ); /* initial save level is always 1 */
232 SaveDC( dce
->hDC
); /* save the state again for next time */
233 dce
->DCXflags
&= ~DCX_DCEBUSY
;
234 if (dce
->DCXflags
& DCX_DCEDIRTY
)
236 /* don't keep around invalidated entries
237 * because RestoreDC() disables hVisRgn updates
238 * by removing dirty bit. */
239 if (dce
->hwndCurrent
&& USER_Driver
.pReleaseDC
)
240 USER_Driver
.pReleaseDC( dce
->hwndCurrent
, dce
->hDC
);
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 = %p, (%ld,%ld - %ld,%ld)\n",
268 hwndScope
, pRectUpdate
->left
,pRectUpdate
->top
,
269 pRectUpdate
->right
,pRectUpdate
->bottom
);
273 /* walk all DCEs and fixup non-empty entries */
275 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, DCE
, entry
)
277 if (dce
->DCXflags
& DCX_DCEEMPTY
) continue;
278 if ((dce
->hwndCurrent
== hwndScope
) && !(dce
->DCXflags
& DCX_CLIPCHILDREN
))
279 continue; /* child window positions don't bother us */
281 /* check if DCE window is within the z-order scope */
283 if (hwndScope
== dce
->hwndCurrent
|| IsChild( hwndScope
, dce
->hwndCurrent
))
285 if (hwnd
!= dce
->hwndCurrent
)
287 /* check if the window rectangle intersects this DCE window */
289 GetWindowRect( dce
->hwndCurrent
, &rect
);
290 MapWindowPoints( 0, hwndScope
, (POINT
*)&rect
, 2 );
291 if (!IntersectRect( &rect
, &rect
, pRectUpdate
)) continue;
294 if( !(dce
->DCXflags
& DCX_DCEBUSY
) )
296 /* Don't bother with visible regions of unused DCEs */
298 TRACE("\tpurged %p dce [%p]\n", dce
, dce
->hwndCurrent
);
299 if (dce
->hwndCurrent
&& USER_Driver
.pReleaseDC
)
300 USER_Driver
.pReleaseDC( dce
->hwndCurrent
, dce
->hDC
);
301 dce
->hwndCurrent
= 0;
302 dce
->DCXflags
&= DCX_CACHE
;
303 dce
->DCXflags
|= DCX_DCEEMPTY
;
307 /* Set dirty bits in the hDC and DCE structs */
309 TRACE("\tfixed up %p dce [%p]\n", dce
, dce
->hwndCurrent
);
310 dce
->DCXflags
|= DCX_DCEDIRTY
;
311 SetHookFlags16( HDC_16(dce
->hDC
), DCHF_INVALIDATEVISRGN
);
321 /***********************************************************************
324 * Unimplemented flags: DCX_LOCKWINDOWUPDATE
326 * FIXME: Full support for hrgnClip == 1 (alias for entire window).
328 HDC WINAPI
GetDCEx( HWND hwnd
, HRGN hrgnClip
, DWORD flags
)
334 BOOL bUpdateVisRgn
= TRUE
;
335 BOOL bUpdateClipOrigin
= FALSE
;
339 TRACE("hwnd %p, hrgnClip %p, flags %08lx\n", hwnd
, hrgnClip
, flags
);
341 if (flags
& (DCX_LOCKWINDOWUPDATE
)) {
342 FIXME("not yet supported - see source\n");
343 /* See the comment in LockWindowUpdate for more explanation. This flag is not implemented
344 * by that patch, but we need LockWindowUpdate implemented correctly before this can be done.
348 if (!hwnd
) hwnd
= GetDesktopWindow();
349 else hwnd
= WIN_GetFullHandle( hwnd
);
351 if (!(wndPtr
= WIN_GetPtr( hwnd
))) return 0;
352 if (wndPtr
== WND_OTHER_PROCESS
)
358 window_style
= GetWindowLongW( hwnd
, GWL_STYLE
);
362 if (flags
& (DCX_WINDOW
| DCX_PARENTCLIP
)) flags
|= DCX_CACHE
;
364 if (flags
& DCX_USESTYLE
)
366 flags
&= ~( DCX_CLIPCHILDREN
| DCX_CLIPSIBLINGS
| DCX_PARENTCLIP
);
368 if( window_style
& WS_CLIPSIBLINGS
)
369 flags
|= DCX_CLIPSIBLINGS
;
371 if ( !(flags
& DCX_WINDOW
) )
373 if (GetClassLongW( hwnd
, GCL_STYLE
) & CS_PARENTDC
) flags
|= DCX_PARENTCLIP
;
375 if (window_style
& WS_CLIPCHILDREN
&& !(window_style
& WS_MINIMIZE
))
376 flags
|= DCX_CLIPCHILDREN
;
377 if (!wndPtr
|| !wndPtr
->dce
) flags
|= DCX_CACHE
;
381 if (flags
& DCX_WINDOW
) flags
&= ~DCX_CLIPCHILDREN
;
383 parent
= GetAncestor( hwnd
, GA_PARENT
);
384 if (!parent
|| (parent
== GetDesktopWindow()))
385 flags
= (flags
& ~DCX_PARENTCLIP
) | DCX_CLIPSIBLINGS
;
387 /* it seems parent clip is ignored when clipping siblings or children */
388 if (flags
& (DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
)) flags
&= ~DCX_PARENTCLIP
;
390 if( flags
& DCX_PARENTCLIP
)
392 LONG parent_style
= GetWindowLongW( parent
, GWL_STYLE
);
393 if( (window_style
& WS_VISIBLE
) && (parent_style
& WS_VISIBLE
) )
395 flags
&= ~DCX_CLIPCHILDREN
;
396 if (parent_style
& WS_CLIPSIBLINGS
) flags
|= DCX_CLIPSIBLINGS
;
400 /* find a suitable DCE */
402 dcxFlags
= flags
& (DCX_PARENTCLIP
| DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
|
403 DCX_CACHE
| DCX_WINDOW
);
405 if (flags
& DCX_CACHE
)
407 DCE
*dceEmpty
= NULL
, *dceUnused
= NULL
;
409 /* Strategy: First, we attempt to find a non-empty but unused DCE with
410 * compatible flags. Next, we look for an empty entry. If the cache is
411 * full we have to purge one of the unused entries.
414 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, DCE
, entry
)
416 if ((dce
->DCXflags
& (DCX_CACHE
| DCX_DCEBUSY
)) == DCX_CACHE
)
420 if (dce
->DCXflags
& DCX_DCEEMPTY
)
423 if ((dce
->hwndCurrent
== hwnd
) &&
424 ((dce
->DCXflags
& (DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
|
425 DCX_CACHE
| DCX_WINDOW
| DCX_PARENTCLIP
)) == dcxFlags
))
427 TRACE("\tfound valid %p dce [%p], flags %08lx\n",
428 dce
, hwnd
, dcxFlags
);
429 bUpdateVisRgn
= FALSE
;
430 bUpdateClipOrigin
= TRUE
;
436 if (&dce
->entry
== &dce_list
) /* nothing found */
437 dce
= dceEmpty
? dceEmpty
: dceUnused
;
439 /* if there's no dce empty or unused, allocate a new one */
442 dce
= DCE_AllocDCE( 0, DCE_CACHE_DC
);
448 if (dce
&& dce
->hwndCurrent
== hwnd
)
450 TRACE("\tskipping hVisRgn update\n");
451 bUpdateVisRgn
= FALSE
; /* updated automatically, via DCHook() */
460 if (!(flags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
))) hrgnClip
= 0;
462 if (((flags
^ dce
->DCXflags
) & (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
)) &&
463 (dce
->hClipRgn
!= hrgnClip
))
465 /* if the extra clip region has changed, get rid of the old one */
466 DCE_DeleteClipRgn( dce
);
469 dce
->hwndCurrent
= hwnd
;
470 dce
->hClipRgn
= hrgnClip
;
471 dce
->DCXflags
= flags
& (DCX_PARENTCLIP
| DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
|
472 DCX_CACHE
| DCX_WINDOW
| DCX_WINDOWPAINT
|
473 DCX_INTERSECTRGN
| DCX_EXCLUDERGN
);
474 dce
->DCXflags
|= DCX_DCEBUSY
;
475 dce
->DCXflags
&= ~DCX_DCEDIRTY
;
478 if (bUpdateVisRgn
) SetHookFlags16( HDC_16(hdc
), DCHF_INVALIDATEVISRGN
); /* force update */
480 if (!USER_Driver
.pGetDC
|| !USER_Driver
.pGetDC( hwnd
, hdc
, hrgnClip
, flags
))
483 TRACE("(%p,%p,0x%lx): returning %p\n", hwnd
, hrgnClip
, flags
, hdc
);
485 if (wndPtr
) WIN_ReleasePtr(wndPtr
);
491 /***********************************************************************
494 * Get a device context.
497 * Success: Handle to the device context
500 HDC WINAPI
GetDC( HWND hwnd
)
503 return GetDCEx( 0, 0, DCX_CACHE
| DCX_WINDOW
);
504 return GetDCEx( hwnd
, 0, DCX_USESTYLE
);
508 /***********************************************************************
509 * GetWindowDC (USER32.@)
511 HDC WINAPI
GetWindowDC( HWND hwnd
)
513 return GetDCEx( hwnd
, 0, DCX_USESTYLE
| DCX_WINDOW
);
517 /***********************************************************************
518 * ReleaseDC (USER32.@)
520 * Release a device context.
523 * Success: Non-zero. Resources used by hdc are released.
526 INT WINAPI
ReleaseDC( HWND hwnd
, HDC hdc
)
531 TRACE("%p %p\n", hwnd
, hdc
);
534 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, DCE
, entry
)
538 if ( dce
->DCXflags
& DCX_DCEBUSY
)
539 nRet
= DCE_ReleaseDC( dce
);
549 /***********************************************************************
552 * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..
554 BOOL16 WINAPI
DCHook16( HDC16 hDC
, WORD code
, DWORD data
, LPARAM lParam
)
557 DCE
*dce
= (DCE
*)data
;
559 TRACE("hDC = %04x, %i\n", hDC
, code
);
562 assert( HDC_16(dce
->hDC
) == hDC
);
564 /* Grab the windows lock before doing anything else */
569 case DCHC_INVALIDVISRGN
:
570 /* GDI code calls this when it detects that the
571 * DC is dirty (usually after SetHookFlags()). This
572 * means that we have to recompute the visible region.
574 if( dce
->DCXflags
& DCX_DCEBUSY
)
576 /* Dirty bit has been cleared by caller, set it again so that
577 * pGetDC recomputes the visible region. */
578 SetHookFlags16( hDC
, DCHF_INVALIDATEVISRGN
);
579 if (USER_Driver
.pGetDC
)
580 USER_Driver
.pGetDC( dce
->hwndCurrent
, dce
->hDC
, dce
->hClipRgn
, dce
->DCXflags
);
582 else /* non-fatal but shouldn't happen */
583 WARN("DC is not in use!\n");
588 * Windows will not let you delete a DC that is busy
589 * (between GetDC and ReleaseDC)
592 if ( dce
->DCXflags
& DCX_DCEBUSY
)
594 WARN("Application trying to delete a busy DC\n");
597 else DCE_FreeDCE( dce
);
601 FIXME("unknown code\n");
604 USER_Unlock(); /* Release the wnd lock */
609 /**********************************************************************
610 * WindowFromDC (USER32.@)
612 HWND WINAPI
WindowFromDC( HDC hDC
)
618 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, DCE
, entry
)
622 hwnd
= dce
->hwndCurrent
;
632 /***********************************************************************
633 * LockWindowUpdate (USER32.@)
635 BOOL WINAPI
LockWindowUpdate( HWND hwnd
)
637 static HWND lockedWnd
;
639 /* This function is fully implemented by the following patch:
641 * http://www.winehq.org/hypermail/wine-patches/2004/01/0142.html
643 * but in order to work properly, it needs the ability to invalidate
644 * DCEs in other processes when the lock window is changed, which
645 * isn't possible yet.
649 FIXME("(%p), partial stub!\n",hwnd
);
656 /* Unlock lockedWnd */
657 /* FIXME: Do something */
661 /* Attempted to lock a second window */
662 /* Return FALSE and do nothing */