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
)
152 escape
.drawable
= data
->icon_window
;
153 escape
.fbconfig_id
= 0;
154 escape
.gl_drawable
= 0;
159 escape
.drawable
= X11DRV_get_whole_window( top
);
160 escape
.fbconfig_id
= X11DRV_get_fbconfig_id( dce
->hwnd
);
161 escape
.gl_drawable
= X11DRV_get_gl_drawable( dce
->hwnd
);
162 escape
.pixmap
= X11DRV_get_gl_pixmap( dce
->hwnd
);
165 escape
.code
= X11DRV_SET_DRAWABLE
;
166 escape
.mode
= IncludeInferiors
;
167 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPSTR
)&escape
, 0, NULL
);
169 /* map region to DC coordinates */
171 -(escape
.drawable_rect
.left
+ escape
.dc_rect
.left
),
172 -(escape
.drawable_rect
.top
+ escape
.dc_rect
.top
) );
173 SelectVisRgn16( HDC_16(dce
->hdc
), HRGN_16(vis_rgn
) );
174 DeleteObject( vis_rgn
);
178 /***********************************************************************
181 static void release_dce( struct dce
*dce
)
183 struct x11drv_escape_set_drawable escape
;
185 if (!dce
->hwnd
) return; /* already released */
187 if (dce
->clip_rgn
) DeleteObject( dce
->clip_rgn
);
190 dce
->flags
&= DCX_CACHE
;
192 escape
.code
= X11DRV_SET_DRAWABLE
;
193 escape
.drawable
= root_window
;
194 escape
.mode
= IncludeInferiors
;
195 escape
.drawable_rect
= virtual_screen_rect
;
196 SetRect( &escape
.dc_rect
, 0, 0, virtual_screen_rect
.right
- virtual_screen_rect
.left
,
197 virtual_screen_rect
.bottom
- virtual_screen_rect
.top
);
198 escape
.fbconfig_id
= 0;
199 escape
.gl_drawable
= 0;
201 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPSTR
)&escape
, 0, NULL
);
205 /***********************************************************************
208 static void delete_clip_rgn( struct dce
*dce
)
210 if (!dce
->clip_rgn
) return; /* nothing to do */
212 dce
->flags
&= ~(DCX_EXCLUDERGN
| DCX_INTERSECTRGN
);
213 DeleteObject( dce
->clip_rgn
);
216 /* make it dirty so that the vis rgn gets recomputed next time */
217 SetHookFlags16( HDC_16(dce
->hdc
), DCHF_INVALIDATEVISRGN
);
221 /***********************************************************************
224 * Allocate a new cache DCE.
226 static struct dce
*alloc_cache_dce(void)
228 struct x11drv_escape_set_dce escape
;
231 if (!(dce
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dce
) ))) return NULL
;
232 if (!(dce
->hdc
= CreateDCW( displayW
, NULL
, NULL
, NULL
)))
234 HeapFree( GetProcessHeap(), 0, dce
);
239 /* store DCE handle in DC hook data field */
240 SetDCHook( dce
->hdc
, dc_hook
, (DWORD_PTR
)dce
);
244 dce
->flags
= DCX_CACHE
;
245 dce
->class_ptr
= NULL
;
248 EnterCriticalSection( &dce_section
);
249 list_add_head( &dce_list
, &dce
->entry
);
250 LeaveCriticalSection( &dce_section
);
252 escape
.code
= X11DRV_SET_DCE
;
254 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
, 0, NULL
);
260 /***********************************************************************
263 * Allocate a DCE for a newly created window if necessary.
265 void alloc_window_dce( struct x11drv_win_data
*data
)
267 struct x11drv_escape_set_dce escape
;
269 void *class_ptr
= NULL
;
270 LONG style
= GetClassLongW( data
->hwnd
, GCL_STYLE
);
272 if (!(style
& (CS_CLASSDC
|CS_OWNDC
))) return; /* nothing to do */
274 if (!(style
& CS_OWNDC
)) /* class dc */
276 /* hack: get the class pointer from the window structure */
277 WND
*win
= WIN_GetPtr( data
->hwnd
);
278 class_ptr
= win
->class;
279 WIN_ReleasePtr( win
);
281 EnterCriticalSection( &dce_section
);
282 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
284 if (dce
->class_ptr
== class_ptr
)
288 LeaveCriticalSection( &dce_section
);
292 LeaveCriticalSection( &dce_section
);
295 /* now allocate a new one */
297 if (!(dce
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dce
) ))) return;
298 if (!(dce
->hdc
= CreateDCW( displayW
, NULL
, NULL
, NULL
)))
300 HeapFree( GetProcessHeap(), 0, dce
);
304 /* store DCE handle in DC hook data field */
306 SetDCHook( dce
->hdc
, dc_hook
, (DWORD_PTR
)dce
);
308 dce
->hwnd
= data
->hwnd
;
311 dce
->class_ptr
= class_ptr
;
314 if (style
& CS_OWNDC
)
316 LONG win_style
= GetWindowLongW( data
->hwnd
, GWL_STYLE
);
317 if (win_style
& WS_CLIPCHILDREN
) dce
->flags
|= DCX_CLIPCHILDREN
;
318 if (win_style
& WS_CLIPSIBLINGS
) dce
->flags
|= DCX_CLIPSIBLINGS
;
320 SetHookFlags16( HDC_16(dce
->hdc
), DCHF_INVALIDATEVISRGN
);
322 EnterCriticalSection( &dce_section
);
323 list_add_tail( &dce_list
, &dce
->entry
);
324 LeaveCriticalSection( &dce_section
);
327 escape
.code
= X11DRV_SET_DCE
;
329 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
, 0, NULL
);
333 /***********************************************************************
336 * Free a class or window DCE.
338 void free_window_dce( struct x11drv_win_data
*data
)
340 struct dce
*dce
= data
->dce
;
344 EnterCriticalSection( &dce_section
);
347 list_remove( &dce
->entry
);
348 SetDCHook(dce
->hdc
, NULL
, 0L);
349 DeleteDC( dce
->hdc
);
350 if (dce
->clip_rgn
) DeleteObject( dce
->clip_rgn
);
351 HeapFree( GetProcessHeap(), 0, dce
);
353 else if (dce
->hwnd
== data
->hwnd
)
357 LeaveCriticalSection( &dce_section
);
361 /* now check for cache DCEs */
363 EnterCriticalSection( &dce_section
);
364 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
366 if (dce
->hwnd
!= data
->hwnd
) continue;
367 if (!(dce
->flags
& DCX_CACHE
)) continue;
369 if (dce
->count
) WARN( "GetDC() without ReleaseDC() for window %p\n", data
->hwnd
);
373 LeaveCriticalSection( &dce_section
);
377 /***********************************************************************
380 * It is called from SetWindowPos() - we have to
381 * mark as dirty all busy DCEs for windows that have pWnd->parent as
382 * an ancestor and whose client rect intersects with specified update
383 * rectangle. In addition, pWnd->parent DCEs may need to be updated if
384 * DCX_CLIPCHILDREN flag is set.
386 void invalidate_dce( HWND hwnd
, const RECT
*rect
)
388 HWND hwndScope
= GetAncestor( hwnd
, GA_PARENT
);
394 TRACE("scope hwnd = %p %s\n", hwndScope
, wine_dbgstr_rect(rect
) );
395 if (TRACE_ON(dc
)) dump_cache();
397 /* walk all DCEs and fixup non-empty entries */
399 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
401 if (!dce
->hwnd
) continue;
402 if ((dce
->hwnd
== hwndScope
) && !(dce
->flags
& DCX_CLIPCHILDREN
))
403 continue; /* child window positions don't bother us */
405 /* check if DCE window is within the z-order scope */
407 if (hwndScope
== dce
->hwnd
|| hwndScope
== GetDesktopWindow() || IsChild( hwndScope
, dce
->hwnd
))
409 if (hwnd
!= dce
->hwnd
)
411 /* check if the window rectangle intersects this DCE window */
413 GetWindowRect( dce
->hwnd
, &tmp
);
414 MapWindowPoints( 0, hwndScope
, (POINT
*)&tmp
, 2 );
415 if (!IntersectRect( &tmp
, &tmp
, rect
)) continue;
420 /* Don't bother with visible regions of unused DCEs */
422 TRACE("\tpurged %p dce [%p]\n", dce
, dce
->hwnd
);
427 /* Set dirty bits in the hDC and DCE structs */
429 TRACE("\tfixed up %p dce [%p]\n", dce
, dce
->hwnd
);
430 SetHookFlags16( HDC_16(dce
->hdc
), DCHF_INVALIDATEVISRGN
);
438 /***********************************************************************
439 * X11DRV_GetDCEx (X11DRV.@)
441 * Unimplemented flags: DCX_LOCKWINDOWUPDATE
443 HDC
X11DRV_GetDCEx( HWND hwnd
, HRGN hrgnClip
, DWORD flags
)
445 static const DWORD clip_flags
= DCX_PARENTCLIP
| DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
| DCX_WINDOW
;
447 struct x11drv_win_data
*data
= X11DRV_get_win_data( hwnd
);
449 BOOL bUpdateVisRgn
= TRUE
;
451 LONG window_style
= GetWindowLongW( hwnd
, GWL_STYLE
);
453 TRACE("hwnd %p, hrgnClip %p, flags %08x\n", hwnd
, hrgnClip
, flags
);
457 if (flags
& (DCX_WINDOW
| DCX_PARENTCLIP
)) flags
|= DCX_CACHE
;
459 if (flags
& DCX_USESTYLE
)
461 flags
&= ~(DCX_CLIPCHILDREN
| DCX_CLIPSIBLINGS
| DCX_PARENTCLIP
);
463 if (window_style
& WS_CLIPSIBLINGS
) flags
|= DCX_CLIPSIBLINGS
;
465 if (!(flags
& DCX_WINDOW
))
467 if (GetClassLongW( hwnd
, GCL_STYLE
) & CS_PARENTDC
) flags
|= DCX_PARENTCLIP
;
469 if (window_style
& WS_CLIPCHILDREN
&& !(window_style
& WS_MINIMIZE
))
470 flags
|= DCX_CLIPCHILDREN
;
471 if (!data
|| !data
->dce
) flags
|= DCX_CACHE
;
475 if (flags
& DCX_WINDOW
) flags
&= ~DCX_CLIPCHILDREN
;
477 parent
= GetAncestor( hwnd
, GA_PARENT
);
478 if (!parent
|| (parent
== GetDesktopWindow()))
479 flags
= (flags
& ~DCX_PARENTCLIP
) | DCX_CLIPSIBLINGS
;
481 /* it seems parent clip is ignored when clipping siblings or children */
482 if (flags
& (DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
)) flags
&= ~DCX_PARENTCLIP
;
484 if( flags
& DCX_PARENTCLIP
)
486 LONG parent_style
= GetWindowLongW( parent
, GWL_STYLE
);
487 if( (window_style
& WS_VISIBLE
) && (parent_style
& WS_VISIBLE
) )
489 flags
&= ~DCX_CLIPCHILDREN
;
490 if (parent_style
& WS_CLIPSIBLINGS
) flags
|= DCX_CLIPSIBLINGS
;
494 /* find a suitable DCE */
496 if (flags
& DCX_CACHE
)
498 struct dce
*dceEmpty
= NULL
, *dceUnused
= NULL
;
500 /* Strategy: First, we attempt to find a non-empty but unused DCE with
501 * compatible flags. Next, we look for an empty entry. If the cache is
502 * full we have to purge one of the unused entries.
504 EnterCriticalSection( &dce_section
);
505 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
507 if ((dce
->flags
& DCX_CACHE
) && !dce
->count
)
511 if (!dce
->hwnd
) dceEmpty
= dce
;
512 else if ((dce
->hwnd
== hwnd
) && !((dce
->flags
^ flags
) & clip_flags
))
514 TRACE("\tfound valid %p dce [%p], flags %08x\n",
515 dce
, hwnd
, dce
->flags
);
516 bUpdateVisRgn
= FALSE
;
522 if (&dce
->entry
== &dce_list
) /* nothing found */
523 dce
= dceEmpty
? dceEmpty
: dceUnused
;
525 if (dce
) dce
->count
= 1;
527 LeaveCriticalSection( &dce_section
);
529 /* if there's no dce empty or unused, allocate a new one */
530 if (!dce
) dce
= alloc_cache_dce();
535 flags
|= DCX_NORESETATTRS
;
537 if (dce
->hwnd
== hwnd
)
539 TRACE("\tskipping hVisRgn update\n");
540 bUpdateVisRgn
= FALSE
; /* updated automatically, via DCHook() */
544 /* we should free dce->clip_rgn here, but Windows apparently doesn't */
545 dce
->flags
&= ~(DCX_EXCLUDERGN
| DCX_INTERSECTRGN
);
550 if (flags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
))
552 /* if the extra clip region has changed, get rid of the old one */
553 if (dce
->clip_rgn
!= hrgnClip
|| ((flags
^ dce
->flags
) & (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
)))
554 delete_clip_rgn( dce
);
555 dce
->clip_rgn
= hrgnClip
;
556 if (!dce
->clip_rgn
) dce
->clip_rgn
= CreateRectRgn( 0, 0, 0, 0 );
557 dce
->flags
|= flags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
);
558 bUpdateVisRgn
= TRUE
;
562 dce
->flags
= (dce
->flags
& ~clip_flags
) | (flags
& clip_flags
);
564 if (SetHookFlags16( HDC_16(dce
->hdc
), DCHF_VALIDATEVISRGN
))
565 bUpdateVisRgn
= TRUE
; /* DC was dirty */
567 if (bUpdateVisRgn
) update_visible_region( dce
);
569 if (!(flags
& DCX_NORESETATTRS
))
571 RestoreDC( dce
->hdc
, 1 ); /* initial save level is always 1 */
572 SaveDC( dce
->hdc
); /* save the state again for next time */
575 TRACE("(%p,%p,0x%x): returning %p\n", hwnd
, hrgnClip
, flags
, dce
->hdc
);
580 /***********************************************************************
581 * X11DRV_ReleaseDC (X11DRV.@)
583 INT
X11DRV_ReleaseDC( HWND hwnd
, HDC hdc
, BOOL end_paint
)
585 enum x11drv_escape_codes escape
= X11DRV_GET_DCE
;
589 TRACE("%p %p\n", hwnd
, hdc
);
591 EnterCriticalSection( &dce_section
);
592 if (!ExtEscape( hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
,
593 sizeof(dce
), (LPSTR
)&dce
)) dce
= NULL
;
594 if (dce
&& dce
->count
)
596 if (end_paint
|| (dce
->flags
& DCX_CACHE
)) delete_clip_rgn( dce
);
597 if (dce
->flags
& DCX_CACHE
) dce
->count
= 0;
600 LeaveCriticalSection( &dce_section
);
604 /***********************************************************************
607 * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..
609 static BOOL CALLBACK
dc_hook( HDC hDC
, WORD code
, DWORD_PTR data
, LPARAM lParam
)
612 struct dce
*dce
= (struct dce
*)data
;
614 TRACE("hDC = %p, %u\n", hDC
, code
);
617 assert( dce
->hdc
== hDC
);
621 case DCHC_INVALIDVISRGN
:
622 /* GDI code calls this when it detects that the
623 * DC is dirty (usually after SetHookFlags()). This
624 * means that we have to recompute the visible region.
626 if (dce
->count
) update_visible_region( dce
);
627 else /* non-fatal but shouldn't happen */
628 WARN("DC is not in use!\n");
632 * Windows will not let you delete a DC that is busy
633 * (between GetDC and ReleaseDC)
637 WARN("Application trying to delete a busy DC %p\n", dce
->hdc
);
642 EnterCriticalSection( &dce_section
);
643 list_remove( &dce
->entry
);
644 LeaveCriticalSection( &dce_section
);
645 if (dce
->clip_rgn
) DeleteObject( dce
->clip_rgn
);
646 HeapFree( GetProcessHeap(), 0, dce
);
654 /**********************************************************************
655 * WindowFromDC (X11DRV.@)
657 HWND
X11DRV_WindowFromDC( HDC hdc
)
659 enum x11drv_escape_codes escape
= X11DRV_GET_DCE
;
663 EnterCriticalSection( &dce_section
);
664 if (!ExtEscape( hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
,
665 sizeof(dce
), (LPSTR
)&dce
)) dce
= NULL
;
666 if (dce
) hwnd
= dce
->hwnd
;
667 LeaveCriticalSection( &dce_section
);