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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 #include "wine/winbase16.h"
32 #include "wine/wingdi16.h"
33 #include "wine/server.h"
34 #include "wine/list.h"
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(dc
);
41 struct list entry
; /* entry in global DCE list */
46 void *class_ptr
; /* ptr to identify window class for class DCEs */
47 ULONG count
; /* usage count; 0 or 1 for cache DCEs, always 1 for window DCEs,
48 always >= 1 for class DCEs */
51 static struct list dce_list
= LIST_INIT(dce_list
);
53 static BOOL16 CALLBACK
dc_hook( HDC16 hDC
, WORD code
, DWORD data
, LPARAM lParam
);
55 static CRITICAL_SECTION dce_section
;
56 static CRITICAL_SECTION_DEBUG critsect_debug
=
59 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
60 0, 0, { 0, (DWORD
)(__FILE__
": dce_section") }
62 static CRITICAL_SECTION dce_section
= { &critsect_debug
, -1, 0, 0, 0, 0 };
64 static const WCHAR displayW
[] = { 'D','I','S','P','L','A','Y',0 };
67 /***********************************************************************
70 static void dump_cache(void)
74 EnterCriticalSection( &dce_section
);
76 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
78 TRACE("%p: hwnd %p dcx %08lx %s %s\n",
79 dce
, dce
->hwnd
, dce
->flags
,
80 (dce
->flags
& DCX_CACHE
) ? "Cache" : "Owned",
81 dce
->count
? "InUse" : "" );
84 LeaveCriticalSection( &dce_section
);
88 /***********************************************************************
89 * update_visible_region
91 * Set the visible region and X11 drawable for the DC associated to
94 static void update_visible_region( struct dce
*dce
)
99 struct x11drv_escape_set_drawable escape
;
100 struct x11drv_win_data
*data
;
101 DWORD flags
= dce
->flags
;
104 /* don't clip siblings if using parent clip region */
105 if (flags
& DCX_PARENTCLIP
) flags
&= ~DCX_CLIPSIBLINGS
;
107 /* fetch the visible region from the server */
110 RGNDATA
*data
= HeapAlloc( GetProcessHeap(), 0, sizeof(*data
) + size
- 1 );
113 SERVER_START_REQ( get_visible_region
)
115 req
->window
= dce
->hwnd
;
117 wine_server_set_reply( req
, data
->Buffer
, size
);
118 if (!(status
= wine_server_call( req
)))
120 size_t reply_size
= wine_server_reply_size( reply
);
121 data
->rdh
.dwSize
= sizeof(data
->rdh
);
122 data
->rdh
.iType
= RDH_RECTANGLES
;
123 data
->rdh
.nCount
= reply_size
/ sizeof(RECT
);
124 data
->rdh
.nRgnSize
= reply_size
;
125 vis_rgn
= ExtCreateRegion( NULL
, size
, data
);
127 top
= reply
->top_win
;
128 escape
.org
.x
= reply
->win_org_x
- reply
->top_org_x
;
129 escape
.org
.y
= reply
->win_org_y
- reply
->top_org_y
;
130 escape
.drawable_org
.x
= reply
->top_org_x
;
131 escape
.drawable_org
.y
= reply
->top_org_y
;
133 else size
= reply
->total_size
;
136 HeapFree( GetProcessHeap(), 0, data
);
137 } while (status
== STATUS_BUFFER_OVERFLOW
);
139 if (status
|| !vis_rgn
) return;
141 if (dce
->clip_rgn
) CombineRgn( vis_rgn
, vis_rgn
, dce
->clip_rgn
,
142 (flags
& DCX_INTERSECTRGN
) ? RGN_AND
: RGN_DIFF
);
144 if (top
== dce
->hwnd
&& ((data
= X11DRV_get_win_data( dce
->hwnd
)) != NULL
) &&
145 IsIconic( dce
->hwnd
) && data
->icon_window
)
146 escape
.drawable
= data
->icon_window
;
148 escape
.drawable
= X11DRV_get_whole_window( top
);
150 escape
.code
= X11DRV_SET_DRAWABLE
;
151 escape
.mode
= IncludeInferiors
;
152 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPSTR
)&escape
, 0, NULL
);
154 /* map region to DC coordinates */
156 -(escape
.drawable_org
.x
+ escape
.org
.x
),
157 -(escape
.drawable_org
.y
+ escape
.org
.y
) );
158 SelectVisRgn16( HDC_16(dce
->hdc
), HRGN_16(vis_rgn
) );
159 DeleteObject( vis_rgn
);
163 /***********************************************************************
166 static void release_dce( struct dce
*dce
)
168 struct x11drv_escape_set_drawable escape
;
170 if (!dce
->hwnd
) return; /* already released */
172 if (dce
->clip_rgn
) DeleteObject( dce
->clip_rgn
);
175 dce
->flags
&= DCX_CACHE
;
177 escape
.code
= X11DRV_SET_DRAWABLE
;
178 escape
.drawable
= root_window
;
179 escape
.mode
= IncludeInferiors
;
180 escape
.org
.x
= escape
.org
.y
= 0;
181 escape
.drawable_org
.x
= escape
.drawable_org
.y
= 0;
182 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPSTR
)&escape
, 0, NULL
);
186 /***********************************************************************
189 static void delete_clip_rgn( struct dce
*dce
)
191 if (!dce
->clip_rgn
) return; /* nothing to do */
193 dce
->flags
&= ~(DCX_EXCLUDERGN
| DCX_INTERSECTRGN
);
194 DeleteObject( dce
->clip_rgn
);
197 /* make it dirty so that the vis rgn gets recomputed next time */
198 SetHookFlags16( HDC_16(dce
->hdc
), DCHF_INVALIDATEVISRGN
);
202 /***********************************************************************
205 * Allocate a new cache DCE.
207 static struct dce
*alloc_cache_dce(void)
209 struct x11drv_escape_set_dce escape
;
212 if (!(dce
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dce
) ))) return NULL
;
213 if (!(dce
->hdc
= CreateDCW( displayW
, NULL
, NULL
, NULL
)))
215 HeapFree( GetProcessHeap(), 0, dce
);
220 /* store DCE handle in DC hook data field */
221 SetDCHook( dce
->hdc
, dc_hook
, (DWORD
)dce
);
225 dce
->flags
= DCX_CACHE
;
226 dce
->class_ptr
= NULL
;
229 EnterCriticalSection( &dce_section
);
230 list_add_head( &dce_list
, &dce
->entry
);
231 LeaveCriticalSection( &dce_section
);
233 escape
.code
= X11DRV_SET_DCE
;
235 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
, 0, NULL
);
241 /***********************************************************************
244 * Allocate a DCE for a newly created window if necessary.
246 void alloc_window_dce( struct x11drv_win_data
*data
)
248 struct x11drv_escape_set_dce escape
;
250 void *class_ptr
= NULL
;
251 LONG style
= GetClassLongW( data
->hwnd
, GCL_STYLE
);
253 if (!(style
& (CS_CLASSDC
|CS_OWNDC
))) return; /* nothing to do */
255 if (!(style
& CS_OWNDC
)) /* class dc */
257 /* hack: get the class pointer from the window structure */
258 WND
*win
= WIN_GetPtr( data
->hwnd
);
259 class_ptr
= win
->class;
260 WIN_ReleasePtr( win
);
262 EnterCriticalSection( &dce_section
);
263 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
265 if (dce
->class_ptr
== class_ptr
)
269 LeaveCriticalSection( &dce_section
);
273 LeaveCriticalSection( &dce_section
);
276 /* now allocate a new one */
278 if (!(dce
= HeapAlloc( GetProcessHeap(), 0, sizeof(*dce
) ))) return;
279 if (!(dce
->hdc
= CreateDCW( displayW
, NULL
, NULL
, NULL
)))
281 HeapFree( GetProcessHeap(), 0, dce
);
285 /* store DCE handle in DC hook data field */
287 SetDCHook( dce
->hdc
, dc_hook
, (DWORD
)dce
);
289 dce
->hwnd
= data
->hwnd
;
292 dce
->class_ptr
= class_ptr
;
295 if (style
& CS_OWNDC
)
297 LONG win_style
= GetWindowLongW( data
->hwnd
, GWL_STYLE
);
298 if (win_style
& WS_CLIPCHILDREN
) dce
->flags
|= DCX_CLIPCHILDREN
;
299 if (win_style
& WS_CLIPSIBLINGS
) dce
->flags
|= DCX_CLIPSIBLINGS
;
301 SetHookFlags16( HDC_16(dce
->hdc
), DCHF_INVALIDATEVISRGN
);
303 EnterCriticalSection( &dce_section
);
304 list_add_tail( &dce_list
, &dce
->entry
);
305 LeaveCriticalSection( &dce_section
);
308 escape
.code
= X11DRV_SET_DCE
;
310 ExtEscape( dce
->hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
, 0, NULL
);
314 /***********************************************************************
317 * Free a class or window DCE.
319 void free_window_dce( struct x11drv_win_data
*data
)
321 struct dce
*dce
= data
->dce
;
325 EnterCriticalSection( &dce_section
);
328 list_remove( &dce
->entry
);
329 SetDCHook(dce
->hdc
, NULL
, 0L);
330 DeleteDC( dce
->hdc
);
331 if (dce
->clip_rgn
) DeleteObject( dce
->clip_rgn
);
332 HeapFree( GetProcessHeap(), 0, dce
);
334 else if (dce
->hwnd
== data
->hwnd
)
338 LeaveCriticalSection( &dce_section
);
342 /* now check for cache DCEs */
344 EnterCriticalSection( &dce_section
);
345 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
347 if (dce
->hwnd
!= data
->hwnd
) continue;
348 if (!(dce
->flags
& DCX_CACHE
)) continue;
350 if (dce
->count
) WARN( "GetDC() without ReleaseDC() for window %p\n", data
->hwnd
);
354 LeaveCriticalSection( &dce_section
);
358 /***********************************************************************
361 * It is called from SetWindowPos() - we have to
362 * mark as dirty all busy DCEs for windows that have pWnd->parent as
363 * an ancestor and whose client rect intersects with specified update
364 * rectangle. In addition, pWnd->parent DCEs may need to be updated if
365 * DCX_CLIPCHILDREN flag is set.
367 void invalidate_dce( HWND hwnd
, const RECT
*rect
)
369 HWND hwndScope
= GetAncestor( hwnd
, GA_PARENT
);
375 TRACE("scope hwnd = %p %s\n", hwndScope
, wine_dbgstr_rect(rect
) );
376 if (TRACE_ON(dc
)) dump_cache();
378 /* walk all DCEs and fixup non-empty entries */
380 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
382 if (!dce
->hwnd
) continue;
383 if ((dce
->hwnd
== hwndScope
) && !(dce
->flags
& DCX_CLIPCHILDREN
))
384 continue; /* child window positions don't bother us */
386 /* check if DCE window is within the z-order scope */
388 if (hwndScope
== dce
->hwnd
|| IsChild( hwndScope
, dce
->hwnd
))
390 if (hwnd
!= dce
->hwnd
)
392 /* check if the window rectangle intersects this DCE window */
394 GetWindowRect( dce
->hwnd
, &tmp
);
395 MapWindowPoints( 0, hwndScope
, (POINT
*)&tmp
, 2 );
396 if (!IntersectRect( &tmp
, &tmp
, rect
)) continue;
401 /* Don't bother with visible regions of unused DCEs */
403 TRACE("\tpurged %p dce [%p]\n", dce
, dce
->hwnd
);
408 /* Set dirty bits in the hDC and DCE structs */
410 TRACE("\tfixed up %p dce [%p]\n", dce
, dce
->hwnd
);
411 SetHookFlags16( HDC_16(dce
->hdc
), DCHF_INVALIDATEVISRGN
);
419 /***********************************************************************
420 * X11DRV_GetDCEx (X11DRV.@)
422 * Unimplemented flags: DCX_LOCKWINDOWUPDATE
424 HDC
X11DRV_GetDCEx( HWND hwnd
, HRGN hrgnClip
, DWORD flags
)
426 static const DWORD clip_flags
= DCX_PARENTCLIP
| DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
| DCX_WINDOW
;
428 struct x11drv_win_data
*data
= X11DRV_get_win_data( hwnd
);
430 BOOL bUpdateVisRgn
= TRUE
;
432 LONG window_style
= GetWindowLongW( hwnd
, GWL_STYLE
);
434 TRACE("hwnd %p, hrgnClip %p, flags %08lx\n", hwnd
, hrgnClip
, flags
);
438 if (flags
& (DCX_WINDOW
| DCX_PARENTCLIP
)) flags
|= DCX_CACHE
;
440 if (flags
& DCX_USESTYLE
)
442 flags
&= ~(DCX_CLIPCHILDREN
| DCX_CLIPSIBLINGS
| DCX_PARENTCLIP
);
444 if (window_style
& WS_CLIPSIBLINGS
) flags
|= DCX_CLIPSIBLINGS
;
446 if (!(flags
& DCX_WINDOW
))
448 if (GetClassLongW( hwnd
, GCL_STYLE
) & CS_PARENTDC
) flags
|= DCX_PARENTCLIP
;
450 if (window_style
& WS_CLIPCHILDREN
&& !(window_style
& WS_MINIMIZE
))
451 flags
|= DCX_CLIPCHILDREN
;
452 if (!data
|| !data
->dce
) flags
|= DCX_CACHE
;
456 if (flags
& DCX_WINDOW
) flags
&= ~DCX_CLIPCHILDREN
;
458 parent
= GetAncestor( hwnd
, GA_PARENT
);
459 if (!parent
|| (parent
== GetDesktopWindow()))
460 flags
= (flags
& ~DCX_PARENTCLIP
) | DCX_CLIPSIBLINGS
;
462 /* it seems parent clip is ignored when clipping siblings or children */
463 if (flags
& (DCX_CLIPSIBLINGS
| DCX_CLIPCHILDREN
)) flags
&= ~DCX_PARENTCLIP
;
465 if( flags
& DCX_PARENTCLIP
)
467 LONG parent_style
= GetWindowLongW( parent
, GWL_STYLE
);
468 if( (window_style
& WS_VISIBLE
) && (parent_style
& WS_VISIBLE
) )
470 flags
&= ~DCX_CLIPCHILDREN
;
471 if (parent_style
& WS_CLIPSIBLINGS
) flags
|= DCX_CLIPSIBLINGS
;
475 /* find a suitable DCE */
477 if (flags
& DCX_CACHE
)
479 struct dce
*dceEmpty
= NULL
, *dceUnused
= NULL
;
481 /* Strategy: First, we attempt to find a non-empty but unused DCE with
482 * compatible flags. Next, we look for an empty entry. If the cache is
483 * full we have to purge one of the unused entries.
485 EnterCriticalSection( &dce_section
);
486 LIST_FOR_EACH_ENTRY( dce
, &dce_list
, struct dce
, entry
)
488 if ((dce
->flags
& DCX_CACHE
) && !dce
->count
)
492 if (!dce
->hwnd
) dceEmpty
= dce
;
493 else if ((dce
->hwnd
== hwnd
) && !((dce
->flags
^ flags
) & clip_flags
))
495 TRACE("\tfound valid %p dce [%p], flags %08lx\n",
496 dce
, hwnd
, dce
->flags
);
497 bUpdateVisRgn
= FALSE
;
503 if (&dce
->entry
== &dce_list
) /* nothing found */
504 dce
= dceEmpty
? dceEmpty
: dceUnused
;
506 if (dce
) dce
->count
= 1;
508 LeaveCriticalSection( &dce_section
);
510 /* if there's no dce empty or unused, allocate a new one */
511 if (!dce
) dce
= alloc_cache_dce();
516 flags
|= DCX_NORESETATTRS
;
518 if (dce
->hwnd
== hwnd
)
520 TRACE("\tskipping hVisRgn update\n");
521 bUpdateVisRgn
= FALSE
; /* updated automatically, via DCHook() */
525 /* we should free dce->clip_rgn here, but Windows apparently doesn't */
526 dce
->flags
&= ~(DCX_EXCLUDERGN
| DCX_INTERSECTRGN
);
531 if (flags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
))
533 /* if the extra clip region has changed, get rid of the old one */
534 if (dce
->clip_rgn
!= hrgnClip
|| ((flags
^ dce
->flags
) & (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
)))
535 delete_clip_rgn( dce
);
536 dce
->clip_rgn
= hrgnClip
;
537 if (!dce
->clip_rgn
) dce
->clip_rgn
= CreateRectRgn( 0, 0, 0, 0 );
538 dce
->flags
|= flags
& (DCX_INTERSECTRGN
| DCX_EXCLUDERGN
);
539 bUpdateVisRgn
= TRUE
;
543 dce
->flags
= (dce
->flags
& ~clip_flags
) | (flags
& clip_flags
);
545 if (SetHookFlags16( HDC_16(dce
->hdc
), DCHF_VALIDATEVISRGN
))
546 bUpdateVisRgn
= TRUE
; /* DC was dirty */
548 if (bUpdateVisRgn
) update_visible_region( dce
);
550 if (!(flags
& DCX_NORESETATTRS
))
552 RestoreDC( dce
->hdc
, 1 ); /* initial save level is always 1 */
553 SaveDC( dce
->hdc
); /* save the state again for next time */
556 TRACE("(%p,%p,0x%lx): returning %p\n", hwnd
, hrgnClip
, flags
, dce
->hdc
);
561 /***********************************************************************
562 * X11DRV_ReleaseDC (X11DRV.@)
564 BOOL
X11DRV_ReleaseDC( HWND hwnd
, HDC hdc
, BOOL end_paint
)
566 enum x11drv_escape_codes escape
= X11DRV_GET_DCE
;
570 TRACE("%p %p\n", hwnd
, hdc
);
572 EnterCriticalSection( &dce_section
);
573 if (!ExtEscape( hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
,
574 sizeof(dce
), (LPSTR
)&dce
)) dce
= NULL
;
575 if (dce
&& dce
->count
)
577 if (end_paint
|| (dce
->flags
& DCX_CACHE
)) delete_clip_rgn( dce
);
578 if (dce
->flags
& DCX_CACHE
) dce
->count
= 0;
581 LeaveCriticalSection( &dce_section
);
585 /***********************************************************************
588 * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..
590 static BOOL16 CALLBACK
dc_hook( HDC16 hDC
, WORD code
, DWORD data
, LPARAM lParam
)
593 struct dce
*dce
= (struct dce
*)data
;
595 TRACE("hDC = %04x, %i\n", hDC
, code
);
598 assert( HDC_16(dce
->hdc
) == hDC
);
602 case DCHC_INVALIDVISRGN
:
603 /* GDI code calls this when it detects that the
604 * DC is dirty (usually after SetHookFlags()). This
605 * means that we have to recompute the visible region.
607 if (dce
->count
) update_visible_region( dce
);
608 else /* non-fatal but shouldn't happen */
609 WARN("DC is not in use!\n");
613 * Windows will not let you delete a DC that is busy
614 * (between GetDC and ReleaseDC)
618 WARN("Application trying to delete a busy DC %p\n", dce
->hdc
);
623 EnterCriticalSection( &dce_section
);
624 list_remove( &dce
->entry
);
625 LeaveCriticalSection( &dce_section
);
626 if (dce
->clip_rgn
) DeleteObject( dce
->clip_rgn
);
627 HeapFree( GetProcessHeap(), 0, dce
);
635 /**********************************************************************
636 * WindowFromDC (X11DRV.@)
638 HWND
X11DRV_WindowFromDC( HDC hdc
)
640 enum x11drv_escape_codes escape
= X11DRV_GET_DCE
;
644 EnterCriticalSection( &dce_section
);
645 if (!ExtEscape( hdc
, X11DRV_ESCAPE
, sizeof(escape
), (LPCSTR
)&escape
,
646 sizeof(dce
), (LPSTR
)&dce
)) dce
= NULL
;
647 if (dce
) hwnd
= dce
->hwnd
;
648 LeaveCriticalSection( &dce_section
);