cmd: DIR command outputs free space for the path.
[wine.git] / dlls / win32u / dce.c
blob5df30550cae3bae74e80c1d79ff6f0f1ea6d7b83
1 /*
2 * Window painting functions
4 * Copyright 1993, 1994, 1995, 2001, 2004, 2005, 2008 Alexandre Julliard
5 * Copyright 1996, 1997, 1999 Alex Korobka
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #if 0
23 #pragma makedep unix
24 #endif
26 #include <assert.h>
27 #include <pthread.h>
28 #include "ntstatus.h"
29 #define WIN32_NO_STATUS
30 #include "ntgdi_private.h"
31 #include "ntuser_private.h"
32 #include "wine/server.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(win);
37 struct dce
39 struct list entry; /* entry in global DCE list */
40 HDC hdc;
41 HWND hwnd;
42 HRGN clip_rgn;
43 UINT flags;
44 LONG count; /* usage count; 0 or 1 for cache DCEs, always 1 for window DCEs,
45 always >= 1 for class DCEs */
48 static struct list dce_list = LIST_INIT(dce_list);
50 #define DCE_CACHE_SIZE 64
52 static struct list window_surfaces = LIST_INIT( window_surfaces );
53 static pthread_mutex_t surfaces_lock = PTHREAD_MUTEX_INITIALIZER;
55 /*******************************************************************
56 * Dummy window surface for windows that shouldn't get painted.
59 static void dummy_surface_lock( struct window_surface *window_surface )
61 /* nothing to do */
64 static void dummy_surface_unlock( struct window_surface *window_surface )
66 /* nothing to do */
69 static void *dummy_surface_get_bitmap_info( struct window_surface *window_surface, BITMAPINFO *info )
71 static DWORD dummy_data;
73 info->bmiHeader.biSize = sizeof( info->bmiHeader );
74 info->bmiHeader.biWidth = dummy_surface.rect.right;
75 info->bmiHeader.biHeight = dummy_surface.rect.bottom;
76 info->bmiHeader.biPlanes = 1;
77 info->bmiHeader.biBitCount = 32;
78 info->bmiHeader.biCompression = BI_RGB;
79 info->bmiHeader.biSizeImage = 0;
80 info->bmiHeader.biXPelsPerMeter = 0;
81 info->bmiHeader.biYPelsPerMeter = 0;
82 info->bmiHeader.biClrUsed = 0;
83 info->bmiHeader.biClrImportant = 0;
84 return &dummy_data;
87 static RECT *dummy_surface_get_bounds( struct window_surface *window_surface )
89 static RECT dummy_bounds;
90 return &dummy_bounds;
93 static void dummy_surface_set_region( struct window_surface *window_surface, HRGN region )
95 /* nothing to do */
98 static void dummy_surface_flush( struct window_surface *window_surface )
100 /* nothing to do */
103 static void dummy_surface_destroy( struct window_surface *window_surface )
105 /* nothing to do */
108 static const struct window_surface_funcs dummy_surface_funcs =
110 dummy_surface_lock,
111 dummy_surface_unlock,
112 dummy_surface_get_bitmap_info,
113 dummy_surface_get_bounds,
114 dummy_surface_set_region,
115 dummy_surface_flush,
116 dummy_surface_destroy
119 struct window_surface dummy_surface = { &dummy_surface_funcs, { NULL, NULL }, 1, { 0, 0, 1, 1 } };
121 /*******************************************************************
122 * Off-screen window surface.
125 struct offscreen_window_surface
127 struct window_surface header;
128 pthread_mutex_t mutex;
129 RECT bounds;
130 char *bits;
131 BITMAPINFO info;
134 static const struct window_surface_funcs offscreen_window_surface_funcs;
136 static struct offscreen_window_surface *impl_from_window_surface( struct window_surface *base )
138 if (!base || base->funcs != &offscreen_window_surface_funcs) return NULL;
139 return CONTAINING_RECORD( base, struct offscreen_window_surface, header );
142 static void offscreen_window_surface_lock( struct window_surface *base )
144 struct offscreen_window_surface *impl = impl_from_window_surface( base );
145 pthread_mutex_lock( &impl->mutex );
148 static void offscreen_window_surface_unlock( struct window_surface *base )
150 struct offscreen_window_surface *impl = impl_from_window_surface( base );
151 pthread_mutex_unlock( &impl->mutex );
154 static RECT *offscreen_window_surface_get_bounds( struct window_surface *base )
156 struct offscreen_window_surface *impl = impl_from_window_surface( base );
157 return &impl->bounds;
160 static void *offscreen_window_surface_get_bitmap_info( struct window_surface *base, BITMAPINFO *info )
162 struct offscreen_window_surface *impl = impl_from_window_surface( base );
163 info->bmiHeader = impl->info.bmiHeader;
164 return impl->bits;
167 static void offscreen_window_surface_set_region( struct window_surface *base, HRGN region )
171 static void offscreen_window_surface_flush( struct window_surface *base )
173 struct offscreen_window_surface *impl = impl_from_window_surface( base );
174 base->funcs->lock( base );
175 reset_bounds( &impl->bounds );
176 base->funcs->unlock( base );
179 static void offscreen_window_surface_destroy( struct window_surface *base )
181 struct offscreen_window_surface *impl = impl_from_window_surface( base );
182 free( impl );
185 static const struct window_surface_funcs offscreen_window_surface_funcs =
187 offscreen_window_surface_lock,
188 offscreen_window_surface_unlock,
189 offscreen_window_surface_get_bitmap_info,
190 offscreen_window_surface_get_bounds,
191 offscreen_window_surface_set_region,
192 offscreen_window_surface_flush,
193 offscreen_window_surface_destroy
196 void create_offscreen_window_surface( const RECT *visible_rect, struct window_surface **surface )
198 struct offscreen_window_surface *impl;
199 SIZE_T size;
200 RECT surface_rect = *visible_rect;
201 pthread_mutexattr_t attr;
203 TRACE( "visible_rect %s, surface %p.\n", wine_dbgstr_rect( visible_rect ), surface );
205 OffsetRect( &surface_rect, -surface_rect.left, -surface_rect.top );
206 surface_rect.right = (surface_rect.right + 0x1f) & ~0x1f;
207 surface_rect.bottom = (surface_rect.bottom + 0x1f) & ~0x1f;
209 /* check that old surface is an offscreen_window_surface, or release it */
210 if ((impl = impl_from_window_surface( *surface )))
212 /* if the rect didn't change, keep the same surface */
213 if (EqualRect( &surface_rect, &impl->header.rect )) return;
214 window_surface_release( &impl->header );
216 else if (*surface) window_surface_release( *surface );
218 /* create a new window surface */
219 *surface = NULL;
220 size = surface_rect.right * surface_rect.bottom * 4;
221 if (!(impl = calloc(1, offsetof( struct offscreen_window_surface, info.bmiColors[0] ) + size))) return;
223 impl->header.funcs = &offscreen_window_surface_funcs;
224 impl->header.ref = 1;
225 impl->header.rect = surface_rect;
227 pthread_mutexattr_init( &attr );
228 pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
229 pthread_mutex_init( &impl->mutex, &attr );
230 pthread_mutexattr_destroy( &attr );
232 reset_bounds( &impl->bounds );
234 impl->bits = (char *)&impl->info.bmiColors[0];
235 impl->info.bmiHeader.biSize = sizeof( impl->info );
236 impl->info.bmiHeader.biWidth = surface_rect.right;
237 impl->info.bmiHeader.biHeight = surface_rect.bottom;
238 impl->info.bmiHeader.biPlanes = 1;
239 impl->info.bmiHeader.biBitCount = 32;
240 impl->info.bmiHeader.biCompression = BI_RGB;
241 impl->info.bmiHeader.biSizeImage = size;
243 TRACE( "created window surface %p\n", &impl->header );
245 *surface = &impl->header;
248 /* window surface used to implement the DIB.DRV driver */
250 struct dib_window_surface
252 struct window_surface header;
253 RECT bounds;
254 void *bits;
255 UINT info_size;
256 BITMAPINFO info; /* variable size, must be last */
259 static struct dib_window_surface *get_dib_surface( struct window_surface *surface )
261 return (struct dib_window_surface *)surface;
264 /***********************************************************************
265 * dib_surface_lock
267 static void dib_surface_lock( struct window_surface *window_surface )
269 /* nothing to do */
272 /***********************************************************************
273 * dib_surface_unlock
275 static void dib_surface_unlock( struct window_surface *window_surface )
277 /* nothing to do */
280 /***********************************************************************
281 * dib_surface_get_bitmap_info
283 static void *dib_surface_get_bitmap_info( struct window_surface *window_surface, BITMAPINFO *info )
285 struct dib_window_surface *surface = get_dib_surface( window_surface );
287 memcpy( info, &surface->info, surface->info_size );
288 return surface->bits;
291 /***********************************************************************
292 * dib_surface_get_bounds
294 static RECT *dib_surface_get_bounds( struct window_surface *window_surface )
296 struct dib_window_surface *surface = get_dib_surface( window_surface );
298 return &surface->bounds;
301 /***********************************************************************
302 * dib_surface_set_region
304 static void dib_surface_set_region( struct window_surface *window_surface, HRGN region )
306 /* nothing to do */
309 /***********************************************************************
310 * dib_surface_flush
312 static void dib_surface_flush( struct window_surface *window_surface )
314 /* nothing to do */
317 /***********************************************************************
318 * dib_surface_destroy
320 static void dib_surface_destroy( struct window_surface *window_surface )
322 struct dib_window_surface *surface = get_dib_surface( window_surface );
324 TRACE( "freeing %p\n", surface );
325 free( surface );
328 static const struct window_surface_funcs dib_surface_funcs =
330 dib_surface_lock,
331 dib_surface_unlock,
332 dib_surface_get_bitmap_info,
333 dib_surface_get_bounds,
334 dib_surface_set_region,
335 dib_surface_flush,
336 dib_surface_destroy
339 BOOL create_dib_surface( HDC hdc, const BITMAPINFO *info )
341 struct dib_window_surface *surface;
342 int color = 0;
343 HRGN region;
344 RECT rect;
346 if (info->bmiHeader.biBitCount <= 8)
347 color = info->bmiHeader.biClrUsed ? info->bmiHeader.biClrUsed : (1 << info->bmiHeader.biBitCount);
348 else if (info->bmiHeader.biCompression == BI_BITFIELDS)
349 color = 3;
351 surface = calloc( 1, offsetof( struct dib_window_surface, info.bmiColors[color] ));
352 if (!surface) return FALSE;
354 rect.left = 0;
355 rect.top = 0;
356 rect.right = info->bmiHeader.biWidth;
357 rect.bottom = abs(info->bmiHeader.biHeight);
359 surface->header.funcs = &dib_surface_funcs;
360 surface->header.rect = rect;
361 surface->header.ref = 1;
362 surface->info_size = offsetof( BITMAPINFO, bmiColors[color] );
363 surface->bits = (char *)info + surface->info_size;
364 memcpy( &surface->info, info, surface->info_size );
366 TRACE( "created %p %ux%u for info %p bits %p\n",
367 surface, (int)rect.right, (int)rect.bottom, info, surface->bits );
369 region = NtGdiCreateRectRgn( rect.left, rect.top, rect.right, rect.bottom );
370 set_visible_region( hdc, region, &rect, &rect, &surface->header );
371 TRACE( "using hdc %p surface %p\n", hdc, surface );
372 window_surface_release( &surface->header );
373 return TRUE;
376 /*******************************************************************
377 * register_window_surface
379 * Register a window surface in the global list, possibly replacing another one.
381 void register_window_surface( struct window_surface *old, struct window_surface *new )
383 if (old == &dummy_surface) old = NULL;
384 if (new == &dummy_surface) new = NULL;
385 if (old == new) return;
386 pthread_mutex_lock( &surfaces_lock );
387 if (old) list_remove( &old->entry );
388 if (new) list_add_tail( &window_surfaces, &new->entry );
389 pthread_mutex_unlock( &surfaces_lock );
392 /*******************************************************************
393 * flush_window_surfaces
395 * Flush pending output from all window surfaces.
397 void flush_window_surfaces( BOOL idle )
399 static DWORD last_idle;
400 DWORD now;
401 struct window_surface *surface;
403 pthread_mutex_lock( &surfaces_lock );
404 now = NtGetTickCount();
405 if (idle) last_idle = now;
406 /* if not idle, we only flush if there's evidence that the app never goes idle */
407 else if ((int)(now - last_idle) < 50) goto done;
409 LIST_FOR_EACH_ENTRY( surface, &window_surfaces, struct window_surface, entry )
410 surface->funcs->flush( surface );
411 done:
412 pthread_mutex_unlock( &surfaces_lock );
415 /***********************************************************************
416 * dump_rdw_flags
418 static void dump_rdw_flags(UINT flags)
420 TRACE("flags:");
421 if (flags & RDW_INVALIDATE) TRACE(" RDW_INVALIDATE");
422 if (flags & RDW_INTERNALPAINT) TRACE(" RDW_INTERNALPAINT");
423 if (flags & RDW_ERASE) TRACE(" RDW_ERASE");
424 if (flags & RDW_VALIDATE) TRACE(" RDW_VALIDATE");
425 if (flags & RDW_NOINTERNALPAINT) TRACE(" RDW_NOINTERNALPAINT");
426 if (flags & RDW_NOERASE) TRACE(" RDW_NOERASE");
427 if (flags & RDW_NOCHILDREN) TRACE(" RDW_NOCHILDREN");
428 if (flags & RDW_ALLCHILDREN) TRACE(" RDW_ALLCHILDREN");
429 if (flags & RDW_UPDATENOW) TRACE(" RDW_UPDATENOW");
430 if (flags & RDW_ERASENOW) TRACE(" RDW_ERASENOW");
431 if (flags & RDW_FRAME) TRACE(" RDW_FRAME");
432 if (flags & RDW_NOFRAME) TRACE(" RDW_NOFRAME");
434 #define RDW_FLAGS \
435 (RDW_INVALIDATE | \
436 RDW_INTERNALPAINT | \
437 RDW_ERASE | \
438 RDW_VALIDATE | \
439 RDW_NOINTERNALPAINT | \
440 RDW_NOERASE | \
441 RDW_NOCHILDREN | \
442 RDW_ALLCHILDREN | \
443 RDW_UPDATENOW | \
444 RDW_ERASENOW | \
445 RDW_FRAME | \
446 RDW_NOFRAME)
448 if (flags & ~RDW_FLAGS) TRACE(" %04x", flags & ~RDW_FLAGS);
449 TRACE("\n");
450 #undef RDW_FLAGS
453 /***********************************************************************
454 * update_visible_region
456 * Set the visible region and X11 drawable for the DC associated to
457 * a given window.
459 static void update_visible_region( struct dce *dce )
461 struct window_surface *surface = NULL;
462 NTSTATUS status;
463 HRGN vis_rgn = 0;
464 HWND top_win = 0;
465 DWORD flags = dce->flags;
466 DWORD paint_flags = 0;
467 size_t size = 256;
468 RECT win_rect, top_rect;
469 WND *win;
471 /* don't clip siblings if using parent clip region */
472 if (flags & DCX_PARENTCLIP) flags &= ~DCX_CLIPSIBLINGS;
474 /* fetch the visible region from the server */
477 RGNDATA *data = malloc( sizeof(*data) + size - 1 );
478 if (!data) return;
480 SERVER_START_REQ( get_visible_region )
482 req->window = wine_server_user_handle( dce->hwnd );
483 req->flags = flags;
484 wine_server_set_reply( req, data->Buffer, size );
485 if (!(status = wine_server_call( req )))
487 size_t reply_size = wine_server_reply_size( reply );
488 data->rdh.dwSize = sizeof(data->rdh);
489 data->rdh.iType = RDH_RECTANGLES;
490 data->rdh.nCount = reply_size / sizeof(RECT);
491 data->rdh.nRgnSize = reply_size;
492 vis_rgn = NtGdiExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data );
494 top_win = wine_server_ptr_handle( reply->top_win );
495 win_rect.left = reply->win_rect.left;
496 win_rect.top = reply->win_rect.top;
497 win_rect.right = reply->win_rect.right;
498 win_rect.bottom = reply->win_rect.bottom;
499 top_rect.left = reply->top_rect.left;
500 top_rect.top = reply->top_rect.top;
501 top_rect.right = reply->top_rect.right;
502 top_rect.bottom = reply->top_rect.bottom;
503 paint_flags = reply->paint_flags;
505 else size = reply->total_size;
507 SERVER_END_REQ;
508 free( data );
509 } while (status == STATUS_BUFFER_OVERFLOW);
511 if (status || !vis_rgn) return;
513 user_driver->pGetDC( dce->hdc, dce->hwnd, top_win, &win_rect, &top_rect, flags );
515 if (dce->clip_rgn) NtGdiCombineRgn( vis_rgn, vis_rgn, dce->clip_rgn,
516 (flags & DCX_INTERSECTRGN) ? RGN_AND : RGN_DIFF );
518 /* don't use a surface to paint the client area of OpenGL windows */
519 if (!(paint_flags & SET_WINPOS_PIXEL_FORMAT) || (flags & DCX_WINDOW))
521 win = get_win_ptr( top_win );
522 if (win && win != WND_DESKTOP && win != WND_OTHER_PROCESS)
524 surface = win->surface;
525 if (surface) window_surface_add_ref( surface );
526 release_win_ptr( win );
530 if (!surface) SetRectEmpty( &top_rect );
531 set_visible_region( dce->hdc, vis_rgn, &win_rect, &top_rect, surface );
532 if (surface) window_surface_release( surface );
535 /***********************************************************************
536 * release_dce
538 static void release_dce( struct dce *dce )
540 if (!dce->hwnd) return; /* already released */
542 set_visible_region( dce->hdc, 0, &dummy_surface.rect, &dummy_surface.rect, &dummy_surface );
543 user_driver->pReleaseDC( dce->hwnd, dce->hdc );
545 if (dce->clip_rgn) NtGdiDeleteObjectApp( dce->clip_rgn );
546 dce->clip_rgn = 0;
547 dce->hwnd = 0;
548 dce->flags &= DCX_CACHE;
551 /***********************************************************************
552 * delete_clip_rgn
554 static void delete_clip_rgn( struct dce *dce )
556 if (!dce->clip_rgn) return; /* nothing to do */
558 dce->flags &= ~(DCX_EXCLUDERGN | DCX_INTERSECTRGN);
559 NtGdiDeleteObjectApp( dce->clip_rgn );
560 dce->clip_rgn = 0;
562 /* make it dirty so that the vis rgn gets recomputed next time */
563 set_dce_flags( dce->hdc, DCHF_INVALIDATEVISRGN );
566 /***********************************************************************
567 * delete_dce
569 BOOL delete_dce( struct dce *dce )
571 BOOL ret = TRUE;
573 TRACE( "hdc = %p\n", dce->hdc );
575 user_lock();
576 if (!(dce->flags & DCX_CACHE))
578 WARN("Application trying to delete an owned DC %p\n", dce->hdc);
579 ret = FALSE;
581 else
583 list_remove( &dce->entry );
584 if (dce->clip_rgn) NtGdiDeleteObjectApp( dce->clip_rgn );
585 free( dce );
587 user_unlock();
588 return ret;
591 /***********************************************************************
592 * update_dc
594 * Make sure the DC vis region is up to date.
595 * This function may need user lock so the GDI lock should _not_
596 * be held when calling it.
598 void update_dc( DC *dc )
600 if (!dc->dirty) return;
601 dc->dirty = 0;
602 if (dc->dce)
604 if (dc->dce->count) update_visible_region( dc->dce );
605 else /* non-fatal but shouldn't happen */
606 WARN("DC is not in use!\n");
610 /***********************************************************************
611 * alloc_dce
613 * Allocate a new DCE.
615 static struct dce *alloc_dce(void)
617 struct dce *dce;
619 if (!(dce = malloc( sizeof(*dce) ))) return NULL;
620 if (!(dce->hdc = NtGdiOpenDCW( NULL, NULL, NULL, 0, TRUE, 0, NULL, NULL )))
622 free( dce );
623 return 0;
625 dce->hwnd = 0;
626 dce->clip_rgn = 0;
627 dce->flags = 0;
628 dce->count = 1;
630 set_dc_dce( dce->hdc, dce );
631 return dce;
634 /***********************************************************************
635 * get_window_dce
637 static struct dce *get_window_dce( HWND hwnd )
639 struct dce *dce;
640 WND *win = get_win_ptr( hwnd );
642 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return NULL;
644 dce = win->dce;
645 if (!dce && (dce = get_class_dce( win->class )))
647 win->dce = dce;
648 dce->count++;
650 release_win_ptr( win );
652 if (!dce) /* try to allocate one */
654 struct dce *dce_to_free = NULL;
655 LONG class_style = get_class_long( hwnd, GCL_STYLE, FALSE );
657 if (class_style & CS_CLASSDC)
659 if (!(dce = alloc_dce())) return NULL;
661 win = get_win_ptr( hwnd );
662 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
664 if (win->dce) /* another thread beat us to it */
666 dce_to_free = dce;
667 dce = win->dce;
669 else if ((win->dce = set_class_dce( win->class, dce )) != dce)
671 dce_to_free = dce;
672 dce = win->dce;
673 dce->count++;
675 else
677 dce->count++;
678 list_add_tail( &dce_list, &dce->entry );
680 release_win_ptr( win );
682 else dce_to_free = dce;
684 else if (class_style & CS_OWNDC)
686 if (!(dce = alloc_dce())) return NULL;
688 win = get_win_ptr( hwnd );
689 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
691 if (win->dwStyle & WS_CLIPCHILDREN) dce->flags |= DCX_CLIPCHILDREN;
692 if (win->dwStyle & WS_CLIPSIBLINGS) dce->flags |= DCX_CLIPSIBLINGS;
693 if (win->dce) /* another thread beat us to it */
695 dce_to_free = dce;
696 dce = win->dce;
698 else
700 win->dce = dce;
701 dce->hwnd = hwnd;
702 list_add_tail( &dce_list, &dce->entry );
704 release_win_ptr( win );
706 else dce_to_free = dce;
709 if (dce_to_free)
711 set_dc_dce( dce_to_free->hdc, NULL );
712 NtGdiDeleteObjectApp( dce_to_free->hdc );
713 free( dce_to_free );
714 if (dce_to_free == dce)
715 dce = NULL;
718 return dce;
721 /***********************************************************************
722 * free_dce
724 * Free a class or window DCE.
726 void free_dce( struct dce *dce, HWND hwnd )
728 struct dce *dce_to_free = NULL;
730 user_lock();
732 if (dce)
734 if (!--dce->count)
736 release_dce( dce );
737 list_remove( &dce->entry );
738 dce_to_free = dce;
740 else if (dce->hwnd == hwnd)
742 release_dce( dce );
746 /* now check for cache DCEs */
748 if (hwnd)
750 LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
752 if (dce->hwnd != hwnd) continue;
753 if (!(dce->flags & DCX_CACHE)) break;
755 release_dce( dce );
756 if (dce->count)
758 WARN( "GetDC() without ReleaseDC() for window %p\n", hwnd );
759 dce->count = 0;
760 set_dce_flags( dce->hdc, DCHF_DISABLEDC );
765 user_unlock();
767 if (dce_to_free)
769 set_dc_dce( dce_to_free->hdc, NULL );
770 NtGdiDeleteObjectApp( dce_to_free->hdc );
771 free( dce_to_free );
775 /***********************************************************************
776 * make_dc_dirty
778 * Mark the associated DC as dirty to force a refresh of the visible region
780 static void make_dc_dirty( struct dce *dce )
782 if (!dce->count)
784 /* Don't bother with visible regions of unused DCEs */
785 TRACE("purged %p hwnd %p\n", dce->hdc, dce->hwnd);
786 release_dce( dce );
788 else
790 /* Set dirty bits in the hDC and DCE structs */
791 TRACE("fixed up %p hwnd %p\n", dce->hdc, dce->hwnd);
792 set_dce_flags( dce->hdc, DCHF_INVALIDATEVISRGN );
796 /***********************************************************************
797 * invalidate_dce
799 * It is called from SetWindowPos() - we have to
800 * mark as dirty all busy DCEs for windows that have pWnd->parent as
801 * an ancestor and whose client rect intersects with specified update
802 * rectangle. In addition, pWnd->parent DCEs may need to be updated if
803 * DCX_CLIPCHILDREN flag is set.
805 void invalidate_dce( WND *win, const RECT *extra_rect )
807 DPI_AWARENESS_CONTEXT context;
808 RECT window_rect;
809 struct dce *dce;
811 if (!win->parent) return;
813 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( win->obj.handle ));
814 get_window_rect( win->obj.handle, &window_rect, get_thread_dpi() );
816 TRACE("%p parent %p %s (%s)\n",
817 win->obj.handle, win->parent, wine_dbgstr_rect(&window_rect), wine_dbgstr_rect(extra_rect) );
819 /* walk all DCEs and fixup non-empty entries */
821 LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
823 if (!dce->hwnd) continue;
825 TRACE( "%p: hwnd %p dcx %08x %s %s\n", dce->hdc, dce->hwnd, dce->flags,
826 (dce->flags & DCX_CACHE) ? "Cache" : "Owned", dce->count ? "InUse" : "" );
828 if ((dce->hwnd == win->parent) && !(dce->flags & DCX_CLIPCHILDREN))
829 continue; /* child window positions don't bother us */
831 /* if DCE window is a child of hwnd, it has to be invalidated */
832 if (dce->hwnd == win->obj.handle || is_child( win->obj.handle, dce->hwnd ))
834 make_dc_dirty( dce );
835 continue;
838 /* otherwise check if the window rectangle intersects this DCE window */
839 if (win->parent == dce->hwnd || is_child( win->parent, dce->hwnd ))
841 RECT dce_rect, tmp;
842 get_window_rect( dce->hwnd, &dce_rect, get_thread_dpi() );
843 if (intersect_rect( &tmp, &dce_rect, &window_rect ) ||
844 (extra_rect && intersect_rect( &tmp, &dce_rect, extra_rect )))
845 make_dc_dirty( dce );
848 SetThreadDpiAwarenessContext( context );
851 /***********************************************************************
852 * release_dc
854 static INT release_dc( HWND hwnd, HDC hdc, BOOL end_paint )
856 struct dce *dce;
857 BOOL ret = FALSE;
859 TRACE( "%p %p\n", hwnd, hdc );
861 user_lock();
862 dce = get_dc_dce( hdc );
863 if (dce && dce->count && dce->hwnd)
865 if (!(dce->flags & DCX_NORESETATTRS)) set_dce_flags( dce->hdc, DCHF_RESETDC );
866 if (end_paint || (dce->flags & DCX_CACHE)) delete_clip_rgn( dce );
867 if (dce->flags & DCX_CACHE)
869 dce->count = 0;
870 set_dce_flags( dce->hdc, DCHF_DISABLEDC );
872 ret = TRUE;
874 user_unlock();
875 return ret;
878 /***********************************************************************
879 * NtUserGetDCEx (win32u.@)
881 HDC WINAPI NtUserGetDCEx( HWND hwnd, HRGN clip_rgn, DWORD flags )
883 const DWORD clip_flags = DCX_PARENTCLIP | DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_WINDOW;
884 const DWORD user_flags = clip_flags | DCX_NORESETATTRS; /* flags that can be set by user */
885 BOOL update_vis_rgn = TRUE;
886 struct dce *dce;
887 HWND parent;
888 LONG window_style = get_window_long( hwnd, GWL_STYLE );
890 if (!hwnd) hwnd = get_desktop_window();
891 else hwnd = get_full_window_handle( hwnd );
893 TRACE( "hwnd %p, clip_rgn %p, flags %08x\n", hwnd, clip_rgn, (int)flags );
895 if (!is_window(hwnd)) return 0;
897 /* fixup flags */
899 if (flags & (DCX_WINDOW | DCX_PARENTCLIP)) flags |= DCX_CACHE;
901 if (flags & DCX_USESTYLE)
903 flags &= ~(DCX_CLIPCHILDREN | DCX_CLIPSIBLINGS | DCX_PARENTCLIP);
905 if (window_style & WS_CLIPSIBLINGS) flags |= DCX_CLIPSIBLINGS;
907 if (!(flags & DCX_WINDOW))
909 if (get_class_long( hwnd, GCL_STYLE, FALSE ) & CS_PARENTDC) flags |= DCX_PARENTCLIP;
911 if (window_style & WS_CLIPCHILDREN && !(window_style & WS_MINIMIZE))
912 flags |= DCX_CLIPCHILDREN;
916 if (flags & DCX_WINDOW) flags &= ~DCX_CLIPCHILDREN;
918 parent = NtUserGetAncestor( hwnd, GA_PARENT );
919 if (!parent || (parent == get_desktop_window()))
920 flags = (flags & ~DCX_PARENTCLIP) | DCX_CLIPSIBLINGS;
922 /* it seems parent clip is ignored when clipping siblings or children */
923 if (flags & (DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN)) flags &= ~DCX_PARENTCLIP;
925 if( flags & DCX_PARENTCLIP )
927 LONG parent_style = get_window_long( parent, GWL_STYLE );
928 if( (window_style & WS_VISIBLE) && (parent_style & WS_VISIBLE) )
930 flags &= ~DCX_CLIPCHILDREN;
931 if (parent_style & WS_CLIPSIBLINGS) flags |= DCX_CLIPSIBLINGS;
935 /* find a suitable DCE */
937 if ((flags & DCX_CACHE) || !(dce = get_window_dce( hwnd )))
939 struct dce *dceEmpty = NULL, *dceUnused = NULL, *found = NULL;
940 unsigned int count = 0;
942 /* Strategy: First, we attempt to find a non-empty but unused DCE with
943 * compatible flags. Next, we look for an empty entry. If the cache is
944 * full we have to purge one of the unused entries.
946 user_lock();
947 LIST_FOR_EACH_ENTRY( dce, &dce_list, struct dce, entry )
949 if (!(dce->flags & DCX_CACHE)) break;
950 count++;
951 if (dce->count) continue;
952 dceUnused = dce;
953 if (!dce->hwnd) dceEmpty = dce;
954 else if ((dce->hwnd == hwnd) && !((dce->flags ^ flags) & clip_flags))
956 TRACE( "found valid %p hwnd %p, flags %08x\n", dce->hdc, hwnd, dce->flags );
957 found = dce;
958 update_vis_rgn = FALSE;
959 break;
962 if (!found) found = dceEmpty;
963 if (!found && count >= DCE_CACHE_SIZE) found = dceUnused;
965 dce = found;
966 if (dce)
968 dce->count = 1;
969 set_dce_flags( dce->hdc, DCHF_ENABLEDC );
971 user_unlock();
973 /* if there's no dce empty or unused, allocate a new one */
974 if (!dce)
976 if (!(dce = alloc_dce())) return 0;
977 dce->flags = DCX_CACHE;
978 user_lock();
979 list_add_head( &dce_list, &dce->entry );
980 user_unlock();
983 else
985 flags |= DCX_NORESETATTRS;
986 if (dce->hwnd != hwnd)
988 /* we should free dce->clip_rgn here, but Windows apparently doesn't */
989 dce->flags &= ~(DCX_EXCLUDERGN | DCX_INTERSECTRGN);
990 dce->clip_rgn = 0;
992 else update_vis_rgn = FALSE; /* updated automatically, via DCHook() */
995 if (flags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN))
997 /* if the extra clip region has changed, get rid of the old one */
998 if (dce->clip_rgn != clip_rgn || ((flags ^ dce->flags) & (DCX_INTERSECTRGN | DCX_EXCLUDERGN)))
999 delete_clip_rgn( dce );
1000 dce->clip_rgn = clip_rgn;
1001 if (!dce->clip_rgn) dce->clip_rgn = NtGdiCreateRectRgn( 0, 0, 0, 0 );
1002 dce->flags |= flags & (DCX_INTERSECTRGN | DCX_EXCLUDERGN);
1003 update_vis_rgn = TRUE;
1006 if (get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL)
1007 NtGdiSetLayout( dce->hdc, -1, LAYOUT_RTL );
1009 dce->hwnd = hwnd;
1010 dce->flags = (dce->flags & ~user_flags) | (flags & user_flags);
1012 /* cross-process invalidation is not supported yet, so always update the vis rgn */
1013 if (!is_current_process_window( hwnd )) update_vis_rgn = TRUE;
1015 if (set_dce_flags( dce->hdc, DCHF_VALIDATEVISRGN )) update_vis_rgn = TRUE; /* DC was dirty */
1017 if (update_vis_rgn) update_visible_region( dce );
1019 TRACE( "(%p,%p,0x%x): returning %p%s\n", hwnd, clip_rgn, (int)flags, dce->hdc,
1020 update_vis_rgn ? " (updated)" : "" );
1021 return dce->hdc;
1024 /***********************************************************************
1025 * NtUserReleaseDC (win32u.@)
1027 INT WINAPI NtUserReleaseDC( HWND hwnd, HDC hdc )
1029 return release_dc( hwnd, hdc, FALSE );
1032 /***********************************************************************
1033 * NtUserGetDC (win32u.@)
1035 HDC WINAPI NtUserGetDC( HWND hwnd )
1037 if (!hwnd) return NtUserGetDCEx( 0, 0, DCX_CACHE | DCX_WINDOW );
1038 return NtUserGetDCEx( hwnd, 0, DCX_USESTYLE );
1041 /***********************************************************************
1042 * NtUserGetWindowDC (win32u.@)
1044 HDC WINAPI NtUserGetWindowDC( HWND hwnd )
1046 return NtUserGetDCEx( hwnd, 0, DCX_USESTYLE | DCX_WINDOW );
1049 /**********************************************************************
1050 * NtUserWindowFromDC (win32u.@)
1052 HWND WINAPI NtUserWindowFromDC( HDC hdc )
1054 struct dce *dce;
1055 HWND hwnd = 0;
1057 user_lock();
1058 dce = get_dc_dce( hdc );
1059 if (dce) hwnd = dce->hwnd;
1060 user_unlock();
1061 return hwnd;
1064 /***********************************************************************
1065 * get_update_region
1067 * Return update region (in screen coordinates) for a window.
1069 static HRGN get_update_region( HWND hwnd, UINT *flags, HWND *child )
1071 HRGN hrgn = 0;
1072 NTSTATUS status;
1073 RGNDATA *data;
1074 size_t size = 256;
1078 if (!(data = malloc( sizeof(*data) + size - 1 )))
1080 RtlSetLastWin32Error( ERROR_OUTOFMEMORY );
1081 return 0;
1084 SERVER_START_REQ( get_update_region )
1086 req->window = wine_server_user_handle( hwnd );
1087 req->from_child = wine_server_user_handle( child ? *child : 0 );
1088 req->flags = *flags;
1089 wine_server_set_reply( req, data->Buffer, size );
1090 if (!(status = wine_server_call( req )))
1092 size_t reply_size = wine_server_reply_size( reply );
1093 data->rdh.dwSize = sizeof(data->rdh);
1094 data->rdh.iType = RDH_RECTANGLES;
1095 data->rdh.nCount = reply_size / sizeof(RECT);
1096 data->rdh.nRgnSize = reply_size;
1097 hrgn = NtGdiExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data );
1098 if (child) *child = wine_server_ptr_handle( reply->child );
1099 *flags = reply->flags;
1101 else size = reply->total_size;
1103 SERVER_END_REQ;
1104 free( data );
1105 } while (status == STATUS_BUFFER_OVERFLOW);
1107 if (status) RtlSetLastWin32Error( RtlNtStatusToDosError(status) );
1108 return hrgn;
1111 /***********************************************************************
1112 * redraw_window_rects
1114 * Redraw part of a window.
1116 static BOOL redraw_window_rects( HWND hwnd, UINT flags, const RECT *rects, UINT count )
1118 BOOL ret;
1120 if (!(flags & (RDW_INVALIDATE|RDW_VALIDATE|RDW_INTERNALPAINT|RDW_NOINTERNALPAINT)))
1121 return TRUE; /* nothing to do */
1123 SERVER_START_REQ( redraw_window )
1125 req->window = wine_server_user_handle( hwnd );
1126 req->flags = flags;
1127 wine_server_add_data( req, rects, count * sizeof(RECT) );
1128 ret = !wine_server_call_err( req );
1130 SERVER_END_REQ;
1131 return ret;
1134 /***********************************************************************
1135 * get_update_flags
1137 * Get only the update flags, not the update region.
1139 static BOOL get_update_flags( HWND hwnd, HWND *child, UINT *flags )
1141 BOOL ret;
1143 SERVER_START_REQ( get_update_region )
1145 req->window = wine_server_user_handle( hwnd );
1146 req->from_child = wine_server_user_handle( child ? *child : 0 );
1147 req->flags = *flags | UPDATE_NOREGION;
1148 if ((ret = !wine_server_call_err( req )))
1150 if (child) *child = wine_server_ptr_handle( reply->child );
1151 *flags = reply->flags;
1154 SERVER_END_REQ;
1155 return ret;
1158 /***********************************************************************
1159 * send_ncpaint
1161 * Send a WM_NCPAINT message if needed, and return the resulting update region (in screen coords).
1162 * Helper for erase_now and BeginPaint.
1164 static HRGN send_ncpaint( HWND hwnd, HWND *child, UINT *flags )
1166 HRGN whole_rgn = get_update_region( hwnd, flags, child );
1167 HRGN client_rgn = 0;
1168 DWORD style;
1170 if (child) hwnd = *child;
1172 if (hwnd == get_desktop_window()) return whole_rgn;
1174 if (whole_rgn)
1176 DPI_AWARENESS_CONTEXT context;
1177 RECT client, window, update;
1178 INT type;
1180 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
1182 /* check if update rgn overlaps with nonclient area */
1183 type = NtGdiGetRgnBox( whole_rgn, &update );
1184 get_window_rects( hwnd, COORDS_SCREEN, &window, &client, get_thread_dpi() );
1186 if ((*flags & UPDATE_NONCLIENT) ||
1187 update.left < client.left || update.top < client.top ||
1188 update.right > client.right || update.bottom > client.bottom)
1190 client_rgn = NtGdiCreateRectRgn( client.left, client.top, client.right, client.bottom );
1191 NtGdiCombineRgn( client_rgn, client_rgn, whole_rgn, RGN_AND );
1193 /* check if update rgn contains complete nonclient area */
1194 if (type == SIMPLEREGION && EqualRect( &window, &update ))
1196 NtGdiDeleteObjectApp( whole_rgn );
1197 whole_rgn = (HRGN)1;
1200 else
1202 client_rgn = whole_rgn;
1203 whole_rgn = 0;
1206 if (whole_rgn) /* NOTE: WM_NCPAINT allows wParam to be 1 */
1208 if (*flags & UPDATE_NONCLIENT)
1210 /* Mark standard scroll bars as not painted before sending WM_NCPAINT */
1211 style = get_window_long( hwnd, GWL_STYLE );
1212 if (style & WS_HSCROLL)
1213 set_standard_scroll_painted( hwnd, SB_HORZ, FALSE );
1214 if (style & WS_VSCROLL)
1215 set_standard_scroll_painted( hwnd, SB_VERT, FALSE );
1217 send_message( hwnd, WM_NCPAINT, (WPARAM)whole_rgn, 0 );
1219 if (whole_rgn > (HRGN)1) NtGdiDeleteObjectApp( whole_rgn );
1221 SetThreadDpiAwarenessContext( context );
1223 return client_rgn;
1226 /***********************************************************************
1227 * send_erase
1229 * Send a WM_ERASEBKGND message if needed, and optionally return the DC for painting.
1230 * If a DC is requested, the region is selected into it. In all cases the region is deleted.
1231 * Helper for erase_now and BeginPaint.
1233 static BOOL send_erase( HWND hwnd, UINT flags, HRGN client_rgn,
1234 RECT *clip_rect, HDC *hdc_ret )
1236 BOOL need_erase = (flags & UPDATE_DELAYED_ERASE) != 0;
1237 HDC hdc = 0;
1238 RECT dummy;
1240 if (!clip_rect) clip_rect = &dummy;
1241 if (hdc_ret || (flags & UPDATE_ERASE))
1243 UINT dcx_flags = DCX_INTERSECTRGN | DCX_USESTYLE;
1244 if (is_iconic(hwnd)) dcx_flags |= DCX_WINDOW;
1246 if ((hdc = NtUserGetDCEx( hwnd, client_rgn, dcx_flags )))
1248 INT type = NtGdiGetAppClipBox( hdc, clip_rect );
1250 if (flags & UPDATE_ERASE)
1252 /* don't erase if the clip box is empty */
1253 if (type != NULLREGION)
1254 need_erase = !send_message( hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0 );
1256 if (!hdc_ret) release_dc( hwnd, hdc, TRUE );
1259 if (hdc_ret) *hdc_ret = hdc;
1261 if (!hdc) NtGdiDeleteObjectApp( client_rgn );
1262 return need_erase;
1265 /***********************************************************************
1266 * copy_bits_from_surface
1268 * Copy bits from a window surface; helper for move_window_bits and move_window_bits_parent.
1270 static void copy_bits_from_surface( HWND hwnd, struct window_surface *surface,
1271 const RECT *dst, const RECT *src )
1273 char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
1274 BITMAPINFO *info = (BITMAPINFO *)buffer;
1275 void *bits;
1276 UINT flags = UPDATE_NOCHILDREN | UPDATE_CLIPCHILDREN;
1277 HRGN rgn = get_update_region( hwnd, &flags, NULL );
1278 HDC hdc = NtUserGetDCEx( hwnd, rgn, DCX_CACHE | DCX_WINDOW | DCX_EXCLUDERGN );
1280 bits = surface->funcs->get_info( surface, info );
1281 surface->funcs->lock( surface );
1282 NtGdiSetDIBitsToDeviceInternal( hdc, dst->left, dst->top, dst->right - dst->left, dst->bottom - dst->top,
1283 src->left - surface->rect.left, surface->rect.bottom - src->bottom,
1284 0, surface->rect.bottom - surface->rect.top,
1285 bits, info, DIB_RGB_COLORS, 0, 0, FALSE, NULL );
1286 surface->funcs->unlock( surface );
1287 NtUserReleaseDC( hwnd, hdc );
1290 /***********************************************************************
1291 * move_window_bits
1293 * Move the window bits when a window is resized or its surface recreated.
1295 void move_window_bits( HWND hwnd, struct window_surface *old_surface,
1296 struct window_surface *new_surface,
1297 const RECT *visible_rect, const RECT *old_visible_rect,
1298 const RECT *window_rect, const RECT *valid_rects )
1300 RECT dst = valid_rects[0];
1301 RECT src = valid_rects[1];
1303 if (new_surface != old_surface ||
1304 src.left - old_visible_rect->left != dst.left - visible_rect->left ||
1305 src.top - old_visible_rect->top != dst.top - visible_rect->top)
1307 TRACE( "copying %s -> %s\n", wine_dbgstr_rect( &src ), wine_dbgstr_rect( &dst ));
1308 OffsetRect( &src, -old_visible_rect->left, -old_visible_rect->top );
1309 OffsetRect( &dst, -window_rect->left, -window_rect->top );
1310 copy_bits_from_surface( hwnd, old_surface, &dst, &src );
1315 /***********************************************************************
1316 * move_window_bits_parent
1318 * Move the window bits in the parent surface when a child is moved.
1320 void move_window_bits_parent( HWND hwnd, HWND parent, const RECT *window_rect, const RECT *valid_rects )
1322 struct window_surface *surface;
1323 RECT dst = valid_rects[0];
1324 RECT src = valid_rects[1];
1325 WND *win;
1327 if (src.left == dst.left && src.top == dst.top) return;
1329 if (!(win = get_win_ptr( parent ))) return;
1330 if (win == WND_DESKTOP || win == WND_OTHER_PROCESS) return;
1331 if (!(surface = win->surface))
1333 release_win_ptr( win );
1334 return;
1337 TRACE( "copying %s -> %s\n", wine_dbgstr_rect( &src ), wine_dbgstr_rect( &dst ));
1338 map_window_points( NtUserGetAncestor( hwnd, GA_PARENT ), parent, (POINT *)&src, 2, get_thread_dpi() );
1339 OffsetRect( &src, win->client_rect.left - win->visible_rect.left,
1340 win->client_rect.top - win->visible_rect.top );
1341 OffsetRect( &dst, -window_rect->left, -window_rect->top );
1342 window_surface_add_ref( surface );
1343 release_win_ptr( win );
1345 copy_bits_from_surface( hwnd, surface, &dst, &src );
1346 window_surface_release( surface );
1349 /***********************************************************************
1350 * NtUserBeginPaint (win32u.@)
1352 HDC WINAPI NtUserBeginPaint( HWND hwnd, PAINTSTRUCT *ps )
1354 HRGN hrgn;
1355 HDC hdc;
1356 BOOL erase;
1357 RECT rect;
1358 UINT flags = UPDATE_NONCLIENT | UPDATE_ERASE | UPDATE_PAINT | UPDATE_INTERNALPAINT | UPDATE_NOCHILDREN;
1360 NtUserHideCaret( hwnd );
1362 if (!(hrgn = send_ncpaint( hwnd, NULL, &flags ))) return 0;
1364 erase = send_erase( hwnd, flags, hrgn, &rect, &hdc );
1366 TRACE( "hdc = %p box = (%s), fErase = %d\n", hdc, wine_dbgstr_rect(&rect), erase );
1368 if (!ps)
1370 release_dc( hwnd, hdc, TRUE );
1371 return 0;
1373 ps->fErase = erase;
1374 ps->rcPaint = rect;
1375 ps->hdc = hdc;
1376 return hdc;
1379 /***********************************************************************
1380 * NtUserEndPaint (win32u.@)
1382 BOOL WINAPI NtUserEndPaint( HWND hwnd, const PAINTSTRUCT *ps )
1384 NtUserShowCaret( hwnd );
1385 flush_window_surfaces( FALSE );
1386 if (!ps) return FALSE;
1387 release_dc( hwnd, ps->hdc, TRUE );
1388 return TRUE;
1391 /***********************************************************************
1392 * erase_now
1394 * Implementation of RDW_ERASENOW behavior.
1396 void erase_now( HWND hwnd, UINT rdw_flags )
1398 HWND child = 0;
1399 HRGN hrgn;
1400 BOOL need_erase = FALSE;
1402 /* loop while we find a child to repaint */
1403 for (;;)
1405 UINT flags = UPDATE_NONCLIENT | UPDATE_ERASE;
1407 if (rdw_flags & RDW_NOCHILDREN) flags |= UPDATE_NOCHILDREN;
1408 else if (rdw_flags & RDW_ALLCHILDREN) flags |= UPDATE_ALLCHILDREN;
1409 if (need_erase) flags |= UPDATE_DELAYED_ERASE;
1411 if (!(hrgn = send_ncpaint( hwnd, &child, &flags ))) break;
1412 need_erase = send_erase( child, flags, hrgn, NULL, NULL );
1414 if (!flags) break; /* nothing more to do */
1415 if ((rdw_flags & RDW_NOCHILDREN) && !need_erase) break;
1419 /***********************************************************************
1420 * update_now
1422 * Implementation of RDW_UPDATENOW behavior.
1424 static void update_now( HWND hwnd, UINT rdw_flags )
1426 HWND child = 0;
1428 /* desktop window never gets WM_PAINT, only WM_ERASEBKGND */
1429 if (hwnd == get_desktop_window()) erase_now( hwnd, rdw_flags | RDW_NOCHILDREN );
1431 /* loop while we find a child to repaint */
1432 for (;;)
1434 UINT flags = UPDATE_PAINT | UPDATE_INTERNALPAINT;
1436 if (rdw_flags & RDW_NOCHILDREN) flags |= UPDATE_NOCHILDREN;
1437 else if (rdw_flags & RDW_ALLCHILDREN) flags |= UPDATE_ALLCHILDREN;
1439 if (!get_update_flags( hwnd, &child, &flags )) break;
1440 if (!flags) break; /* nothing more to do */
1442 send_message( child, WM_PAINT, 0, 0 );
1443 if (rdw_flags & RDW_NOCHILDREN) break;
1447 /***********************************************************************
1448 * NtUserRedrawWindow (win32u.@)
1450 BOOL WINAPI NtUserRedrawWindow( HWND hwnd, const RECT *rect, HRGN hrgn, UINT flags )
1452 static const RECT empty;
1453 BOOL ret;
1455 if (TRACE_ON(win))
1457 if (hrgn)
1459 RECT r;
1460 NtGdiGetRgnBox( hrgn, &r );
1461 TRACE( "%p region %p box %s ", hwnd, hrgn, wine_dbgstr_rect(&r) );
1463 else if (rect)
1464 TRACE( "%p rect %s ", hwnd, wine_dbgstr_rect(rect) );
1465 else
1466 TRACE( "%p whole window ", hwnd );
1468 dump_rdw_flags(flags);
1471 /* process pending expose events before painting */
1472 if (flags & RDW_UPDATENOW) user_driver->pProcessEvents( QS_PAINT );
1474 if (rect && !hrgn)
1476 RECT ordered = *rect;
1478 order_rect( &ordered );
1479 if (IsRectEmpty( &ordered )) ordered = empty;
1480 ret = redraw_window_rects( hwnd, flags, &ordered, 1 );
1482 else if (!hrgn)
1484 ret = redraw_window_rects( hwnd, flags, NULL, 0 );
1486 else /* need to build a list of the region rectangles */
1488 DWORD size;
1489 RGNDATA *data;
1491 if (!(size = NtGdiGetRegionData( hrgn, 0, NULL ))) return FALSE;
1492 if (!(data = malloc( size ))) return FALSE;
1493 NtGdiGetRegionData( hrgn, size, data );
1494 if (!data->rdh.nCount) /* empty region -> use a single all-zero rectangle */
1495 ret = redraw_window_rects( hwnd, flags, &empty, 1 );
1496 else
1497 ret = redraw_window_rects( hwnd, flags, (const RECT *)data->Buffer, data->rdh.nCount );
1498 free( data );
1501 if (!hwnd) hwnd = get_desktop_window();
1503 if (flags & RDW_UPDATENOW) update_now( hwnd, flags );
1504 else if (flags & RDW_ERASENOW) erase_now( hwnd, flags );
1506 return ret;
1509 /***********************************************************************
1510 * NtUserValidateRect (win32u.@)
1512 BOOL WINAPI NtUserValidateRect( HWND hwnd, const RECT *rect )
1514 UINT flags = RDW_VALIDATE;
1516 if (!hwnd)
1518 flags = RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW;
1519 rect = NULL;
1522 return NtUserRedrawWindow( hwnd, rect, 0, flags );
1525 /***********************************************************************
1526 * NtUserGetUpdateRgn (win32u.@)
1528 INT WINAPI NtUserGetUpdateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
1530 DPI_AWARENESS_CONTEXT context;
1531 INT retval = ERROR;
1532 UINT flags = UPDATE_NOCHILDREN;
1533 HRGN update_rgn;
1535 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
1537 if (erase) flags |= UPDATE_NONCLIENT | UPDATE_ERASE;
1539 if ((update_rgn = send_ncpaint( hwnd, NULL, &flags )))
1541 retval = NtGdiCombineRgn( hrgn, update_rgn, 0, RGN_COPY );
1542 if (send_erase( hwnd, flags, update_rgn, NULL, NULL ))
1544 flags = UPDATE_DELAYED_ERASE;
1545 get_update_flags( hwnd, NULL, &flags );
1547 /* map region to client coordinates */
1548 map_window_region( 0, hwnd, hrgn );
1550 SetThreadDpiAwarenessContext( context );
1551 return retval;
1554 /***********************************************************************
1555 * NtUserGetUpdateRect (win32u.@)
1557 BOOL WINAPI NtUserGetUpdateRect( HWND hwnd, RECT *rect, BOOL erase )
1559 UINT flags = UPDATE_NOCHILDREN;
1560 HRGN update_rgn;
1561 BOOL need_erase;
1563 if (erase) flags |= UPDATE_NONCLIENT | UPDATE_ERASE;
1565 if (!(update_rgn = send_ncpaint( hwnd, NULL, &flags ))) return FALSE;
1567 if (rect && NtGdiGetRgnBox( update_rgn, rect ) != NULLREGION)
1569 HDC hdc = NtUserGetDCEx( hwnd, 0, DCX_USESTYLE );
1570 DWORD layout = NtGdiSetLayout( hdc, -1, 0 ); /* map_window_points mirrors already */
1571 UINT win_dpi = get_dpi_for_window( hwnd );
1572 map_window_points( 0, hwnd, (POINT *)rect, 2, win_dpi );
1573 *rect = map_dpi_rect( *rect, win_dpi, get_thread_dpi() );
1574 NtGdiTransformPoints( hdc, (POINT *)rect, (POINT *)rect, 2, NtGdiDPtoLP );
1575 NtGdiSetLayout( hdc, -1, layout );
1576 NtUserReleaseDC( hwnd, hdc );
1578 need_erase = send_erase( hwnd, flags, update_rgn, NULL, NULL );
1580 /* check if we still have an update region */
1581 flags = UPDATE_PAINT | UPDATE_NOCHILDREN;
1582 if (need_erase) flags |= UPDATE_DELAYED_ERASE;
1583 return get_update_flags( hwnd, NULL, &flags ) && (flags & UPDATE_PAINT);
1586 /***********************************************************************
1587 * NtUserExcludeUpdateRgn (win32u.@)
1589 INT WINAPI NtUserExcludeUpdateRgn( HDC hdc, HWND hwnd )
1591 HRGN update_rgn = NtGdiCreateRectRgn( 0, 0, 0, 0 );
1592 INT ret = NtUserGetUpdateRgn( hwnd, update_rgn, FALSE );
1594 if (ret != ERROR)
1596 DPI_AWARENESS_CONTEXT context;
1597 POINT pt;
1599 context = SetThreadDpiAwarenessContext( get_window_dpi_awareness_context( hwnd ));
1600 NtGdiGetDCPoint( hdc, NtGdiGetDCOrg, &pt );
1601 map_window_points( 0, hwnd, &pt, 1, get_thread_dpi() );
1602 NtGdiOffsetRgn( update_rgn, -pt.x, -pt.y );
1603 ret = NtGdiExtSelectClipRgn( hdc, update_rgn, RGN_DIFF );
1604 SetThreadDpiAwarenessContext( context );
1606 NtGdiDeleteObjectApp( update_rgn );
1607 return ret;
1610 /***********************************************************************
1611 * NtUserInvalidateRgn (win32u.@)
1613 BOOL WINAPI NtUserInvalidateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
1615 if (!hwnd)
1617 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
1618 return FALSE;
1621 return NtUserRedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | (erase ? RDW_ERASE : 0) );
1624 /***********************************************************************
1625 * NtUserInvalidateRect (win32u.@)
1627 BOOL WINAPI NtUserInvalidateRect( HWND hwnd, const RECT *rect, BOOL erase )
1629 UINT flags = RDW_INVALIDATE | (erase ? RDW_ERASE : 0);
1631 if (!hwnd)
1633 flags = RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW;
1634 rect = NULL;
1637 return NtUserRedrawWindow( hwnd, rect, 0, flags );
1640 /***********************************************************************
1641 * NtUserLockWindowUpdate (win32u.@)
1643 BOOL WINAPI NtUserLockWindowUpdate( HWND hwnd )
1645 static HWND locked_hwnd;
1647 FIXME( "(%p), partial stub!\n", hwnd );
1649 if (!hwnd)
1651 locked_hwnd = NULL;
1652 return TRUE;
1654 return !InterlockedCompareExchangePointer( (void **)&locked_hwnd, hwnd, 0 );
1657 /*************************************************************************
1658 * fix_caret
1660 * Helper for NtUserScrollWindowEx:
1661 * If the return value is 0, no special caret handling is necessary.
1662 * Otherwise the return value is the handle of the window that owns the
1663 * caret. Its caret needs to be hidden during the scroll operation and
1664 * moved to new_caret_pos if move_caret is TRUE.
1666 static HWND fix_caret( HWND hwnd, const RECT *scroll_rect, INT dx, INT dy,
1667 UINT flags, BOOL *move_caret, POINT *new_caret_pos )
1669 RECT rect, mapped_caret;
1670 GUITHREADINFO info;
1672 info.cbSize = sizeof(info);
1673 if (!NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info )) return 0;
1674 if (!info.hwndCaret) return 0;
1676 mapped_caret = info.rcCaret;
1677 if (info.hwndCaret == hwnd)
1679 /* The caret needs to be moved along with scrolling even if it's
1680 * outside the visible area. Otherwise, when the caret is scrolled
1681 * out from the view, the position won't get updated anymore and
1682 * the caret will never scroll back again. */
1683 *move_caret = TRUE;
1684 new_caret_pos->x = info.rcCaret.left + dx;
1685 new_caret_pos->y = info.rcCaret.top + dy;
1687 else
1689 *move_caret = FALSE;
1690 if (!(flags & SW_SCROLLCHILDREN) || !is_child( hwnd, info.hwndCaret ))
1691 return 0;
1692 map_window_points( info.hwndCaret, hwnd, (POINT *)&mapped_caret, 2, get_thread_dpi() );
1695 /* If the caret is not in the src/dest rects, all is fine done. */
1696 if (!intersect_rect( &rect, scroll_rect, &mapped_caret ))
1698 rect = *scroll_rect;
1699 OffsetRect( &rect, dx, dy );
1700 if (!intersect_rect( &rect, &rect, &mapped_caret ))
1701 return 0;
1704 /* Indicate that the caret needs to be updated during the scrolling. */
1705 return info.hwndCaret;
1708 /*************************************************************************
1709 * NtUserScrollWindowEx (win32u.@)
1711 * Note: contrary to what the doc says, pixels that are scrolled from the
1712 * outside of clipRect to the inside are NOT painted.
1714 INT WINAPI NtUserScrollWindowEx( HWND hwnd, INT dx, INT dy, const RECT *rect,
1715 const RECT *clip_rect, HRGN update_rgn,
1716 RECT *update_rect, UINT flags )
1718 BOOL update = update_rect || update_rgn || flags & (SW_INVALIDATE | SW_ERASE);
1719 BOOL own_rgn = TRUE, move_caret = FALSE;
1720 HRGN temp_rgn, winupd_rgn = 0;
1721 INT retval = NULLREGION;
1722 HWND caret_hwnd = NULL;
1723 POINT new_caret_pos;
1724 RECT rc, cliprc;
1725 int rdw_flags;
1726 HDC hdc;
1728 TRACE( "%p, %d,%d update_rgn=%p update_rect = %p %s %04x\n",
1729 hwnd, dx, dy, update_rgn, update_rect, wine_dbgstr_rect(rect), flags );
1730 TRACE( "clip_rect = %s\n", wine_dbgstr_rect(clip_rect) );
1731 if (flags & ~(SW_SCROLLCHILDREN | SW_INVALIDATE | SW_ERASE | SW_NODCCACHE))
1732 FIXME( "some flags (%04x) are unhandled\n", flags );
1734 rdw_flags = (flags & SW_ERASE) && (flags & SW_INVALIDATE) ?
1735 RDW_INVALIDATE | RDW_ERASE : RDW_INVALIDATE;
1737 if (!is_window_drawable( hwnd, TRUE )) return ERROR;
1738 hwnd = get_full_window_handle( hwnd );
1740 get_client_rect( hwnd, &rc );
1741 if (clip_rect) intersect_rect( &cliprc, &rc, clip_rect );
1742 else cliprc = rc;
1744 if (rect) intersect_rect( &rc, &rc, rect );
1745 if (update_rgn) own_rgn = FALSE;
1746 else if (update) update_rgn = NtGdiCreateRectRgn( 0, 0, 0, 0 );
1748 new_caret_pos.x = new_caret_pos.y = 0;
1750 if (!IsRectEmpty( &cliprc ) && (dx || dy))
1752 DWORD style = get_window_long( hwnd, GWL_STYLE );
1753 DWORD dcxflags = 0;
1755 caret_hwnd = fix_caret( hwnd, &rc, dx, dy, flags, &move_caret, &new_caret_pos );
1756 if (caret_hwnd) NtUserHideCaret( caret_hwnd );
1758 if (!(flags & SW_NODCCACHE)) dcxflags |= DCX_CACHE;
1759 if (style & WS_CLIPSIBLINGS) dcxflags |= DCX_CLIPSIBLINGS;
1760 if (get_class_long( hwnd, GCL_STYLE, FALSE ) & CS_PARENTDC) dcxflags |= DCX_PARENTCLIP;
1761 if (!(flags & SW_SCROLLCHILDREN) && (style & WS_CLIPCHILDREN))
1762 dcxflags |= DCX_CLIPCHILDREN;
1763 hdc = NtUserGetDCEx( hwnd, 0, dcxflags);
1764 if (hdc)
1766 NtUserScrollDC( hdc, dx, dy, &rc, &cliprc, update_rgn, update_rect );
1767 NtUserReleaseDC( hwnd, hdc );
1768 if (!update) NtUserRedrawWindow( hwnd, NULL, update_rgn, rdw_flags );
1771 /* If the windows has an update region, this must be scrolled as well.
1772 * Keep a copy in winupd_rgn to be added to hrngUpdate at the end. */
1773 temp_rgn = NtGdiCreateRectRgn( 0, 0, 0, 0 );
1774 retval = NtUserGetUpdateRgn( hwnd, temp_rgn, FALSE );
1775 if (retval != NULLREGION)
1777 HRGN clip_rgn = NtGdiCreateRectRgn( cliprc.left, cliprc.top,
1778 cliprc.right, cliprc.bottom );
1779 if (!own_rgn)
1781 winupd_rgn = NtGdiCreateRectRgn( 0, 0, 0, 0);
1782 NtGdiCombineRgn( winupd_rgn, temp_rgn, 0, RGN_COPY);
1784 NtGdiOffsetRgn( temp_rgn, dx, dy );
1785 NtGdiCombineRgn( temp_rgn, temp_rgn, clip_rgn, RGN_AND );
1786 if (!own_rgn) NtGdiCombineRgn( winupd_rgn, winupd_rgn, temp_rgn, RGN_OR );
1787 NtUserRedrawWindow( hwnd, NULL, temp_rgn, rdw_flags );
1790 * Catch the case where the scrolling amount exceeds the size of the
1791 * original window. This generated a second update area that is the
1792 * location where the original scrolled content would end up.
1793 * This second region is not returned by the ScrollDC and sets
1794 * ScrollWindowEx apart from just a ScrollDC.
1796 * This has been verified with testing on windows.
1798 if (abs( dx ) > abs( rc.right - rc.left ) || abs( dy ) > abs( rc.bottom - rc.top ))
1800 NtGdiSetRectRgn( temp_rgn, rc.left + dx, rc.top + dy, rc.right+dx, rc.bottom + dy );
1801 NtGdiCombineRgn( temp_rgn, temp_rgn, clip_rgn, RGN_AND );
1802 NtGdiCombineRgn( update_rgn, update_rgn, temp_rgn, RGN_OR );
1804 if (update_rect)
1806 RECT temp_rect;
1807 NtGdiGetRgnBox( temp_rgn, &temp_rect );
1808 union_rect( update_rect, update_rect, &temp_rect );
1811 if (!own_rgn) NtGdiCombineRgn( winupd_rgn, winupd_rgn, temp_rgn, RGN_OR );
1813 NtGdiDeleteObjectApp( clip_rgn );
1815 NtGdiDeleteObjectApp( temp_rgn );
1817 else
1819 /* nothing was scrolled */
1820 if (!own_rgn) NtGdiSetRectRgn( update_rgn, 0, 0, 0, 0 );
1821 SetRectEmpty( update_rect );
1824 if (flags & SW_SCROLLCHILDREN)
1826 HWND *list = list_window_children( 0, hwnd, NULL, 0 );
1827 if (list)
1829 RECT r, dummy;
1830 int i;
1832 for (i = 0; list[i]; i++)
1834 get_window_rects( list[i], COORDS_PARENT, &r, NULL, get_thread_dpi() );
1835 if (!rect || intersect_rect( &dummy, &r, rect ))
1836 NtUserSetWindowPos( list[i], 0, r.left + dx, r.top + dy, 0, 0,
1837 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
1838 SWP_NOREDRAW | SWP_DEFERERASE );
1840 free( list );
1844 if (flags & (SW_INVALIDATE | SW_ERASE))
1845 NtUserRedrawWindow( hwnd, NULL, update_rgn, rdw_flags |
1846 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0 ) );
1848 if (winupd_rgn)
1850 NtGdiCombineRgn( update_rgn, update_rgn, winupd_rgn, RGN_OR );
1851 NtGdiDeleteObjectApp( winupd_rgn );
1854 if (move_caret) set_caret_pos( new_caret_pos.x, new_caret_pos.y );
1855 if (caret_hwnd) NtUserShowCaret( caret_hwnd );
1856 if (own_rgn && update_rgn) NtGdiDeleteObjectApp( update_rgn );
1858 return retval;
1861 /************************************************************************
1862 * NtUserPrintWindow (win32u.@)
1864 BOOL WINAPI NtUserPrintWindow( HWND hwnd, HDC hdc, UINT flags )
1866 UINT prf_flags = PRF_CHILDREN | PRF_ERASEBKGND | PRF_OWNED | PRF_CLIENT;
1867 if (!(flags & PW_CLIENTONLY)) prf_flags |= PRF_NONCLIENT;
1868 send_message( hwnd, WM_PRINT, (WPARAM)hdc, prf_flags );
1869 return TRUE;