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 BOOL CALLBACK
dc_hook( HDC hDC
, WORD code
, DWORD_PTR 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 %08x %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
.dc_rect
.left
= reply
->win_rect
.left
- reply
->top_rect
.left
;
130 escape
.dc_rect
.top
= reply
->win_rect
.top
- reply
->top_rect
.top
;
131 escape
.dc_rect
.right
= reply
->win_rect
.right
- reply
->top_rect
.left
;
132 escape
.dc_rect
.bottom
= reply
->win_rect
.bottom
- reply
->top_rect
.top
;
133 escape
.drawable_rect
.left
= reply
->top_rect
.left
;
134 escape
.drawable_rect
.top
= reply
->top_rect
.top
;
135 escape
.drawable_rect
.right
= reply
->top_rect
.right
;
136 escape
.drawable_rect
.bottom
= reply
->top_rect
.bottom
;
138 else size
= reply
->total_size
;
141 HeapFree( GetProcessHeap(), 0, data
);
142 } while (status
== STATUS_BUFFER_OVERFLOW
);
144 if (status
|| !vis_rgn
) return;
146 if (dce
->clip_rgn
) CombineRgn( vis_rgn
, vis_rgn
, dce
->clip_rgn
,
147 (flags
& DCX_INTERSECTRGN
) ? RGN_AND
: RGN_DIFF
);
149 if (top
== dce
->hwnd
&& ((data
= X11DRV_get_win_data( dce
->hwnd
)) != NULL
) &&
150 IsIconic( dce
->hwnd
) && data
->icon_window
)
151 escape
.drawable
= data
->icon_window
;
153 escape
.drawable
= X11DRV_get_whole_window( top
);
155 escape
.code
= X11DRV_SET_DRAWABLE
;
156 escape
.mode
= IncludeInferiors
;
157 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPSTR
)&escape
, 0, NULL
);
159 /* map region to DC coordinates */
161 -(escape
.drawable_rect
.left
+ escape
.dc_rect
.left
),
162 -(escape
.drawable_rect
.top
+ escape
.dc_rect
.top
) );
163 SelectVisRgn16( HDC_16(dce
->hdc
), HRGN_16(vis_rgn
) );
164 DeleteObject( vis_rgn
);
168 /***********************************************************************
171 static void release_dce( struct dce
*dce
)
173 struct x11drv_escape_set_drawable escape
;
175 if (!dce
->hwnd
) return; /* already released */
177 if (dce
->clip_rgn
) DeleteObject( dce
->clip_rgn
);
180 dce
->flags
&= DCX_CACHE
;
182 escape
.code
= X11DRV_SET_DRAWABLE
;
183 escape
.drawable
= root_window
;
184 escape
.mode
= IncludeInferiors
;
185 escape
.drawable_rect
= virtual_screen_rect
;
186 SetRect( &escape
.dc_rect
, 0, 0, virtual_screen_rect
.right
- virtual_screen_rect
.left
,
187 virtual_screen_rect
.bottom
- virtual_screen_rect
.top
);
188 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPSTR
)&escape
, 0, NULL
);
192 /***********************************************************************
195 static void delete_clip_rgn( struct dce
*dce
)
197 if (!dce
->clip_rgn
) return; /* nothing to do */
199 dce
->flags
&= ~(DCX_EXCLUDERGN
| DCX_INTERSECTRGN
);
200 DeleteObject( dce
->clip_rgn
);
203 /* make it dirty so that the vis rgn gets recomputed next time */
204 SetHookFlags16( HDC_16(dce
->hdc
), DCHF_INVALIDATEVISRGN
);
208 /***********************************************************************
211 * Allocate a new cache DCE.
213 static struct dce
*alloc_cache_dce(void)
215 struct x11drv_escape_set_dce escape
;
218 if (!(dce
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dce
) ))) return NULL
;
219 if (!(dce
->hdc
= CreateDCW( displayW
, NULL
, NULL
, NULL
)))
221 HeapFree( GetProcessHeap(), 0, dce
);
226 /* store DCE handle in DC hook data field */
227 SetDCHook( dce
->hdc
, dc_hook
, (DWORD_PTR
)dce
);
231 dce
->flags
= DCX_CACHE
;
232 dce
->class_ptr
= NULL
;
235 EnterCriticalSection( &dce_section
);
236 list_add_head( &dce_list
, &dce
->entry
);
237 LeaveCriticalSection( &dce_section
);
239 escape
.code
= X11DRV_SET_DCE
;
241 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
, 0, NULL
);
247 /***********************************************************************
250 * Allocate a DCE for a newly created window if necessary.
252 void alloc_window_dce( struct x11drv_win_data
*data
)
254 struct x11drv_escape_set_dce escape
;
256 void *class_ptr
= NULL
;
257 LONG style
= GetClassLongW( data
->hwnd
, GCL_STYLE
);
259 if (!(style
& (CS_CLASSDC
|CS_OWNDC
))) return; /* nothing to do */
261 if (!(style
& CS_OWNDC
)) /* class dc */
263 /* hack: get the class pointer from the window structure */
264 WND
*win
= WIN_GetPtr( data
->hwnd
);
265 class_ptr
= win
->class;
266 WIN_ReleasePtr( win
);
268 EnterCriticalSection( &dce_section
);
269 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
271 if (dce
->class_ptr
== class_ptr
)
275 LeaveCriticalSection( &dce_section
);
279 LeaveCriticalSection( &dce_section
);
282 /* now allocate a new one */
284 if (!(dce
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dce
) ))) return;
285 if (!(dce
->hdc
= CreateDCW( displayW
, NULL
, NULL
, NULL
)))
287 HeapFree( GetProcessHeap(), 0, dce
);
291 /* store DCE handle in DC hook data field */
293 SetDCHook( dce
->hdc
, dc_hook
, (DWORD_PTR
)dce
);
295 dce
->hwnd
= data
->hwnd
;
298 dce
->class_ptr
= class_ptr
;
301 if (style
& CS_OWNDC
)
303 LONG win_style
= GetWindowLongW( data
->hwnd
, GWL_STYLE
);
304 if (win_style
& WS_CLIPCHILDREN
) dce
->flags
|= DCX_CLIPCHILDREN
;
305 if (win_style
& WS_CLIPSIBLINGS
) dce
->flags
|= DCX_CLIPSIBLINGS
;
307 SetHookFlags16( HDC_16(dce
->hdc
), DCHF_INVALIDATEVISRGN
);
309 EnterCriticalSection( &dce_section
);
310 list_add_tail( &dce_list
, &dce
->entry
);
311 LeaveCriticalSection( &dce_section
);
314 escape
.code
= X11DRV_SET_DCE
;
316 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
, 0, NULL
);
320 /***********************************************************************
323 * Free a class or window DCE.
325 void free_window_dce( struct x11drv_win_data
*data
)
327 struct dce
*dce
= data
->dce
;
331 EnterCriticalSection( &dce_section
);
334 list_remove( &dce
->entry
);
335 SetDCHook(dce
->hdc
, NULL
, 0L);
336 DeleteDC( dce
->hdc
);
337 if (dce
->clip_rgn
) DeleteObject( dce
->clip_rgn
);
338 HeapFree( GetProcessHeap(), 0, dce
);
340 else if (dce
->hwnd
== data
->hwnd
)
344 LeaveCriticalSection( &dce_section
);
348 /* now check for cache DCEs */
350 EnterCriticalSection( &dce_section
);
351 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
353 if (dce
->hwnd
!= data
->hwnd
) continue;
354 if (!(dce
->flags
& DCX_CACHE
)) continue;
356 if (dce
->count
) WARN( "GetDC() without ReleaseDC() for window %p\n", data
->hwnd
);
360 LeaveCriticalSection( &dce_section
);
364 /***********************************************************************
367 * It is called from SetWindowPos() - we have to
368 * mark as dirty all busy DCEs for windows that have pWnd->parent as
369 * an ancestor and whose client rect intersects with specified update
370 * rectangle. In addition, pWnd->parent DCEs may need to be updated if
371 * DCX_CLIPCHILDREN flag is set.
373 void invalidate_dce( HWND hwnd
, const RECT
*rect
)
375 HWND hwndScope
= GetAncestor( hwnd
, GA_PARENT
);
381 TRACE("scope hwnd = %p %s\n", hwndScope
, wine_dbgstr_rect(rect
) );
382 if (TRACE_ON(dc
)) dump_cache();
384 /* walk all DCEs and fixup non-empty entries */
386 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
388 if (!dce
->hwnd
) continue;
389 if ((dce
->hwnd
== hwndScope
) && !(dce
->flags
& DCX_CLIPCHILDREN
))
390 continue; /* child window positions don't bother us */
392 /* check if DCE window is within the z-order scope */
394 if (hwndScope
== dce
->hwnd
|| IsChild( hwndScope
, dce
->hwnd
))
396 if (hwnd
!= dce
->hwnd
)
398 /* check if the window rectangle intersects this DCE window */
400 GetWindowRect( dce
->hwnd
, &tmp
);
401 MapWindowPoints( 0, hwndScope
, (POINT
*)&tmp
, 2 );
402 if (!IntersectRect( &tmp
, &tmp
, rect
)) continue;
407 /* Don't bother with visible regions of unused DCEs */
409 TRACE("\tpurged %p dce [%p]\n", dce
, dce
->hwnd
);
414 /* Set dirty bits in the hDC and DCE structs */
416 TRACE("\tfixed up %p dce [%p]\n", dce
, dce
->hwnd
);
417 SetHookFlags16( HDC_16(dce
->hdc
), DCHF_INVALIDATEVISRGN
);
425 /***********************************************************************
426 * X11DRV_GetDCEx (X11DRV.@)
428 * Unimplemented flags: DCX_LOCKWINDOWUPDATE
430 HDC
X11DRV_GetDCEx( HWND hwnd
, HRGN hrgnClip
, DWORD flags
)
432 static const DWORD clip_flags
= DCX_PARENTCLIP
| DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
| DCX_WINDOW
;
434 struct x11drv_win_data
*data
= X11DRV_get_win_data( hwnd
);
436 BOOL bUpdateVisRgn
= TRUE
;
438 LONG window_style
= GetWindowLongW( hwnd
, GWL_STYLE
);
440 TRACE("hwnd %p, hrgnClip %p, flags %08x\n", hwnd
, hrgnClip
, flags
);
444 if (flags
& (DCX_WINDOW
| DCX_PARENTCLIP
)) flags
|= DCX_CACHE
;
446 if (flags
& DCX_USESTYLE
)
448 flags
&= ~(DCX_CLIPCHILDREN
| DCX_CLIPSIBLINGS
| DCX_PARENTCLIP
);
450 if (window_style
& WS_CLIPSIBLINGS
) flags
|= DCX_CLIPSIBLINGS
;
452 if (!(flags
& DCX_WINDOW
))
454 if (GetClassLongW( hwnd
, GCL_STYLE
) & CS_PARENTDC
) flags
|= DCX_PARENTCLIP
;
456 if (window_style
& WS_CLIPCHILDREN
&& !(window_style
& WS_MINIMIZE
))
457 flags
|= DCX_CLIPCHILDREN
;
458 if (!data
|| !data
->dce
) flags
|= DCX_CACHE
;
462 if (flags
& DCX_WINDOW
) flags
&= ~DCX_CLIPCHILDREN
;
464 parent
= GetAncestor( hwnd
, GA_PARENT
);
465 if (!parent
|| (parent
== GetDesktopWindow()))
466 flags
= (flags
& ~DCX_PARENTCLIP
) | DCX_CLIPSIBLINGS
;
468 /* it seems parent clip is ignored when clipping siblings or children */
469 if (flags
& (DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
)) flags
&= ~DCX_PARENTCLIP
;
471 if( flags
& DCX_PARENTCLIP
)
473 LONG parent_style
= GetWindowLongW( parent
, GWL_STYLE
);
474 if( (window_style
& WS_VISIBLE
) && (parent_style
& WS_VISIBLE
) )
476 flags
&= ~DCX_CLIPCHILDREN
;
477 if (parent_style
& WS_CLIPSIBLINGS
) flags
|= DCX_CLIPSIBLINGS
;
481 /* find a suitable DCE */
483 if (flags
& DCX_CACHE
)
485 struct dce
*dceEmpty
= NULL
, *dceUnused
= NULL
;
487 /* Strategy: First, we attempt to find a non-empty but unused DCE with
488 * compatible flags. Next, we look for an empty entry. If the cache is
489 * full we have to purge one of the unused entries.
491 EnterCriticalSection( &dce_section
);
492 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
494 if ((dce
->flags
& DCX_CACHE
) && !dce
->count
)
498 if (!dce
->hwnd
) dceEmpty
= dce
;
499 else if ((dce
->hwnd
== hwnd
) && !((dce
->flags
^ flags
) & clip_flags
))
501 TRACE("\tfound valid %p dce [%p], flags %08x\n",
502 dce
, hwnd
, dce
->flags
);
503 bUpdateVisRgn
= FALSE
;
509 if (&dce
->entry
== &dce_list
) /* nothing found */
510 dce
= dceEmpty
? dceEmpty
: dceUnused
;
512 if (dce
) dce
->count
= 1;
514 LeaveCriticalSection( &dce_section
);
516 /* if there's no dce empty or unused, allocate a new one */
517 if (!dce
) dce
= alloc_cache_dce();
522 flags
|= DCX_NORESETATTRS
;
524 if (dce
->hwnd
== hwnd
)
526 TRACE("\tskipping hVisRgn update\n");
527 bUpdateVisRgn
= FALSE
; /* updated automatically, via DCHook() */
531 /* we should free dce->clip_rgn here, but Windows apparently doesn't */
532 dce
->flags
&= ~(DCX_EXCLUDERGN
| DCX_INTERSECTRGN
);
537 if (flags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
))
539 /* if the extra clip region has changed, get rid of the old one */
540 if (dce
->clip_rgn
!= hrgnClip
|| ((flags
^ dce
->flags
) & (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
)))
541 delete_clip_rgn( dce
);
542 dce
->clip_rgn
= hrgnClip
;
543 if (!dce
->clip_rgn
) dce
->clip_rgn
= CreateRectRgn( 0, 0, 0, 0 );
544 dce
->flags
|= flags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
);
545 bUpdateVisRgn
= TRUE
;
549 dce
->flags
= (dce
->flags
& ~clip_flags
) | (flags
& clip_flags
);
551 if (SetHookFlags16( HDC_16(dce
->hdc
), DCHF_VALIDATEVISRGN
))
552 bUpdateVisRgn
= TRUE
; /* DC was dirty */
554 if (bUpdateVisRgn
) update_visible_region( dce
);
556 if (!(flags
& DCX_NORESETATTRS
))
558 RestoreDC( dce
->hdc
, 1 ); /* initial save level is always 1 */
559 SaveDC( dce
->hdc
); /* save the state again for next time */
562 TRACE("(%p,%p,0x%x): returning %p\n", hwnd
, hrgnClip
, flags
, dce
->hdc
);
567 /***********************************************************************
568 * X11DRV_ReleaseDC (X11DRV.@)
570 INT
X11DRV_ReleaseDC( HWND hwnd
, HDC hdc
, BOOL end_paint
)
572 enum x11drv_escape_codes escape
= X11DRV_GET_DCE
;
576 TRACE("%p %p\n", hwnd
, hdc
);
578 EnterCriticalSection( &dce_section
);
579 if (!ExtEscape( hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
,
580 sizeof(dce
), (LPSTR
)&dce
)) dce
= NULL
;
581 if (dce
&& dce
->count
)
583 if (end_paint
|| (dce
->flags
& DCX_CACHE
)) delete_clip_rgn( dce
);
584 if (dce
->flags
& DCX_CACHE
) dce
->count
= 0;
587 LeaveCriticalSection( &dce_section
);
591 /***********************************************************************
594 * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..
596 static BOOL CALLBACK
dc_hook( HDC hDC
, WORD code
, DWORD_PTR data
, LPARAM lParam
)
599 struct dce
*dce
= (struct dce
*)data
;
601 TRACE("hDC = %p, %u\n", hDC
, code
);
604 assert( dce
->hdc
== hDC
);
608 case DCHC_INVALIDVISRGN
:
609 /* GDI code calls this when it detects that the
610 * DC is dirty (usually after SetHookFlags()). This
611 * means that we have to recompute the visible region.
613 if (dce
->count
) update_visible_region( dce
);
614 else /* non-fatal but shouldn't happen */
615 WARN("DC is not in use!\n");
619 * Windows will not let you delete a DC that is busy
620 * (between GetDC and ReleaseDC)
624 WARN("Application trying to delete a busy DC %p\n", dce
->hdc
);
629 EnterCriticalSection( &dce_section
);
630 list_remove( &dce
->entry
);
631 LeaveCriticalSection( &dce_section
);
632 if (dce
->clip_rgn
) DeleteObject( dce
->clip_rgn
);
633 HeapFree( GetProcessHeap(), 0, dce
);
641 /**********************************************************************
642 * WindowFromDC (X11DRV.@)
644 HWND
X11DRV_WindowFromDC( HDC hdc
)
646 enum x11drv_escape_codes escape
= X11DRV_GET_DCE
;
650 EnterCriticalSection( &dce_section
);
651 if (!ExtEscape( hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
,
652 sizeof(dce
), (LPSTR
)&dce
)) dce
= NULL
;
653 if (dce
) hwnd
= dce
->hwnd
;
654 LeaveCriticalSection( &dce_section
);