mshtml: Store information if script was already parsed in script element object.
[wine] / dlls / user32 / painting.c
1 /*
2  * Window painting functions
3  *
4  * Copyright 1993, 1994, 1995, 2001, 2004, 2005, 2008 Alexandre Julliard
5  * Copyright 1996, 1997, 1999 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 #include "wine/port.h"
24
25 #include <assert.h>
26 #include <stdarg.h>
27 #include <string.h>
28
29 #include "ntstatus.h"
30 #define WIN32_NO_STATUS
31 #include "windef.h"
32 #include "winbase.h"
33 #include "wingdi.h"
34 #include "winuser.h"
35 #include "wine/server.h"
36 #include "win.h"
37 #include "user_private.h"
38 #include "controls.h"
39 #include "wine/gdi_driver.h"
40 #include "wine/list.h"
41 #include "wine/debug.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(win);
44
45
46 struct dce
47 {
48     struct list entry;         /* entry in global DCE list */
49     HDC         hdc;
50     HWND        hwnd;
51     HRGN        clip_rgn;
52     DWORD       flags;
53     LONG        count;         /* usage count; 0 or 1 for cache DCEs, always 1 for window DCEs,
54                                   always >= 1 for class DCEs */
55 };
56
57 static struct list dce_list = LIST_INIT(dce_list);
58
59 static BOOL CALLBACK dc_hook( HDC hDC, WORD code, DWORD_PTR data, LPARAM lParam );
60
61 static const WCHAR displayW[] = { 'D','I','S','P','L','A','Y',0 };
62
63
64 /***********************************************************************
65  *           dump_rdw_flags
66  */
67 static void dump_rdw_flags(UINT flags)
68 {
69     TRACE("flags:");
70     if (flags & RDW_INVALIDATE) TRACE(" RDW_INVALIDATE");
71     if (flags & RDW_INTERNALPAINT) TRACE(" RDW_INTERNALPAINT");
72     if (flags & RDW_ERASE) TRACE(" RDW_ERASE");
73     if (flags & RDW_VALIDATE) TRACE(" RDW_VALIDATE");
74     if (flags & RDW_NOINTERNALPAINT) TRACE(" RDW_NOINTERNALPAINT");
75     if (flags & RDW_NOERASE) TRACE(" RDW_NOERASE");
76     if (flags & RDW_NOCHILDREN) TRACE(" RDW_NOCHILDREN");
77     if (flags & RDW_ALLCHILDREN) TRACE(" RDW_ALLCHILDREN");
78     if (flags & RDW_UPDATENOW) TRACE(" RDW_UPDATENOW");
79     if (flags & RDW_ERASENOW) TRACE(" RDW_ERASENOW");
80     if (flags & RDW_FRAME) TRACE(" RDW_FRAME");
81     if (flags & RDW_NOFRAME) TRACE(" RDW_NOFRAME");
82
83 #define RDW_FLAGS \
84     (RDW_INVALIDATE | \
85     RDW_INTERNALPAINT | \
86     RDW_ERASE | \
87     RDW_VALIDATE | \
88     RDW_NOINTERNALPAINT | \
89     RDW_NOERASE | \
90     RDW_NOCHILDREN | \
91     RDW_ALLCHILDREN | \
92     RDW_UPDATENOW | \
93     RDW_ERASENOW | \
94     RDW_FRAME | \
95     RDW_NOFRAME)
96
97     if (flags & ~RDW_FLAGS) TRACE(" %04x", flags & ~RDW_FLAGS);
98     TRACE("\n");
99 #undef RDW_FLAGS
100 }
101
102
103 /***********************************************************************
104  *              update_visible_region
105  *
106  * Set the visible region and X11 drawable for the DC associated to
107  * a given window.
108  */
109 static void update_visible_region( struct dce *dce )
110 {
111     struct window_surface *surface = NULL;
112     NTSTATUS status;
113     HRGN vis_rgn = 0;
114     HWND top_win = 0;
115     DWORD flags = dce->flags;
116     size_t size = 256;
117     RECT win_rect, top_rect;
118     WND *win;
119
120     /* don't clip siblings if using parent clip region */
121     if (flags & DCX_PARENTCLIP) flags &= ~DCX_CLIPSIBLINGS;
122
123     /* fetch the visible region from the server */
124     do
125     {
126         RGNDATA *data = HeapAlloc( GetProcessHeap(), 0, sizeof(*data) + size - 1 );
127         if (!data) return;
128
129         SERVER_START_REQ( get_visible_region )
130         {
131             req->window  = wine_server_user_handle( dce->hwnd );
132             req->flags   = flags;
133             wine_server_set_reply( req, data->Buffer, size );
134             if (!(status = wine_server_call( req )))
135             {
136                 size_t reply_size = wine_server_reply_size( reply );
137                 data->rdh.dwSize   = sizeof(data->rdh);
138                 data->rdh.iType    = RDH_RECTANGLES;
139                 data->rdh.nCount   = reply_size / sizeof(RECT);
140                 data->rdh.nRgnSize = reply_size;
141                 vis_rgn = ExtCreateRegion( NULL, size, data );
142
143                 top_win         = wine_server_ptr_handle( reply->top_win );
144                 win_rect.left   = reply->win_rect.left;
145                 win_rect.top    = reply->win_rect.top;
146                 win_rect.right  = reply->win_rect.right;
147                 win_rect.bottom = reply->win_rect.bottom;
148                 top_rect.left   = reply->top_rect.left;
149                 top_rect.top    = reply->top_rect.top;
150                 top_rect.right  = reply->top_rect.right;
151                 top_rect.bottom = reply->top_rect.bottom;
152             }
153             else size = reply->total_size;
154         }
155         SERVER_END_REQ;
156         HeapFree( GetProcessHeap(), 0, data );
157     } while (status == STATUS_BUFFER_OVERFLOW);
158
159     if (status || !vis_rgn) return;
160
161     USER_Driver->pGetDC( dce->hdc, dce->hwnd, top_win, &win_rect, &top_rect, flags );
162
163     if (dce->clip_rgn) CombineRgn( vis_rgn, vis_rgn, dce->clip_rgn,
164                                    (flags & DCX_INTERSECTRGN) ? RGN_AND : RGN_DIFF );
165
166     if ((win = WIN_GetPtr( top_win )) && win != WND_DESKTOP && win != WND_OTHER_PROCESS)
167     {
168         surface = win->surface;
169         if (surface) window_surface_add_ref( surface );
170         WIN_ReleasePtr( win );
171     }
172
173     if (!surface) top_rect = get_virtual_screen_rect();
174     __wine_set_visible_region( dce->hdc, vis_rgn, &win_rect, &top_rect, surface );
175     if (surface) window_surface_release( surface );
176 }
177
178
179 /***********************************************************************
180  *              reset_dce_attrs
181  */
182 static void reset_dce_attrs( struct dce *dce )
183 {
184     RestoreDC( dce->hdc, 1 );  /* initial save level is always 1 */
185     SaveDC( dce->hdc );  /* save the state again for next time */
186 }
187
188
189 /***********************************************************************
190  *              release_dce
191  */
192 static void release_dce( struct dce *dce )
193 {
194     if (!dce->hwnd) return;  /* already released */
195
196     __wine_set_visible_region( dce->hdc, 0, &dummy_surface.rect, &dummy_surface.rect, &dummy_surface );
197     USER_Driver->pReleaseDC( dce->hwnd, dce->hdc );
198
199     if (dce->clip_rgn) DeleteObject( dce->clip_rgn );
200     dce->clip_rgn = 0;
201     dce->hwnd     = 0;
202     dce->flags   &= DCX_CACHE;
203 }
204
205
206 /***********************************************************************
207  *   delete_clip_rgn
208  */
209 static void delete_clip_rgn( struct dce *dce )
210 {
211     if (!dce->clip_rgn) return;  /* nothing to do */
212
213     dce->flags &= ~(DCX_EXCLUDERGN | DCX_INTERSECTRGN);
214     DeleteObject( dce->clip_rgn );
215     dce->clip_rgn = 0;
216
217     /* make it dirty so that the vis rgn gets recomputed next time */
218     SetHookFlags( dce->hdc, DCHF_INVALIDATEVISRGN );
219 }
220
221
222 /***********************************************************************
223  *           alloc_dce
224  *
225  * Allocate a new DCE.
226  */
227 static struct dce *alloc_dce(void)
228 {
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     dce->hwnd      = 0;
240     dce->clip_rgn  = 0;
241     dce->flags     = 0;
242     dce->count     = 1;
243
244     /* store DCE handle in DC hook data field */
245     SetDCHook( dce->hdc, dc_hook, (DWORD_PTR)dce );
246     SetHookFlags( dce->hdc, DCHF_INVALIDATEVISRGN );
247     return dce;
248 }
249
250
251 /***********************************************************************
252  *              get_window_dce
253  */
254 static struct dce *get_window_dce( HWND hwnd )
255 {
256     struct dce *dce;
257     WND *win = WIN_GetPtr( hwnd );
258
259     if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return NULL;
260
261     dce = win->dce;
262     if (!dce && (dce = get_class_dce( win->class )))
263     {
264         win->dce = dce;
265         dce->count++;
266     }
267     WIN_ReleasePtr( win );
268
269     if (!dce)  /* try to allocate one */
270     {
271         struct dce *dce_to_free = NULL;
272         LONG class_style = GetClassLongW( hwnd, GCL_STYLE );
273
274         if (class_style & CS_CLASSDC)
275         {
276             if (!(dce = alloc_dce())) return NULL;
277
278             win = WIN_GetPtr( hwnd );
279             if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
280             {
281                 if (win->dce)  /* another thread beat us to it */
282                 {
283                     dce_to_free = dce;
284                     dce = win->dce;
285                 }
286                 else if ((win->dce = set_class_dce( win->class, dce )) != dce)
287                 {
288                     dce_to_free = dce;
289                     dce = win->dce;
290                     dce->count++;
291                 }
292                 else
293                 {
294                     dce->count++;
295                     list_add_tail( &dce_list, &dce->entry );
296                 }
297                 WIN_ReleasePtr( win );
298             }
299             else dce_to_free = dce;
300         }
301         else if (class_style & CS_OWNDC)
302         {
303             if (!(dce = alloc_dce())) return NULL;
304
305             win = WIN_GetPtr( hwnd );
306             if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
307             {
308                 if (win->dwStyle & WS_CLIPCHILDREN) dce->flags |= DCX_CLIPCHILDREN;
309                 if (win->dwStyle & WS_CLIPSIBLINGS) dce->flags |= DCX_CLIPSIBLINGS;
310                 if (win->dce)  /* another thread beat us to it */
311                 {
312                     dce_to_free = dce;
313                     dce = win->dce;
314                 }
315                 else
316                 {
317                     win->dce = dce;
318                     dce->hwnd = hwnd;
319                     dce->count++;
320                     list_add_tail( &dce_list, &dce->entry );
321                 }
322                 WIN_ReleasePtr( win );
323             }
324             else dce_to_free = dce;
325         }
326
327         if (dce_to_free)
328         {
329             SetDCHook( dce_to_free->hdc, NULL, 0 );
330             DeleteDC( dce_to_free->hdc );
331             HeapFree( GetProcessHeap(), 0, dce_to_free );
332             if (dce_to_free == dce)
333                 dce = NULL;
334         }
335     }
336     return dce;
337 }
338
339
340 /***********************************************************************
341  *           free_dce
342  *
343  * Free a class or window DCE.
344  */
345 void free_dce( struct dce *dce, HWND hwnd )
346 {
347     USER_Lock();
348
349     if (dce)
350     {
351         if (!--dce->count)
352         {
353             /* turn it into a cache entry */
354             reset_dce_attrs( dce );
355             release_dce( dce );
356             dce->flags |= DCX_CACHE;
357         }
358         else if (dce->hwnd == hwnd)
359         {
360             release_dce( dce );
361         }
362     }
363
364     /* now check for cache DCEs */
365
366     if (hwnd)
367     {
368         LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
369         {
370             if (dce->hwnd != hwnd) continue;
371             if (!(dce->flags & DCX_CACHE)) continue;
372
373             if (dce->count) WARN( "GetDC() without ReleaseDC() for window %p\n", hwnd );
374             dce->count = 0;
375             release_dce( dce );
376         }
377     }
378
379     USER_Unlock();
380 }
381
382
383 /***********************************************************************
384  *           make_dc_dirty
385  *
386  * Mark the associated DC as dirty to force a refresh of the visible region
387  */
388 static void make_dc_dirty( struct dce *dce )
389 {
390     if (!dce->count)
391     {
392         /* Don't bother with visible regions of unused DCEs */
393         TRACE("\tpurged %p dce [%p]\n", dce, dce->hwnd);
394         release_dce( dce );
395     }
396     else
397     {
398         /* Set dirty bits in the hDC and DCE structs */
399         TRACE("\tfixed up %p dce [%p]\n", dce, dce->hwnd);
400         SetHookFlags( dce->hdc, DCHF_INVALIDATEVISRGN );
401     }
402 }
403
404
405 /***********************************************************************
406  *   invalidate_dce
407  *
408  * It is called from SetWindowPos() - we have to
409  * mark as dirty all busy DCEs for windows that have pWnd->parent as
410  * an ancestor and whose client rect intersects with specified update
411  * rectangle. In addition, pWnd->parent DCEs may need to be updated if
412  * DCX_CLIPCHILDREN flag is set.
413  */
414 void invalidate_dce( WND *win, const RECT *extra_rect )
415 {
416     RECT window_rect;
417     struct dce *dce;
418
419     if (!win->parent) return;
420
421     GetWindowRect( win->obj.handle, &window_rect );
422
423     TRACE("%p parent %p %s (%s)\n",
424           win->obj.handle, win->parent, wine_dbgstr_rect(&window_rect), wine_dbgstr_rect(extra_rect) );
425
426     /* walk all DCEs and fixup non-empty entries */
427
428     LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
429     {
430         TRACE( "%p: hwnd %p dcx %08x %s %s\n", dce, dce->hwnd, dce->flags,
431                (dce->flags & DCX_CACHE) ? "Cache" : "Owned", dce->count ? "InUse" : "" );
432
433         if (!dce->hwnd) continue;
434         if ((dce->hwnd == win->parent) && !(dce->flags & DCX_CLIPCHILDREN))
435             continue;  /* child window positions don't bother us */
436
437         /* if DCE window is a child of hwnd, it has to be invalidated */
438         if (dce->hwnd == win->obj.handle || IsChild( win->obj.handle, dce->hwnd ))
439         {
440             make_dc_dirty( dce );
441             continue;
442         }
443
444         /* otherwise check if the window rectangle intersects this DCE window */
445         if (win->parent == dce->hwnd || IsChild( win->parent, dce->hwnd ))
446         {
447             RECT dce_rect, tmp;
448             GetWindowRect( dce->hwnd, &dce_rect );
449             if (IntersectRect( &tmp, &dce_rect, &window_rect ) ||
450                 (extra_rect && IntersectRect( &tmp, &dce_rect, extra_rect )))
451                 make_dc_dirty( dce );
452         }
453     }
454 }
455
456 /***********************************************************************
457  *              release_dc
458  *
459  * Implementation of ReleaseDC.
460  */
461 static INT release_dc( HWND hwnd, HDC hdc, BOOL end_paint )
462 {
463     struct dce *dce;
464     BOOL ret = FALSE;
465
466     TRACE("%p %p\n", hwnd, hdc );
467
468     USER_Lock();
469     dce = (struct dce *)GetDCHook( hdc, NULL );
470     if (dce && dce->count)
471     {
472         if (!(dce->flags & DCX_NORESETATTRS)) reset_dce_attrs( dce );
473         if (end_paint || (dce->flags & DCX_CACHE)) delete_clip_rgn( dce );
474         if (dce->flags & DCX_CACHE) dce->count = 0;
475         ret = TRUE;
476     }
477     USER_Unlock();
478     return ret;
479 }
480
481
482 /***********************************************************************
483  *              dc_hook
484  *
485  * See "Undoc. Windows" for hints (DC, SetDCHook, SetHookFlags)..
486  */
487 static BOOL CALLBACK dc_hook( HDC hDC, WORD code, DWORD_PTR data, LPARAM lParam )
488 {
489     BOOL retv = TRUE;
490     struct dce *dce = (struct dce *)data;
491
492     TRACE("hDC = %p, %u\n", hDC, code);
493
494     if (!dce) return 0;
495     assert( dce->hdc == hDC );
496
497     switch( code )
498     {
499     case DCHC_INVALIDVISRGN:
500         /* GDI code calls this when it detects that the
501          * DC is dirty (usually after SetHookFlags()). This
502          * means that we have to recompute the visible region.
503          */
504         if (dce->count) update_visible_region( dce );
505         else /* non-fatal but shouldn't happen */
506             WARN("DC is not in use!\n");
507         break;
508     case DCHC_DELETEDC:
509         /*
510          * Windows will not let you delete a DC that is busy
511          * (between GetDC and ReleaseDC)
512          */
513         USER_Lock();
514         if (dce->count > 1)
515         {
516             WARN("Application trying to delete a busy DC %p\n", dce->hdc);
517             retv = FALSE;
518         }
519         else
520         {
521             list_remove( &dce->entry );
522             if (dce->clip_rgn) DeleteObject( dce->clip_rgn );
523             HeapFree( GetProcessHeap(), 0, dce );
524         }
525         USER_Unlock();
526         break;
527     }
528     return retv;
529 }
530
531
532 /***********************************************************************
533  *           get_update_region
534  *
535  * Return update region (in screen coordinates) for a window.
536  */
537 static HRGN get_update_region( HWND hwnd, UINT *flags, HWND *child )
538 {
539     HRGN hrgn = 0;
540     NTSTATUS status;
541     RGNDATA *data;
542     size_t size = 256;
543
544     do
545     {
546         if (!(data = HeapAlloc( GetProcessHeap(), 0, sizeof(*data) + size - 1 )))
547         {
548             SetLastError( ERROR_OUTOFMEMORY );
549             return 0;
550         }
551
552         SERVER_START_REQ( get_update_region )
553         {
554             req->window     = wine_server_user_handle( hwnd );
555             req->from_child = wine_server_user_handle( child ? *child : 0 );
556             req->flags      = *flags;
557             wine_server_set_reply( req, data->Buffer, size );
558             if (!(status = wine_server_call( req )))
559             {
560                 size_t reply_size = wine_server_reply_size( reply );
561                 data->rdh.dwSize   = sizeof(data->rdh);
562                 data->rdh.iType    = RDH_RECTANGLES;
563                 data->rdh.nCount   = reply_size / sizeof(RECT);
564                 data->rdh.nRgnSize = reply_size;
565                 hrgn = ExtCreateRegion( NULL, size, data );
566                 if (child) *child = wine_server_ptr_handle( reply->child );
567                 *flags = reply->flags;
568             }
569             else size = reply->total_size;
570         }
571         SERVER_END_REQ;
572         HeapFree( GetProcessHeap(), 0, data );
573     } while (status == STATUS_BUFFER_OVERFLOW);
574
575     if (status) SetLastError( RtlNtStatusToDosError(status) );
576     return hrgn;
577 }
578
579
580 /***********************************************************************
581  *           get_update_flags
582  *
583  * Get only the update flags, not the update region.
584  */
585 static BOOL get_update_flags( HWND hwnd, HWND *child, UINT *flags )
586 {
587     BOOL ret;
588
589     SERVER_START_REQ( get_update_region )
590     {
591         req->window     = wine_server_user_handle( hwnd );
592         req->from_child = wine_server_user_handle( child ? *child : 0 );
593         req->flags      = *flags | UPDATE_NOREGION;
594         if ((ret = !wine_server_call_err( req )))
595         {
596             if (child) *child = wine_server_ptr_handle( reply->child );
597             *flags = reply->flags;
598         }
599     }
600     SERVER_END_REQ;
601     return ret;
602 }
603
604
605 /***********************************************************************
606  *           redraw_window_rects
607  *
608  * Redraw part of a window.
609  */
610 static BOOL redraw_window_rects( HWND hwnd, UINT flags, const RECT *rects, UINT count )
611 {
612     BOOL ret;
613
614     if (!(flags & (RDW_INVALIDATE|RDW_VALIDATE|RDW_INTERNALPAINT|RDW_NOINTERNALPAINT)))
615         return TRUE;  /* nothing to do */
616
617     SERVER_START_REQ( redraw_window )
618     {
619         req->window = wine_server_user_handle( hwnd );
620         req->flags  = flags;
621         wine_server_add_data( req, rects, count * sizeof(RECT) );
622         ret = !wine_server_call_err( req );
623     }
624     SERVER_END_REQ;
625     return ret;
626 }
627
628
629 /***********************************************************************
630  *           send_ncpaint
631  *
632  * Send a WM_NCPAINT message if needed, and return the resulting update region (in screen coords).
633  * Helper for erase_now and BeginPaint.
634  */
635 static HRGN send_ncpaint( HWND hwnd, HWND *child, UINT *flags )
636 {
637     HRGN whole_rgn = get_update_region( hwnd, flags, child );
638     HRGN client_rgn = 0;
639
640     if (child) hwnd = *child;
641
642     if (hwnd == GetDesktopWindow()) return whole_rgn;
643
644     if (whole_rgn)
645     {
646         RECT client, update;
647         INT type;
648
649         /* check if update rgn overlaps with nonclient area */
650         type = GetRgnBox( whole_rgn, &update );
651         WIN_GetRectangles( hwnd, COORDS_SCREEN, 0, &client );
652
653         if ((*flags & UPDATE_NONCLIENT) ||
654             update.left < client.left || update.top < client.top ||
655             update.right > client.right || update.bottom > client.bottom)
656         {
657             client_rgn = CreateRectRgnIndirect( &client );
658             CombineRgn( client_rgn, client_rgn, whole_rgn, RGN_AND );
659
660             /* check if update rgn contains complete nonclient area */
661             if (type == SIMPLEREGION)
662             {
663                 RECT window;
664                 GetWindowRect( hwnd, &window );
665                 if (EqualRect( &window, &update ))
666                 {
667                     DeleteObject( whole_rgn );
668                     whole_rgn = (HRGN)1;
669                 }
670             }
671         }
672         else
673         {
674             client_rgn = whole_rgn;
675             whole_rgn = 0;
676         }
677
678         if (whole_rgn) /* NOTE: WM_NCPAINT allows wParam to be 1 */
679         {
680             if (*flags & UPDATE_NONCLIENT) SendMessageW( hwnd, WM_NCPAINT, (WPARAM)whole_rgn, 0 );
681             if (whole_rgn > (HRGN)1) DeleteObject( whole_rgn );
682         }
683     }
684     return client_rgn;
685 }
686
687
688 /***********************************************************************
689  *           send_erase
690  *
691  * Send a WM_ERASEBKGND message if needed, and optionally return the DC for painting.
692  * If a DC is requested, the region is selected into it. In all cases the region is deleted.
693  * Helper for erase_now and BeginPaint.
694  */
695 static BOOL send_erase( HWND hwnd, UINT flags, HRGN client_rgn,
696                         RECT *clip_rect, HDC *hdc_ret )
697 {
698     BOOL need_erase = (flags & UPDATE_DELAYED_ERASE) != 0;
699     HDC hdc = 0;
700     RECT dummy;
701
702     if (!clip_rect) clip_rect = &dummy;
703     if (hdc_ret || (flags & UPDATE_ERASE))
704     {
705         UINT dcx_flags = DCX_INTERSECTRGN | DCX_USESTYLE;
706         if (IsIconic(hwnd)) dcx_flags |= DCX_WINDOW;
707
708         if ((hdc = GetDCEx( hwnd, client_rgn, dcx_flags )))
709         {
710             INT type = GetClipBox( hdc, clip_rect );
711
712             if (flags & UPDATE_ERASE)
713             {
714                 /* don't erase if the clip box is empty */
715                 if (type != NULLREGION)
716                     need_erase = !SendMessageW( hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0 );
717             }
718             if (!hdc_ret) release_dc( hwnd, hdc, TRUE );
719         }
720
721         if (hdc_ret) *hdc_ret = hdc;
722     }
723     if (!hdc) DeleteObject( client_rgn );
724     return need_erase;
725 }
726
727
728 /***********************************************************************
729  *           erase_now
730  *
731  * Implementation of RDW_ERASENOW behavior.
732  */
733 void erase_now( HWND hwnd, UINT rdw_flags )
734 {
735     HWND child = 0;
736     HRGN hrgn;
737     BOOL need_erase = FALSE;
738
739     /* loop while we find a child to repaint */
740     for (;;)
741     {
742         UINT flags = UPDATE_NONCLIENT | UPDATE_ERASE;
743
744         if (rdw_flags & RDW_NOCHILDREN) flags |= UPDATE_NOCHILDREN;
745         else if (rdw_flags & RDW_ALLCHILDREN) flags |= UPDATE_ALLCHILDREN;
746         if (need_erase) flags |= UPDATE_DELAYED_ERASE;
747
748         if (!(hrgn = send_ncpaint( hwnd, &child, &flags ))) break;
749         need_erase = send_erase( child, flags, hrgn, NULL, NULL );
750
751         if (!flags) break;  /* nothing more to do */
752         if ((rdw_flags & RDW_NOCHILDREN) && !need_erase) break;
753     }
754 }
755
756
757 /***********************************************************************
758  *           move_window_bits
759  *
760  * Move the window bits when a window is resized or its surface recreated.
761  */
762 void move_window_bits( HWND hwnd, struct window_surface *old_surface,
763                        struct window_surface *new_surface,
764                        const RECT *visible_rect, const RECT *old_visible_rect,
765                        const RECT *client_rect, const RECT *valid_rects )
766 {
767     RECT dst = valid_rects[0];
768     RECT src = valid_rects[1];
769
770     if (new_surface != old_surface ||
771         src.left - old_visible_rect->left != dst.left - visible_rect->left ||
772         src.top - old_visible_rect->top != dst.top - visible_rect->top)
773     {
774         char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
775         BITMAPINFO *info = (BITMAPINFO *)buffer;
776         void *bits;
777         UINT flags = UPDATE_NOCHILDREN;
778         HRGN rgn = get_update_region( hwnd, &flags, NULL );
779         HDC hdc = GetDCEx( hwnd, rgn, DCX_CACHE | DCX_EXCLUDERGN );
780
781         OffsetRect( &dst, -client_rect->left, -client_rect->top );
782         TRACE( "copying  %s -> %s\n", wine_dbgstr_rect(&src), wine_dbgstr_rect(&dst) );
783         bits = old_surface->funcs->get_info( old_surface, info );
784         old_surface->funcs->lock( old_surface );
785         SetDIBitsToDevice( hdc, dst.left, dst.top, dst.right - dst.left, dst.bottom - dst.top,
786                            src.left - old_visible_rect->left - old_surface->rect.left,
787                            old_surface->rect.bottom - (src.bottom - old_visible_rect->top),
788                            0, old_surface->rect.bottom - old_surface->rect.top,
789                            bits, info, DIB_RGB_COLORS );
790         old_surface->funcs->unlock( old_surface );
791         ReleaseDC( hwnd, hdc );
792         DeleteObject( rgn );
793     }
794 }
795
796
797 /***********************************************************************
798  *           update_now
799  *
800  * Implementation of RDW_UPDATENOW behavior.
801  */
802 static void update_now( HWND hwnd, UINT rdw_flags )
803 {
804     HWND child = 0;
805
806     /* desktop window never gets WM_PAINT, only WM_ERASEBKGND */
807     if (hwnd == GetDesktopWindow()) erase_now( hwnd, rdw_flags | RDW_NOCHILDREN );
808
809     /* loop while we find a child to repaint */
810     for (;;)
811     {
812         UINT flags = UPDATE_PAINT | UPDATE_INTERNALPAINT;
813
814         if (rdw_flags & RDW_NOCHILDREN) flags |= UPDATE_NOCHILDREN;
815         else if (rdw_flags & RDW_ALLCHILDREN) flags |= UPDATE_ALLCHILDREN;
816
817         if (!get_update_flags( hwnd, &child, &flags )) break;
818         if (!flags) break;  /* nothing more to do */
819
820         SendMessageW( child, WM_PAINT, 0, 0 );
821         if (rdw_flags & RDW_NOCHILDREN) break;
822     }
823 }
824
825
826 /*************************************************************************
827  *             fix_caret
828  *
829  * Helper for ScrollWindowEx:
830  * If the return value is 0, no special caret handling is necessary.
831  * Otherwise the return value is the handle of the window that owns the
832  * caret. Its caret needs to be hidden during the scroll operation and
833  * moved to new_caret_pos if move_caret is TRUE.
834  */
835 static HWND fix_caret(HWND hWnd, const RECT *scroll_rect, INT dx, INT dy,
836                      UINT flags, LPBOOL move_caret, LPPOINT new_caret_pos)
837 {
838     GUITHREADINFO info;
839     RECT rect, mapped_rcCaret;
840     BOOL hide_caret = FALSE;
841
842     info.cbSize = sizeof(info);
843     if (!GetGUIThreadInfo( GetCurrentThreadId(), &info )) return 0;
844     if (!info.hwndCaret) return 0;
845     
846     if (info.hwndCaret == hWnd)
847     {
848         /* Move the caret if it's (partially) in the source rectangle */
849         if (IntersectRect(&rect, scroll_rect, &info.rcCaret))
850         {
851             *move_caret = TRUE;
852             hide_caret = TRUE;
853             new_caret_pos->x = info.rcCaret.left + dx;
854             new_caret_pos->y = info.rcCaret.top + dy;
855         }
856         else
857         {
858             *move_caret = FALSE;
859             
860             /* Hide the caret if it's in the destination rectangle */
861             rect = *scroll_rect;
862             OffsetRect(&rect, dx, dy);
863             hide_caret = IntersectRect(&rect, &rect, &info.rcCaret);
864         }
865     }
866     else
867     {
868         if ((flags & SW_SCROLLCHILDREN) && IsChild(hWnd, info.hwndCaret))
869         {
870             *move_caret = FALSE;
871             
872             /* Hide the caret if it's in the source or in the destination
873                rectangle */
874             mapped_rcCaret = info.rcCaret;
875             MapWindowPoints(info.hwndCaret, hWnd, (LPPOINT)&mapped_rcCaret, 2);
876             
877             if (IntersectRect(&rect, scroll_rect, &mapped_rcCaret))
878             {
879                 hide_caret = TRUE;
880             }
881             else
882             {
883                 rect = *scroll_rect;
884                 OffsetRect(&rect, dx, dy);
885                 hide_caret = IntersectRect(&rect, &rect, &mapped_rcCaret);
886             }
887         }
888         else
889             return 0;
890     }
891
892     if (hide_caret)
893     {    
894         HideCaret(info.hwndCaret);
895         return info.hwndCaret;
896     }
897     else
898         return 0;
899 }
900
901
902 /***********************************************************************
903  *              BeginPaint (USER32.@)
904  */
905 HDC WINAPI BeginPaint( HWND hwnd, PAINTSTRUCT *lps )
906 {
907     HRGN hrgn;
908     UINT flags = UPDATE_NONCLIENT | UPDATE_ERASE | UPDATE_PAINT | UPDATE_INTERNALPAINT | UPDATE_NOCHILDREN;
909
910     if (!lps) return 0;
911
912     HideCaret( hwnd );
913
914     if (!(hrgn = send_ncpaint( hwnd, NULL, &flags ))) return 0;
915
916     lps->fErase = send_erase( hwnd, flags, hrgn, &lps->rcPaint, &lps->hdc );
917
918     TRACE("hdc = %p box = (%s), fErase = %d\n",
919           lps->hdc, wine_dbgstr_rect(&lps->rcPaint), lps->fErase);
920
921     return lps->hdc;
922 }
923
924
925 /***********************************************************************
926  *              EndPaint (USER32.@)
927  */
928 BOOL WINAPI EndPaint( HWND hwnd, const PAINTSTRUCT *lps )
929 {
930     if (!lps) return FALSE;
931     release_dc( hwnd, lps->hdc, TRUE );
932     ShowCaret( hwnd );
933     flush_window_surfaces( FALSE );
934     return TRUE;
935 }
936
937
938 /***********************************************************************
939  *              GetDCEx (USER32.@)
940  */
941 HDC WINAPI GetDCEx( HWND hwnd, HRGN hrgnClip, DWORD flags )
942 {
943     const DWORD clip_flags = DCX_PARENTCLIP | DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_WINDOW;
944     const DWORD user_flags = clip_flags | DCX_NORESETATTRS; /* flags that can be set by user */
945     struct dce *dce;
946     BOOL bUpdateVisRgn = TRUE;
947     HWND parent;
948     LONG window_style = GetWindowLongW( hwnd, GWL_STYLE );
949
950     if (!hwnd) hwnd = GetDesktopWindow();
951     else hwnd = WIN_GetFullHandle( hwnd );
952
953     TRACE("hwnd %p, hrgnClip %p, flags %08x\n", hwnd, hrgnClip, flags);
954
955     if (!IsWindow(hwnd)) return 0;
956
957     /* fixup flags */
958
959     if (flags & (DCX_WINDOW | DCX_PARENTCLIP)) flags |= DCX_CACHE;
960
961     if (flags & DCX_USESTYLE)
962     {
963         flags &= ~(DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS | DCX_PARENTCLIP);
964
965         if (window_style & WS_CLIPSIBLINGS) flags |= DCX_CLIPSIBLINGS;
966
967         if (!(flags & DCX_WINDOW))
968         {
969             if (GetClassLongW( hwnd, GCL_STYLE ) & CS_PARENTDC) flags |= DCX_PARENTCLIP;
970
971             if (window_style & WS_CLIPCHILDREN && !(window_style & WS_MINIMIZE))
972                 flags |= DCX_CLIPCHILDREN;
973         }
974     }
975
976     if (flags & DCX_WINDOW) flags &= ~DCX_CLIPCHILDREN;
977
978     parent = GetAncestor( hwnd, GA_PARENT );
979     if (!parent || (parent == GetDesktopWindow()))
980         flags = (flags & ~DCX_PARENTCLIP) | DCX_CLIPSIBLINGS;
981
982     /* it seems parent clip is ignored when clipping siblings or children */
983     if (flags & (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN)) flags &= ~DCX_PARENTCLIP;
984
985     if( flags & DCX_PARENTCLIP )
986     {
987         LONG parent_style = GetWindowLongW( parent, GWL_STYLE );
988         if( (window_style & WS_VISIBLE) && (parent_style & WS_VISIBLE) )
989         {
990             flags &= ~DCX_CLIPCHILDREN;
991             if (parent_style & WS_CLIPSIBLINGS) flags |= DCX_CLIPSIBLINGS;
992         }
993     }
994
995     /* find a suitable DCE */
996
997     if ((flags & DCX_CACHE) || !(dce = get_window_dce( hwnd )))
998     {
999         struct dce *dceEmpty = NULL, *dceUnused = NULL;
1000
1001         /* Strategy: First, we attempt to find a non-empty but unused DCE with
1002          * compatible flags. Next, we look for an empty entry. If the cache is
1003          * full we have to purge one of the unused entries.
1004          */
1005         USER_Lock();
1006         LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
1007         {
1008             if ((dce->flags & DCX_CACHE) && !dce->count)
1009             {
1010                 dceUnused = dce;
1011
1012                 if (!dce->hwnd) dceEmpty = dce;
1013                 else if ((dce->hwnd == hwnd) && !((dce->flags ^ flags) & clip_flags))
1014                 {
1015                     TRACE("\tfound valid %p dce [%p], flags %08x\n",
1016                           dce, hwnd, dce->flags );
1017                     bUpdateVisRgn = FALSE;
1018                     break;
1019                 }
1020             }
1021         }
1022
1023         if (&dce->entry == &dce_list)  /* nothing found */
1024             dce = dceEmpty ? dceEmpty : dceUnused;
1025
1026         if (dce) dce->count = 1;
1027
1028         USER_Unlock();
1029
1030         /* if there's no dce empty or unused, allocate a new one */
1031         if (!dce)
1032         {
1033             if (!(dce = alloc_dce())) return 0;
1034             dce->flags = DCX_CACHE;
1035             USER_Lock();
1036             list_add_head( &dce_list, &dce->entry );
1037             USER_Unlock();
1038         }
1039     }
1040     else
1041     {
1042         flags |= DCX_NORESETATTRS;
1043         if (dce->hwnd == hwnd)
1044         {
1045             TRACE("\tskipping hVisRgn update\n");
1046             bUpdateVisRgn = FALSE; /* updated automatically, via DCHook() */
1047         }
1048         else
1049         {
1050             /* we should free dce->clip_rgn here, but Windows apparently doesn't */
1051             dce->flags &= ~(DCX_EXCLUDERGN | DCX_INTERSECTRGN);
1052             dce->clip_rgn = 0;
1053         }
1054     }
1055
1056     if (flags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN))
1057     {
1058         /* if the extra clip region has changed, get rid of the old one */
1059         if (dce->clip_rgn != hrgnClip || ((flags ^ dce->flags) & (DCX_INTERSECTRGN | DCX_EXCLUDERGN)))
1060             delete_clip_rgn( dce );
1061         dce->clip_rgn = hrgnClip;
1062         if (!dce->clip_rgn) dce->clip_rgn = CreateRectRgn( 0, 0, 0, 0 );
1063         dce->flags |= flags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN);
1064         bUpdateVisRgn = TRUE;
1065     }
1066
1067     if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) SetLayout( dce->hdc, LAYOUT_RTL );
1068
1069     dce->hwnd = hwnd;
1070     dce->flags = (dce->flags & ~user_flags) | (flags & user_flags);
1071
1072     if (SetHookFlags( dce->hdc, DCHF_VALIDATEVISRGN )) bUpdateVisRgn = TRUE;  /* DC was dirty */
1073
1074     if (bUpdateVisRgn) update_visible_region( dce );
1075
1076     TRACE("(%p,%p,0x%x): returning %p\n", hwnd, hrgnClip, flags, dce->hdc);
1077     return dce->hdc;
1078 }
1079
1080
1081 /***********************************************************************
1082  *              GetDC (USER32.@)
1083  *
1084  * Get a device context.
1085  *
1086  * RETURNS
1087  *      Success: Handle to the device context
1088  *      Failure: NULL.
1089  */
1090 HDC WINAPI GetDC( HWND hwnd )
1091 {
1092     if (!hwnd) return GetDCEx( 0, 0, DCX_CACHE | DCX_WINDOW );
1093     return GetDCEx( hwnd, 0, DCX_USESTYLE );
1094 }
1095
1096
1097 /***********************************************************************
1098  *              GetWindowDC (USER32.@)
1099  */
1100 HDC WINAPI GetWindowDC( HWND hwnd )
1101 {
1102     return GetDCEx( hwnd, 0, DCX_USESTYLE | DCX_WINDOW );
1103 }
1104
1105
1106 /***********************************************************************
1107  *              ReleaseDC (USER32.@)
1108  *
1109  * Release a device context.
1110  *
1111  * RETURNS
1112  *      Success: Non-zero. Resources used by hdc are released.
1113  *      Failure: 0.
1114  */
1115 INT WINAPI ReleaseDC( HWND hwnd, HDC hdc )
1116 {
1117     return release_dc( hwnd, hdc, FALSE );
1118 }
1119
1120
1121 /**********************************************************************
1122  *              WindowFromDC (USER32.@)
1123  */
1124 HWND WINAPI WindowFromDC( HDC hdc )
1125 {
1126     struct dce *dce;
1127     HWND hwnd = 0;
1128
1129     USER_Lock();
1130     dce = (struct dce *)GetDCHook( hdc, NULL );
1131     if (dce) hwnd = dce->hwnd;
1132     USER_Unlock();
1133     return hwnd;
1134 }
1135
1136
1137 /***********************************************************************
1138  *              LockWindowUpdate (USER32.@)
1139  *
1140  * Enables or disables painting in the chosen window.
1141  *
1142  * PARAMS
1143  *  hwnd [I] handle to a window.
1144  *
1145  * RETURNS
1146  *  If successful, returns nonzero value. Otherwise,
1147  *  returns 0.
1148  *
1149  * NOTES
1150  *  You can lock only one window at a time.
1151  */
1152 BOOL WINAPI LockWindowUpdate( HWND hwnd )
1153 {
1154     static HWND lockedWnd;
1155
1156     FIXME("(%p), partial stub!\n",hwnd);
1157
1158     USER_Lock();
1159     if (lockedWnd)
1160     {
1161         if (!hwnd)
1162         {
1163             /* Unlock lockedWnd */
1164             /* FIXME: Do something */
1165         }
1166         else
1167         {
1168             /* Attempted to lock a second window */
1169             /* Return FALSE and do nothing */
1170             USER_Unlock();
1171             return FALSE;
1172         }
1173     }
1174     lockedWnd = hwnd;
1175     USER_Unlock();
1176     return TRUE;
1177 }
1178
1179
1180 /***********************************************************************
1181  *              RedrawWindow (USER32.@)
1182  */
1183 BOOL WINAPI RedrawWindow( HWND hwnd, const RECT *rect, HRGN hrgn, UINT flags )
1184 {
1185     static const RECT empty;
1186     BOOL ret;
1187
1188     if (!hwnd) hwnd = GetDesktopWindow();
1189
1190     if (TRACE_ON(win))
1191     {
1192         if (hrgn)
1193         {
1194             RECT r;
1195             GetRgnBox( hrgn, &r );
1196             TRACE( "%p region %p box %s ", hwnd, hrgn, wine_dbgstr_rect(&r) );
1197         }
1198         else if (rect)
1199             TRACE( "%p rect %s ", hwnd, wine_dbgstr_rect(rect) );
1200         else
1201             TRACE( "%p whole window ", hwnd );
1202
1203         dump_rdw_flags(flags);
1204     }
1205
1206     /* process pending expose events before painting */
1207     if (flags & RDW_UPDATENOW) USER_Driver->pMsgWaitForMultipleObjectsEx( 0, NULL, 0, QS_PAINT, 0 );
1208
1209     if (rect && !hrgn)
1210     {
1211         if (IsRectEmpty( rect )) rect = &empty;
1212         ret = redraw_window_rects( hwnd, flags, rect, 1 );
1213     }
1214     else if (!hrgn)
1215     {
1216         ret = redraw_window_rects( hwnd, flags, NULL, 0 );
1217     }
1218     else  /* need to build a list of the region rectangles */
1219     {
1220         DWORD size;
1221         RGNDATA *data = NULL;
1222
1223         if (!(size = GetRegionData( hrgn, 0, NULL ))) return FALSE;
1224         if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
1225         GetRegionData( hrgn, size, data );
1226         if (!data->rdh.nCount)  /* empty region -> use a single all-zero rectangle */
1227             ret = redraw_window_rects( hwnd, flags, &empty, 1 );
1228         else
1229             ret = redraw_window_rects( hwnd, flags, (const RECT *)data->Buffer, data->rdh.nCount );
1230         HeapFree( GetProcessHeap(), 0, data );
1231     }
1232
1233     if (flags & RDW_UPDATENOW) update_now( hwnd, flags );
1234     else if (flags & RDW_ERASENOW) erase_now( hwnd, flags );
1235
1236     return ret;
1237 }
1238
1239
1240 /***********************************************************************
1241  *              UpdateWindow (USER32.@)
1242  */
1243 BOOL WINAPI UpdateWindow( HWND hwnd )
1244 {
1245     if (!hwnd)
1246     {
1247         SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1248         return FALSE;
1249     }
1250
1251     return RedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN );
1252 }
1253
1254
1255 /***********************************************************************
1256  *              InvalidateRgn (USER32.@)
1257  */
1258 BOOL WINAPI InvalidateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
1259 {
1260     if (!hwnd)
1261     {
1262         SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1263         return FALSE;
1264     }
1265
1266     return RedrawWindow(hwnd, NULL, hrgn, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) );
1267 }
1268
1269
1270 /***********************************************************************
1271  *              InvalidateRect (USER32.@)
1272  *
1273  * MSDN: if hwnd parameter is NULL, InvalidateRect invalidates and redraws
1274  * all windows and sends WM_ERASEBKGND and WM_NCPAINT.
1275  */
1276 BOOL WINAPI InvalidateRect( HWND hwnd, const RECT *rect, BOOL erase )
1277 {
1278     UINT flags = RDW_INVALIDATE | (erase ? RDW_ERASE : 0);
1279
1280     if (!hwnd)
1281     {
1282         flags = RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW;
1283         rect = NULL;
1284     }
1285
1286     return RedrawWindow( hwnd, rect, 0, flags );
1287 }
1288
1289
1290 /***********************************************************************
1291  *              ValidateRgn (USER32.@)
1292  */
1293 BOOL WINAPI ValidateRgn( HWND hwnd, HRGN hrgn )
1294 {
1295     if (!hwnd)
1296     {
1297         SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1298         return FALSE;
1299     }
1300
1301     return RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE );
1302 }
1303
1304
1305 /***********************************************************************
1306  *              ValidateRect (USER32.@)
1307  *
1308  * MSDN: if hwnd parameter is NULL, ValidateRect invalidates and redraws
1309  * all windows and sends WM_ERASEBKGND and WM_NCPAINT.
1310  */
1311 BOOL WINAPI ValidateRect( HWND hwnd, const RECT *rect )
1312 {
1313     UINT flags = RDW_VALIDATE;
1314
1315     if (!hwnd)
1316     {
1317         flags = RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW;
1318         rect = NULL;
1319     }
1320
1321     return RedrawWindow( hwnd, rect, 0, flags );
1322 }
1323
1324
1325 /***********************************************************************
1326  *              GetUpdateRgn (USER32.@)
1327  */
1328 INT WINAPI GetUpdateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
1329 {
1330     INT retval = ERROR;
1331     UINT flags = UPDATE_NOCHILDREN;
1332     HRGN update_rgn;
1333
1334     if (erase) flags |= UPDATE_NONCLIENT | UPDATE_ERASE;
1335
1336     if ((update_rgn = send_ncpaint( hwnd, NULL, &flags )))
1337     {
1338         retval = CombineRgn( hrgn, update_rgn, 0, RGN_COPY );
1339         if (send_erase( hwnd, flags, update_rgn, NULL, NULL ))
1340         {
1341             flags = UPDATE_DELAYED_ERASE;
1342             get_update_flags( hwnd, NULL, &flags );
1343         }
1344         /* map region to client coordinates */
1345         map_window_region( 0, hwnd, hrgn );
1346     }
1347     return retval;
1348 }
1349
1350
1351 /***********************************************************************
1352  *              GetUpdateRect (USER32.@)
1353  */
1354 BOOL WINAPI GetUpdateRect( HWND hwnd, LPRECT rect, BOOL erase )
1355 {
1356     UINT flags = UPDATE_NOCHILDREN;
1357     HRGN update_rgn;
1358     BOOL need_erase;
1359
1360     if (erase) flags |= UPDATE_NONCLIENT | UPDATE_ERASE;
1361
1362     if (!(update_rgn = send_ncpaint( hwnd, NULL, &flags ))) return FALSE;
1363
1364     if (rect)
1365     {
1366         if (GetRgnBox( update_rgn, rect ) != NULLREGION)
1367         {
1368             HDC hdc = GetDCEx( hwnd, 0, DCX_USESTYLE );
1369             DWORD layout = SetLayout( hdc, 0 );  /* MapWindowPoints mirrors already */
1370             MapWindowPoints( 0, hwnd, (LPPOINT)rect, 2 );
1371             DPtoLP( hdc, (LPPOINT)rect, 2 );
1372             SetLayout( hdc, layout );
1373             ReleaseDC( hwnd, hdc );
1374         }
1375     }
1376     need_erase = send_erase( hwnd, flags, update_rgn, NULL, NULL );
1377
1378     /* check if we still have an update region */
1379     flags = UPDATE_PAINT | UPDATE_NOCHILDREN;
1380     if (need_erase) flags |= UPDATE_DELAYED_ERASE;
1381     return (get_update_flags( hwnd, NULL, &flags ) && (flags & UPDATE_PAINT));
1382 }
1383
1384
1385 /***********************************************************************
1386  *              ExcludeUpdateRgn (USER32.@)
1387  */
1388 INT WINAPI ExcludeUpdateRgn( HDC hdc, HWND hwnd )
1389 {
1390     HRGN update_rgn = CreateRectRgn( 0, 0, 0, 0 );
1391     INT ret = GetUpdateRgn( hwnd, update_rgn, FALSE );
1392
1393     if (ret != ERROR)
1394     {
1395         POINT pt;
1396
1397         GetDCOrgEx( hdc, &pt );
1398         MapWindowPoints( 0, hwnd, &pt, 1 );
1399         OffsetRgn( update_rgn, -pt.x, -pt.y );
1400         ret = ExtSelectClipRgn( hdc, update_rgn, RGN_DIFF );
1401         DeleteObject( update_rgn );
1402     }
1403     return ret;
1404 }
1405
1406
1407 static INT scroll_window( HWND hwnd, INT dx, INT dy, const RECT *rect, const RECT *clipRect,
1408                           HRGN hrgnUpdate, LPRECT rcUpdate, UINT flags, BOOL is_ex )
1409 {
1410     INT   retVal = NULLREGION;
1411     BOOL  bOwnRgn = TRUE;
1412     BOOL  bUpdate = (rcUpdate || hrgnUpdate || flags & (SW_INVALIDATE | SW_ERASE));
1413     int rdw_flags;
1414     HRGN  hrgnTemp;
1415     HRGN  hrgnWinupd = 0;
1416     HDC   hDC;
1417     RECT  rc, cliprc;
1418     HWND hwndCaret = NULL;
1419     BOOL moveCaret = FALSE;
1420     POINT newCaretPos;
1421
1422     TRACE( "%p, %d,%d hrgnUpdate=%p rcUpdate = %p %s %04x\n",
1423            hwnd, dx, dy, hrgnUpdate, rcUpdate, wine_dbgstr_rect(rect), flags );
1424     TRACE( "clipRect = %s\n", wine_dbgstr_rect(clipRect));
1425     if( flags & ~( SW_SCROLLCHILDREN | SW_INVALIDATE | SW_ERASE))
1426         FIXME("some flags (%04x) are unhandled\n", flags);
1427
1428     rdw_flags = (flags & SW_ERASE) && (flags & SW_INVALIDATE) ?
1429                                 RDW_INVALIDATE | RDW_ERASE  : RDW_INVALIDATE ;
1430
1431     if (!WIN_IsWindowDrawable( hwnd, TRUE )) return ERROR;
1432     hwnd = WIN_GetFullHandle( hwnd );
1433
1434     GetClientRect(hwnd, &rc);
1435
1436     if (clipRect) IntersectRect(&cliprc,&rc,clipRect);
1437     else cliprc = rc;
1438
1439     if (rect) IntersectRect(&rc, &rc, rect);
1440
1441     if( hrgnUpdate ) bOwnRgn = FALSE;
1442     else if( bUpdate ) hrgnUpdate = CreateRectRgn( 0, 0, 0, 0 );
1443
1444     newCaretPos.x = newCaretPos.y = 0;
1445
1446     if( !IsRectEmpty(&cliprc) && (dx || dy)) {
1447         DWORD dcxflags = 0;
1448         DWORD style = GetWindowLongW( hwnd, GWL_STYLE );
1449
1450         hwndCaret = fix_caret(hwnd, &rc, dx, dy, flags, &moveCaret, &newCaretPos);
1451
1452         if (is_ex) dcxflags |= DCX_CACHE;
1453         if( style & WS_CLIPSIBLINGS) dcxflags |= DCX_CLIPSIBLINGS;
1454         if( GetClassLongW( hwnd, GCL_STYLE ) & CS_PARENTDC)
1455             dcxflags |= DCX_PARENTCLIP;
1456         if( !(flags & SW_SCROLLCHILDREN) && (style & WS_CLIPCHILDREN))
1457             dcxflags |= DCX_CLIPCHILDREN;
1458         hDC = GetDCEx( hwnd, 0, dcxflags);
1459         if (hDC)
1460         {
1461             ScrollDC( hDC, dx, dy, &rc, &cliprc, hrgnUpdate, rcUpdate );
1462
1463             ReleaseDC( hwnd, hDC );
1464
1465             if (!bUpdate)
1466                 RedrawWindow( hwnd, NULL, hrgnUpdate, rdw_flags);
1467         }
1468
1469         /* If the windows has an update region, this must be
1470          * scrolled as well. Keep a copy in hrgnWinupd
1471          * to be added to hrngUpdate at the end. */
1472         hrgnTemp = CreateRectRgn( 0, 0, 0, 0 );
1473         retVal = GetUpdateRgn( hwnd, hrgnTemp, FALSE );
1474         if (retVal != NULLREGION)
1475         {
1476             HRGN hrgnClip = CreateRectRgnIndirect(&cliprc);
1477             if( !bOwnRgn) {
1478                 hrgnWinupd = CreateRectRgn( 0, 0, 0, 0);
1479                 CombineRgn( hrgnWinupd, hrgnTemp, 0, RGN_COPY);
1480             }
1481             OffsetRgn( hrgnTemp, dx, dy );
1482             CombineRgn( hrgnTemp, hrgnTemp, hrgnClip, RGN_AND );
1483             if( !bOwnRgn)
1484                 CombineRgn( hrgnWinupd, hrgnWinupd, hrgnTemp, RGN_OR );
1485             RedrawWindow( hwnd, NULL, hrgnTemp, rdw_flags);
1486
1487            /* Catch the case where the scrolling amount exceeds the size of the
1488             * original window. This generated a second update area that is the
1489             * location where the original scrolled content would end up.
1490             * This second region is not returned by the ScrollDC and sets
1491             * ScrollWindowEx apart from just a ScrollDC.
1492             *
1493             * This has been verified with testing on windows.
1494             */
1495             if (abs(dx) > abs(rc.right - rc.left) ||
1496                 abs(dy) > abs(rc.bottom - rc.top))
1497             {
1498                 SetRectRgn( hrgnTemp, rc.left + dx, rc.top + dy, rc.right+dx, rc.bottom + dy);
1499                 CombineRgn( hrgnTemp, hrgnTemp, hrgnClip, RGN_AND );
1500                 CombineRgn( hrgnUpdate, hrgnUpdate, hrgnTemp, RGN_OR );
1501
1502                 if( !bOwnRgn)
1503                     CombineRgn( hrgnWinupd, hrgnWinupd, hrgnTemp, RGN_OR );
1504             }
1505             DeleteObject( hrgnClip );
1506         }
1507         DeleteObject( hrgnTemp );
1508     } else {
1509         /* nothing was scrolled */
1510         if( !bOwnRgn)
1511             SetRectRgn( hrgnUpdate, 0, 0, 0, 0 );
1512         SetRectEmpty( rcUpdate);
1513     }
1514
1515     if( flags & SW_SCROLLCHILDREN )
1516     {
1517         HWND *list = WIN_ListChildren( hwnd );
1518         if (list)
1519         {
1520             int i;
1521             RECT r, dummy;
1522             for (i = 0; list[i]; i++)
1523             {
1524                 WIN_GetRectangles( list[i], COORDS_PARENT, &r, NULL );
1525                 if (!rect || IntersectRect(&dummy, &r, rect))
1526                     SetWindowPos( list[i], 0, r.left + dx, r.top  + dy, 0, 0,
1527                                   SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
1528                                   SWP_NOREDRAW | SWP_DEFERERASE );
1529             }
1530             HeapFree( GetProcessHeap(), 0, list );
1531         }
1532     }
1533
1534     if( flags & (SW_INVALIDATE | SW_ERASE) )
1535         RedrawWindow( hwnd, NULL, hrgnUpdate, rdw_flags |
1536                       ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0 ) );
1537
1538     if( hrgnWinupd) {
1539         CombineRgn( hrgnUpdate, hrgnUpdate, hrgnWinupd, RGN_OR);
1540         DeleteObject( hrgnWinupd);
1541     }
1542
1543     if( hwndCaret ) {
1544         if ( moveCaret ) SetCaretPos( newCaretPos.x, newCaretPos.y );
1545         ShowCaret(hwndCaret);
1546     }
1547
1548     if( bOwnRgn && hrgnUpdate ) DeleteObject( hrgnUpdate );
1549
1550     return retVal;
1551 }
1552
1553
1554 /*************************************************************************
1555  *              ScrollWindowEx (USER32.@)
1556  *
1557  * Note: contrary to what the doc says, pixels that are scrolled from the
1558  *      outside of clipRect to the inside are NOT painted.
1559  *
1560  */
1561 INT WINAPI ScrollWindowEx( HWND hwnd, INT dx, INT dy,
1562                            const RECT *rect, const RECT *clipRect,
1563                            HRGN hrgnUpdate, LPRECT rcUpdate,
1564                            UINT flags )
1565 {
1566     return scroll_window( hwnd, dx, dy, rect, clipRect, hrgnUpdate, rcUpdate, flags, TRUE );
1567 }
1568
1569 /*************************************************************************
1570  *              ScrollWindow (USER32.@)
1571  *
1572  */
1573 BOOL WINAPI ScrollWindow( HWND hwnd, INT dx, INT dy,
1574                           const RECT *rect, const RECT *clipRect )
1575 {
1576     return scroll_window( hwnd, dx, dy, rect, clipRect, 0, NULL,
1577                           SW_INVALIDATE | SW_ERASE | (rect ? 0 : SW_SCROLLCHILDREN), FALSE ) != ERROR;
1578 }
1579
1580
1581 /*************************************************************************
1582  *              ScrollDC (USER32.@)
1583  *
1584  * dx, dy, lprcScroll and lprcClip are all in logical coordinates (msdn is
1585  * wrong) hrgnUpdate is returned in device coordinates with rcUpdate in
1586  * logical coordinates.
1587  */
1588 BOOL WINAPI ScrollDC( HDC hdc, INT dx, INT dy, const RECT *lprcScroll,
1589                       const RECT *lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate )
1590
1591 {
1592     return USER_Driver->pScrollDC( hdc, dx, dy, lprcScroll, lprcClip, hrgnUpdate, lprcUpdate );
1593 }
1594
1595 /************************************************************************
1596  *              PrintWindow (USER32.@)
1597  *
1598  */
1599 BOOL WINAPI PrintWindow(HWND hwnd, HDC hdcBlt, UINT nFlags)
1600 {
1601     UINT flags = PRF_CHILDREN | PRF_ERASEBKGND | PRF_OWNED | PRF_CLIENT;
1602     if(!(nFlags & PW_CLIENTONLY))
1603     {
1604         flags |= PRF_NONCLIENT;
1605     }
1606     SendMessageW(hwnd, WM_PRINT, (WPARAM)hdcBlt, flags);
1607     return TRUE;
1608 }