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
31 #include "wine/server.h"
32 #include "wine/list.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(dc
);
39 struct list entry
; /* entry in global DCE list */
44 void *class_ptr
; /* ptr to identify window class for class DCEs */
45 ULONG count
; /* usage count; 0 or 1 for cache DCEs, always 1 for window DCEs,
46 always >= 1 for class DCEs */
49 static struct list dce_list
= LIST_INIT(dce_list
);
51 static BOOL CALLBACK
dc_hook( HDC hDC
, WORD code
, DWORD_PTR data
, LPARAM lParam
);
53 static CRITICAL_SECTION dce_section
;
54 static CRITICAL_SECTION_DEBUG critsect_debug
=
57 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
58 0, 0, { (DWORD_PTR
)(__FILE__
": dce_section") }
60 static CRITICAL_SECTION dce_section
= { &critsect_debug
, -1, 0, 0, 0, 0 };
62 static const WCHAR displayW
[] = { 'D','I','S','P','L','A','Y',0 };
65 /***********************************************************************
68 static void dump_cache(void)
72 EnterCriticalSection( &dce_section
);
74 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
76 TRACE("%p: hwnd %p dcx %08x %s %s\n",
77 dce
, dce
->hwnd
, dce
->flags
,
78 (dce
->flags
& DCX_CACHE
) ? "Cache" : "Owned",
79 dce
->count
? "InUse" : "" );
82 LeaveCriticalSection( &dce_section
);
86 /***********************************************************************
87 * update_visible_region
89 * Set the visible region and X11 drawable for the DC associated to
92 static void update_visible_region( struct dce
*dce
)
97 struct x11drv_escape_set_drawable escape
;
98 struct x11drv_win_data
*data
;
99 DWORD flags
= dce
->flags
;
102 /* don't clip siblings if using parent clip region */
103 if (flags
& DCX_PARENTCLIP
) flags
&= ~DCX_CLIPSIBLINGS
;
105 /* fetch the visible region from the server */
108 RGNDATA
*data
= HeapAlloc( GetProcessHeap(), 0, sizeof(*data
) + size
- 1 );
111 SERVER_START_REQ( get_visible_region
)
113 req
->window
= dce
->hwnd
;
115 wine_server_set_reply( req
, data
->Buffer
, size
);
116 if (!(status
= wine_server_call( req
)))
118 size_t reply_size
= wine_server_reply_size( reply
);
119 data
->rdh
.dwSize
= sizeof(data
->rdh
);
120 data
->rdh
.iType
= RDH_RECTANGLES
;
121 data
->rdh
.nCount
= reply_size
/ sizeof(RECT
);
122 data
->rdh
.nRgnSize
= reply_size
;
123 vis_rgn
= ExtCreateRegion( NULL
, size
, data
);
125 top
= reply
->top_win
;
126 escape
.dc_rect
.left
= reply
->win_rect
.left
- reply
->top_rect
.left
;
127 escape
.dc_rect
.top
= reply
->win_rect
.top
- reply
->top_rect
.top
;
128 escape
.dc_rect
.right
= reply
->win_rect
.right
- reply
->top_rect
.left
;
129 escape
.dc_rect
.bottom
= reply
->win_rect
.bottom
- reply
->top_rect
.top
;
130 escape
.drawable_rect
.left
= reply
->top_rect
.left
;
131 escape
.drawable_rect
.top
= reply
->top_rect
.top
;
132 escape
.drawable_rect
.right
= reply
->top_rect
.right
;
133 escape
.drawable_rect
.bottom
= reply
->top_rect
.bottom
;
135 else size
= reply
->total_size
;
138 HeapFree( GetProcessHeap(), 0, data
);
139 } while (status
== STATUS_BUFFER_OVERFLOW
);
141 if (status
|| !vis_rgn
) return;
143 if (dce
->clip_rgn
) CombineRgn( vis_rgn
, vis_rgn
, dce
->clip_rgn
,
144 (flags
& DCX_INTERSECTRGN
) ? RGN_AND
: RGN_DIFF
);
146 if (top
== dce
->hwnd
&& ((data
= X11DRV_get_win_data( dce
->hwnd
)) != NULL
) &&
147 IsIconic( dce
->hwnd
) && data
->icon_window
)
149 escape
.drawable
= data
->icon_window
;
150 escape
.fbconfig_id
= 0;
151 escape
.gl_drawable
= 0;
156 escape
.drawable
= X11DRV_get_whole_window( top
);
157 escape
.fbconfig_id
= X11DRV_get_fbconfig_id( dce
->hwnd
);
158 escape
.gl_drawable
= X11DRV_get_gl_drawable( dce
->hwnd
);
159 escape
.pixmap
= X11DRV_get_gl_pixmap( dce
->hwnd
);
162 escape
.code
= X11DRV_SET_DRAWABLE
;
163 escape
.mode
= IncludeInferiors
;
164 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPSTR
)&escape
, 0, NULL
);
166 /* map region to DC coordinates */
168 -(escape
.drawable_rect
.left
+ escape
.dc_rect
.left
),
169 -(escape
.drawable_rect
.top
+ escape
.dc_rect
.top
) );
170 SelectVisRgn( dce
->hdc
, vis_rgn
);
171 DeleteObject( vis_rgn
);
175 /***********************************************************************
178 static void release_dce( struct dce
*dce
)
180 struct x11drv_escape_set_drawable escape
;
182 if (!dce
->hwnd
) return; /* already released */
184 if (dce
->clip_rgn
) DeleteObject( dce
->clip_rgn
);
187 dce
->flags
&= DCX_CACHE
;
189 escape
.code
= X11DRV_SET_DRAWABLE
;
190 escape
.drawable
= root_window
;
191 escape
.mode
= IncludeInferiors
;
192 escape
.drawable_rect
= virtual_screen_rect
;
193 SetRect( &escape
.dc_rect
, 0, 0, virtual_screen_rect
.right
- virtual_screen_rect
.left
,
194 virtual_screen_rect
.bottom
- virtual_screen_rect
.top
);
195 escape
.fbconfig_id
= 0;
196 escape
.gl_drawable
= 0;
198 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPSTR
)&escape
, 0, NULL
);
202 /***********************************************************************
205 static void delete_clip_rgn( struct dce
*dce
)
207 if (!dce
->clip_rgn
) return; /* nothing to do */
209 dce
->flags
&= ~(DCX_EXCLUDERGN
| DCX_INTERSECTRGN
);
210 DeleteObject( dce
->clip_rgn
);
213 /* make it dirty so that the vis rgn gets recomputed next time */
214 SetHookFlags( dce
->hdc
, DCHF_INVALIDATEVISRGN
);
218 /***********************************************************************
221 * Allocate a new cache DCE.
223 static struct dce
*alloc_cache_dce(void)
225 struct x11drv_escape_set_dce escape
;
228 if (!(dce
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dce
) ))) return NULL
;
229 if (!(dce
->hdc
= CreateDCW( displayW
, NULL
, NULL
, NULL
)))
231 HeapFree( GetProcessHeap(), 0, dce
);
236 /* store DCE handle in DC hook data field */
237 SetDCHook( dce
->hdc
, dc_hook
, (DWORD_PTR
)dce
);
241 dce
->flags
= DCX_CACHE
;
242 dce
->class_ptr
= NULL
;
245 EnterCriticalSection( &dce_section
);
246 list_add_head( &dce_list
, &dce
->entry
);
247 LeaveCriticalSection( &dce_section
);
249 escape
.code
= X11DRV_SET_DCE
;
251 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
, 0, NULL
);
257 /***********************************************************************
260 * Allocate a DCE for a newly created window if necessary.
262 void alloc_window_dce( struct x11drv_win_data
*data
)
264 struct x11drv_escape_set_dce escape
;
266 void *class_ptr
= NULL
;
267 LONG style
= GetClassLongW( data
->hwnd
, GCL_STYLE
);
269 if (!(style
& (CS_CLASSDC
|CS_OWNDC
))) return; /* nothing to do */
271 if (!(style
& CS_OWNDC
)) /* class dc */
273 /* hack: get the class pointer from the window structure */
274 WND
*win
= WIN_GetPtr( data
->hwnd
);
275 class_ptr
= win
->class;
276 WIN_ReleasePtr( win
);
278 EnterCriticalSection( &dce_section
);
279 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
281 if (dce
->class_ptr
== class_ptr
)
285 LeaveCriticalSection( &dce_section
);
289 LeaveCriticalSection( &dce_section
);
292 /* now allocate a new one */
294 if (!(dce
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dce
) ))) return;
295 if (!(dce
->hdc
= CreateDCW( displayW
, NULL
, NULL
, NULL
)))
297 HeapFree( GetProcessHeap(), 0, dce
);
301 /* store DCE handle in DC hook data field */
303 SetDCHook( dce
->hdc
, dc_hook
, (DWORD_PTR
)dce
);
305 dce
->hwnd
= data
->hwnd
;
308 dce
->class_ptr
= class_ptr
;
311 if (style
& CS_OWNDC
)
313 LONG win_style
= GetWindowLongW( data
->hwnd
, GWL_STYLE
);
314 if (win_style
& WS_CLIPCHILDREN
) dce
->flags
|= DCX_CLIPCHILDREN
;
315 if (win_style
& WS_CLIPSIBLINGS
) dce
->flags
|= DCX_CLIPSIBLINGS
;
317 SetHookFlags( dce
->hdc
, DCHF_INVALIDATEVISRGN
);
319 EnterCriticalSection( &dce_section
);
320 list_add_tail( &dce_list
, &dce
->entry
);
321 LeaveCriticalSection( &dce_section
);
324 escape
.code
= X11DRV_SET_DCE
;
326 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
, 0, NULL
);
330 /***********************************************************************
333 * Free a class or window DCE.
335 void free_window_dce( struct x11drv_win_data
*data
)
337 struct dce
*dce
= data
->dce
;
341 EnterCriticalSection( &dce_section
);
344 list_remove( &dce
->entry
);
345 SetDCHook(dce
->hdc
, NULL
, 0L);
346 DeleteDC( dce
->hdc
);
347 if (dce
->clip_rgn
) DeleteObject( dce
->clip_rgn
);
348 HeapFree( GetProcessHeap(), 0, dce
);
350 else if (dce
->hwnd
== data
->hwnd
)
354 LeaveCriticalSection( &dce_section
);
358 /* now check for cache DCEs */
360 EnterCriticalSection( &dce_section
);
361 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
363 if (dce
->hwnd
!= data
->hwnd
) continue;
364 if (!(dce
->flags
& DCX_CACHE
)) continue;
366 if (dce
->count
) WARN( "GetDC() without ReleaseDC() for window %p\n", data
->hwnd
);
370 LeaveCriticalSection( &dce_section
);
374 /***********************************************************************
377 * It is called from SetWindowPos() - we have to
378 * mark as dirty all busy DCEs for windows that have pWnd->parent as
379 * an ancestor and whose client rect intersects with specified update
380 * rectangle. In addition, pWnd->parent DCEs may need to be updated if
381 * DCX_CLIPCHILDREN flag is set.
383 void invalidate_dce( HWND hwnd
, const RECT
*rect
)
385 HWND hwndScope
= GetAncestor( hwnd
, GA_PARENT
);
391 TRACE("scope hwnd = %p %s\n", hwndScope
, wine_dbgstr_rect(rect
) );
392 if (TRACE_ON(dc
)) dump_cache();
394 /* walk all DCEs and fixup non-empty entries */
396 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
398 if (!dce
->hwnd
) continue;
399 if ((dce
->hwnd
== hwndScope
) && !(dce
->flags
& DCX_CLIPCHILDREN
))
400 continue; /* child window positions don't bother us */
402 /* check if DCE window is within the z-order scope */
404 if (hwndScope
== dce
->hwnd
|| hwndScope
== GetDesktopWindow() || IsChild( hwndScope
, dce
->hwnd
))
406 if (hwnd
!= dce
->hwnd
)
408 /* check if the window rectangle intersects this DCE window */
410 GetWindowRect( dce
->hwnd
, &tmp
);
411 MapWindowPoints( 0, hwndScope
, (POINT
*)&tmp
, 2 );
412 if (!IntersectRect( &tmp
, &tmp
, rect
)) continue;
417 /* Don't bother with visible regions of unused DCEs */
419 TRACE("\tpurged %p dce [%p]\n", dce
, dce
->hwnd
);
424 /* Set dirty bits in the hDC and DCE structs */
426 TRACE("\tfixed up %p dce [%p]\n", dce
, dce
->hwnd
);
427 SetHookFlags( dce
->hdc
, DCHF_INVALIDATEVISRGN
);
435 /***********************************************************************
436 * X11DRV_GetDCEx (X11DRV.@)
438 * Unimplemented flags: DCX_LOCKWINDOWUPDATE
440 HDC
X11DRV_GetDCEx( HWND hwnd
, HRGN hrgnClip
, DWORD flags
)
442 static const DWORD clip_flags
= DCX_PARENTCLIP
| DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
| DCX_WINDOW
;
444 struct x11drv_win_data
*data
= X11DRV_get_win_data( hwnd
);
446 BOOL bUpdateVisRgn
= TRUE
;
448 LONG window_style
= GetWindowLongW( hwnd
, GWL_STYLE
);
450 TRACE("hwnd %p, hrgnClip %p, flags %08x\n", hwnd
, hrgnClip
, flags
);
454 if (flags
& (DCX_WINDOW
| DCX_PARENTCLIP
)) flags
|= DCX_CACHE
;
456 if (flags
& DCX_USESTYLE
)
458 flags
&= ~(DCX_CLIPCHILDREN
| DCX_CLIPSIBLINGS
| DCX_PARENTCLIP
);
460 if (window_style
& WS_CLIPSIBLINGS
) flags
|= DCX_CLIPSIBLINGS
;
462 if (!(flags
& DCX_WINDOW
))
464 if (GetClassLongW( hwnd
, GCL_STYLE
) & CS_PARENTDC
) flags
|= DCX_PARENTCLIP
;
466 if (window_style
& WS_CLIPCHILDREN
&& !(window_style
& WS_MINIMIZE
))
467 flags
|= DCX_CLIPCHILDREN
;
468 if (!data
|| !data
->dce
) flags
|= DCX_CACHE
;
472 if (flags
& DCX_WINDOW
) flags
&= ~DCX_CLIPCHILDREN
;
474 parent
= GetAncestor( hwnd
, GA_PARENT
);
475 if (!parent
|| (parent
== GetDesktopWindow()))
476 flags
= (flags
& ~DCX_PARENTCLIP
) | DCX_CLIPSIBLINGS
;
478 /* it seems parent clip is ignored when clipping siblings or children */
479 if (flags
& (DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
)) flags
&= ~DCX_PARENTCLIP
;
481 if( flags
& DCX_PARENTCLIP
)
483 LONG parent_style
= GetWindowLongW( parent
, GWL_STYLE
);
484 if( (window_style
& WS_VISIBLE
) && (parent_style
& WS_VISIBLE
) )
486 flags
&= ~DCX_CLIPCHILDREN
;
487 if (parent_style
& WS_CLIPSIBLINGS
) flags
|= DCX_CLIPSIBLINGS
;
491 /* find a suitable DCE */
493 if (flags
& DCX_CACHE
)
495 struct dce
*dceEmpty
= NULL
, *dceUnused
= NULL
;
497 /* Strategy: First, we attempt to find a non-empty but unused DCE with
498 * compatible flags. Next, we look for an empty entry. If the cache is
499 * full we have to purge one of the unused entries.
501 EnterCriticalSection( &dce_section
);
502 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
504 if ((dce
->flags
& DCX_CACHE
) && !dce
->count
)
508 if (!dce
->hwnd
) dceEmpty
= dce
;
509 else if ((dce
->hwnd
== hwnd
) && !((dce
->flags
^ flags
) & clip_flags
))
511 TRACE("\tfound valid %p dce [%p], flags %08x\n",
512 dce
, hwnd
, dce
->flags
);
513 bUpdateVisRgn
= FALSE
;
519 if (&dce
->entry
== &dce_list
) /* nothing found */
520 dce
= dceEmpty
? dceEmpty
: dceUnused
;
522 if (dce
) dce
->count
= 1;
524 LeaveCriticalSection( &dce_section
);
526 /* if there's no dce empty or unused, allocate a new one */
527 if (!dce
) dce
= alloc_cache_dce();
532 flags
|= DCX_NORESETATTRS
;
534 if (dce
->hwnd
== hwnd
)
536 TRACE("\tskipping hVisRgn update\n");
537 bUpdateVisRgn
= FALSE
; /* updated automatically, via DCHook() */
541 /* we should free dce->clip_rgn here, but Windows apparently doesn't */
542 dce
->flags
&= ~(DCX_EXCLUDERGN
| DCX_INTERSECTRGN
);
547 if (flags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
))
549 /* if the extra clip region has changed, get rid of the old one */
550 if (dce
->clip_rgn
!= hrgnClip
|| ((flags
^ dce
->flags
) & (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
)))
551 delete_clip_rgn( dce
);
552 dce
->clip_rgn
= hrgnClip
;
553 if (!dce
->clip_rgn
) dce
->clip_rgn
= CreateRectRgn( 0, 0, 0, 0 );
554 dce
->flags
|= flags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
);
555 bUpdateVisRgn
= TRUE
;
559 dce
->flags
= (dce
->flags
& ~clip_flags
) | (flags
& clip_flags
);
561 if (SetHookFlags( dce
->hdc
, DCHF_VALIDATEVISRGN
))
562 bUpdateVisRgn
= TRUE
; /* DC was dirty */
564 if (bUpdateVisRgn
) update_visible_region( dce
);
566 if (!(flags
& DCX_NORESETATTRS
))
568 RestoreDC( dce
->hdc
, 1 ); /* initial save level is always 1 */
569 SaveDC( dce
->hdc
); /* save the state again for next time */
572 TRACE("(%p,%p,0x%x): returning %p\n", hwnd
, hrgnClip
, flags
, dce
->hdc
);
577 /***********************************************************************
578 * X11DRV_ReleaseDC (X11DRV.@)
580 INT
X11DRV_ReleaseDC( HWND hwnd
, HDC hdc
, BOOL end_paint
)
582 enum x11drv_escape_codes escape
= X11DRV_GET_DCE
;
586 TRACE("%p %p\n", hwnd
, hdc
);
588 EnterCriticalSection( &dce_section
);
589 if (!ExtEscape( hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
,
590 sizeof(dce
), (LPSTR
)&dce
)) dce
= NULL
;
591 if (dce
&& dce
->count
)
593 if (end_paint
|| (dce
->flags
& DCX_CACHE
)) delete_clip_rgn( dce
);
594 if (dce
->flags
& DCX_CACHE
) dce
->count
= 0;
597 LeaveCriticalSection( &dce_section
);
601 /***********************************************************************
604 * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..
606 static BOOL CALLBACK
dc_hook( HDC hDC
, WORD code
, DWORD_PTR data
, LPARAM lParam
)
609 struct dce
*dce
= (struct dce
*)data
;
611 TRACE("hDC = %p, %u\n", hDC
, code
);
614 assert( dce
->hdc
== hDC
);
618 case DCHC_INVALIDVISRGN
:
619 /* GDI code calls this when it detects that the
620 * DC is dirty (usually after SetHookFlags()). This
621 * means that we have to recompute the visible region.
623 if (dce
->count
) update_visible_region( dce
);
624 else /* non-fatal but shouldn't happen */
625 WARN("DC is not in use!\n");
629 * Windows will not let you delete a DC that is busy
630 * (between GetDC and ReleaseDC)
634 WARN("Application trying to delete a busy DC %p\n", dce
->hdc
);
639 EnterCriticalSection( &dce_section
);
640 list_remove( &dce
->entry
);
641 LeaveCriticalSection( &dce_section
);
642 if (dce
->clip_rgn
) DeleteObject( dce
->clip_rgn
);
643 HeapFree( GetProcessHeap(), 0, dce
);
651 /**********************************************************************
652 * WindowFromDC (X11DRV.@)
654 HWND
X11DRV_WindowFromDC( HDC hdc
)
656 enum x11drv_escape_codes escape
= X11DRV_GET_DCE
;
660 EnterCriticalSection( &dce_section
);
661 if (!ExtEscape( hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
,
662 sizeof(dce
), (LPSTR
)&dce
)) dce
= NULL
;
663 if (dce
) hwnd
= dce
->hwnd
;
664 LeaveCriticalSection( &dce_section
);