Fixed bitmap range checking to avoid integer overflows.
[wine] / windows / dce.c
1 /*
2  * USER DCE functions
3  *
4  * Copyright 1993 Alexandre Julliard
5  *           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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  *
22  * Note: Visible regions of CS_OWNDC/CS_CLASSDC window DCs
23  * have to be updated dynamically.
24  *
25  * Internal DCX flags:
26  *
27  * DCX_DCEEMPTY    - dce is uninitialized
28  * DCX_DCEBUSY     - dce is in use
29  * DCX_DCEDIRTY    - ReleaseDC() should wipe instead of caching
30  * DCX_KEEPCLIPRGN - ReleaseDC() should not delete the clipping region
31  * DCX_WINDOWPAINT - BeginPaint() is in effect
32  */
33
34 #include <assert.h>
35 #include "dce.h"
36 #include "win.h"
37 #include "gdi.h"
38 #include "user.h"
39 #include "wine/debug.h"
40 #include "windef.h"
41 #include "wingdi.h"
42 #include "wownt32.h"
43 #include "wine/winbase16.h"
44 #include "wine/winuser16.h"
45
46 WINE_DEFAULT_DEBUG_CHANNEL(dc);
47
48 static DCE *firstDCE;
49 static HDC16 defaultDCstate;
50
51 static void DCE_DeleteClipRgn( DCE* );
52 static INT DCE_ReleaseDC( DCE* );
53
54
55 /***********************************************************************
56  *           DCE_DumpCache
57  */
58 static void DCE_DumpCache(void)
59 {
60     DCE *dce;
61
62     USER_Lock();
63     dce = firstDCE;
64
65     DPRINTF("DCE:\n");
66     while( dce )
67     {
68         DPRINTF("\t[0x%08x] hWnd %p, dcx %08x, %s %s\n",
69              (unsigned)dce, dce->hwndCurrent, (unsigned)dce->DCXflags,
70              (dce->DCXflags & DCX_CACHE) ? "Cache" : "Owned",
71              (dce->DCXflags & DCX_DCEBUSY) ? "InUse" : "" );
72         dce = dce->next;
73     }
74
75     USER_Unlock();
76 }
77
78 /***********************************************************************
79  *           DCE_AllocDCE
80  *
81  * Allocate a new DCE.
82  */
83 DCE *DCE_AllocDCE( HWND hWnd, DCE_TYPE type )
84 {
85     DCE * dce;
86
87     if (!(dce = HeapAlloc( GetProcessHeap(), 0, sizeof(DCE) ))) return NULL;
88     if (!(dce->hDC = CreateDCA( "DISPLAY", NULL, NULL, NULL )))
89     {
90         HeapFree( GetProcessHeap(), 0, dce );
91         return 0;
92     }
93     if (!defaultDCstate) defaultDCstate = GetDCState16( HDC_16(dce->hDC) );
94
95     /* store DCE handle in DC hook data field */
96
97     SetDCHook( dce->hDC, DCHook16, (DWORD)dce );
98
99     dce->hwndCurrent = WIN_GetFullHandle( hWnd );
100     dce->hClipRgn    = 0;
101
102     if( type != DCE_CACHE_DC ) /* owned or class DC */
103     {
104         dce->DCXflags = DCX_DCEBUSY;
105         if( hWnd )
106         {
107             LONG style = GetWindowLongW( hWnd, GWL_STYLE );
108             if (style & WS_CLIPCHILDREN) dce->DCXflags |= DCX_CLIPCHILDREN;
109             if (style & WS_CLIPSIBLINGS) dce->DCXflags |= DCX_CLIPSIBLINGS;
110         }
111         SetHookFlags16( HDC_16(dce->hDC), DCHF_INVALIDATEVISRGN );
112     }
113     else dce->DCXflags = DCX_CACHE | DCX_DCEEMPTY;
114
115     USER_Lock();
116     dce->next = firstDCE;
117     firstDCE = dce;
118     USER_Unlock();
119     return dce;
120 }
121
122
123 /***********************************************************************
124  *           DCE_FreeDCE
125  */
126 DCE* DCE_FreeDCE( DCE *dce )
127 {
128     DCE **ppDCE, *ret;
129
130     if (!dce) return NULL;
131
132     USER_Lock();
133
134     ppDCE = &firstDCE;
135
136     while (*ppDCE && (*ppDCE != dce)) ppDCE = &(*ppDCE)->next;
137     if (*ppDCE == dce) *ppDCE = dce->next;
138     ret = *ppDCE;
139     USER_Unlock();
140
141     SetDCHook(dce->hDC, NULL, 0L);
142
143     DeleteDC( dce->hDC );
144     if( dce->hClipRgn && !(dce->DCXflags & DCX_KEEPCLIPRGN) )
145         DeleteObject(dce->hClipRgn);
146     HeapFree( GetProcessHeap(), 0, dce );
147
148     return ret;
149 }
150
151 /***********************************************************************
152  *           DCE_FreeWindowDCE
153  *
154  * Remove owned DCE and reset unreleased cache DCEs.
155  */
156 void DCE_FreeWindowDCE( HWND hwnd )
157 {
158     DCE *pDCE;
159     WND *pWnd = WIN_GetPtr( hwnd );
160
161     pDCE = firstDCE;
162     while( pDCE )
163     {
164         if( pDCE->hwndCurrent == hwnd )
165         {
166             if( pDCE == pWnd->dce ) /* owned or Class DCE*/
167             {
168                 if (pWnd->clsStyle & CS_OWNDC)  /* owned DCE*/
169                 {
170                     pDCE = DCE_FreeDCE( pDCE );
171                     pWnd->dce = NULL;
172                     continue;
173                 }
174                 else if( pDCE->DCXflags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN) ) /* Class DCE*/
175                 {
176                     if (USER_Driver.pReleaseDC)
177                         USER_Driver.pReleaseDC( pDCE->hwndCurrent, pDCE->hDC );
178                     DCE_DeleteClipRgn( pDCE );
179                     pDCE->hwndCurrent = 0;
180                 }
181             }
182             else
183             {
184                 if( pDCE->DCXflags & DCX_DCEBUSY ) /* shared cache DCE */
185                 {
186                     /* FIXME: AFAICS we are doing the right thing here so
187                      * this should be a WARN. But this is best left as an ERR
188                      * because the 'application error' is likely to come from
189                      * another part of Wine (i.e. it's our fault after all).
190                      * We should change this to WARN when Wine is more stable
191                      * (for 1.0?).
192                      */
193                     ERR("[%p] GetDC() without ReleaseDC()!\n",hwnd);
194                     DCE_ReleaseDC( pDCE );
195                 }
196
197                 if (pDCE->hwndCurrent && USER_Driver.pReleaseDC)
198                     USER_Driver.pReleaseDC( pDCE->hwndCurrent, pDCE->hDC );
199                 pDCE->DCXflags &= DCX_CACHE;
200                 pDCE->DCXflags |= DCX_DCEEMPTY;
201                 pDCE->hwndCurrent = 0;
202             }
203         }
204         pDCE = pDCE->next;
205     }
206     WIN_ReleasePtr( pWnd );
207 }
208
209
210 /***********************************************************************
211  *   DCE_DeleteClipRgn
212  */
213 static void DCE_DeleteClipRgn( DCE* dce )
214 {
215     dce->DCXflags &= ~(DCX_EXCLUDERGN | DCX_INTERSECTRGN | DCX_WINDOWPAINT);
216
217     if( dce->DCXflags & DCX_KEEPCLIPRGN )
218         dce->DCXflags &= ~DCX_KEEPCLIPRGN;
219     else
220         if( dce->hClipRgn > (HRGN)1 )
221             DeleteObject( dce->hClipRgn );
222
223     dce->hClipRgn = 0;
224
225     /* make it dirty so that the vis rgn gets recomputed next time */
226     dce->DCXflags |= DCX_DCEDIRTY;
227     SetHookFlags16( HDC_16(dce->hDC), DCHF_INVALIDATEVISRGN );
228 }
229
230
231 /***********************************************************************
232  *   DCE_ReleaseDC
233  */
234 static INT DCE_ReleaseDC( DCE* dce )
235 {
236     if ((dce->DCXflags & (DCX_DCEEMPTY | DCX_DCEBUSY)) != DCX_DCEBUSY) return 0;
237
238     /* restore previous visible region */
239
240     if ((dce->DCXflags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN)) &&
241         (dce->DCXflags & (DCX_CACHE | DCX_WINDOWPAINT)) )
242         DCE_DeleteClipRgn( dce );
243
244     if (dce->DCXflags & DCX_CACHE)
245     {
246         /* make the DC clean so that SetDCState doesn't try to update the vis rgn */
247         SetHookFlags16( HDC_16(dce->hDC), DCHF_VALIDATEVISRGN );
248         SetDCState16( HDC_16(dce->hDC), defaultDCstate );
249         dce->DCXflags &= ~DCX_DCEBUSY;
250         if (dce->DCXflags & DCX_DCEDIRTY)
251         {
252             /* don't keep around invalidated entries
253              * because SetDCState() disables hVisRgn updates
254              * by removing dirty bit. */
255             if (dce->hwndCurrent && USER_Driver.pReleaseDC)
256                 USER_Driver.pReleaseDC( dce->hwndCurrent, dce->hDC );
257             dce->hwndCurrent = 0;
258             dce->DCXflags &= DCX_CACHE;
259             dce->DCXflags |= DCX_DCEEMPTY;
260         }
261     }
262     return 1;
263 }
264
265
266 /***********************************************************************
267  *   DCE_InvalidateDCE
268  *
269  * It is called from SetWindowPos() and EVENT_MapNotify - we have to
270  * mark as dirty all busy DCEs for windows that have pWnd->parent as
271  * an ancestor and whose client rect intersects with specified update
272  * rectangle. In addition, pWnd->parent DCEs may need to be updated if
273  * DCX_CLIPCHILDREN flag is set.  */
274 BOOL DCE_InvalidateDCE(HWND hwnd, const RECT* pRectUpdate)
275 {
276     HWND hwndScope = GetAncestor( hwnd, GA_PARENT );
277     BOOL bRet = FALSE;
278
279     if( hwndScope )
280     {
281         DCE *dce;
282
283         TRACE("scope hwnd = %p, (%ld,%ld - %ld,%ld)\n",
284               hwndScope, pRectUpdate->left,pRectUpdate->top,
285               pRectUpdate->right,pRectUpdate->bottom);
286         if(TRACE_ON(dc))
287           DCE_DumpCache();
288
289         /* walk all DCEs and fixup non-empty entries */
290
291         for (dce = firstDCE; (dce); dce = dce->next)
292         {
293             if (dce->DCXflags & DCX_DCEEMPTY) continue;
294             if ((dce->hwndCurrent == hwndScope) && !(dce->DCXflags & DCX_CLIPCHILDREN))
295                 continue;  /* child window positions don't bother us */
296
297             /* check if DCE window is within the z-order scope */
298
299             if (hwndScope == dce->hwndCurrent || IsChild( hwndScope, dce->hwndCurrent ))
300             {
301                 if (hwnd != dce->hwndCurrent)
302                 {
303                     /* check if the window rectangle intersects this DCE window */
304                     RECT rect;
305                     GetWindowRect( dce->hwndCurrent, &rect );
306                     MapWindowPoints( 0, hwndScope, (POINT *)&rect, 2 );
307                     if (!IntersectRect( &rect, &rect, pRectUpdate )) continue;
308
309                 }
310                 if( !(dce->DCXflags & DCX_DCEBUSY) )
311                 {
312                     /* Don't bother with visible regions of unused DCEs */
313
314                     TRACE("\tpurged %p dce [%p]\n", dce, dce->hwndCurrent);
315                     if (dce->hwndCurrent && USER_Driver.pReleaseDC)
316                         USER_Driver.pReleaseDC( dce->hwndCurrent, dce->hDC );
317                     dce->hwndCurrent = 0;
318                     dce->DCXflags &= DCX_CACHE;
319                     dce->DCXflags |= DCX_DCEEMPTY;
320                 }
321                 else
322                 {
323                     /* Set dirty bits in the hDC and DCE structs */
324
325                     TRACE("\tfixed up %p dce [%p]\n", dce, dce->hwndCurrent);
326                     dce->DCXflags |= DCX_DCEDIRTY;
327                     SetHookFlags16( HDC_16(dce->hDC), DCHF_INVALIDATEVISRGN );
328                     bRet = TRUE;
329                 }
330             }
331         } /* dce list */
332     }
333     return bRet;
334 }
335
336
337 /***********************************************************************
338  *           DCE_ExcludeRgn
339  *
340  *  Translate given region from the wnd client to the DC coordinates
341  *  and add it to the clipping region.
342  */
343 INT DCE_ExcludeRgn( HDC hDC, HWND hwnd, HRGN hRgn )
344 {
345   POINT  pt = {0, 0};
346   DCE     *dce = firstDCE;
347
348   while (dce && (dce->hDC != hDC)) dce = dce->next;
349   if (!dce) return ERROR;
350
351   MapWindowPoints( hwnd, dce->hwndCurrent, &pt, 1);
352   if( dce->DCXflags & DCX_WINDOW )
353   {
354       WND *wnd = WIN_FindWndPtr(dce->hwndCurrent);
355       pt.x += wnd->rectClient.left - wnd->rectWindow.left;
356       pt.y += wnd->rectClient.top - wnd->rectWindow.top;
357       WIN_ReleaseWndPtr(wnd);
358   }
359   OffsetRgn(hRgn, pt.x, pt.y);
360
361   return ExtSelectClipRgn( hDC, hRgn, RGN_DIFF );
362 }
363
364
365 /***********************************************************************
366  *              GetDCEx (USER32.@)
367  *
368  * Unimplemented flags: DCX_LOCKWINDOWUPDATE
369  *
370  * FIXME: Full support for hrgnClip == 1 (alias for entire window).
371  */
372 HDC WINAPI GetDCEx( HWND hwnd, HRGN hrgnClip, DWORD flags )
373 {
374     HDC         hdc = 0;
375     DCE *       dce;
376     WND *       wndPtr;
377     DWORD       dcxFlags = 0;
378     BOOL        bUpdateVisRgn = TRUE;
379     BOOL        bUpdateClipOrigin = FALSE;
380     HWND parent, full;
381
382     TRACE("hwnd %p, hrgnClip %p, flags %08lx\n", hwnd, hrgnClip, flags);
383
384     if (!hwnd) hwnd = GetDesktopWindow();
385     if (!(full = WIN_IsCurrentProcess( hwnd )))
386     {
387         FIXME( "not supported yet on other process window %p\n", hwnd );
388         return 0;
389     }
390     hwnd = full;
391     if (!(wndPtr = WIN_GetPtr( hwnd ))) return 0;
392
393     /* fixup flags */
394
395     if (flags & (DCX_WINDOW | DCX_PARENTCLIP)) flags |= DCX_CACHE;
396
397     if (flags & DCX_USESTYLE)
398     {
399         flags &= ~( DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS | DCX_PARENTCLIP);
400
401         if( wndPtr->dwStyle & WS_CLIPSIBLINGS )
402             flags |= DCX_CLIPSIBLINGS;
403
404         if ( !(flags & DCX_WINDOW) )
405         {
406             if (wndPtr->clsStyle & CS_PARENTDC) flags |= DCX_PARENTCLIP;
407
408             if (wndPtr->dwStyle & WS_CLIPCHILDREN &&
409                      !(wndPtr->dwStyle & WS_MINIMIZE) ) flags |= DCX_CLIPCHILDREN;
410             if (!wndPtr->dce) flags |= DCX_CACHE;
411         }
412     }
413
414     if (flags & DCX_WINDOW) flags &= ~DCX_CLIPCHILDREN;
415
416     parent = GetAncestor( hwnd, GA_PARENT );
417     if (!parent || (parent == GetDesktopWindow()))
418         flags = (flags & ~DCX_PARENTCLIP) | DCX_CLIPSIBLINGS;
419
420     /* it seems parent clip is ignored when clipping siblings or children */
421     if (flags & (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN)) flags &= ~DCX_PARENTCLIP;
422
423     if( flags & DCX_PARENTCLIP )
424     {
425         LONG parent_style = GetWindowLongW( parent, GWL_STYLE );
426         if( (wndPtr->dwStyle & WS_VISIBLE) && (parent_style & WS_VISIBLE) )
427         {
428             flags &= ~DCX_CLIPCHILDREN;
429             if (parent_style & WS_CLIPSIBLINGS) flags |= DCX_CLIPSIBLINGS;
430         }
431     }
432
433     /* find a suitable DCE */
434
435     dcxFlags = flags & (DCX_PARENTCLIP | DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN |
436                         DCX_CACHE | DCX_WINDOW);
437
438     if (flags & DCX_CACHE)
439     {
440         DCE*    dceEmpty;
441         DCE*    dceUnused;
442
443         dceEmpty = dceUnused = NULL;
444
445         /* Strategy: First, we attempt to find a non-empty but unused DCE with
446          * compatible flags. Next, we look for an empty entry. If the cache is
447          * full we have to purge one of the unused entries.
448          */
449
450         for (dce = firstDCE; (dce); dce = dce->next)
451         {
452             if ((dce->DCXflags & (DCX_CACHE | DCX_DCEBUSY)) == DCX_CACHE )
453             {
454                 dceUnused = dce;
455
456                 if (dce->DCXflags & DCX_DCEEMPTY)
457                     dceEmpty = dce;
458                 else
459                 if ((dce->hwndCurrent == hwnd) &&
460                    ((dce->DCXflags & (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN |
461                                       DCX_CACHE | DCX_WINDOW | DCX_PARENTCLIP)) == dcxFlags))
462                 {
463                     TRACE("\tfound valid %p dce [%p], flags %08lx\n",
464                           dce, hwnd, dcxFlags );
465                     bUpdateVisRgn = FALSE;
466                     bUpdateClipOrigin = TRUE;
467                     break;
468                 }
469             }
470         }
471
472         if (!dce) dce = (dceEmpty) ? dceEmpty : dceUnused;
473
474         /* if there's no dce empty or unused, allocate a new one */
475         if (!dce)
476         {
477             dce = DCE_AllocDCE( 0, DCE_CACHE_DC );
478         }
479     }
480     else
481     {
482         dce = wndPtr->dce;
483         if (dce && dce->hwndCurrent == hwnd)
484         {
485             TRACE("\tskipping hVisRgn update\n");
486             bUpdateVisRgn = FALSE; /* updated automatically, via DCHook() */
487         }
488     }
489     if (!dce)
490     {
491         hdc = 0;
492         goto END;
493     }
494
495     if (!(flags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN))) hrgnClip = 0;
496
497     if (((flags ^ dce->DCXflags) & (DCX_INTERSECTRGN | DCX_EXCLUDERGN)) &&
498         (dce->hClipRgn != hrgnClip))
499     {
500         /* if the extra clip region has changed, get rid of the old one */
501         DCE_DeleteClipRgn( dce );
502     }
503
504     dce->hwndCurrent = hwnd;
505     dce->hClipRgn = hrgnClip;
506     dce->DCXflags = flags & (DCX_PARENTCLIP | DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN |
507                              DCX_CACHE | DCX_WINDOW | DCX_WINDOWPAINT |
508                              DCX_KEEPCLIPRGN | DCX_INTERSECTRGN | DCX_EXCLUDERGN);
509     dce->DCXflags |= DCX_DCEBUSY;
510     dce->DCXflags &= ~DCX_DCEDIRTY;
511     hdc = dce->hDC;
512
513     if (bUpdateVisRgn) SetHookFlags16( HDC_16(hdc), DCHF_INVALIDATEVISRGN ); /* force update */
514
515     if (!USER_Driver.pGetDC( hwnd, hdc, hrgnClip, flags )) hdc = 0;
516
517     TRACE("(%p,%p,0x%lx): returning %p\n", hwnd, hrgnClip, flags, hdc);
518 END:
519     WIN_ReleasePtr(wndPtr);
520     return hdc;
521 }
522
523
524 /***********************************************************************
525  *              GetDC (USER32.@)
526  *
527  * Get a device context.
528  *
529  * RETURNS
530  *      Success: Handle to the device context
531  *      Failure: NULL.
532  */
533 HDC WINAPI GetDC(
534              HWND hwnd /* [in] handle of window - may be NULL */
535 ) {
536     if (!hwnd)
537         return GetDCEx( 0, 0, DCX_CACHE | DCX_WINDOW );
538     return GetDCEx( hwnd, 0, DCX_USESTYLE );
539 }
540
541
542 /***********************************************************************
543  *              GetWindowDC (USER32.@)
544  */
545 HDC WINAPI GetWindowDC( HWND hwnd )
546 {
547     return GetDCEx( hwnd, 0, DCX_USESTYLE | DCX_WINDOW );
548 }
549
550
551 /***********************************************************************
552  *              ReleaseDC (USER32.@)
553  *
554  * Release a device context.
555  *
556  * RETURNS
557  *      Success: Non-zero. Resources used by hdc are released.
558  *      Failure: 0.
559  */
560 INT WINAPI ReleaseDC(
561              HWND hwnd, /* [in] Handle of window - ignored */
562              HDC hdc   /* [in] Handle of device context */
563 ) {
564     DCE * dce;
565     INT nRet = 0;
566
567     USER_Lock();
568     dce = firstDCE;
569
570     TRACE("%p %p\n", hwnd, hdc );
571
572     while (dce && (dce->hDC != hdc)) dce = dce->next;
573
574     if ( dce )
575         if ( dce->DCXflags & DCX_DCEBUSY )
576             nRet = DCE_ReleaseDC( dce );
577
578     USER_Unlock();
579
580     return nRet;
581 }
582
583 /***********************************************************************
584  *              DCHook (USER.362)
585  *
586  * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..
587  */
588 BOOL16 WINAPI DCHook16( HDC16 hDC, WORD code, DWORD data, LPARAM lParam )
589 {
590     BOOL retv = TRUE;
591     DCE *dce = (DCE *)data;
592
593     TRACE("hDC = %04x, %i\n", hDC, code);
594
595     if (!dce) return 0;
596     assert( HDC_16(dce->hDC) == hDC );
597
598     /* Grab the windows lock before doing anything else  */
599     USER_Lock();
600
601     switch( code )
602     {
603       case DCHC_INVALIDVISRGN:
604            /* GDI code calls this when it detects that the
605             * DC is dirty (usually after SetHookFlags()). This
606             * means that we have to recompute the visible region.
607             */
608            if( dce->DCXflags & DCX_DCEBUSY )
609            {
610                /* Dirty bit has been cleared by caller, set it again so that
611                 * pGetDC recomputes the visible region. */
612                SetHookFlags16( hDC, DCHF_INVALIDATEVISRGN );
613                USER_Driver.pGetDC( dce->hwndCurrent, dce->hDC, dce->hClipRgn, dce->DCXflags );
614            }
615            else /* non-fatal but shouldn't happen */
616              WARN("DC is not in use!\n");
617            break;
618
619       case DCHC_DELETEDC:
620            /*
621             * Windows will not let you delete a DC that is busy
622             * (between GetDC and ReleaseDC)
623             */
624
625            if ( dce->DCXflags & DCX_DCEBUSY )
626            {
627                WARN("Application trying to delete a busy DC\n");
628                retv = FALSE;
629            }
630            else DCE_FreeDCE( dce );
631            break;
632
633       default:
634            FIXME("unknown code\n");
635     }
636
637   USER_Unlock();  /* Release the wnd lock */
638   return retv;
639 }
640
641
642 /**********************************************************************
643  *              WindowFromDC (USER32.@)
644  */
645 HWND WINAPI WindowFromDC( HDC hDC )
646 {
647     DCE *dce;
648     HWND hwnd;
649
650     USER_Lock();
651     dce = firstDCE;
652
653     while (dce && (dce->hDC != hDC)) dce = dce->next;
654
655     hwnd = dce ? dce->hwndCurrent : 0;
656     USER_Unlock();
657
658     return hwnd;
659 }
660
661
662 /***********************************************************************
663  *              LockWindowUpdate (USER32.@)
664  */
665 BOOL WINAPI LockWindowUpdate( HWND hwnd )
666 {
667     static HWND lockedWnd;
668
669     FIXME("(%p), partial stub!\n",hwnd);
670
671     USER_Lock();
672     if (lockedWnd)
673     {
674         if (!hwnd)
675         {
676             /* Unlock lockedWnd */
677             /* FIXME: Do something */
678         }
679         else
680         {
681             /* Attempted to lock a second window */
682             /* Return FALSE and do nothing */
683             USER_Unlock();
684             return FALSE;
685         }
686     }
687     lockedWnd = hwnd;
688     USER_Unlock();
689     return TRUE;
690 }