windows.media.speech: Add IIterable<IInspectable*> stubs.
[wine.git] / dlls / win32u / dce.c
blobf0545c8bba4ad1551c85649bb8bfd06b8e29d3c1
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 DWORD 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 offset_rect( &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, rect.right, 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 = set_thread_dpi_awareness_context( 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 set_thread_dpi_awareness_context( 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, 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, 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 * NtUserWindowFromDC (win32u.@)
1035 HWND WINAPI NtUserWindowFromDC( HDC hdc )
1037 struct dce *dce;
1038 HWND hwnd = 0;
1040 user_lock();
1041 dce = get_dc_dce( hdc );
1042 if (dce) hwnd = dce->hwnd;
1043 user_unlock();
1044 return hwnd;
1047 /***********************************************************************
1048 * get_update_region
1050 * Return update region (in screen coordinates) for a window.
1052 static HRGN get_update_region( HWND hwnd, UINT *flags, HWND *child )
1054 HRGN hrgn = 0;
1055 NTSTATUS status;
1056 RGNDATA *data;
1057 size_t size = 256;
1061 if (!(data = malloc( sizeof(*data) + size - 1 )))
1063 SetLastError( ERROR_OUTOFMEMORY );
1064 return 0;
1067 SERVER_START_REQ( get_update_region )
1069 req->window = wine_server_user_handle( hwnd );
1070 req->from_child = wine_server_user_handle( child ? *child : 0 );
1071 req->flags = *flags;
1072 wine_server_set_reply( req, data->Buffer, size );
1073 if (!(status = wine_server_call( req )))
1075 size_t reply_size = wine_server_reply_size( reply );
1076 data->rdh.dwSize = sizeof(data->rdh);
1077 data->rdh.iType = RDH_RECTANGLES;
1078 data->rdh.nCount = reply_size / sizeof(RECT);
1079 data->rdh.nRgnSize = reply_size;
1080 hrgn = NtGdiExtCreateRegion( NULL, data->rdh.dwSize + data->rdh.nRgnSize, data );
1081 if (child) *child = wine_server_ptr_handle( reply->child );
1082 *flags = reply->flags;
1084 else size = reply->total_size;
1086 SERVER_END_REQ;
1087 free( data );
1088 } while (status == STATUS_BUFFER_OVERFLOW);
1090 if (status) SetLastError( RtlNtStatusToDosError(status) );
1091 return hrgn;
1094 /***********************************************************************
1095 * redraw_window_rects
1097 * Redraw part of a window.
1099 static BOOL redraw_window_rects( HWND hwnd, UINT flags, const RECT *rects, UINT count )
1101 BOOL ret;
1103 if (!(flags & (RDW_INVALIDATE|RDW_VALIDATE|RDW_INTERNALPAINT|RDW_NOINTERNALPAINT)))
1104 return TRUE; /* nothing to do */
1106 SERVER_START_REQ( redraw_window )
1108 req->window = wine_server_user_handle( hwnd );
1109 req->flags = flags;
1110 wine_server_add_data( req, rects, count * sizeof(RECT) );
1111 ret = !wine_server_call_err( req );
1113 SERVER_END_REQ;
1114 return ret;
1117 /***********************************************************************
1118 * get_update_flags
1120 * Get only the update flags, not the update region.
1122 static BOOL get_update_flags( HWND hwnd, HWND *child, UINT *flags )
1124 BOOL ret;
1126 SERVER_START_REQ( get_update_region )
1128 req->window = wine_server_user_handle( hwnd );
1129 req->from_child = wine_server_user_handle( child ? *child : 0 );
1130 req->flags = *flags | UPDATE_NOREGION;
1131 if ((ret = !wine_server_call_err( req )))
1133 if (child) *child = wine_server_ptr_handle( reply->child );
1134 *flags = reply->flags;
1137 SERVER_END_REQ;
1138 return ret;
1141 /***********************************************************************
1142 * send_ncpaint
1144 * Send a WM_NCPAINT message if needed, and return the resulting update region (in screen coords).
1145 * Helper for erase_now and BeginPaint.
1147 static HRGN send_ncpaint( HWND hwnd, HWND *child, UINT *flags )
1149 HRGN whole_rgn = get_update_region( hwnd, flags, child );
1150 HRGN client_rgn = 0;
1151 DWORD style;
1153 if (child) hwnd = *child;
1155 if (hwnd == get_desktop_window()) return whole_rgn;
1157 if (whole_rgn)
1159 DPI_AWARENESS_CONTEXT context;
1160 RECT client, window, update;
1161 INT type;
1163 context = set_thread_dpi_awareness_context( get_window_dpi_awareness_context( hwnd ));
1165 /* check if update rgn overlaps with nonclient area */
1166 type = NtGdiGetRgnBox( whole_rgn, &update );
1167 get_window_rects( hwnd, COORDS_SCREEN, &window, &client, get_thread_dpi() );
1169 if ((*flags & UPDATE_NONCLIENT) ||
1170 update.left < client.left || update.top < client.top ||
1171 update.right > client.right || update.bottom > client.bottom)
1173 client_rgn = NtGdiCreateRectRgn( client.left, client.top, client.right, client.bottom );
1174 NtGdiCombineRgn( client_rgn, client_rgn, whole_rgn, RGN_AND );
1176 /* check if update rgn contains complete nonclient area */
1177 if (type == SIMPLEREGION && EqualRect( &window, &update ))
1179 NtGdiDeleteObjectApp( whole_rgn );
1180 whole_rgn = (HRGN)1;
1183 else
1185 client_rgn = whole_rgn;
1186 whole_rgn = 0;
1189 if (whole_rgn) /* NOTE: WM_NCPAINT allows wParam to be 1 */
1191 if ((*flags & UPDATE_NONCLIENT) && user_callbacks)
1193 /* Mark standard scroll bars as not painted before sending WM_NCPAINT */
1194 style = get_window_long( hwnd, GWL_STYLE );
1195 if (style & WS_HSCROLL)
1196 user_callbacks->set_standard_scroll_painted( hwnd, SB_HORZ, FALSE );
1197 if (style & WS_VSCROLL)
1198 user_callbacks->set_standard_scroll_painted( hwnd, SB_VERT, FALSE );
1200 send_message( hwnd, WM_NCPAINT, (WPARAM)whole_rgn, 0 );
1202 if (whole_rgn > (HRGN)1) NtGdiDeleteObjectApp( whole_rgn );
1204 set_thread_dpi_awareness_context( context );
1206 return client_rgn;
1209 /***********************************************************************
1210 * send_erase
1212 * Send a WM_ERASEBKGND message if needed, and optionally return the DC for painting.
1213 * If a DC is requested, the region is selected into it. In all cases the region is deleted.
1214 * Helper for erase_now and BeginPaint.
1216 static BOOL send_erase( HWND hwnd, UINT flags, HRGN client_rgn,
1217 RECT *clip_rect, HDC *hdc_ret )
1219 BOOL need_erase = (flags & UPDATE_DELAYED_ERASE) != 0;
1220 HDC hdc = 0;
1221 RECT dummy;
1223 if (!clip_rect) clip_rect = &dummy;
1224 if (hdc_ret || (flags & UPDATE_ERASE))
1226 UINT dcx_flags = DCX_INTERSECTRGN | DCX_USESTYLE;
1227 if (is_iconic(hwnd)) dcx_flags |= DCX_WINDOW;
1229 if ((hdc = NtUserGetDCEx( hwnd, client_rgn, dcx_flags )))
1231 INT type = NtGdiGetAppClipBox( hdc, clip_rect );
1233 if (flags & UPDATE_ERASE)
1235 /* don't erase if the clip box is empty */
1236 if (type != NULLREGION)
1237 need_erase = !send_message( hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0 );
1239 if (!hdc_ret) release_dc( hwnd, hdc, TRUE );
1242 if (hdc_ret) *hdc_ret = hdc;
1244 if (!hdc) NtGdiDeleteObjectApp( client_rgn );
1245 return need_erase;
1248 /***********************************************************************
1249 * copy_bits_from_surface
1251 * Copy bits from a window surface; helper for move_window_bits and move_window_bits_parent.
1253 static void copy_bits_from_surface( HWND hwnd, struct window_surface *surface,
1254 const RECT *dst, const RECT *src )
1256 char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
1257 BITMAPINFO *info = (BITMAPINFO *)buffer;
1258 void *bits;
1259 UINT flags = UPDATE_NOCHILDREN | UPDATE_CLIPCHILDREN;
1260 HRGN rgn = get_update_region( hwnd, &flags, NULL );
1261 HDC hdc = NtUserGetDCEx( hwnd, rgn, DCX_CACHE | DCX_WINDOW | DCX_EXCLUDERGN );
1263 bits = surface->funcs->get_info( surface, info );
1264 surface->funcs->lock( surface );
1265 NtGdiSetDIBitsToDeviceInternal( hdc, dst->left, dst->top, dst->right - dst->left, dst->bottom - dst->top,
1266 src->left - surface->rect.left, surface->rect.bottom - src->bottom,
1267 0, surface->rect.bottom - surface->rect.top,
1268 bits, info, DIB_RGB_COLORS, 0, 0, FALSE, NULL );
1269 surface->funcs->unlock( surface );
1270 NtUserReleaseDC( hwnd, hdc );
1273 /***********************************************************************
1274 * move_window_bits
1276 * Move the window bits when a window is resized or its surface recreated.
1278 void move_window_bits( HWND hwnd, struct window_surface *old_surface,
1279 struct window_surface *new_surface,
1280 const RECT *visible_rect, const RECT *old_visible_rect,
1281 const RECT *window_rect, const RECT *valid_rects )
1283 RECT dst = valid_rects[0];
1284 RECT src = valid_rects[1];
1286 if (new_surface != old_surface ||
1287 src.left - old_visible_rect->left != dst.left - visible_rect->left ||
1288 src.top - old_visible_rect->top != dst.top - visible_rect->top)
1290 TRACE( "copying %s -> %s\n", wine_dbgstr_rect( &src ), wine_dbgstr_rect( &dst ));
1291 OffsetRect( &src, -old_visible_rect->left, -old_visible_rect->top );
1292 OffsetRect( &dst, -window_rect->left, -window_rect->top );
1293 copy_bits_from_surface( hwnd, old_surface, &dst, &src );
1298 /***********************************************************************
1299 * move_window_bits_parent
1301 * Move the window bits in the parent surface when a child is moved.
1303 void move_window_bits_parent( HWND hwnd, HWND parent, const RECT *window_rect, const RECT *valid_rects )
1305 struct window_surface *surface;
1306 RECT dst = valid_rects[0];
1307 RECT src = valid_rects[1];
1308 WND *win;
1310 if (src.left == dst.left && src.top == dst.top) return;
1312 if (!(win = get_win_ptr( parent ))) return;
1313 if (win == WND_DESKTOP || win == WND_OTHER_PROCESS) return;
1314 if (!(surface = win->surface))
1316 release_win_ptr( win );
1317 return;
1320 TRACE( "copying %s -> %s\n", wine_dbgstr_rect( &src ), wine_dbgstr_rect( &dst ));
1321 map_window_points( NtUserGetAncestor( hwnd, GA_PARENT ), parent, (POINT *)&src, 2, get_thread_dpi() );
1322 offset_rect( &src, win->client_rect.left - win->visible_rect.left,
1323 win->client_rect.top - win->visible_rect.top );
1324 offset_rect( &dst, -window_rect->left, -window_rect->top );
1325 window_surface_add_ref( surface );
1326 release_win_ptr( win );
1328 copy_bits_from_surface( hwnd, surface, &dst, &src );
1329 window_surface_release( surface );
1332 /***********************************************************************
1333 * NtUserBeginPaint (win32u.@)
1335 HDC WINAPI NtUserBeginPaint( HWND hwnd, PAINTSTRUCT *ps )
1337 HRGN hrgn;
1338 HDC hdc;
1339 BOOL erase;
1340 RECT rect;
1341 UINT flags = UPDATE_NONCLIENT | UPDATE_ERASE | UPDATE_PAINT | UPDATE_INTERNALPAINT | UPDATE_NOCHILDREN;
1343 if (user_callbacks) user_callbacks->pHideCaret( hwnd );
1345 if (!(hrgn = send_ncpaint( hwnd, NULL, &flags ))) return 0;
1347 erase = send_erase( hwnd, flags, hrgn, &rect, &hdc );
1349 TRACE( "hdc = %p box = (%s), fErase = %d\n", hdc, wine_dbgstr_rect(&rect), erase );
1351 if (!ps)
1353 release_dc( hwnd, hdc, TRUE );
1354 return 0;
1356 ps->fErase = erase;
1357 ps->rcPaint = rect;
1358 ps->hdc = hdc;
1359 return hdc;
1362 /***********************************************************************
1363 * NtUserEndPaint (win32u.@)
1365 BOOL WINAPI NtUserEndPaint( HWND hwnd, const PAINTSTRUCT *ps )
1367 if (user_callbacks) user_callbacks->pShowCaret( hwnd );
1368 flush_window_surfaces( FALSE );
1369 if (!ps) return FALSE;
1370 release_dc( hwnd, ps->hdc, TRUE );
1371 return TRUE;
1374 /***********************************************************************
1375 * erase_now
1377 * Implementation of RDW_ERASENOW behavior.
1379 void erase_now( HWND hwnd, UINT rdw_flags )
1381 HWND child = 0;
1382 HRGN hrgn;
1383 BOOL need_erase = FALSE;
1385 /* loop while we find a child to repaint */
1386 for (;;)
1388 UINT flags = UPDATE_NONCLIENT | UPDATE_ERASE;
1390 if (rdw_flags & RDW_NOCHILDREN) flags |= UPDATE_NOCHILDREN;
1391 else if (rdw_flags & RDW_ALLCHILDREN) flags |= UPDATE_ALLCHILDREN;
1392 if (need_erase) flags |= UPDATE_DELAYED_ERASE;
1394 if (!(hrgn = send_ncpaint( hwnd, &child, &flags ))) break;
1395 need_erase = send_erase( child, flags, hrgn, NULL, NULL );
1397 if (!flags) break; /* nothing more to do */
1398 if ((rdw_flags & RDW_NOCHILDREN) && !need_erase) break;
1402 /***********************************************************************
1403 * update_now
1405 * Implementation of RDW_UPDATENOW behavior.
1407 static void update_now( HWND hwnd, UINT rdw_flags )
1409 HWND child = 0;
1411 /* desktop window never gets WM_PAINT, only WM_ERASEBKGND */
1412 if (hwnd == get_desktop_window()) erase_now( hwnd, rdw_flags | RDW_NOCHILDREN );
1414 /* loop while we find a child to repaint */
1415 for (;;)
1417 UINT flags = UPDATE_PAINT | UPDATE_INTERNALPAINT;
1419 if (rdw_flags & RDW_NOCHILDREN) flags |= UPDATE_NOCHILDREN;
1420 else if (rdw_flags & RDW_ALLCHILDREN) flags |= UPDATE_ALLCHILDREN;
1422 if (!get_update_flags( hwnd, &child, &flags )) break;
1423 if (!flags) break; /* nothing more to do */
1425 send_message( child, WM_PAINT, 0, 0 );
1426 if (rdw_flags & RDW_NOCHILDREN) break;
1430 /***********************************************************************
1431 * NtUserRedrawWindow (win32u.@)
1433 BOOL WINAPI NtUserRedrawWindow( HWND hwnd, const RECT *rect, HRGN hrgn, UINT flags )
1435 static const RECT empty;
1436 BOOL ret;
1438 if (TRACE_ON(win))
1440 if (hrgn)
1442 RECT r;
1443 NtGdiGetRgnBox( hrgn, &r );
1444 TRACE( "%p region %p box %s ", hwnd, hrgn, wine_dbgstr_rect(&r) );
1446 else if (rect)
1447 TRACE( "%p rect %s ", hwnd, wine_dbgstr_rect(rect) );
1448 else
1449 TRACE( "%p whole window ", hwnd );
1451 dump_rdw_flags(flags);
1454 /* process pending expose events before painting */
1455 if (flags & RDW_UPDATENOW) user_driver->pMsgWaitForMultipleObjectsEx( 0, NULL, 0, QS_PAINT, 0 );
1457 if (rect && !hrgn)
1459 if (IsRectEmpty( rect )) rect = &empty;
1460 ret = redraw_window_rects( hwnd, flags, rect, 1 );
1462 else if (!hrgn)
1464 ret = redraw_window_rects( hwnd, flags, NULL, 0 );
1466 else /* need to build a list of the region rectangles */
1468 DWORD size;
1469 RGNDATA *data;
1471 if (!(size = NtGdiGetRegionData( hrgn, 0, NULL ))) return FALSE;
1472 if (!(data = malloc( size ))) return FALSE;
1473 NtGdiGetRegionData( hrgn, size, data );
1474 if (!data->rdh.nCount) /* empty region -> use a single all-zero rectangle */
1475 ret = redraw_window_rects( hwnd, flags, &empty, 1 );
1476 else
1477 ret = redraw_window_rects( hwnd, flags, (const RECT *)data->Buffer, data->rdh.nCount );
1478 free( data );
1481 if (!hwnd) hwnd = get_desktop_window();
1483 if (flags & RDW_UPDATENOW) update_now( hwnd, flags );
1484 else if (flags & RDW_ERASENOW) erase_now( hwnd, flags );
1486 return ret;
1489 /***********************************************************************
1490 * NtUserGetUpdateRgn (win32u.@)
1492 INT WINAPI NtUserGetUpdateRgn( HWND hwnd, HRGN hrgn, BOOL erase )
1494 DPI_AWARENESS_CONTEXT context;
1495 INT retval = ERROR;
1496 UINT flags = UPDATE_NOCHILDREN;
1497 HRGN update_rgn;
1499 context = set_thread_dpi_awareness_context( get_window_dpi_awareness_context( hwnd ));
1501 if (erase) flags |= UPDATE_NONCLIENT | UPDATE_ERASE;
1503 if ((update_rgn = send_ncpaint( hwnd, NULL, &flags )))
1505 retval = NtGdiCombineRgn( hrgn, update_rgn, 0, RGN_COPY );
1506 if (send_erase( hwnd, flags, update_rgn, NULL, NULL ))
1508 flags = UPDATE_DELAYED_ERASE;
1509 get_update_flags( hwnd, NULL, &flags );
1511 /* map region to client coordinates */
1512 map_window_region( 0, hwnd, hrgn );
1514 set_thread_dpi_awareness_context( context );
1515 return retval;
1518 /***********************************************************************
1519 * NtUserGetUpdateRect (win32u.@)
1521 BOOL WINAPI NtUserGetUpdateRect( HWND hwnd, RECT *rect, BOOL erase )
1523 UINT flags = UPDATE_NOCHILDREN;
1524 HRGN update_rgn;
1525 BOOL need_erase;
1527 if (erase) flags |= UPDATE_NONCLIENT | UPDATE_ERASE;
1529 if (!(update_rgn = send_ncpaint( hwnd, NULL, &flags ))) return FALSE;
1531 if (rect && NtGdiGetRgnBox( update_rgn, rect ) != NULLREGION)
1533 HDC hdc = NtUserGetDCEx( hwnd, 0, DCX_USESTYLE );
1534 DWORD layout = NtGdiSetLayout( hdc, -1, 0 ); /* map_window_points mirrors already */
1535 UINT win_dpi = get_dpi_for_window( hwnd );
1536 map_window_points( 0, hwnd, (POINT *)rect, 2, win_dpi );
1537 *rect = map_dpi_rect( *rect, win_dpi, get_thread_dpi() );
1538 NtGdiTransformPoints( hdc, (POINT *)rect, (POINT *)rect, 2, NtGdiDPtoLP );
1539 NtGdiSetLayout( hdc, -1, layout );
1540 NtUserReleaseDC( hwnd, hdc );
1542 need_erase = send_erase( hwnd, flags, update_rgn, NULL, NULL );
1544 /* check if we still have an update region */
1545 flags = UPDATE_PAINT | UPDATE_NOCHILDREN;
1546 if (need_erase) flags |= UPDATE_DELAYED_ERASE;
1547 return get_update_flags( hwnd, NULL, &flags ) && (flags & UPDATE_PAINT);