msvcrt: Resolve symbols clashes with FreeBSD libc.
[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     }
155     else
156     {
157         escape.drawable = X11DRV_get_whole_window( top );
158         escape.fbconfig_id = X11DRV_get_fbconfig_id( dce->hwnd );
159     }
160
161     escape.code = X11DRV_SET_DRAWABLE;
162     escape.mode = IncludeInferiors;
163     ExtEscape( dce->hdc, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL );
164
165     /* map region to DC coordinates */
166     OffsetRgn( vis_rgn,
167                -(escape.drawable_rect.left + escape.dc_rect.left),
168                -(escape.drawable_rect.top + escape.dc_rect.top) );
169     SelectVisRgn16( HDC_16(dce->hdc), HRGN_16(vis_rgn) );
170     DeleteObject( vis_rgn );
171 }
172
173
174 /***********************************************************************
175  *              release_dce
176  */
177 static void release_dce( struct dce *dce )
178 {
179     struct x11drv_escape_set_drawable escape;
180
181     if (!dce->hwnd) return;  /* already released */
182
183     if (dce->clip_rgn) DeleteObject( dce->clip_rgn );
184     dce->clip_rgn = 0;
185     dce->hwnd     = 0;
186     dce->flags   &= DCX_CACHE;
187
188     escape.code = X11DRV_SET_DRAWABLE;
189     escape.drawable = root_window;
190     escape.mode = IncludeInferiors;
191     escape.drawable_rect = virtual_screen_rect;
192     SetRect( &escape.dc_rect, 0, 0, virtual_screen_rect.right - virtual_screen_rect.left,
193              virtual_screen_rect.bottom - virtual_screen_rect.top );
194     escape.fbconfig_id = 0;
195     ExtEscape( dce->hdc, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL );
196 }
197
198
199 /***********************************************************************
200  *   delete_clip_rgn
201  */
202 static void delete_clip_rgn( struct dce *dce )
203 {
204     if (!dce->clip_rgn) return;  /* nothing to do */
205
206     dce->flags &= ~(DCX_EXCLUDERGN | DCX_INTERSECTRGN);
207     DeleteObject( dce->clip_rgn );
208     dce->clip_rgn = 0;
209
210     /* make it dirty so that the vis rgn gets recomputed next time */
211     SetHookFlags16( HDC_16(dce->hdc), DCHF_INVALIDATEVISRGN );
212 }
213
214
215 /***********************************************************************
216  *           alloc_cache_dce
217  *
218  * Allocate a new cache DCE.
219  */
220 static struct dce *alloc_cache_dce(void)
221 {
222     struct x11drv_escape_set_dce escape;
223     struct dce *dce;
224
225     if (!(dce = HeapAlloc( GetProcessHeap(), 0, sizeof(*dce) ))) return NULL;
226     if (!(dce->hdc = CreateDCW( displayW, NULL, NULL, NULL )))
227     {
228         HeapFree( GetProcessHeap(), 0, dce );
229         return 0;
230     }
231     SaveDC( dce->hdc );
232
233     /* store DCE handle in DC hook data field */
234     SetDCHook( dce->hdc, dc_hook, (DWORD_PTR)dce );
235
236     dce->hwnd      = 0;
237     dce->clip_rgn  = 0;
238     dce->flags     = DCX_CACHE;
239     dce->class_ptr = NULL;
240     dce->count     = 1;
241
242     EnterCriticalSection( &dce_section );
243     list_add_head( &dce_list, &dce->entry );
244     LeaveCriticalSection( &dce_section );
245
246     escape.code = X11DRV_SET_DCE;
247     escape.dce  = dce;
248     ExtEscape( dce->hdc, X11DRV_ESCAPE, sizeof(escape), (LPCSTR)&escape, 0, NULL );
249
250     return dce;
251 }
252
253
254 /***********************************************************************
255  *           alloc_window_dce
256  *
257  * Allocate a DCE for a newly created window if necessary.
258  */
259 void alloc_window_dce( struct x11drv_win_data *data )
260 {
261     struct x11drv_escape_set_dce escape;
262     struct dce *dce;
263     void *class_ptr = NULL;
264     LONG style = GetClassLongW( data->hwnd, GCL_STYLE );
265
266     if (!(style & (CS_CLASSDC|CS_OWNDC))) return;  /* nothing to do */
267
268     if (!(style & CS_OWNDC))  /* class dc */
269     {
270         /* hack: get the class pointer from the window structure */
271         WND *win = WIN_GetPtr( data->hwnd );
272         class_ptr = win->class;
273         WIN_ReleasePtr( win );
274
275         EnterCriticalSection( &dce_section );
276         LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
277         {
278             if (dce->class_ptr == class_ptr)
279             {
280                 dce->count++;
281                 data->dce = dce;
282                 LeaveCriticalSection( &dce_section );
283                 return;
284             }
285         }
286         LeaveCriticalSection( &dce_section );
287     }
288
289     /* now allocate a new one */
290
291     if (!(dce = HeapAlloc( GetProcessHeap(), 0, sizeof(*dce) ))) return;
292     if (!(dce->hdc = CreateDCW( displayW, NULL, NULL, NULL )))
293     {
294         HeapFree( GetProcessHeap(), 0, dce );
295         return;
296     }
297
298     /* store DCE handle in DC hook data field */
299
300     SetDCHook( dce->hdc, dc_hook, (DWORD_PTR)dce );
301
302     dce->hwnd      = data->hwnd;
303     dce->clip_rgn  = 0;
304     dce->flags     = 0;
305     dce->class_ptr = class_ptr;
306     dce->count     = 1;
307
308     if (style & CS_OWNDC)
309     {
310         LONG win_style = GetWindowLongW( data->hwnd, GWL_STYLE );
311         if (win_style & WS_CLIPCHILDREN) dce->flags |= DCX_CLIPCHILDREN;
312         if (win_style & WS_CLIPSIBLINGS) dce->flags |= DCX_CLIPSIBLINGS;
313     }
314     SetHookFlags16( HDC_16(dce->hdc), DCHF_INVALIDATEVISRGN );
315
316     EnterCriticalSection( &dce_section );
317     list_add_tail( &dce_list, &dce->entry );
318     LeaveCriticalSection( &dce_section );
319     data->dce = dce;
320
321     escape.code = X11DRV_SET_DCE;
322     escape.dce  = dce;
323     ExtEscape( dce->hdc, X11DRV_ESCAPE, sizeof(escape), (LPCSTR)&escape, 0, NULL );
324 }
325
326
327 /***********************************************************************
328  *           free_window_dce
329  *
330  * Free a class or window DCE.
331  */
332 void free_window_dce( struct x11drv_win_data *data )
333 {
334     struct dce *dce = data->dce;
335
336     if (dce)
337     {
338         EnterCriticalSection( &dce_section );
339         if (!--dce->count)
340         {
341             list_remove( &dce->entry );
342             SetDCHook(dce->hdc, NULL, 0L);
343             DeleteDC( dce->hdc );
344             if (dce->clip_rgn) DeleteObject( dce->clip_rgn );
345             HeapFree( GetProcessHeap(), 0, dce );
346         }
347         else if (dce->hwnd == data->hwnd)
348         {
349             release_dce( dce );
350         }
351         LeaveCriticalSection( &dce_section );
352         data->dce = NULL;
353     }
354
355     /* now check for cache DCEs */
356
357     EnterCriticalSection( &dce_section );
358     LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
359     {
360         if (dce->hwnd != data->hwnd) continue;
361         if (!(dce->flags & DCX_CACHE)) continue;
362
363         if (dce->count) WARN( "GetDC() without ReleaseDC() for window %p\n", data->hwnd );
364         release_dce( dce );
365         dce->count = 0;
366     }
367     LeaveCriticalSection( &dce_section );
368 }
369
370
371 /***********************************************************************
372  *   invalidate_dce
373  *
374  * It is called from SetWindowPos() - we have to
375  * mark as dirty all busy DCEs for windows that have pWnd->parent as
376  * an ancestor and whose client rect intersects with specified update
377  * rectangle. In addition, pWnd->parent DCEs may need to be updated if
378  * DCX_CLIPCHILDREN flag is set.
379  */
380 void invalidate_dce( HWND hwnd, const RECT *rect )
381 {
382     HWND hwndScope = GetAncestor( hwnd, GA_PARENT );
383
384     if( hwndScope )
385     {
386         struct dce *dce;
387
388         TRACE("scope hwnd = %p %s\n", hwndScope, wine_dbgstr_rect(rect) );
389         if (TRACE_ON(dc)) dump_cache();
390
391         /* walk all DCEs and fixup non-empty entries */
392
393         LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
394         {
395             if (!dce->hwnd) continue;
396             if ((dce->hwnd == hwndScope) && !(dce->flags & DCX_CLIPCHILDREN))
397                 continue;  /* child window positions don't bother us */
398
399             /* check if DCE window is within the z-order scope */
400
401             if (hwndScope == dce->hwnd || hwndScope == GetDesktopWindow() || IsChild( hwndScope, dce->hwnd ))
402             {
403                 if (hwnd != dce->hwnd)
404                 {
405                     /* check if the window rectangle intersects this DCE window */
406                     RECT tmp;
407                     GetWindowRect( dce->hwnd, &tmp );
408                     MapWindowPoints( 0, hwndScope, (POINT *)&tmp, 2 );
409                     if (!IntersectRect( &tmp, &tmp, rect )) continue;
410
411                 }
412                 if (!dce->count)
413                 {
414                     /* Don't bother with visible regions of unused DCEs */
415
416                     TRACE("\tpurged %p dce [%p]\n", dce, dce->hwnd);
417                     release_dce( dce );
418                 }
419                 else
420                 {
421                     /* Set dirty bits in the hDC and DCE structs */
422
423                     TRACE("\tfixed up %p dce [%p]\n", dce, dce->hwnd);
424                     SetHookFlags16( HDC_16(dce->hdc), DCHF_INVALIDATEVISRGN );
425                 }
426             }
427         } /* dce list */
428     }
429 }
430
431
432 /***********************************************************************
433  *              X11DRV_GetDCEx   (X11DRV.@)
434  *
435  * Unimplemented flags: DCX_LOCKWINDOWUPDATE
436  */
437 HDC X11DRV_GetDCEx( HWND hwnd, HRGN hrgnClip, DWORD flags )
438 {
439     static const DWORD clip_flags = DCX_PARENTCLIP | DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_WINDOW;
440
441     struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
442     struct dce *dce;
443     BOOL bUpdateVisRgn = TRUE;
444     HWND parent;
445     LONG window_style = GetWindowLongW( hwnd, GWL_STYLE );
446
447     TRACE("hwnd %p, hrgnClip %p, flags %08x\n", hwnd, hrgnClip, flags);
448
449     /* fixup flags */
450
451     if (flags & (DCX_WINDOW | DCX_PARENTCLIP)) flags |= DCX_CACHE;
452
453     if (flags & DCX_USESTYLE)
454     {
455         flags &= ~(DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS | DCX_PARENTCLIP);
456
457         if (window_style & WS_CLIPSIBLINGS) flags |= DCX_CLIPSIBLINGS;
458
459         if (!(flags & DCX_WINDOW))
460         {
461             if (GetClassLongW( hwnd, GCL_STYLE ) & CS_PARENTDC) flags |= DCX_PARENTCLIP;
462
463             if (window_style & WS_CLIPCHILDREN && !(window_style & WS_MINIMIZE))
464                 flags |= DCX_CLIPCHILDREN;
465             if (!data || !data->dce) flags |= DCX_CACHE;
466         }
467     }
468
469     if (flags & DCX_WINDOW) flags &= ~DCX_CLIPCHILDREN;
470
471     parent = GetAncestor( hwnd, GA_PARENT );
472     if (!parent || (parent == GetDesktopWindow()))
473         flags = (flags & ~DCX_PARENTCLIP) | DCX_CLIPSIBLINGS;
474
475     /* it seems parent clip is ignored when clipping siblings or children */
476     if (flags & (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN)) flags &= ~DCX_PARENTCLIP;
477
478     if( flags & DCX_PARENTCLIP )
479     {
480         LONG parent_style = GetWindowLongW( parent, GWL_STYLE );
481         if( (window_style & WS_VISIBLE) && (parent_style & WS_VISIBLE) )
482         {
483             flags &= ~DCX_CLIPCHILDREN;
484             if (parent_style & WS_CLIPSIBLINGS) flags |= DCX_CLIPSIBLINGS;
485         }
486     }
487
488     /* find a suitable DCE */
489
490     if (flags & DCX_CACHE)
491     {
492         struct dce *dceEmpty = NULL, *dceUnused = NULL;
493
494         /* Strategy: First, we attempt to find a non-empty but unused DCE with
495          * compatible flags. Next, we look for an empty entry. If the cache is
496          * full we have to purge one of the unused entries.
497          */
498         EnterCriticalSection( &dce_section );
499         LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
500         {
501             if ((dce->flags & DCX_CACHE) && !dce->count)
502             {
503                 dceUnused = dce;
504
505                 if (!dce->hwnd) dceEmpty = dce;
506                 else if ((dce->hwnd == hwnd) && !((dce->flags ^ flags) & clip_flags))
507                 {
508                     TRACE("\tfound valid %p dce [%p], flags %08x\n",
509                           dce, hwnd, dce->flags );
510                     bUpdateVisRgn = FALSE;
511                     break;
512                 }
513             }
514         }
515
516         if (&dce->entry == &dce_list)  /* nothing found */
517             dce = dceEmpty ? dceEmpty : dceUnused;
518
519         if (dce) dce->count = 1;
520
521         LeaveCriticalSection( &dce_section );
522
523         /* if there's no dce empty or unused, allocate a new one */
524         if (!dce) dce = alloc_cache_dce();
525         if (!dce) return 0;
526     }
527     else
528     {
529         flags |= DCX_NORESETATTRS;
530         dce = data->dce;
531         if (dce->hwnd == hwnd)
532         {
533             TRACE("\tskipping hVisRgn update\n");
534             bUpdateVisRgn = FALSE; /* updated automatically, via DCHook() */
535         }
536         else
537         {
538             /* we should free dce->clip_rgn here, but Windows apparently doesn't */
539             dce->flags &= ~(DCX_EXCLUDERGN | DCX_INTERSECTRGN);
540             dce->clip_rgn = 0;
541         }
542     }
543
544     if (flags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN))
545     {
546         /* if the extra clip region has changed, get rid of the old one */
547         if (dce->clip_rgn != hrgnClip || ((flags ^ dce->flags) & (DCX_INTERSECTRGN | DCX_EXCLUDERGN)))
548             delete_clip_rgn( dce );
549         dce->clip_rgn = hrgnClip;
550         if (!dce->clip_rgn) dce->clip_rgn = CreateRectRgn( 0, 0, 0, 0 );
551         dce->flags |= flags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN);
552         bUpdateVisRgn = TRUE;
553     }
554
555     dce->hwnd = hwnd;
556     dce->flags = (dce->flags & ~clip_flags) | (flags & clip_flags);
557
558     if (SetHookFlags16( HDC_16(dce->hdc), DCHF_VALIDATEVISRGN ))
559         bUpdateVisRgn = TRUE;  /* DC was dirty */
560
561     if (bUpdateVisRgn) update_visible_region( dce );
562
563     if (!(flags & DCX_NORESETATTRS))
564     {
565         RestoreDC( dce->hdc, 1 );  /* initial save level is always 1 */
566         SaveDC( dce->hdc );  /* save the state again for next time */
567     }
568
569     TRACE("(%p,%p,0x%x): returning %p\n", hwnd, hrgnClip, flags, dce->hdc);
570     return dce->hdc;
571 }
572
573
574 /***********************************************************************
575  *              X11DRV_ReleaseDC  (X11DRV.@)
576  */
577 INT X11DRV_ReleaseDC( HWND hwnd, HDC hdc, BOOL end_paint )
578 {
579     enum x11drv_escape_codes escape = X11DRV_GET_DCE;
580     struct dce *dce;
581     BOOL ret = FALSE;
582
583     TRACE("%p %p\n", hwnd, hdc );
584
585     EnterCriticalSection( &dce_section );
586     if (!ExtEscape( hdc, X11DRV_ESCAPE, sizeof(escape), (LPCSTR)&escape,
587                     sizeof(dce), (LPSTR)&dce )) dce = NULL;
588     if (dce && dce->count)
589     {
590         if (end_paint || (dce->flags & DCX_CACHE)) delete_clip_rgn( dce );
591         if (dce->flags & DCX_CACHE) dce->count = 0;
592         ret = TRUE;
593     }
594     LeaveCriticalSection( &dce_section );
595     return ret;
596 }
597
598 /***********************************************************************
599  *              dc_hook
600  *
601  * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..
602  */
603 static BOOL CALLBACK dc_hook( HDC hDC, WORD code, DWORD_PTR data, LPARAM lParam )
604 {
605     BOOL retv = TRUE;
606     struct dce *dce = (struct dce *)data;
607
608     TRACE("hDC = %p, %u\n", hDC, code);
609
610     if (!dce) return 0;
611     assert( dce->hdc == hDC );
612
613     switch( code )
614     {
615     case DCHC_INVALIDVISRGN:
616         /* GDI code calls this when it detects that the
617          * DC is dirty (usually after SetHookFlags()). This
618          * means that we have to recompute the visible region.
619          */
620         if (dce->count) update_visible_region( dce );
621         else /* non-fatal but shouldn't happen */
622             WARN("DC is not in use!\n");
623         break;
624     case DCHC_DELETEDC:
625         /*
626          * Windows will not let you delete a DC that is busy
627          * (between GetDC and ReleaseDC)
628          */
629         if (dce->count)
630         {
631             WARN("Application trying to delete a busy DC %p\n", dce->hdc);
632             retv = FALSE;
633         }
634         else
635         {
636             EnterCriticalSection( &dce_section );
637             list_remove( &dce->entry );
638             LeaveCriticalSection( &dce_section );
639             if (dce->clip_rgn) DeleteObject( dce->clip_rgn );
640             HeapFree( GetProcessHeap(), 0, dce );
641         }
642         break;
643     }
644     return retv;
645 }
646
647
648 /**********************************************************************
649  *              WindowFromDC   (X11DRV.@)
650  */
651 HWND X11DRV_WindowFromDC( HDC hdc )
652 {
653     enum x11drv_escape_codes escape = X11DRV_GET_DCE;
654     struct dce *dce;
655     HWND hwnd = 0;
656
657     EnterCriticalSection( &dce_section );
658     if (!ExtEscape( hdc, X11DRV_ESCAPE, sizeof(escape), (LPCSTR)&escape,
659                     sizeof(dce), (LPSTR)&dce )) dce = NULL;
660     if (dce) hwnd = dce->hwnd;
661     LeaveCriticalSection( &dce_section );
662     return hwnd;
663 }