Changes in crossover-wine-src-6.1.0 except for configure
[wine/hacks.git] / dlls / winex11.drv / dce.c
blob2ec0a14bef91c8c10dd36282e1b6e7d1e8af7a10
1 /*
2 * USER DCE functions
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
22 #include "config.h"
24 #include <assert.h>
25 #include "ntstatus.h"
26 #define WIN32_NO_STATUS
27 #include "win.h"
28 #include "windef.h"
29 #include "wingdi.h"
30 #include "wownt32.h"
31 #include "x11drv.h"
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);
40 struct dce
42 struct list entry; /* entry in global DCE list */
43 HDC hdc;
44 HWND hwnd;
45 HRGN clip_rgn;
46 DWORD flags;
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 =
59 0, 0, &dce_section,
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 /***********************************************************************
69 * dump_cache
71 static void dump_cache(void)
73 struct dce *dce;
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
93 * a given window.
95 static void update_visible_region( struct dce *dce )
97 NTSTATUS status;
98 HRGN vis_rgn = 0;
99 HWND top = 0;
100 struct x11drv_escape_set_drawable escape;
101 struct x11drv_win_data *data = X11DRV_get_win_data( dce->hwnd );
102 DWORD flags = dce->flags;
103 size_t size = 256;
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 );
112 if (!data) return;
114 SERVER_START_REQ( get_visible_region )
116 req->window = dce->hwnd;
117 req->flags = flags;
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;
140 SERVER_END_REQ;
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 && IsIconic( dce->hwnd ) && data->icon_window)
150 escape.drawable = data->icon_window;
151 else
152 escape.drawable = X11DRV_get_whole_window( top );
154 if (data && !data->toplevel)
156 HWND parent = dce->hwnd;
158 while (parent != top)
160 struct x11drv_win_data *parent_data = X11DRV_get_win_data( parent );
161 if (!parent_data) break;
162 if (parent_data->whole_window)
164 POINT pt = { 0, 0 };
165 MapWindowPoints( top, parent, &pt, 1 );
166 OffsetRect( &escape.dc_rect, pt.x, pt.y );
167 OffsetRect( &escape.drawable_rect, -pt.x, -pt.y );
168 escape.drawable = parent_data->whole_window;
169 break;
171 parent = GetAncestor( parent, GA_PARENT );
175 escape.code = X11DRV_SET_DRAWABLE;
176 escape.mode = IncludeInferiors;
177 ExtEscape( dce->hdc, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL );
179 /* map region to DC coordinates */
180 OffsetRgn( vis_rgn,
181 -(escape.drawable_rect.left + escape.dc_rect.left),
182 -(escape.drawable_rect.top + escape.dc_rect.top) );
183 SelectVisRgn16( HDC_16(dce->hdc), HRGN_16(vis_rgn) );
184 DeleteObject( vis_rgn );
188 /***********************************************************************
189 * release_dce
191 static void release_dce( struct dce *dce )
193 struct x11drv_escape_set_drawable escape;
195 if (!dce->hwnd) return; /* already released */
197 if (dce->clip_rgn) DeleteObject( dce->clip_rgn );
198 dce->clip_rgn = 0;
199 dce->hwnd = 0;
200 dce->flags &= DCX_CACHE;
202 escape.code = X11DRV_SET_DRAWABLE;
203 escape.drawable = root_window;
204 escape.mode = IncludeInferiors;
205 escape.drawable_rect = virtual_screen_rect;
206 SetRect( &escape.dc_rect, 0, 0, virtual_screen_rect.right - virtual_screen_rect.left,
207 virtual_screen_rect.bottom - virtual_screen_rect.top );
208 ExtEscape( dce->hdc, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL );
212 /***********************************************************************
213 * delete_clip_rgn
215 static void delete_clip_rgn( struct dce *dce )
217 if (!dce->clip_rgn) return; /* nothing to do */
219 dce->flags &= ~(DCX_EXCLUDERGN | DCX_INTERSECTRGN);
220 DeleteObject( dce->clip_rgn );
221 dce->clip_rgn = 0;
223 /* make it dirty so that the vis rgn gets recomputed next time */
224 SetHookFlags16( HDC_16(dce->hdc), DCHF_INVALIDATEVISRGN );
228 /***********************************************************************
229 * alloc_cache_dce
231 * Allocate a new cache DCE.
233 static struct dce *alloc_cache_dce(void)
235 struct x11drv_escape_set_dce escape;
236 struct dce *dce;
238 if (!(dce = HeapAlloc( GetProcessHeap(), 0, sizeof(*dce) ))) return NULL;
239 if (!(dce->hdc = CreateDCW( displayW, NULL, NULL, NULL )))
241 HeapFree( GetProcessHeap(), 0, dce );
242 return 0;
244 SaveDC( dce->hdc );
246 /* store DCE handle in DC hook data field */
247 SetDCHook( dce->hdc, dc_hook, (DWORD)dce );
249 dce->hwnd = 0;
250 dce->clip_rgn = 0;
251 dce->flags = DCX_CACHE;
252 dce->class_ptr = NULL;
253 dce->count = 1;
255 EnterCriticalSection( &dce_section );
256 list_add_head( &dce_list, &dce->entry );
257 LeaveCriticalSection( &dce_section );
259 escape.code = X11DRV_SET_DCE;
260 escape.dce = dce;
261 ExtEscape( dce->hdc, X11DRV_ESCAPE, sizeof(escape), (LPCSTR)&escape, 0, NULL );
263 return dce;
267 /***********************************************************************
268 * alloc_window_dce
270 * Allocate a DCE for a newly created window if necessary.
272 void alloc_window_dce( struct x11drv_win_data *data )
274 struct x11drv_escape_set_dce escape;
275 struct dce *dce;
276 void *class_ptr = NULL;
277 LONG style = GetClassLongW( data->hwnd, GCL_STYLE );
279 if (!(style & (CS_CLASSDC|CS_OWNDC))) return; /* nothing to do */
281 if (!(style & CS_OWNDC)) /* class dc */
283 /* hack: get the class pointer from the window structure */
284 WND *win = WIN_GetPtr( data->hwnd );
285 class_ptr = win->class;
286 WIN_ReleasePtr( win );
288 EnterCriticalSection( &dce_section );
289 LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
291 if (dce->class_ptr == class_ptr)
293 dce->count++;
294 data->dce = dce;
295 LeaveCriticalSection( &dce_section );
296 return;
299 LeaveCriticalSection( &dce_section );
302 /* now allocate a new one */
304 if (!(dce = HeapAlloc( GetProcessHeap(), 0, sizeof(*dce) ))) return;
305 if (!(dce->hdc = CreateDCW( displayW, NULL, NULL, NULL )))
307 HeapFree( GetProcessHeap(), 0, dce );
308 return;
311 /* store DCE handle in DC hook data field */
313 SetDCHook( dce->hdc, dc_hook, (DWORD)dce );
315 dce->hwnd = data->hwnd;
316 dce->clip_rgn = 0;
317 dce->flags = 0;
318 dce->class_ptr = class_ptr;
319 dce->count = 1;
321 if (style & CS_OWNDC)
323 LONG win_style = GetWindowLongW( data->hwnd, GWL_STYLE );
324 if (win_style & WS_CLIPCHILDREN) dce->flags |= DCX_CLIPCHILDREN;
325 if (win_style & WS_CLIPSIBLINGS) dce->flags |= DCX_CLIPSIBLINGS;
327 SetHookFlags16( HDC_16(dce->hdc), DCHF_INVALIDATEVISRGN );
329 EnterCriticalSection( &dce_section );
330 list_add_tail( &dce_list, &dce->entry );
331 LeaveCriticalSection( &dce_section );
332 data->dce = dce;
334 escape.code = X11DRV_SET_DCE;
335 escape.dce = dce;
336 ExtEscape( dce->hdc, X11DRV_ESCAPE, sizeof(escape), (LPCSTR)&escape, 0, NULL );
340 /***********************************************************************
341 * free_window_dce
343 * Free a class or window DCE.
345 void free_window_dce( struct x11drv_win_data *data )
347 struct dce *dce = data->dce;
349 if (dce)
351 EnterCriticalSection( &dce_section );
352 if (!--dce->count)
354 list_remove( &dce->entry );
355 SetDCHook(dce->hdc, NULL, 0L);
356 DeleteDC( dce->hdc );
357 if (dce->clip_rgn) DeleteObject( dce->clip_rgn );
358 HeapFree( GetProcessHeap(), 0, dce );
360 else if (dce->hwnd == data->hwnd)
362 release_dce( dce );
364 LeaveCriticalSection( &dce_section );
365 data->dce = NULL;
368 /* now check for cache DCEs */
370 EnterCriticalSection( &dce_section );
371 LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
373 if (dce->hwnd != data->hwnd) continue;
374 if (!(dce->flags & DCX_CACHE)) continue;
376 if (dce->count) WARN( "GetDC() without ReleaseDC() for window %p\n", data->hwnd );
377 release_dce( dce );
378 dce->count = 0;
380 LeaveCriticalSection( &dce_section );
384 /***********************************************************************
385 * invalidate_dce
387 * It is called from SetWindowPos() - we have to
388 * mark as dirty all busy DCEs for windows that have pWnd->parent as
389 * an ancestor and whose client rect intersects with specified update
390 * rectangle. In addition, pWnd->parent DCEs may need to be updated if
391 * DCX_CLIPCHILDREN flag is set.
393 void invalidate_dce( HWND hwnd, const RECT *rect )
395 HWND hwndScope = GetAncestor( hwnd, GA_PARENT );
397 if( hwndScope )
399 struct dce *dce;
401 TRACE("scope hwnd = %p %s\n", hwndScope, wine_dbgstr_rect(rect) );
402 if (TRACE_ON(dc)) dump_cache();
404 /* walk all DCEs and fixup non-empty entries */
406 LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
408 if (!dce->hwnd) continue;
409 if ((dce->hwnd == hwndScope) && !(dce->flags & DCX_CLIPCHILDREN))
410 continue; /* child window positions don't bother us */
412 /* check if DCE window is within the z-order scope */
414 if (hwndScope == dce->hwnd || IsChild( hwndScope, dce->hwnd ))
416 if (hwnd != dce->hwnd)
418 /* check if the window rectangle intersects this DCE window */
419 RECT tmp;
420 GetWindowRect( dce->hwnd, &tmp );
421 MapWindowPoints( 0, hwndScope, (POINT *)&tmp, 2 );
422 if (!IntersectRect( &tmp, &tmp, rect )) continue;
425 if (!dce->count)
427 /* Don't bother with visible regions of unused DCEs */
429 TRACE("\tpurged %p dce [%p]\n", dce, dce->hwnd);
430 release_dce( dce );
432 else
434 /* Set dirty bits in the hDC and DCE structs */
436 TRACE("\tfixed up %p dce [%p]\n", dce, dce->hwnd);
437 SetHookFlags16( HDC_16(dce->hdc), DCHF_INVALIDATEVISRGN );
440 } /* dce list */
445 /***********************************************************************
446 * X11DRV_GetDCEx (X11DRV.@)
448 * Unimplemented flags: DCX_LOCKWINDOWUPDATE
450 HDC X11DRV_GetDCEx( HWND hwnd, HRGN hrgnClip, DWORD flags )
452 static const DWORD clip_flags = DCX_PARENTCLIP | DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_WINDOW;
454 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
455 struct dce *dce;
456 BOOL bUpdateVisRgn = TRUE;
457 HWND parent;
458 LONG window_style = GetWindowLongW( hwnd, GWL_STYLE );
460 TRACE("hwnd %p, hrgnClip %p, flags %08x\n", hwnd, hrgnClip, flags);
462 /* fixup flags */
464 if (flags & (DCX_WINDOW | DCX_PARENTCLIP)) flags |= DCX_CACHE;
466 if (flags & DCX_USESTYLE)
468 flags &= ~(DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS | DCX_PARENTCLIP);
470 if (window_style & WS_CLIPSIBLINGS) flags |= DCX_CLIPSIBLINGS;
472 if (!(flags & DCX_WINDOW))
474 if (GetClassLongW( hwnd, GCL_STYLE ) & CS_PARENTDC) flags |= DCX_PARENTCLIP;
476 if (window_style & WS_CLIPCHILDREN && !(window_style & WS_MINIMIZE))
477 flags |= DCX_CLIPCHILDREN;
478 if (!data || !data->dce) flags |= DCX_CACHE;
482 if (flags & DCX_WINDOW) flags &= ~DCX_CLIPCHILDREN;
484 parent = GetAncestor( hwnd, GA_PARENT );
485 if (!parent || (parent == GetDesktopWindow()))
486 flags = (flags & ~DCX_PARENTCLIP) | DCX_CLIPSIBLINGS;
488 /* it seems parent clip is ignored when clipping siblings or children */
489 if (flags & (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN)) flags &= ~DCX_PARENTCLIP;
491 if( flags & DCX_PARENTCLIP )
493 LONG parent_style = GetWindowLongW( parent, GWL_STYLE );
494 if( (window_style & WS_VISIBLE) && (parent_style & WS_VISIBLE) )
496 flags &= ~DCX_CLIPCHILDREN;
497 if (parent_style & WS_CLIPSIBLINGS) flags |= DCX_CLIPSIBLINGS;
501 /* find a suitable DCE */
503 if (flags & DCX_CACHE)
505 struct dce *dceEmpty = NULL, *dceUnused = NULL;
507 /* Strategy: First, we attempt to find a non-empty but unused DCE with
508 * compatible flags. Next, we look for an empty entry. If the cache is
509 * full we have to purge one of the unused entries.
511 EnterCriticalSection( &dce_section );
512 LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
514 if ((dce->flags & DCX_CACHE) && !dce->count)
516 dceUnused = dce;
518 if (!dce->hwnd) dceEmpty = dce;
519 else if ((dce->hwnd == hwnd) && !((dce->flags ^ flags) & clip_flags))
521 TRACE("\tfound valid %p dce [%p], flags %08x\n",
522 dce, hwnd, dce->flags );
523 bUpdateVisRgn = FALSE;
524 break;
529 if (&dce->entry == &dce_list) /* nothing found */
530 dce = dceEmpty ? dceEmpty : dceUnused;
532 if (dce) dce->count = 1;
534 LeaveCriticalSection( &dce_section );
536 /* if there's no dce empty or unused, allocate a new one */
537 if (!dce) dce = alloc_cache_dce();
538 if (!dce) return 0;
540 else
542 flags |= DCX_NORESETATTRS;
543 dce = data->dce;
544 if (dce->hwnd == hwnd)
546 TRACE("\tskipping hVisRgn update\n");
547 bUpdateVisRgn = FALSE; /* updated automatically, via DCHook() */
549 else
551 /* we should free dce->clip_rgn here, but Windows apparently doesn't */
552 dce->flags &= ~(DCX_EXCLUDERGN | DCX_INTERSECTRGN);
553 dce->clip_rgn = 0;
557 if (flags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN))
559 /* if the extra clip region has changed, get rid of the old one */
560 if (dce->clip_rgn != hrgnClip || ((flags ^ dce->flags) & (DCX_INTERSECTRGN | DCX_EXCLUDERGN)))
561 delete_clip_rgn( dce );
562 dce->clip_rgn = hrgnClip;
563 if (!dce->clip_rgn) dce->clip_rgn = CreateRectRgn( 0, 0, 0, 0 );
564 dce->flags |= flags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN);
565 bUpdateVisRgn = TRUE;
568 dce->hwnd = hwnd;
569 dce->flags = (dce->flags & ~clip_flags) | (flags & clip_flags);
571 if (SetHookFlags16( HDC_16(dce->hdc), DCHF_VALIDATEVISRGN ))
572 bUpdateVisRgn = TRUE; /* DC was dirty */
574 if (bUpdateVisRgn) update_visible_region( dce );
576 if (!(flags & DCX_NORESETATTRS))
578 RestoreDC( dce->hdc, 1 ); /* initial save level is always 1 */
579 SaveDC( dce->hdc ); /* save the state again for next time */
582 TRACE("(%p,%p,0x%x): returning %p\n", hwnd, hrgnClip, flags, dce->hdc);
583 return dce->hdc;
587 /***********************************************************************
588 * X11DRV_ReleaseDC (X11DRV.@)
590 INT X11DRV_ReleaseDC( HWND hwnd, HDC hdc, BOOL end_paint )
592 enum x11drv_escape_codes escape = X11DRV_GET_DCE;
593 struct dce *dce;
594 BOOL ret = FALSE;
596 TRACE("%p %p\n", hwnd, hdc );
598 EnterCriticalSection( &dce_section );
599 if (!ExtEscape( hdc, X11DRV_ESCAPE, sizeof(escape), (LPCSTR)&escape,
600 sizeof(dce), (LPSTR)&dce )) dce = NULL;
601 if (dce && dce->count)
603 if (end_paint || (dce->flags & DCX_CACHE)) delete_clip_rgn( dce );
604 if (dce->flags & DCX_CACHE) dce->count = 0;
605 ret = TRUE;
607 LeaveCriticalSection( &dce_section );
608 return ret;
611 /***********************************************************************
612 * dc_hook
614 * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..
616 static BOOL16 CALLBACK dc_hook( HDC16 hDC, WORD code, DWORD data, LPARAM lParam )
618 BOOL retv = TRUE;
619 struct dce *dce = (struct dce *)data;
621 TRACE("hDC = %04x, %i\n", hDC, code);
623 if (!dce) return 0;
624 assert( HDC_16(dce->hdc) == hDC );
626 switch( code )
628 case DCHC_INVALIDVISRGN:
629 /* GDI code calls this when it detects that the
630 * DC is dirty (usually after SetHookFlags()). This
631 * means that we have to recompute the visible region.
633 if (dce->count) update_visible_region( dce );
634 else /* non-fatal but shouldn't happen */
635 WARN("DC is not in use!\n");
636 break;
637 case DCHC_DELETEDC:
639 * Windows will not let you delete a DC that is busy
640 * (between GetDC and ReleaseDC)
642 if (dce->count)
644 WARN("Application trying to delete a busy DC %p\n", dce->hdc);
645 retv = FALSE;
647 else
649 EnterCriticalSection( &dce_section );
650 list_remove( &dce->entry );
651 LeaveCriticalSection( &dce_section );
652 if (dce->clip_rgn) DeleteObject( dce->clip_rgn );
653 HeapFree( GetProcessHeap(), 0, dce );
655 break;
657 return retv;
661 /**********************************************************************
662 * WindowFromDC (X11DRV.@)
664 HWND X11DRV_WindowFromDC( HDC hdc )
666 enum x11drv_escape_codes escape = X11DRV_GET_DCE;
667 struct dce *dce;
668 HWND hwnd = 0;
670 EnterCriticalSection( &dce_section );
671 if (!ExtEscape( hdc, X11DRV_ESCAPE, sizeof(escape), (LPCSTR)&escape,
672 sizeof(dce), (LPSTR)&dce )) dce = NULL;
673 if (dce) hwnd = dce->hwnd;
674 LeaveCriticalSection( &dce_section );
675 return hwnd;