user32: Always clip the button painting to the client rectangle.
[wine] / dlls / winex11.drv / dce.c
1 /*
2 * USER DCE functions
3  *
4  * Copyright 1993, 2005 Alexandre Julliard
5  * Copyright 1996, 1997 Alex Korobka
6  *
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.
11  *
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.
16  *
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
20  */
21
22 #include "config.h"
23
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"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(dc);
39
40 struct dce
41 {
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 */
50 };
51
52 static struct list dce_list = LIST_INIT(dce_list);
53
54 static BOOL CALLBACK dc_hook( HDC hDC, WORD code, DWORD_PTR data, LPARAM lParam );
55
56 static CRITICAL_SECTION dce_section;
57 static CRITICAL_SECTION_DEBUG critsect_debug =
58 {
59     0, 0, &dce_section,
60     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
61       0, 0, { (DWORD_PTR)(__FILE__ ": dce_section") }
62 };
63 static CRITICAL_SECTION dce_section = { &critsect_debug, -1, 0, 0, 0, 0 };
64
65 static const WCHAR displayW[] = { 'D','I','S','P','L','A','Y',0 };
66
67
68 /***********************************************************************
69  *           dump_cache
70  */
71 static void dump_cache(void)
72 {
73     struct dce *dce;
74
75     EnterCriticalSection( &dce_section );
76
77     LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
78     {
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" : "" );
83     }
84
85     LeaveCriticalSection( &dce_section );
86 }
87
88
89 /***********************************************************************
90  *              update_visible_region
91  *
92  * Set the visible region and X11 drawable for the DC associated to
93  * a given window.
94  */
95 static void update_visible_region( struct dce *dce )
96 {
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;
102     DWORD flags = dce->flags;
103     size_t size = 256;
104
105     /* don't clip siblings if using parent clip region */
106     if (flags & DCX_PARENTCLIP) flags &= ~DCX_CLIPSIBLINGS;
107
108     /* fetch the visible region from the server */
109     do
110     {
111         RGNDATA *data = HeapAlloc( GetProcessHeap(), 0, sizeof(*data) + size - 1 );
112         if (!data) return;
113
114         SERVER_START_REQ( get_visible_region )
115         {
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 )))
120             {
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 );
127
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;
137             }
138             else size = reply->total_size;
139         }
140         SERVER_END_REQ;
141         HeapFree( GetProcessHeap(), 0, data );
142     } while (status == STATUS_BUFFER_OVERFLOW);
143
144     if (status || !vis_rgn) return;
145
146     if (dce->clip_rgn) CombineRgn( vis_rgn, vis_rgn, dce->clip_rgn,
147                                    (flags & DCX_INTERSECTRGN) ? RGN_AND : RGN_DIFF );
148
149     if (top == dce->hwnd && ((data = X11DRV_get_win_data( dce->hwnd )) != NULL) &&
150          IsIconic( dce->hwnd ) && data->icon_window)
151     {
152         escape.drawable = data->icon_window;
153         escape.fbconfig_id = 0;
154         escape.gl_drawable = 0;
155         escape.pixmap = 0;
156     }
157     else
158     {
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 );
163     }
164
165     escape.code = X11DRV_SET_DRAWABLE;
166     escape.mode = IncludeInferiors;
167     ExtEscape( dce->hdc, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL );
168
169     /* map region to DC coordinates */
170     OffsetRgn( vis_rgn,
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 );
175 }
176
177
178 /***********************************************************************
179  *              release_dce
180  */
181 static void release_dce( struct dce *dce )
182 {
183     struct x11drv_escape_set_drawable escape;
184
185     if (!dce->hwnd) return;  /* already released */
186
187     if (dce->clip_rgn) DeleteObject( dce->clip_rgn );
188     dce->clip_rgn = 0;
189     dce->hwnd     = 0;
190     dce->flags   &= DCX_CACHE;
191
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;
200     escape.pixmap = 0;
201     ExtEscape( dce->hdc, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL );
202 }
203
204
205 /***********************************************************************
206  *   delete_clip_rgn
207  */
208 static void delete_clip_rgn( struct dce *dce )
209 {
210     if (!dce->clip_rgn) return;  /* nothing to do */
211
212     dce->flags &= ~(DCX_EXCLUDERGN | DCX_INTERSECTRGN);
213     DeleteObject( dce->clip_rgn );
214     dce->clip_rgn = 0;
215
216     /* make it dirty so that the vis rgn gets recomputed next time */
217     SetHookFlags16( HDC_16(dce->hdc), DCHF_INVALIDATEVISRGN );
218 }
219
220
221 /***********************************************************************
222  *           alloc_cache_dce
223  *
224  * Allocate a new cache DCE.
225  */
226 static struct dce *alloc_cache_dce(void)
227 {
228     struct x11drv_escape_set_dce escape;
229     struct dce *dce;
230
231     if (!(dce = HeapAlloc( GetProcessHeap(), 0, sizeof(*dce) ))) return NULL;
232     if (!(dce->hdc = CreateDCW( displayW, NULL, NULL, NULL )))
233     {
234         HeapFree( GetProcessHeap(), 0, dce );
235         return 0;
236     }
237     SaveDC( dce->hdc );
238
239     /* store DCE handle in DC hook data field */
240     SetDCHook( dce->hdc, dc_hook, (DWORD_PTR)dce );
241
242     dce->hwnd      = 0;
243     dce->clip_rgn  = 0;
244     dce->flags     = DCX_CACHE;
245     dce->class_ptr = NULL;
246     dce->count     = 1;
247
248     EnterCriticalSection( &dce_section );
249     list_add_head( &dce_list, &dce->entry );
250     LeaveCriticalSection( &dce_section );
251
252     escape.code = X11DRV_SET_DCE;
253     escape.dce  = dce;
254     ExtEscape( dce->hdc, X11DRV_ESCAPE, sizeof(escape), (LPCSTR)&escape, 0, NULL );
255
256     return dce;
257 }
258
259
260 /***********************************************************************
261  *           alloc_window_dce
262  *
263  * Allocate a DCE for a newly created window if necessary.
264  */
265 void alloc_window_dce( struct x11drv_win_data *data )
266 {
267     struct x11drv_escape_set_dce escape;
268     struct dce *dce;
269     void *class_ptr = NULL;
270     LONG style = GetClassLongW( data->hwnd, GCL_STYLE );
271
272     if (!(style & (CS_CLASSDC|CS_OWNDC))) return;  /* nothing to do */
273
274     if (!(style & CS_OWNDC))  /* class dc */
275     {
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 );
280
281         EnterCriticalSection( &dce_section );
282         LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
283         {
284             if (dce->class_ptr == class_ptr)
285             {
286                 dce->count++;
287                 data->dce = dce;
288                 LeaveCriticalSection( &dce_section );
289                 return;
290             }
291         }
292         LeaveCriticalSection( &dce_section );
293     }
294
295     /* now allocate a new one */
296
297     if (!(dce = HeapAlloc( GetProcessHeap(), 0, sizeof(*dce) ))) return;
298     if (!(dce->hdc = CreateDCW( displayW, NULL, NULL, NULL )))
299     {
300         HeapFree( GetProcessHeap(), 0, dce );
301         return;
302     }
303
304     /* store DCE handle in DC hook data field */
305
306     SetDCHook( dce->hdc, dc_hook, (DWORD_PTR)dce );
307
308     dce->hwnd      = data->hwnd;
309     dce->clip_rgn  = 0;
310     dce->flags     = 0;
311     dce->class_ptr = class_ptr;
312     dce->count     = 1;
313
314     if (style & CS_OWNDC)
315     {
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;
319     }
320     SetHookFlags16( HDC_16(dce->hdc), DCHF_INVALIDATEVISRGN );
321
322     EnterCriticalSection( &dce_section );
323     list_add_tail( &dce_list, &dce->entry );
324     LeaveCriticalSection( &dce_section );
325     data->dce = dce;
326
327     escape.code = X11DRV_SET_DCE;
328     escape.dce  = dce;
329     ExtEscape( dce->hdc, X11DRV_ESCAPE, sizeof(escape), (LPCSTR)&escape, 0, NULL );
330 }
331
332
333 /***********************************************************************
334  *           free_window_dce
335  *
336  * Free a class or window DCE.
337  */
338 void free_window_dce( struct x11drv_win_data *data )
339 {
340     struct dce *dce = data->dce;
341
342     if (dce)
343     {
344         EnterCriticalSection( &dce_section );
345         if (!--dce->count)
346         {
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 );
352         }
353         else if (dce->hwnd == data->hwnd)
354         {
355             release_dce( dce );
356         }
357         LeaveCriticalSection( &dce_section );
358         data->dce = NULL;
359     }
360
361     /* now check for cache DCEs */
362
363     EnterCriticalSection( &dce_section );
364     LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
365     {
366         if (dce->hwnd != data->hwnd) continue;
367         if (!(dce->flags & DCX_CACHE)) continue;
368
369         if (dce->count) WARN( "GetDC() without ReleaseDC() for window %p\n", data->hwnd );
370         release_dce( dce );
371         dce->count = 0;
372     }
373     LeaveCriticalSection( &dce_section );
374 }
375
376
377 /***********************************************************************
378  *   invalidate_dce
379  *
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.
385  */
386 void invalidate_dce( HWND hwnd, const RECT *rect )
387 {
388     HWND hwndScope = GetAncestor( hwnd, GA_PARENT );
389
390     if( hwndScope )
391     {
392         struct dce *dce;
393
394         TRACE("scope hwnd = %p %s\n", hwndScope, wine_dbgstr_rect(rect) );
395         if (TRACE_ON(dc)) dump_cache();
396
397         /* walk all DCEs and fixup non-empty entries */
398
399         LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
400         {
401             if (!dce->hwnd) continue;
402             if ((dce->hwnd == hwndScope) && !(dce->flags & DCX_CLIPCHILDREN))
403                 continue;  /* child window positions don't bother us */
404
405             /* check if DCE window is within the z-order scope */
406
407             if (hwndScope == dce->hwnd || hwndScope == GetDesktopWindow() || IsChild( hwndScope, dce->hwnd ))
408             {
409                 if (hwnd != dce->hwnd)
410                 {
411                     /* check if the window rectangle intersects this DCE window */
412                     RECT tmp;
413                     GetWindowRect( dce->hwnd, &tmp );
414                     MapWindowPoints( 0, hwndScope, (POINT *)&tmp, 2 );
415                     if (!IntersectRect( &tmp, &tmp, rect )) continue;
416
417                 }
418                 if (!dce->count)
419                 {
420                     /* Don't bother with visible regions of unused DCEs */
421
422                     TRACE("\tpurged %p dce [%p]\n", dce, dce->hwnd);
423                     release_dce( dce );
424                 }
425                 else
426                 {
427                     /* Set dirty bits in the hDC and DCE structs */
428
429                     TRACE("\tfixed up %p dce [%p]\n", dce, dce->hwnd);
430                     SetHookFlags16( HDC_16(dce->hdc), DCHF_INVALIDATEVISRGN );
431                 }
432             }
433         } /* dce list */
434     }
435 }
436
437
438 /***********************************************************************
439  *              X11DRV_GetDCEx   (X11DRV.@)
440  *
441  * Unimplemented flags: DCX_LOCKWINDOWUPDATE
442  */
443 HDC X11DRV_GetDCEx( HWND hwnd, HRGN hrgnClip, DWORD flags )
444 {
445     static const DWORD clip_flags = DCX_PARENTCLIP | DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_WINDOW;
446
447     struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
448     struct dce *dce;
449     BOOL bUpdateVisRgn = TRUE;
450     HWND parent;
451     LONG window_style = GetWindowLongW( hwnd, GWL_STYLE );
452
453     TRACE("hwnd %p, hrgnClip %p, flags %08x\n", hwnd, hrgnClip, flags);
454
455     /* fixup flags */
456
457     if (flags & (DCX_WINDOW | DCX_PARENTCLIP)) flags |= DCX_CACHE;
458
459     if (flags & DCX_USESTYLE)
460     {
461         flags &= ~(DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS | DCX_PARENTCLIP);
462
463         if (window_style & WS_CLIPSIBLINGS) flags |= DCX_CLIPSIBLINGS;
464
465         if (!(flags & DCX_WINDOW))
466         {
467             if (GetClassLongW( hwnd, GCL_STYLE ) & CS_PARENTDC) flags |= DCX_PARENTCLIP;
468
469             if (window_style & WS_CLIPCHILDREN && !(window_style & WS_MINIMIZE))
470                 flags |= DCX_CLIPCHILDREN;
471             if (!data || !data->dce) flags |= DCX_CACHE;
472         }
473     }
474
475     if (flags & DCX_WINDOW) flags &= ~DCX_CLIPCHILDREN;
476
477     parent = GetAncestor( hwnd, GA_PARENT );
478     if (!parent || (parent == GetDesktopWindow()))
479         flags = (flags & ~DCX_PARENTCLIP) | DCX_CLIPSIBLINGS;
480
481     /* it seems parent clip is ignored when clipping siblings or children */
482     if (flags & (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN)) flags &= ~DCX_PARENTCLIP;
483
484     if( flags & DCX_PARENTCLIP )
485     {
486         LONG parent_style = GetWindowLongW( parent, GWL_STYLE );
487         if( (window_style & WS_VISIBLE) && (parent_style & WS_VISIBLE) )
488         {
489             flags &= ~DCX_CLIPCHILDREN;
490             if (parent_style & WS_CLIPSIBLINGS) flags |= DCX_CLIPSIBLINGS;
491         }
492     }
493
494     /* find a suitable DCE */
495
496     if (flags & DCX_CACHE)
497     {
498         struct dce *dceEmpty = NULL, *dceUnused = NULL;
499
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.
503          */
504         EnterCriticalSection( &dce_section );
505         LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
506         {
507             if ((dce->flags & DCX_CACHE) && !dce->count)
508             {
509                 dceUnused = dce;
510
511                 if (!dce->hwnd) dceEmpty = dce;
512                 else if ((dce->hwnd == hwnd) && !((dce->flags ^ flags) & clip_flags))
513                 {
514                     TRACE("\tfound valid %p dce [%p], flags %08x\n",
515                           dce, hwnd, dce->flags );
516                     bUpdateVisRgn = FALSE;
517                     break;
518                 }
519             }
520         }
521
522         if (&dce->entry == &dce_list)  /* nothing found */
523             dce = dceEmpty ? dceEmpty : dceUnused;
524
525         if (dce) dce->count = 1;
526
527         LeaveCriticalSection( &dce_section );
528
529         /* if there's no dce empty or unused, allocate a new one */
530         if (!dce) dce = alloc_cache_dce();
531         if (!dce) return 0;
532     }
533     else
534     {
535         flags |= DCX_NORESETATTRS;
536         dce = data->dce;
537         if (dce->hwnd == hwnd)
538         {
539             TRACE("\tskipping hVisRgn update\n");
540             bUpdateVisRgn = FALSE; /* updated automatically, via DCHook() */
541         }
542         else
543         {
544             /* we should free dce->clip_rgn here, but Windows apparently doesn't */
545             dce->flags &= ~(DCX_EXCLUDERGN | DCX_INTERSECTRGN);
546             dce->clip_rgn = 0;
547         }
548     }
549
550     if (flags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN))
551     {
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;
559     }
560
561     dce->hwnd = hwnd;
562     dce->flags = (dce->flags & ~clip_flags) | (flags & clip_flags);
563
564     if (SetHookFlags16( HDC_16(dce->hdc), DCHF_VALIDATEVISRGN ))
565         bUpdateVisRgn = TRUE;  /* DC was dirty */
566
567     if (bUpdateVisRgn) update_visible_region( dce );
568
569     if (!(flags & DCX_NORESETATTRS))
570     {
571         RestoreDC( dce->hdc, 1 );  /* initial save level is always 1 */
572         SaveDC( dce->hdc );  /* save the state again for next time */
573     }
574
575     TRACE("(%p,%p,0x%x): returning %p\n", hwnd, hrgnClip, flags, dce->hdc);
576     return dce->hdc;
577 }
578
579
580 /***********************************************************************
581  *              X11DRV_ReleaseDC  (X11DRV.@)
582  */
583 INT X11DRV_ReleaseDC( HWND hwnd, HDC hdc, BOOL end_paint )
584 {
585     enum x11drv_escape_codes escape = X11DRV_GET_DCE;
586     struct dce *dce;
587     BOOL ret = FALSE;
588
589     TRACE("%p %p\n", hwnd, hdc );
590
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)
595     {
596         if (end_paint || (dce->flags & DCX_CACHE)) delete_clip_rgn( dce );
597         if (dce->flags & DCX_CACHE) dce->count = 0;
598         ret = TRUE;
599     }
600     LeaveCriticalSection( &dce_section );
601     return ret;
602 }
603
604 /***********************************************************************
605  *              dc_hook
606  *
607  * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..
608  */
609 static BOOL CALLBACK dc_hook( HDC hDC, WORD code, DWORD_PTR data, LPARAM lParam )
610 {
611     BOOL retv = TRUE;
612     struct dce *dce = (struct dce *)data;
613
614     TRACE("hDC = %p, %u\n", hDC, code);
615
616     if (!dce) return 0;
617     assert( dce->hdc == hDC );
618
619     switch( code )
620     {
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.
625          */
626         if (dce->count) update_visible_region( dce );
627         else /* non-fatal but shouldn't happen */
628             WARN("DC is not in use!\n");
629         break;
630     case DCHC_DELETEDC:
631         /*
632          * Windows will not let you delete a DC that is busy
633          * (between GetDC and ReleaseDC)
634          */
635         if (dce->count)
636         {
637             WARN("Application trying to delete a busy DC %p\n", dce->hdc);
638             retv = FALSE;
639         }
640         else
641         {
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 );
647         }
648         break;
649     }
650     return retv;
651 }
652
653
654 /**********************************************************************
655  *              WindowFromDC   (X11DRV.@)
656  */
657 HWND X11DRV_WindowFromDC( HDC hdc )
658 {
659     enum x11drv_escape_codes escape = X11DRV_GET_DCE;
660     struct dce *dce;
661     HWND hwnd = 0;
662
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 );
668     return hwnd;
669 }