From e8d86b7cd40e311d83d796f226491b63032d132e Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Wed, 23 Jun 2004 20:44:58 +0000 Subject: [PATCH] Moved visible region calculation to the server. --- dlls/x11drv/winpos.c | 175 +++----- include/wine/server_protocol.h | 21 +- server/Makefile.in | 1 + server/protocol.def | 11 + server/region.c | 737 +++++++++++++++++++++++++++++++++ server/request.h | 2 + server/trace.c | 32 ++ server/user.h | 20 + server/window.c | 126 ++++++ 9 files changed, 1002 insertions(+), 123 deletions(-) create mode 100644 server/region.c diff --git a/dlls/x11drv/winpos.c b/dlls/x11drv/winpos.c index b44afdec57a..833388e265f 100644 --- a/dlls/x11drv/winpos.c +++ b/dlls/x11drv/winpos.c @@ -121,79 +121,46 @@ static int clip_children( HWND parent, HWND last, HRGN hrgn, int whole_window ) /*********************************************************************** - * get_visible_region - * - * Compute the visible region of a window + * get_server_visible_region */ -static HRGN get_visible_region( WND *win, HWND top, UINT flags, int mode ) +static HRGN get_server_visible_region( HWND hwnd, HWND top, UINT flags ) { - HRGN rgn; - RECT rect; - int xoffset, yoffset; - X11DRV_WND_DATA *data = win->pDriverData; + RGNDATA *data; + HRGN ret = 0; + size_t size = 256; + BOOL retry = FALSE; - if (flags & DCX_WINDOW) + do { - xoffset = win->rectWindow.left; - yoffset = win->rectWindow.top; - } - else - { - xoffset = win->rectClient.left; - yoffset = win->rectClient.top; - } - - if (flags & DCX_PARENTCLIP) - GetClientRect( win->parent, &rect ); - else if (flags & DCX_WINDOW) - rect = data->whole_rect; - else - rect = win->rectClient; - - /* vis region is relative to the start of the client/window area */ - OffsetRect( &rect, -xoffset, -yoffset ); - - if (!(rgn = CreateRectRgn( rect.left, rect.top, rect.right, rect.bottom ))) return 0; - - if ((flags & DCX_CLIPCHILDREN) && (mode != ClipByChildren)) - { - /* we need to clip children by hand */ - if (clip_children( win->hwndSelf, 0, rgn, (flags & DCX_WINDOW) ) == NULLREGION) return rgn; - } - - if (top && top != win->hwndSelf) /* need to clip siblings of ancestors */ - { - WND *parent, *ptr = WIN_FindWndPtr( win->hwndSelf ); - HRGN tmp = 0; - - OffsetRgn( rgn, xoffset, yoffset ); - for (;;) + if (!(data = HeapAlloc( GetProcessHeap(), 0, sizeof(*data) + size - 1 ))) return 0; + SERVER_START_REQ( get_visible_region ) { - if (ptr->dwStyle & WS_CLIPSIBLINGS) + req->window = hwnd; + req->top_win = top; + req->flags = flags; + wine_server_set_reply( req, data->Buffer, size ); + if (!wine_server_call_err( req )) { - if (clip_children( ptr->parent, ptr->hwndSelf, rgn, FALSE ) == NULLREGION) break; + if (reply->total_size <= size) + { + size_t reply_size = wine_server_reply_size( reply ); + data->rdh.dwSize = sizeof(data->rdh); + data->rdh.iType = RDH_RECTANGLES; + data->rdh.nCount = reply_size / sizeof(RECT); + data->rdh.nRgnSize = reply_size; + ret = ExtCreateRegion( NULL, size, data ); + } + else + { + size = reply->total_size; + retry = TRUE; + } } - if (ptr->hwndSelf == top) break; - if (!(parent = WIN_FindWndPtr( ptr->parent ))) break; - WIN_ReleaseWndPtr( ptr ); - ptr = parent; - /* clip to parent client area */ - if (tmp) SetRectRgn( tmp, 0, 0, ptr->rectClient.right - ptr->rectClient.left, - ptr->rectClient.bottom - ptr->rectClient.top ); - else tmp = CreateRectRgn( 0, 0, ptr->rectClient.right - ptr->rectClient.left, - ptr->rectClient.bottom - ptr->rectClient.top ); - CombineRgn( rgn, rgn, tmp, RGN_AND ); - OffsetRgn( rgn, ptr->rectClient.left, ptr->rectClient.top ); - xoffset += ptr->rectClient.left; - yoffset += ptr->rectClient.top; } - WIN_ReleaseWndPtr( ptr ); - /* make it relative to the target window again */ - OffsetRgn( rgn, -xoffset, -yoffset ); - if (tmp) DeleteObject( tmp ); - } - - return rgn; + SERVER_END_REQ; + HeapFree( GetProcessHeap(), 0, data ); + } while (retry); + return ret; } @@ -426,52 +393,24 @@ BOOL X11DRV_GetDC( HWND hwnd, HDC hdc, HRGN hrgn, DWORD flags ) HWND top = 0; X11DRV_WND_DATA *data = win->pDriverData; struct x11drv_escape_set_drawable escape; - BOOL visible; escape.mode = IncludeInferiors; /* don't clip siblings if using parent clip region */ if (flags & DCX_PARENTCLIP) flags &= ~DCX_CLIPSIBLINGS; - /* find the top parent in the hierarchy that isn't clipping siblings */ - visible = (win->dwStyle & WS_VISIBLE) != 0; - - if (visible) + top = GetAncestor( hwnd, GA_ROOT ); + if (top != hwnd) { - HWND *list = WIN_ListParents( hwnd ); - if (list) - { - int i; - for (i = 0; list[i] != GetDesktopWindow(); i++) - { - LONG style = GetWindowLongW( list[i], GWL_STYLE ); - if (!(style & WS_VISIBLE)) - { - visible = FALSE; - top = 0; - break; - } - if (!(style & WS_CLIPSIBLINGS)) top = list[i]; - } - HeapFree( GetProcessHeap(), 0, list ); - } - if (!top && visible && !(flags & DCX_CLIPSIBLINGS)) top = hwnd; - } - - if (top) - { - HWND parent = GetAncestor( top, GA_PARENT ); escape.org.x = escape.org.y = 0; if (flags & DCX_WINDOW) { escape.org.x = win->rectWindow.left - win->rectClient.left; escape.org.y = win->rectWindow.top - win->rectClient.top; } - MapWindowPoints( hwnd, parent, &escape.org, 1 ); + MapWindowPoints( hwnd, top, &escape.org, 1 ); escape.drawable_org.x = escape.drawable_org.y = 0; - MapWindowPoints( parent, 0, &escape.drawable_org, 1 ); - /* have to use the parent so that we include siblings */ - if (parent) escape.drawable = X11DRV_get_client_window( parent ); - else escape.drawable = root_window; + MapWindowPoints( top, 0, &escape.drawable_org, 1 ); + escape.drawable = X11DRV_get_client_window( top ); } else { @@ -482,23 +421,22 @@ BOOL X11DRV_GetDC( HWND hwnd, HDC hdc, HRGN hrgn, DWORD flags ) escape.org.y = 0; escape.drawable_org = escape.org; } - else if (flags & DCX_WINDOW) - { - escape.drawable = data->whole_window; - escape.org.x = win->rectWindow.left - data->whole_rect.left; - escape.org.y = win->rectWindow.top - data->whole_rect.top; - escape.drawable_org.x = data->whole_rect.left - win->rectClient.left; - escape.drawable_org.y = data->whole_rect.top - win->rectClient.top; - } else { - escape.drawable = data->client_window; - escape.org.x = 0; - escape.org.y = 0; - escape.drawable_org = escape.org; - if (flags & DCX_CLIPCHILDREN) escape.mode = ClipByChildren; /* can use X11 clipping */ + escape.drawable = data->whole_window; + escape.drawable_org.x = data->whole_rect.left; + escape.drawable_org.y = data->whole_rect.top; + if (flags & DCX_WINDOW) + { + escape.org.x = win->rectWindow.left - data->whole_rect.left; + escape.org.y = win->rectWindow.top - data->whole_rect.top; + } + else + { + escape.org.x = win->rectClient.left - data->whole_rect.left; + escape.org.y = win->rectClient.top - data->whole_rect.top; + } } - MapWindowPoints( hwnd, 0, &escape.drawable_org, 1 ); } escape.code = X11DRV_SET_DRAWABLE; @@ -508,17 +446,10 @@ BOOL X11DRV_GetDC( HWND hwnd, HDC hdc, HRGN hrgn, DWORD flags ) SetHookFlags16( HDC_16(hdc), DCHF_VALIDATEVISRGN )) /* DC was dirty */ { /* need to recompute the visible region */ - HRGN visRgn; + HRGN visRgn = get_server_visible_region( hwnd, top, flags ); - if (visible) - { - visRgn = get_visible_region( win, top, flags, escape.mode ); - - if (flags & (DCX_EXCLUDERGN | DCX_INTERSECTRGN)) - CombineRgn( visRgn, visRgn, hrgn, - (flags & DCX_INTERSECTRGN) ? RGN_AND : RGN_DIFF ); - } - else visRgn = CreateRectRgn( 0, 0, 0, 0 ); + if (flags & (DCX_EXCLUDERGN | DCX_INTERSECTRGN)) + CombineRgn( visRgn, visRgn, hrgn, (flags & DCX_INTERSECTRGN) ? RGN_AND : RGN_DIFF ); SelectVisRgn16( HDC_16(hdc), HRGN_16(visRgn) ); DeleteObject( visRgn ); diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 4bbeefecb8c..f942f243091 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -2614,6 +2614,22 @@ struct get_windows_offset_reply +struct get_visible_region_request +{ + struct request_header __header; + user_handle_t window; + user_handle_t top_win; + unsigned int flags; +}; +struct get_visible_region_reply +{ + struct reply_header __header; + size_t total_size; + /* VARARG(region,rectangles); */ +}; + + + struct set_window_property_request { struct request_header __header; @@ -3189,6 +3205,7 @@ enum request REQ_set_window_text, REQ_inc_window_paint_count, REQ_get_windows_offset, + REQ_get_visible_region, REQ_set_window_property, REQ_remove_window_property, REQ_get_window_property, @@ -3369,6 +3386,7 @@ union generic_request struct set_window_text_request set_window_text_request; struct inc_window_paint_count_request inc_window_paint_count_request; struct get_windows_offset_request get_windows_offset_request; + struct get_visible_region_request get_visible_region_request; struct set_window_property_request set_window_property_request; struct remove_window_property_request remove_window_property_request; struct get_window_property_request get_window_property_request; @@ -3547,6 +3565,7 @@ union generic_reply struct set_window_text_reply set_window_text_reply; struct inc_window_paint_count_reply inc_window_paint_count_reply; struct get_windows_offset_reply get_windows_offset_reply; + struct get_visible_region_reply get_visible_region_reply; struct set_window_property_reply set_window_property_reply; struct remove_window_property_reply remove_window_property_reply; struct get_window_property_reply get_window_property_reply; @@ -3574,6 +3593,6 @@ union generic_reply struct set_global_windows_reply set_global_windows_reply; }; -#define SERVER_PROTOCOL_VERSION 143 +#define SERVER_PROTOCOL_VERSION 144 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/Makefile.in b/server/Makefile.in index 540452d9691..969b33c73d7 100644 --- a/server/Makefile.in +++ b/server/Makefile.in @@ -29,6 +29,7 @@ C_SRCS = \ process.c \ ptrace.c \ queue.c \ + region.c \ registry.c \ request.c \ semaphore.c \ diff --git a/server/protocol.def b/server/protocol.def index 54acaeff795..08eb4766b18 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1838,6 +1838,17 @@ enum message_type @END +/* Get the visible region of a window */ +@REQ(get_visible_region) + user_handle_t window; /* handle to the window */ + user_handle_t top_win; /* top window to clip against */ + unsigned int flags; /* DCX flags */ +@REPLY + size_t total_size; /* total size of the resulting region */ + VARARG(region,rectangles); /* list of rectangles for the region */ +@END + + /* Set a window property */ @REQ(set_window_property) user_handle_t window; /* handle to the window */ diff --git a/server/region.c b/server/region.c new file mode 100644 index 00000000000..7ecefe99a1f --- /dev/null +++ b/server/region.c @@ -0,0 +1,737 @@ +/* + * Server-side region objects. Based on the X11 implementation. + * + * Copyright 1993, 1994, 1995, 2004 Alexandre Julliard + * Modifications and additions: Copyright 1998 Huw Davies + * 1999 Alex Korobka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Note: + * This is a simplified version of the code, without all the explanations. + * Check the equivalent GDI code to make sense of it. + */ + +/************************************************************************ + +Copyright (c) 1987, 1988 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + + +Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +************************************************************************/ + +#include +#include +#include +#include "request.h" + +struct region +{ + int size; + int num_rects; + rectangle_t *rects; + rectangle_t extents; +}; + + +#define RGN_DEFAULT_RECTS 2 + +#define EXTENTCHECK(r1, r2) \ + ((r1)->right > (r2)->left && \ + (r1)->left < (r2)->right && \ + (r1)->bottom > (r2)->top && \ + (r1)->top < (r2)->bottom) + +typedef int (*overlap_func_t)( struct region *reg, const rectangle_t *r1, const rectangle_t *r1End, + const rectangle_t *r2, const rectangle_t *r2End, int top, int bottom ); +typedef int (*non_overlap_func_t)( struct region *reg, const rectangle_t *r, + const rectangle_t *rEnd, int top, int bottom ); + +/* add a rectangle to a region */ +static inline rectangle_t *add_rect( struct region *reg ) +{ + if (reg->num_rects >= reg->size - 1) + { + rectangle_t *new_rect = realloc( reg->rects, 2 * sizeof(rectangle_t) * reg->size ); + if (!new_rect) + { + set_error( STATUS_NO_MEMORY ); + return 0; + } + reg->rects = new_rect; + reg->size *= 2; + } + return reg->rects + reg->num_rects++; +} + +/* make sure all the rectangles are valid and that the region is properly y-x-banded */ +static inline int validate_rectangles( const rectangle_t *rects, unsigned int nb_rects ) +{ + const rectangle_t *ptr, *end; + + for (ptr = rects, end = rects + nb_rects; ptr < end; ptr++) + { + if (ptr->left >= ptr->right || ptr->top >= ptr->bottom) return 0; /* empty rectangle */ + if (ptr == end - 1) break; + if (ptr[0].top == ptr[1].top) /* same band */ + { + if (ptr[0].bottom != ptr[1].bottom) return 0; /* not same y extent */ + if (ptr[0].right >= ptr[1].left) return 0; /* not properly x ordered */ + } + else /* new band */ + { + if (ptr[0].bottom > ptr[1].top) return 0; /* not properly y ordered */ + } + } + return 1; +} + +/* attempt to merge the rects in the current band with those in the */ +/* previous one. Used only by region_op. */ +static int coalesce_region( struct region *pReg, int prevStart, int curStart ) +{ + int curNumRects; + rectangle_t *pRegEnd = &pReg->rects[pReg->num_rects]; + rectangle_t *pPrevRect = &pReg->rects[prevStart]; + rectangle_t *pCurRect = &pReg->rects[curStart]; + int prevNumRects = curStart - prevStart; + int bandtop = pCurRect->top; + + for (curNumRects = 0; + (pCurRect != pRegEnd) && (pCurRect->top == bandtop); + curNumRects++) + { + pCurRect++; + } + + if (pCurRect != pRegEnd) + { + pRegEnd--; + while (pRegEnd[-1].top == pRegEnd->top) pRegEnd--; + curStart = pRegEnd - pReg->rects; + pRegEnd = pReg->rects + pReg->num_rects; + } + + if ((curNumRects == prevNumRects) && (curNumRects != 0)) + { + pCurRect -= curNumRects; + if (pPrevRect->bottom == pCurRect->top) + { + do + { + if ((pPrevRect->left != pCurRect->left) || + (pPrevRect->right != pCurRect->right)) return curStart; + pPrevRect++; + pCurRect++; + prevNumRects -= 1; + } while (prevNumRects != 0); + + pReg->num_rects -= curNumRects; + pCurRect -= curNumRects; + pPrevRect -= curNumRects; + + do + { + pPrevRect->bottom = pCurRect->bottom; + pPrevRect++; + pCurRect++; + curNumRects -= 1; + } while (curNumRects != 0); + + if (pCurRect == pRegEnd) curStart = prevStart; + else do { *pPrevRect++ = *pCurRect++; } while (pCurRect != pRegEnd); + + } + } + return curStart; +} + +/* apply an operation to two regions */ +/* check the GDI version of the code for explanations */ +static int region_op( struct region *newReg, const struct region *reg1, const struct region *reg2, + overlap_func_t overlap_func, + non_overlap_func_t non_overlap1_func, + non_overlap_func_t non_overlap2_func ) +{ + int ybot, ytop, top, bot, prevBand, curBand; + const rectangle_t *r1BandEnd, *r2BandEnd; + + const rectangle_t *r1 = reg1->rects; + const rectangle_t *r2 = reg2->rects; + const rectangle_t *r1End = r1 + reg1->num_rects; + const rectangle_t *r2End = r2 + reg2->num_rects; + + rectangle_t *new_rects, *old_rects = newReg->rects; + int new_size, ret = 0; + + new_size = max( reg1->num_rects, reg2->num_rects ) * 2; + if (!(new_rects = mem_alloc( new_size * sizeof(*newReg->rects) ))) return 0; + + newReg->size = new_size; + newReg->rects = new_rects; + newReg->num_rects = 0; + + if (reg1->extents.top < reg2->extents.top) + ybot = reg1->extents.top; + else + ybot = reg2->extents.top; + + prevBand = 0; + + do + { + curBand = newReg->num_rects; + + r1BandEnd = r1; + while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top)) r1BandEnd++; + + r2BandEnd = r2; + while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top)) r2BandEnd++; + + if (r1->top < r2->top) + { + top = max(r1->top,ybot); + bot = min(r1->bottom,r2->top); + + if ((top != bot) && non_overlap1_func) + { + if (!non_overlap1_func( newReg, r1, r1BandEnd, top, bot )) goto done; + } + + ytop = r2->top; + } + else if (r2->top < r1->top) + { + top = max(r2->top,ybot); + bot = min(r2->bottom,r1->top); + + if ((top != bot) && non_overlap2_func) + { + if (!non_overlap2_func( newReg, r2, r2BandEnd, top, bot )) goto done; + } + + ytop = r1->top; + } + else + { + ytop = r1->top; + } + + if (newReg->num_rects != curBand) + prevBand = coalesce_region(newReg, prevBand, curBand); + + ybot = min(r1->bottom, r2->bottom); + curBand = newReg->num_rects; + if (ybot > ytop) + { + if (!overlap_func( newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot )) goto done; + } + + if (newReg->num_rects != curBand) + prevBand = coalesce_region(newReg, prevBand, curBand); + + if (r1->bottom == ybot) r1 = r1BandEnd; + if (r2->bottom == ybot) r2 = r2BandEnd; + } while ((r1 != r1End) && (r2 != r2End)); + + curBand = newReg->num_rects; + if (r1 != r1End) + { + if (non_overlap1_func) + { + do + { + r1BandEnd = r1; + while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top)) r1BandEnd++; + if (!non_overlap1_func( newReg, r1, r1BandEnd, max(r1->top,ybot), r1->bottom )) + goto done; + r1 = r1BandEnd; + } while (r1 != r1End); + } + } + else if ((r2 != r2End) && non_overlap2_func) + { + do + { + r2BandEnd = r2; + while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top)) r2BandEnd++; + if (!non_overlap2_func( newReg, r2, r2BandEnd, max(r2->top,ybot), r2->bottom )) + goto done; + r2 = r2BandEnd; + } while (r2 != r2End); + } + + if (newReg->num_rects != curBand) coalesce_region(newReg, prevBand, curBand); + + if ((newReg->num_rects < (newReg->size / 2)) && (newReg->size > 2)) + { + new_size = max( newReg->num_rects, RGN_DEFAULT_RECTS ); + if ((new_rects = realloc( newReg->rects, sizeof(*newReg->rects) * new_size ))) + { + newReg->rects = new_rects; + newReg->size = new_size; + } + } + ret = 1; +done: + free( old_rects ); + return ret; +} + +/* recalculate the extents of a region */ +static void set_region_extents( struct region *region ) +{ + rectangle_t *pRect, *pRectEnd; + + if (region->num_rects == 0) + { + region->extents.left = 0; + region->extents.top = 0; + region->extents.right = 0; + region->extents.bottom = 0; + return; + } + + pRect = region->rects; + pRectEnd = &pRect[region->num_rects - 1]; + + region->extents.left = pRect->left; + region->extents.top = pRect->top; + region->extents.right = pRectEnd->right; + region->extents.bottom = pRectEnd->bottom; + + while (pRect <= pRectEnd) + { + if (pRect->left < region->extents.left) region->extents.left = pRect->left; + if (pRect->right > region->extents.right) region->extents.right = pRect->right; + pRect++; + } +} + +/* handle an overlapping band for intersect_region */ +static int intersect_overlapping( struct region *pReg, + const rectangle_t *r1, const rectangle_t *r1End, + const rectangle_t *r2, const rectangle_t *r2End, + int top, int bottom ) + +{ + int left, right; + + while ((r1 != r1End) && (r2 != r2End)) + { + left = max(r1->left, r2->left); + right = min(r1->right, r2->right); + + if (left < right) + { + rectangle_t *rect = add_rect( pReg ); + if (!rect) return 0; + rect->left = left; + rect->top = top; + rect->right = right; + rect->bottom = bottom; + } + + if (r1->right < r2->right) r1++; + else if (r2->right < r1->right) r2++; + else + { + r1++; + r2++; + } + } + return 1; +} + +/* handle a non-overlapping band for subtract_region */ +static int subtract_non_overlapping( struct region *pReg, const rectangle_t *r, + const rectangle_t *rEnd, int top, int bottom ) +{ + while (r != rEnd) + { + rectangle_t *rect = add_rect( pReg ); + if (!rect) return 0; + rect->left = r->left; + rect->top = top; + rect->right = r->right; + rect->bottom = bottom; + r++; + } + return 1; +} + +/* handle an overlapping band for subtract_region */ +static int subtract_overlapping( struct region *pReg, + const rectangle_t *r1, const rectangle_t *r1End, + const rectangle_t *r2, const rectangle_t *r2End, + int top, int bottom ) +{ + int left = r1->left; + + while ((r1 != r1End) && (r2 != r2End)) + { + if (r2->right <= left) r2++; + else if (r2->left <= left) + { + left = r2->right; + if (left >= r1->right) + { + r1++; + if (r1 != r1End) + left = r1->left; + } + else r2++; + } + else if (r2->left < r1->right) + { + rectangle_t *rect = add_rect( pReg ); + if (!rect) return 0; + rect->left = left; + rect->top = top; + rect->right = r2->left; + rect->bottom = bottom; + left = r2->right; + if (left >= r1->right) + { + r1++; + if (r1 != r1End) + left = r1->left; + } + else r2++; + } + else + { + if (r1->right > left) + { + rectangle_t *rect = add_rect( pReg ); + if (!rect) return 0; + rect->left = left; + rect->top = top; + rect->right = r1->right; + rect->bottom = bottom; + } + r1++; + left = r1->left; + } + } + + while (r1 != r1End) + { + rectangle_t *rect = add_rect( pReg ); + if (!rect) return 0; + rect->left = left; + rect->top = top; + rect->right = r1->right; + rect->bottom = bottom; + r1++; + if (r1 != r1End) left = r1->left; + } + return 1; +} + +/* handle a non-overlapping band for union_region */ +static int union_non_overlapping( struct region *pReg, const rectangle_t *r, + const rectangle_t *rEnd, int top, int bottom ) +{ + while (r != rEnd) + { + rectangle_t *rect = add_rect( pReg ); + if (!rect) return 0; + rect->left = r->left; + rect->top = top; + rect->right = r->right; + rect->bottom = bottom; + r++; + } + return 1; +} + +/* handle an overlapping band for union_region */ +static int union_overlapping( struct region *pReg, + const rectangle_t *r1, const rectangle_t *r1End, + const rectangle_t *r2, const rectangle_t *r2End, + int top, int bottom ) +{ +#define MERGERECT(r) \ + if ((pReg->num_rects != 0) && \ + (pReg->rects[pReg->num_rects-1].top == top) && \ + (pReg->rects[pReg->num_rects-1].bottom == bottom) && \ + (pReg->rects[pReg->num_rects-1].right >= r->left)) \ + { \ + if (pReg->rects[pReg->num_rects-1].right < r->right) \ + { \ + pReg->rects[pReg->num_rects-1].right = r->right; \ + } \ + } \ + else \ + { \ + rectangle_t *rect = add_rect( pReg ); \ + if (!rect) return 0; \ + rect->top = top; \ + rect->bottom = bottom; \ + rect->left = r->left; \ + rect->right = r->right; \ + } \ + r++; + + while ((r1 != r1End) && (r2 != r2End)) + { + if (r1->left < r2->left) + { + MERGERECT(r1); + } + else + { + MERGERECT(r2); + } + } + + if (r1 != r1End) + { + do + { + MERGERECT(r1); + } while (r1 != r1End); + } + else while (r2 != r2End) + { + MERGERECT(r2); + } + return 1; +#undef MERGERECT +} + + +/* create a region from an array of rectangles */ +struct region *create_region( const rectangle_t *rects, unsigned int nb_rects ) +{ + struct region *region; + unsigned int size = max( nb_rects, RGN_DEFAULT_RECTS ); + + if (!validate_rectangles( rects, nb_rects )) + { + set_error( STATUS_INVALID_PARAMETER ); + return NULL; + } + if (!(region = mem_alloc( sizeof(*region) ))) return NULL; + if (!(region->rects = mem_alloc( size * sizeof(*region->rects) ))) + { + free( region ); + return NULL; + } + region->size = size; + region->num_rects = nb_rects; + memcpy( region->rects, rects, nb_rects * sizeof(*rects) ); + set_region_extents( region ); + return region; +} + + +/* free a region */ +void free_region( struct region *region ) +{ + free( region->rects ); + free( region ); +} + +/* set region to a simple rectangle */ +void set_region_rect( struct region *region, const rectangle_t *rect ) +{ + if (rect->left < rect->right && rect->top < rect->bottom) + { + region->num_rects = 1; + region->rects[0] = region->extents = *rect; + } + else + { + region->num_rects = 0; + region->extents.left = 0; + region->extents.top = 0; + region->extents.right = 0; + region->extents.bottom = 0; + } +} + +/* retrieve the region data for sending to the client */ +rectangle_t *get_region_data( const struct region *region, size_t *total_size ) +{ + *total_size = region->num_rects * sizeof(rectangle_t); + return memdup( region->rects, *total_size ); +} + +/* retrieve the region data for sending to the client and free the region at the same time */ +rectangle_t *get_region_data_and_free( struct region *region, size_t *total_size ) +{ + rectangle_t *ret = region->rects; + *total_size = region->num_rects * sizeof(rectangle_t); + free( region ); + return ret; +} + +/* check if a given region is empty */ +int is_region_empty( const struct region *region ) +{ + return region->num_rects == 0; +} + + +/* get the extents rect of a region */ +void get_region_extents( const struct region *region, rectangle_t *rect ) +{ + *rect = region->extents; +} + +/* add an offset to a region */ +void offset_region( struct region *region, int x, int y ) +{ + rectangle_t *rect, *end; + + for (rect = region->rects, end = rect + region->num_rects; rect < end; rect++) + { + rect->left += x; + rect->right += x; + rect->top += y; + rect->bottom += y; + } + region->extents.left += x; + region->extents.right += x; + region->extents.top += y; + region->extents.bottom += y; +} + +/* make a copy of a region; returns dst or NULL on error */ +struct region *copy_region( struct region *dst, const struct region *src ) +{ + if (dst == src) return dst; + + if (dst->size < src->num_rects) + { + rectangle_t *rect = realloc( dst->rects, src->num_rects * sizeof(*rect) ); + if (!rect) + { + set_error( STATUS_NO_MEMORY ); + return NULL; + } + dst->rects = rect; + dst->size = src->num_rects; + } + dst->num_rects = src->num_rects; + dst->extents = src->extents; + memcpy( dst->rects, src->rects, src->num_rects * sizeof(*dst->rects) ); + return dst; +} + +/* compute the intersection of two regions into dst, which can be one of the source regions */ +struct region *intersect_region( struct region *dst, const struct region *src1, + const struct region *src2 ) +{ + if (!src1->num_rects || !src2->num_rects || !EXTENTCHECK(&src1->extents, &src2->extents)) + { + dst->num_rects = 0; + dst->extents.left = 0; + dst->extents.top = 0; + dst->extents.right = 0; + dst->extents.bottom = 0; + return dst; + } + if (!region_op( dst, src1, src2, intersect_overlapping, NULL, NULL )) return NULL; + set_region_extents( dst ); + return dst; +} + +/* compute the subtraction of two regions into dst, which can be one of the source regions */ +struct region *subtract_region( struct region *dst, const struct region *src1, + const struct region *src2 ) +{ + if (!src1->num_rects || !src2->num_rects || !EXTENTCHECK(&src1->extents, &src2->extents)) + return copy_region( dst, src1 ); + + if (!region_op( dst, src1, src2, subtract_overlapping, + subtract_non_overlapping, NULL )) return NULL; + set_region_extents( dst ); + return dst; +} + +/* compute the union of two regions into dst, which can be one of the source regions */ +struct region *union_region( struct region *dst, const struct region *src1, + const struct region *src2 ) +{ + if (src1 == src2) return copy_region( dst, src1 ); + if (!src1->num_rects) return copy_region( dst, src2 ); + if (!src2->num_rects) return copy_region( dst, src1 ); + + if ((src1->num_rects == 1) && + (src1->extents.left <= src2->extents.left) && + (src1->extents.top <= src2->extents.top) && + (src1->extents.right >= src2->extents.right) && + (src1->extents.bottom >= src2->extents.bottom)) + return copy_region( dst, src1 ); + + if ((src2->num_rects == 1) && + (src2->extents.left <= src1->extents.left) && + (src2->extents.top <= src1->extents.top) && + (src2->extents.right >= src1->extents.right) && + (src2->extents.bottom >= src1->extents.bottom)) + return copy_region( dst, src2 ); + + if (!region_op( dst, src1, src2, union_overlapping, + union_non_overlapping, union_non_overlapping )) return NULL; + + dst->extents.left = min(src1->extents.left, src2->extents.left); + dst->extents.top = min(src1->extents.top, src2->extents.top); + dst->extents.right = max(src1->extents.right, src2->extents.right); + dst->extents.bottom = max(src1->extents.bottom, src2->extents.bottom); + return dst; +} diff --git a/server/request.h b/server/request.h index 84158ee405e..43dc9a9df71 100644 --- a/server/request.h +++ b/server/request.h @@ -251,6 +251,7 @@ DECL_HANDLER(get_window_text); DECL_HANDLER(set_window_text); DECL_HANDLER(inc_window_paint_count); DECL_HANDLER(get_windows_offset); +DECL_HANDLER(get_visible_region); DECL_HANDLER(set_window_property); DECL_HANDLER(remove_window_property); DECL_HANDLER(get_window_property); @@ -430,6 +431,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_set_window_text, (req_handler)req_inc_window_paint_count, (req_handler)req_get_windows_offset, + (req_handler)req_get_visible_region, (req_handler)req_set_window_property, (req_handler)req_remove_window_property, (req_handler)req_get_window_property, diff --git a/server/trace.c b/server/trace.c index a24098e3a30..8a85720aa88 100644 --- a/server/trace.c +++ b/server/trace.c @@ -375,6 +375,21 @@ static void dump_varargs_input_records( size_t size ) remove_data( size ); } +static void dump_varargs_rectangles( size_t size ) +{ + const rectangle_t *rect = cur_data; + size_t len = size / sizeof(*rect); + + fputc( '{', stderr ); + while (len > 0) + { + dump_rectangle( rect++ ); + if (--len) fputc( ',', stderr ); + } + fputc( '}', stderr ); + remove_data( size ); +} + static void dump_varargs_properties( size_t size ) { const property_data_t *prop = cur_data; @@ -2161,6 +2176,20 @@ static void dump_get_windows_offset_reply( const struct get_windows_offset_reply fprintf( stderr, " y=%d", req->y ); } +static void dump_get_visible_region_request( const struct get_visible_region_request *req ) +{ + fprintf( stderr, " window=%p,", req->window ); + fprintf( stderr, " top_win=%p,", req->top_win ); + fprintf( stderr, " flags=%08x", req->flags ); +} + +static void dump_get_visible_region_reply( const struct get_visible_region_reply *req ) +{ + fprintf( stderr, " total_size=%d,", req->total_size ); + fprintf( stderr, " region=" ); + dump_varargs_rectangles( cur_size ); +} + static void dump_set_window_property_request( const struct set_window_property_request *req ) { fprintf( stderr, " window=%p,", req->window ); @@ -2630,6 +2659,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_set_window_text_request, (dump_func)dump_inc_window_paint_count_request, (dump_func)dump_get_windows_offset_request, + (dump_func)dump_get_visible_region_request, (dump_func)dump_set_window_property_request, (dump_func)dump_remove_window_property_request, (dump_func)dump_get_window_property_request, @@ -2806,6 +2836,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)0, (dump_func)0, (dump_func)dump_get_windows_offset_reply, + (dump_func)dump_get_visible_region_reply, (dump_func)0, (dump_func)dump_remove_window_property_reply, (dump_func)dump_get_window_property_reply, @@ -2982,6 +3013,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "set_window_text", "inc_window_paint_count", "get_windows_offset", + "get_visible_region", "set_window_property", "remove_window_property", "get_window_property", diff --git a/server/user.h b/server/user.h index 62bc80d8946..f6b960df871 100644 --- a/server/user.h +++ b/server/user.h @@ -24,6 +24,7 @@ #include "wine/server_protocol.h" struct thread; +struct region; struct window; struct msg_queue; struct hook_table; @@ -64,6 +65,25 @@ extern int attach_thread_input( struct thread *thread_from, struct thread *threa extern void post_message( user_handle_t win, unsigned int message, unsigned int wparam, unsigned int lparam ); +/* region functions */ + +extern struct region *create_region( const rectangle_t *rects, unsigned int nb_rects ); +extern void free_region( struct region *region ); +extern void set_region_rect( struct region *region, const rectangle_t *rect ); +extern rectangle_t *get_region_data( const struct region *region, size_t *total_size ); +extern rectangle_t *get_region_data_and_free( struct region *region, size_t *total_size ); +extern int is_region_empty( const struct region *region ); +extern void get_region_extents( const struct region *region, rectangle_t *rect ); +extern void offset_region( struct region *region, int x, int y ); +extern struct region *copy_region( struct region *dst, const struct region *src ); +extern struct region *intersect_region( struct region *dst, const struct region *src1, + const struct region *src2 ); +extern struct region *subtract_region( struct region *dst, const struct region *src1, + const struct region *src2 ); +extern struct region *union_region( struct region *dst, const struct region *src1, + const struct region *src2 ); +static inline struct region *create_empty_region(void) { return create_region( NULL, 0 ); } + /* window functions */ extern void destroy_thread_windows( struct thread *thread ); diff --git a/server/window.c b/server/window.c index 8550de11748..dbfde586088 100644 --- a/server/window.c +++ b/server/window.c @@ -463,6 +463,114 @@ user_handle_t find_window_to_repaint( user_handle_t parent, struct thread *threa } +/* clip all children of a given window out of the visible region */ +static struct region *clip_children( struct window *parent, struct window *last, + struct region *region, int offset_x, int offset_y ) +{ + struct window *ptr; + struct region *tmp = create_empty_region(); + + if (!tmp) return NULL; + for (ptr = parent->first_child; ptr && ptr != last; ptr = ptr->next) + { + if (!(ptr->style & WS_VISIBLE)) continue; + if (ptr->ex_style & WS_EX_TRANSPARENT) continue; + set_region_rect( tmp, &ptr->window_rect ); + offset_region( tmp, offset_x, offset_y ); + if (!(region = subtract_region( region, region, tmp ))) break; + if (is_region_empty( region )) break; + } + free_region( tmp ); + return region; +} + + +/* compute the visible region of a window */ +static struct region *get_visible_region( struct window *win, struct window *top, + unsigned int flags ) +{ + struct region *tmp, *region; + struct window *ptr; + rectangle_t rect; + int offset_x, offset_y; + + if (!(region = create_empty_region())) return NULL; + + /* first check if all ancestors are visible */ + + for (ptr = win; ptr != top_window; ptr = ptr->parent) + if (!(ptr->style & WS_VISIBLE)) return region; /* empty region */ + + /* retrieve window rectangle in parent coordinates */ + + if ((flags & DCX_PARENTCLIP) && win->parent) + { + rect.left = rect.top = 0; + rect.right = win->parent->client_rect.right - win->parent->client_rect.left; + rect.bottom = win->parent->client_rect.bottom - win->parent->client_rect.top; + offset_x = win->client_rect.left; + offset_y = win->client_rect.top; + } + else if (flags & DCX_WINDOW) + { + rect = win->window_rect; + offset_x = win->window_rect.left; + offset_y = win->window_rect.top; + } + else + { + rect = win->client_rect; + offset_x = win->client_rect.left; + offset_y = win->client_rect.top; + } + + /* create a region relative to the window itself */ + + set_region_rect( region, &rect ); + offset_region( region, -offset_x, -offset_y ); + + /* clip children */ + + if (flags & DCX_CLIPCHILDREN) + { + if (!clip_children( win, NULL, region, + offset_x - win->client_rect.left, + offset_y - win->client_rect.top )) goto error; + } + + /* clip siblings of ancestors */ + + if (top && top != win && (tmp = create_empty_region()) != NULL) + { + offset_region( region, offset_x, offset_y ); /* make it relative to parent */ + while (win->parent) + { + if (win->style & WS_CLIPSIBLINGS) + { + if (!clip_children( win->parent, win, region, 0, 0 )) goto error; + if (is_region_empty( region )) break; + } + if (win == top) break; + /* clip to parent client area */ + win = win->parent; + offset_x += win->client_rect.left; + offset_y += win->client_rect.top; + offset_region( region, win->client_rect.left, win->client_rect.top ); + set_region_rect( tmp, &win->client_rect ); + if (!intersect_region( region, region, tmp )) goto error; + if (is_region_empty( region )) break; + } + offset_region( region, -offset_x, -offset_y ); /* make it relative to target window again */ + free_region( tmp ); + } + return region; + +error: + free_region( region ); + return NULL; +} + + /* get the window class of a window */ struct window_class* get_window_class( user_handle_t window ) { @@ -825,6 +933,24 @@ DECL_HANDLER(get_windows_offset) } +/* get the visible region of a window */ +DECL_HANDLER(get_visible_region) +{ + struct region *region; + struct window *win = get_window( req->window ); + struct window *top = NULL; + + if (!win) return; + if (req->top_win && !(top = get_window( req->top_win ))) return; + + if ((region = get_visible_region( win, top, req->flags ))) + { + rectangle_t *data = get_region_data_and_free( region, &reply->total_size ); + set_reply_data_ptr( data, min(reply->total_size,get_reply_max_size()) ); + } +} + + /* set a window property */ DECL_HANDLER(set_window_property) {