4 * Copyright 1993, 2005 Alexandre Julliard
5 * Copyright 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #define WIN32_NO_STATUS
32 #include "wine/winbase16.h"
33 #include "wine/wingdi16.h"
34 #include "wine/server.h"
35 #include "wine/list.h"
36 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(dc
);
42 struct list entry
; /* entry in global DCE list */
47 void *class_ptr
; /* ptr to identify window class for class DCEs */
48 ULONG count
; /* usage count; 0 or 1 for cache DCEs, always 1 for window DCEs,
49 always >= 1 for class DCEs */
52 static struct list dce_list
= LIST_INIT(dce_list
);
54 static BOOL16 CALLBACK
dc_hook( HDC16 hDC
, WORD code
, DWORD data
, LPARAM lParam
);
56 static CRITICAL_SECTION dce_section
;
57 static CRITICAL_SECTION_DEBUG critsect_debug
=
60 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
61 0, 0, { (DWORD_PTR
)(__FILE__
": dce_section") }
63 static CRITICAL_SECTION dce_section
= { &critsect_debug
, -1, 0, 0, 0, 0 };
65 static const WCHAR displayW
[] = { 'D','I','S','P','L','A','Y',0 };
68 /***********************************************************************
71 static void dump_cache(void)
75 EnterCriticalSection( &dce_section
);
77 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
79 TRACE("%p: hwnd %p dcx %08lx %s %s\n",
80 dce
, dce
->hwnd
, dce
->flags
,
81 (dce
->flags
& DCX_CACHE
) ? "Cache" : "Owned",
82 dce
->count
? "InUse" : "" );
85 LeaveCriticalSection( &dce_section
);
89 /***********************************************************************
90 * update_visible_region
92 * Set the visible region and X11 drawable for the DC associated to
95 static void update_visible_region( struct dce
*dce
)
100 struct x11drv_escape_set_drawable escape
;
101 struct x11drv_win_data
*data
;
102 DWORD flags
= dce
->flags
;
105 /* don't clip siblings if using parent clip region */
106 if (flags
& DCX_PARENTCLIP
) flags
&= ~DCX_CLIPSIBLINGS
;
108 /* fetch the visible region from the server */
111 RGNDATA
*data
= HeapAlloc( GetProcessHeap(), 0, sizeof(*data
) + size
- 1 );
114 SERVER_START_REQ( get_visible_region
)
116 req
->window
= dce
->hwnd
;
118 wine_server_set_reply( req
, data
->Buffer
, size
);
119 if (!(status
= wine_server_call( req
)))
121 size_t reply_size
= wine_server_reply_size( reply
);
122 data
->rdh
.dwSize
= sizeof(data
->rdh
);
123 data
->rdh
.iType
= RDH_RECTANGLES
;
124 data
->rdh
.nCount
= reply_size
/ sizeof(RECT
);
125 data
->rdh
.nRgnSize
= reply_size
;
126 vis_rgn
= ExtCreateRegion( NULL
, size
, data
);
128 top
= reply
->top_win
;
129 escape
.org
.x
= reply
->win_org_x
- reply
->top_org_x
;
130 escape
.org
.y
= reply
->win_org_y
- reply
->top_org_y
;
131 escape
.drawable_org
.x
= reply
->top_org_x
;
132 escape
.drawable_org
.y
= reply
->top_org_y
;
134 else size
= reply
->total_size
;
137 HeapFree( GetProcessHeap(), 0, data
);
138 } while (status
== STATUS_BUFFER_OVERFLOW
);
140 if (status
|| !vis_rgn
) return;
142 if (dce
->clip_rgn
) CombineRgn( vis_rgn
, vis_rgn
, dce
->clip_rgn
,
143 (flags
& DCX_INTERSECTRGN
) ? RGN_AND
: RGN_DIFF
);
145 if (top
== dce
->hwnd
&& ((data
= X11DRV_get_win_data( dce
->hwnd
)) != NULL
) &&
146 IsIconic( dce
->hwnd
) && data
->icon_window
)
147 escape
.drawable
= data
->icon_window
;
149 escape
.drawable
= X11DRV_get_whole_window( top
);
151 escape
.code
= X11DRV_SET_DRAWABLE
;
152 escape
.mode
= IncludeInferiors
;
153 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPSTR
)&escape
, 0, NULL
);
155 /* map region to DC coordinates */
157 -(escape
.drawable_org
.x
+ escape
.org
.x
),
158 -(escape
.drawable_org
.y
+ escape
.org
.y
) );
159 SelectVisRgn16( HDC_16(dce
->hdc
), HRGN_16(vis_rgn
) );
160 DeleteObject( vis_rgn
);
164 /***********************************************************************
167 static void release_dce( struct dce
*dce
)
169 struct x11drv_escape_set_drawable escape
;
171 if (!dce
->hwnd
) return; /* already released */
173 if (dce
->clip_rgn
) DeleteObject( dce
->clip_rgn
);
176 dce
->flags
&= DCX_CACHE
;
178 escape
.code
= X11DRV_SET_DRAWABLE
;
179 escape
.drawable
= root_window
;
180 escape
.mode
= IncludeInferiors
;
181 escape
.org
.x
= escape
.org
.y
= 0;
182 escape
.drawable_org
.x
= escape
.drawable_org
.y
= 0;
183 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPSTR
)&escape
, 0, NULL
);
187 /***********************************************************************
190 static void delete_clip_rgn( struct dce
*dce
)
192 if (!dce
->clip_rgn
) return; /* nothing to do */
194 dce
->flags
&= ~(DCX_EXCLUDERGN
| DCX_INTERSECTRGN
);
195 DeleteObject( dce
->clip_rgn
);
198 /* make it dirty so that the vis rgn gets recomputed next time */
199 SetHookFlags16( HDC_16(dce
->hdc
), DCHF_INVALIDATEVISRGN
);
203 /***********************************************************************
206 * Allocate a new cache DCE.
208 static struct dce
*alloc_cache_dce(void)
210 struct x11drv_escape_set_dce escape
;
213 if (!(dce
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dce
) ))) return NULL
;
214 if (!(dce
->hdc
= CreateDCW( displayW
, NULL
, NULL
, NULL
)))
216 HeapFree( GetProcessHeap(), 0, dce
);
221 /* store DCE handle in DC hook data field */
222 SetDCHook( dce
->hdc
, dc_hook
, (DWORD
)dce
);
226 dce
->flags
= DCX_CACHE
;
227 dce
->class_ptr
= NULL
;
230 EnterCriticalSection( &dce_section
);
231 list_add_head( &dce_list
, &dce
->entry
);
232 LeaveCriticalSection( &dce_section
);
234 escape
.code
= X11DRV_SET_DCE
;
236 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
, 0, NULL
);
242 /***********************************************************************
245 * Allocate a DCE for a newly created window if necessary.
247 void alloc_window_dce( struct x11drv_win_data
*data
)
249 struct x11drv_escape_set_dce escape
;
251 void *class_ptr
= NULL
;
252 LONG style
= GetClassLongW( data
->hwnd
, GCL_STYLE
);
254 if (!(style
& (CS_CLASSDC
|CS_OWNDC
))) return; /* nothing to do */
256 if (!(style
& CS_OWNDC
)) /* class dc */
258 /* hack: get the class pointer from the window structure */
259 WND
*win
= WIN_GetPtr( data
->hwnd
);
260 class_ptr
= win
->class;
261 WIN_ReleasePtr( win
);
263 EnterCriticalSection( &dce_section
);
264 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
266 if (dce
->class_ptr
== class_ptr
)
270 LeaveCriticalSection( &dce_section
);
274 LeaveCriticalSection( &dce_section
);
277 /* now allocate a new one */
279 if (!(dce
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dce
) ))) return;
280 if (!(dce
->hdc
= CreateDCW( displayW
, NULL
, NULL
, NULL
)))
282 HeapFree( GetProcessHeap(), 0, dce
);
286 /* store DCE handle in DC hook data field */
288 SetDCHook( dce
->hdc
, dc_hook
, (DWORD
)dce
);
290 dce
->hwnd
= data
->hwnd
;
293 dce
->class_ptr
= class_ptr
;
296 if (style
& CS_OWNDC
)
298 LONG win_style
= GetWindowLongW( data
->hwnd
, GWL_STYLE
);
299 if (win_style
& WS_CLIPCHILDREN
) dce
->flags
|= DCX_CLIPCHILDREN
;
300 if (win_style
& WS_CLIPSIBLINGS
) dce
->flags
|= DCX_CLIPSIBLINGS
;
302 SetHookFlags16( HDC_16(dce
->hdc
), DCHF_INVALIDATEVISRGN
);
304 EnterCriticalSection( &dce_section
);
305 list_add_tail( &dce_list
, &dce
->entry
);
306 LeaveCriticalSection( &dce_section
);
309 escape
.code
= X11DRV_SET_DCE
;
311 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
, 0, NULL
);
315 /***********************************************************************
318 * Free a class or window DCE.
320 void free_window_dce( struct x11drv_win_data
*data
)
322 struct dce
*dce
= data
->dce
;
326 EnterCriticalSection( &dce_section
);
329 list_remove( &dce
->entry
);
330 SetDCHook(dce
->hdc
, NULL
, 0L);
331 DeleteDC( dce
->hdc
);
332 if (dce
->clip_rgn
) DeleteObject( dce
->clip_rgn
);
333 HeapFree( GetProcessHeap(), 0, dce
);
335 else if (dce
->hwnd
== data
->hwnd
)
339 LeaveCriticalSection( &dce_section
);
343 /* now check for cache DCEs */
345 EnterCriticalSection( &dce_section
);
346 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
348 if (dce
->hwnd
!= data
->hwnd
) continue;
349 if (!(dce
->flags
& DCX_CACHE
)) continue;
351 if (dce
->count
) WARN( "GetDC() without ReleaseDC() for window %p\n", data
->hwnd
);
355 LeaveCriticalSection( &dce_section
);
359 /***********************************************************************
362 * It is called from SetWindowPos() - we have to
363 * mark as dirty all busy DCEs for windows that have pWnd->parent as
364 * an ancestor and whose client rect intersects with specified update
365 * rectangle. In addition, pWnd->parent DCEs may need to be updated if
366 * DCX_CLIPCHILDREN flag is set.
368 void invalidate_dce( HWND hwnd
, const RECT
*rect
)
370 HWND hwndScope
= GetAncestor( hwnd
, GA_PARENT
);
376 TRACE("scope hwnd = %p %s\n", hwndScope
, wine_dbgstr_rect(rect
) );
377 if (TRACE_ON(dc
)) dump_cache();
379 /* walk all DCEs and fixup non-empty entries */
381 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
383 if (!dce
->hwnd
) continue;
384 if ((dce
->hwnd
== hwndScope
) && !(dce
->flags
& DCX_CLIPCHILDREN
))
385 continue; /* child window positions don't bother us */
387 /* check if DCE window is within the z-order scope */
389 if (hwndScope
== dce
->hwnd
|| IsChild( hwndScope
, dce
->hwnd
))
391 if (hwnd
!= dce
->hwnd
)
393 /* check if the window rectangle intersects this DCE window */
395 GetWindowRect( dce
->hwnd
, &tmp
);
396 MapWindowPoints( 0, hwndScope
, (POINT
*)&tmp
, 2 );
397 if (!IntersectRect( &tmp
, &tmp
, rect
)) continue;
402 /* Don't bother with visible regions of unused DCEs */
404 TRACE("\tpurged %p dce [%p]\n", dce
, dce
->hwnd
);
409 /* Set dirty bits in the hDC and DCE structs */
411 TRACE("\tfixed up %p dce [%p]\n", dce
, dce
->hwnd
);
412 SetHookFlags16( HDC_16(dce
->hdc
), DCHF_INVALIDATEVISRGN
);
420 /***********************************************************************
421 * X11DRV_GetDCEx (X11DRV.@)
423 * Unimplemented flags: DCX_LOCKWINDOWUPDATE
425 HDC
X11DRV_GetDCEx( HWND hwnd
, HRGN hrgnClip
, DWORD flags
)
427 static const DWORD clip_flags
= DCX_PARENTCLIP
| DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
| DCX_WINDOW
;
429 struct x11drv_win_data
*data
= X11DRV_get_win_data( hwnd
);
431 BOOL bUpdateVisRgn
= TRUE
;
433 LONG window_style
= GetWindowLongW( hwnd
, GWL_STYLE
);
435 TRACE("hwnd %p, hrgnClip %p, flags %08lx\n", hwnd
, hrgnClip
, flags
);
439 if (flags
& (DCX_WINDOW
| DCX_PARENTCLIP
)) flags
|= DCX_CACHE
;
441 if (flags
& DCX_USESTYLE
)
443 flags
&= ~(DCX_CLIPCHILDREN
| DCX_CLIPSIBLINGS
| DCX_PARENTCLIP
);
445 if (window_style
& WS_CLIPSIBLINGS
) flags
|= DCX_CLIPSIBLINGS
;
447 if (!(flags
& DCX_WINDOW
))
449 if (GetClassLongW( hwnd
, GCL_STYLE
) & CS_PARENTDC
) flags
|= DCX_PARENTCLIP
;
451 if (window_style
& WS_CLIPCHILDREN
&& !(window_style
& WS_MINIMIZE
))
452 flags
|= DCX_CLIPCHILDREN
;
453 if (!data
|| !data
->dce
) flags
|= DCX_CACHE
;
457 if (flags
& DCX_WINDOW
) flags
&= ~DCX_CLIPCHILDREN
;
459 parent
= GetAncestor( hwnd
, GA_PARENT
);
460 if (!parent
|| (parent
== GetDesktopWindow()))
461 flags
= (flags
& ~DCX_PARENTCLIP
) | DCX_CLIPSIBLINGS
;
463 /* it seems parent clip is ignored when clipping siblings or children */
464 if (flags
& (DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
)) flags
&= ~DCX_PARENTCLIP
;
466 if( flags
& DCX_PARENTCLIP
)
468 LONG parent_style
= GetWindowLongW( parent
, GWL_STYLE
);
469 if( (window_style
& WS_VISIBLE
) && (parent_style
& WS_VISIBLE
) )
471 flags
&= ~DCX_CLIPCHILDREN
;
472 if (parent_style
& WS_CLIPSIBLINGS
) flags
|= DCX_CLIPSIBLINGS
;
476 /* find a suitable DCE */
478 if (flags
& DCX_CACHE
)
480 struct dce
*dceEmpty
= NULL
, *dceUnused
= NULL
;
482 /* Strategy: First, we attempt to find a non-empty but unused DCE with
483 * compatible flags. Next, we look for an empty entry. If the cache is
484 * full we have to purge one of the unused entries.
486 EnterCriticalSection( &dce_section
);
487 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
489 if ((dce
->flags
& DCX_CACHE
) && !dce
->count
)
493 if (!dce
->hwnd
) dceEmpty
= dce
;
494 else if ((dce
->hwnd
== hwnd
) && !((dce
->flags
^ flags
) & clip_flags
))
496 TRACE("\tfound valid %p dce [%p], flags %08lx\n",
497 dce
, hwnd
, dce
->flags
);
498 bUpdateVisRgn
= FALSE
;
504 if (&dce
->entry
== &dce_list
) /* nothing found */
505 dce
= dceEmpty
? dceEmpty
: dceUnused
;
507 if (dce
) dce
->count
= 1;
509 LeaveCriticalSection( &dce_section
);
511 /* if there's no dce empty or unused, allocate a new one */
512 if (!dce
) dce
= alloc_cache_dce();
517 flags
|= DCX_NORESETATTRS
;
519 if (dce
->hwnd
== hwnd
)
521 TRACE("\tskipping hVisRgn update\n");
522 bUpdateVisRgn
= FALSE
; /* updated automatically, via DCHook() */
526 /* we should free dce->clip_rgn here, but Windows apparently doesn't */
527 dce
->flags
&= ~(DCX_EXCLUDERGN
| DCX_INTERSECTRGN
);
532 if (flags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
))
534 /* if the extra clip region has changed, get rid of the old one */
535 if (dce
->clip_rgn
!= hrgnClip
|| ((flags
^ dce
->flags
) & (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
)))
536 delete_clip_rgn( dce
);
537 dce
->clip_rgn
= hrgnClip
;
538 if (!dce
->clip_rgn
) dce
->clip_rgn
= CreateRectRgn( 0, 0, 0, 0 );
539 dce
->flags
|= flags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
);
540 bUpdateVisRgn
= TRUE
;
544 dce
->flags
= (dce
->flags
& ~clip_flags
) | (flags
& clip_flags
);
546 if (SetHookFlags16( HDC_16(dce
->hdc
), DCHF_VALIDATEVISRGN
))
547 bUpdateVisRgn
= TRUE
; /* DC was dirty */
549 if (bUpdateVisRgn
) update_visible_region( dce
);
551 if (!(flags
& DCX_NORESETATTRS
))
553 RestoreDC( dce
->hdc
, 1 ); /* initial save level is always 1 */
554 SaveDC( dce
->hdc
); /* save the state again for next time */
557 TRACE("(%p,%p,0x%lx): returning %p\n", hwnd
, hrgnClip
, flags
, dce
->hdc
);
562 /***********************************************************************
563 * X11DRV_ReleaseDC (X11DRV.@)
565 INT
X11DRV_ReleaseDC( HWND hwnd
, HDC hdc
, BOOL end_paint
)
567 enum x11drv_escape_codes escape
= X11DRV_GET_DCE
;
571 TRACE("%p %p\n", hwnd
, hdc
);
573 EnterCriticalSection( &dce_section
);
574 if (!ExtEscape( hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
,
575 sizeof(dce
), (LPSTR
)&dce
)) dce
= NULL
;
576 if (dce
&& dce
->count
)
578 if (end_paint
|| (dce
->flags
& DCX_CACHE
)) delete_clip_rgn( dce
);
579 if (dce
->flags
& DCX_CACHE
) dce
->count
= 0;
582 LeaveCriticalSection( &dce_section
);
586 /***********************************************************************
589 * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..
591 static BOOL16 CALLBACK
dc_hook( HDC16 hDC
, WORD code
, DWORD data
, LPARAM lParam
)
594 struct dce
*dce
= (struct dce
*)data
;
596 TRACE("hDC = %04x, %i\n", hDC
, code
);
599 assert( HDC_16(dce
->hdc
) == hDC
);
603 case DCHC_INVALIDVISRGN
:
604 /* GDI code calls this when it detects that the
605 * DC is dirty (usually after SetHookFlags()). This
606 * means that we have to recompute the visible region.
608 if (dce
->count
) update_visible_region( dce
);
609 else /* non-fatal but shouldn't happen */
610 WARN("DC is not in use!\n");
614 * Windows will not let you delete a DC that is busy
615 * (between GetDC and ReleaseDC)
619 WARN("Application trying to delete a busy DC %p\n", dce
->hdc
);
624 EnterCriticalSection( &dce_section
);
625 list_remove( &dce
->entry
);
626 LeaveCriticalSection( &dce_section
);
627 if (dce
->clip_rgn
) DeleteObject( dce
->clip_rgn
);
628 HeapFree( GetProcessHeap(), 0, dce
);
636 /**********************************************************************
637 * WindowFromDC (X11DRV.@)
639 HWND
X11DRV_WindowFromDC( HDC hdc
)
641 enum x11drv_escape_codes escape
= X11DRV_GET_DCE
;
645 EnterCriticalSection( &dce_section
);
646 if (!ExtEscape( hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
,
647 sizeof(dce
), (LPSTR
)&dce
)) dce
= NULL
;
648 if (dce
) hwnd
= dce
->hwnd
;
649 LeaveCriticalSection( &dce_section
);