From 618a7e581388a69e7d320af2d8eea0a6b998c703 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 29 Jun 2004 03:53:25 +0000 Subject: [PATCH] Added support for window regions in the server. --- dlls/x11drv/winpos.c | 15 ----- include/wine/server_protocol.h | 35 ++++++++++- server/protocol.def | 16 +++++ server/region.c | 27 ++++++++- server/request.h | 4 ++ server/trace.c | 25 ++++++++ server/user.h | 1 + server/window.c | 80 ++++++++++++++++++++++--- windows/winpos.c | 104 +++++++++++++++++++++++++-------- 9 files changed, 256 insertions(+), 51 deletions(-) diff --git a/dlls/x11drv/winpos.c b/dlls/x11drv/winpos.c index 6570e7bd93b..738c515643d 100644 --- a/dlls/x11drv/winpos.c +++ b/dlls/x11drv/winpos.c @@ -1657,20 +1657,6 @@ int X11DRV_SetWindowRgn( HWND hwnd, HRGN hrgn, BOOL redraw ) return FALSE; } - if (wndPtr->hrgnWnd == hrgn) - { - WIN_ReleasePtr( wndPtr ); - return TRUE; - } - - if (wndPtr->hrgnWnd) - { - /* delete previous region */ - DeleteObject(wndPtr->hrgnWnd); - wndPtr->hrgnWnd = 0; - } - wndPtr->hrgnWnd = hrgn; - #ifdef HAVE_LIBXSHAPE { Display *display = thread_display(); @@ -1706,7 +1692,6 @@ int X11DRV_SetWindowRgn( HWND hwnd, HRGN hrgn, BOOL redraw ) #endif /* HAVE_LIBXSHAPE */ WIN_ReleasePtr( wndPtr ); - if (redraw) RedrawWindow( hwnd, NULL, 0, RDW_FRAME | RDW_INVALIDATE | RDW_ERASE ); return TRUE; } diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index f942f243091..db99ae39985 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -2630,6 +2630,33 @@ struct get_visible_region_reply +struct get_window_region_request +{ + struct request_header __header; + user_handle_t window; +}; +struct get_window_region_reply +{ + struct reply_header __header; + size_t total_size; + /* VARARG(region,rectangles); */ +}; + + + +struct set_window_region_request +{ + struct request_header __header; + user_handle_t window; + /* VARARG(region,rectangles); */ +}; +struct set_window_region_reply +{ + struct reply_header __header; +}; + + + struct set_window_property_request { struct request_header __header; @@ -3206,6 +3233,8 @@ enum request REQ_inc_window_paint_count, REQ_get_windows_offset, REQ_get_visible_region, + REQ_get_window_region, + REQ_set_window_region, REQ_set_window_property, REQ_remove_window_property, REQ_get_window_property, @@ -3387,6 +3416,8 @@ union generic_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 get_window_region_request get_window_region_request; + struct set_window_region_request set_window_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; @@ -3566,6 +3597,8 @@ union generic_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 get_window_region_reply get_window_region_reply; + struct set_window_region_reply set_window_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; @@ -3593,6 +3626,6 @@ union generic_reply struct set_global_windows_reply set_global_windows_reply; }; -#define SERVER_PROTOCOL_VERSION 144 +#define SERVER_PROTOCOL_VERSION 145 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/protocol.def b/server/protocol.def index 08eb4766b18..041dc6d0fab 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1849,6 +1849,22 @@ enum message_type @END +/* Get the window region */ +@REQ(get_window_region) + user_handle_t window; /* handle to the window */ +@REPLY + size_t total_size; /* total size of the resulting region */ + VARARG(region,rectangles); /* list of rectangles for the region */ +@END + + +/* Set the window region */ +@REQ(set_window_region) + user_handle_t window; /* handle to the window */ + 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 index 7ecefe99a1f..ab6645c06cb 100644 --- a/server/region.c +++ b/server/region.c @@ -99,6 +99,8 @@ typedef int (*overlap_func_t)( struct region *reg, const rectangle_t *r1, const typedef int (*non_overlap_func_t)( struct region *reg, const rectangle_t *r, const rectangle_t *rEnd, int top, int bottom ); +static const rectangle_t empty_rect; /* all-zero rectangle for empty regions */ + /* add a rectangle to a region */ static inline rectangle_t *add_rect( struct region *reg ) { @@ -578,6 +580,16 @@ struct region *create_region( const rectangle_t *rects, unsigned int nb_rects ) return region; } +/* create a region from request data */ +struct region *create_region_from_req_data( const void *data, size_t size ) +{ + const rectangle_t *rects = data; + int nb_rects = size / sizeof(rectangle_t); + + /* special case: empty region can be specified by a single all-zero rectangle */ + if (nb_rects == 1 && !memcmp( rects, &empty_rect, sizeof(empty_rect) )) nb_rects = 0; + return create_region( rects, nb_rects ); +} /* free a region */ void free_region( struct region *region ) @@ -607,7 +619,12 @@ void set_region_rect( struct region *region, const rectangle_t *rect ) /* 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); + if (!(*total_size = region->num_rects * sizeof(rectangle_t))) + { + /* return a single empty rect for empty regions */ + *total_size = sizeof(empty_rect); + return memdup( &empty_rect, sizeof(empty_rect) ); + } return memdup( region->rects, *total_size ); } @@ -615,7 +632,13 @@ rectangle_t *get_region_data( const struct region *region, size_t *total_size ) 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); + + if (!(*total_size = region->num_rects * sizeof(rectangle_t))) + { + /* return a single empty rect for empty regions */ + *total_size = sizeof(empty_rect); + ret = memdup( &empty_rect, sizeof(empty_rect) ); + } free( region ); return ret; } diff --git a/server/request.h b/server/request.h index 43dc9a9df71..86c741986c9 100644 --- a/server/request.h +++ b/server/request.h @@ -252,6 +252,8 @@ DECL_HANDLER(set_window_text); DECL_HANDLER(inc_window_paint_count); DECL_HANDLER(get_windows_offset); DECL_HANDLER(get_visible_region); +DECL_HANDLER(get_window_region); +DECL_HANDLER(set_window_region); DECL_HANDLER(set_window_property); DECL_HANDLER(remove_window_property); DECL_HANDLER(get_window_property); @@ -432,6 +434,8 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_inc_window_paint_count, (req_handler)req_get_windows_offset, (req_handler)req_get_visible_region, + (req_handler)req_get_window_region, + (req_handler)req_set_window_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 8a85720aa88..0f293e4d37c 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2190,6 +2190,25 @@ static void dump_get_visible_region_reply( const struct get_visible_region_reply dump_varargs_rectangles( cur_size ); } +static void dump_get_window_region_request( const struct get_window_region_request *req ) +{ + fprintf( stderr, " window=%p", req->window ); +} + +static void dump_get_window_region_reply( const struct get_window_region_reply *req ) +{ + fprintf( stderr, " total_size=%d,", req->total_size ); + fprintf( stderr, " region=" ); + dump_varargs_rectangles( cur_size ); +} + +static void dump_set_window_region_request( const struct set_window_region_request *req ) +{ + fprintf( stderr, " window=%p,", req->window ); + 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 ); @@ -2660,6 +2679,8 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (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_get_window_region_request, + (dump_func)dump_set_window_region_request, (dump_func)dump_set_window_property_request, (dump_func)dump_remove_window_property_request, (dump_func)dump_get_window_property_request, @@ -2837,6 +2858,8 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)0, (dump_func)dump_get_windows_offset_reply, (dump_func)dump_get_visible_region_reply, + (dump_func)dump_get_window_region_reply, + (dump_func)0, (dump_func)0, (dump_func)dump_remove_window_property_reply, (dump_func)dump_get_window_property_reply, @@ -3014,6 +3037,8 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "inc_window_paint_count", "get_windows_offset", "get_visible_region", + "get_window_region", + "set_window_region", "set_window_property", "remove_window_property", "get_window_property", diff --git a/server/user.h b/server/user.h index f6b960df871..80ccddfca37 100644 --- a/server/user.h +++ b/server/user.h @@ -68,6 +68,7 @@ extern void post_message( user_handle_t win, unsigned int message, /* region functions */ extern struct region *create_region( const rectangle_t *rects, unsigned int nb_rects ); +extern struct region *create_region_from_req_data( const void *data, size_t size ); 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 ); diff --git a/server/window.c b/server/window.c index dbfde586088..f93106e86ff 100644 --- a/server/window.c +++ b/server/window.c @@ -68,6 +68,7 @@ struct window user_handle_t last_active; /* last active popup */ rectangle_t window_rect; /* window rectangle */ rectangle_t client_rect; /* client rectangle */ + struct region *win_region; /* window region (for shaped windows) */ unsigned int style; /* window style */ unsigned int ex_style; /* window extended style */ unsigned int id; /* window id */ @@ -261,6 +262,7 @@ static void destroy_window( struct window *win ) free_user_handle( win->handle ); destroy_properties( win ); unlink_window( win ); + if (win->win_region) free_region( win->win_region ); release_class( win->class ); if (win->text) free( win->text ); memset( win, 0x55, sizeof(*win) ); @@ -299,6 +301,7 @@ static struct window *create_window( struct window *parent, struct window *owner win->class = class; win->atom = atom; win->last_active = win->handle; + win->win_region = NULL; win->style = 0; win->ex_style = 0; win->id = 0; @@ -463,6 +466,18 @@ user_handle_t find_window_to_repaint( user_handle_t parent, struct thread *threa } +/* intersect the window region with the specified region, relative to the window parent */ +static struct region *intersect_window_region( struct region *region, struct window *win ) +{ + /* make region relative to window rect */ + offset_region( region, -win->window_rect.left, -win->window_rect.top ); + if (!intersect_region( region, region, win->win_region )) return NULL; + /* make region relative to parent again */ + offset_region( region, win->window_rect.left, win->window_rect.top ); + return region; +} + + /* 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 ) @@ -476,6 +491,11 @@ static struct region *clip_children( struct window *parent, struct window *last, if (!(ptr->style & WS_VISIBLE)) continue; if (ptr->ex_style & WS_EX_TRANSPARENT) continue; set_region_rect( tmp, &ptr->window_rect ); + if (ptr->win_region && !intersect_window_region( tmp, ptr )) + { + free_region( tmp ); + return NULL; + } offset_region( tmp, offset_x, offset_y ); if (!(region = subtract_region( region, region, tmp ))) break; if (is_region_empty( region )) break; @@ -491,7 +511,6 @@ static struct region *get_visible_region( struct window *win, struct window *top { struct region *tmp, *region; struct window *ptr; - rectangle_t rect; int offset_x, offset_y; if (!(region = create_empty_region())) return NULL; @@ -501,32 +520,32 @@ static struct region *get_visible_region( struct window *win, struct window *top for (ptr = win; ptr != top_window; ptr = ptr->parent) if (!(ptr->style & WS_VISIBLE)) return region; /* empty region */ - /* retrieve window rectangle in parent coordinates */ + /* create a region relative to the window itself */ if ((flags & DCX_PARENTCLIP) && win->parent) { + rectangle_t rect; 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; + set_region_rect( region, &rect ); offset_x = win->client_rect.left; offset_y = win->client_rect.top; } else if (flags & DCX_WINDOW) { - rect = win->window_rect; + set_region_rect( region, &win->window_rect ); + if (win->win_region && !intersect_window_region( region, win )) goto error; offset_x = win->window_rect.left; offset_y = win->window_rect.top; } else { - rect = win->client_rect; + set_region_rect( region, &win->client_rect ); + if (win->win_region && !intersect_window_region( region, win )) goto error; 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 */ @@ -557,7 +576,16 @@ static struct region *get_visible_region( struct window *win, struct window *top 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 (win->win_region && !intersect_window_region( tmp, win )) + { + free_region( tmp ); + goto error; + } + if (!intersect_region( region, region, tmp )) + { + free_region( tmp ); + goto error; + } if (is_region_empty( region )) break; } offset_region( region, -offset_x, -offset_y ); /* make it relative to target window again */ @@ -951,6 +979,40 @@ DECL_HANDLER(get_visible_region) } +/* get the window region */ +DECL_HANDLER(get_window_region) +{ + struct window *win = get_window( req->window ); + + if (!win) return; + + reply->total_size = 0; + if (win->win_region) + { + rectangle_t *data = get_region_data( win->win_region, &reply->total_size ); + set_reply_data_ptr( data, min( reply->total_size, get_reply_max_size() ) ); + } +} + + +/* set the window region */ +DECL_HANDLER(set_window_region) +{ + struct region *region = NULL; + struct window *win = get_window( req->window ); + + if (!win) return; + + if (get_req_data_size()) /* no data means remove the region completely */ + { + if (!(region = create_region_from_req_data( get_req_data(), get_req_data_size() ))) + return; + } + if (win->win_region) free_region( win->win_region ); + win->win_region = region; +} + + /* set a window property */ DECL_HANDLER(set_window_property) { diff --git a/windows/winpos.c b/windows/winpos.c index d64a269bd65..4140a8d79df 100644 --- a/windows/winpos.c +++ b/windows/winpos.c @@ -172,21 +172,51 @@ BOOL WINAPI GetWindowRect( HWND hwnd, LPRECT rect ) int WINAPI GetWindowRgn ( HWND hwnd, HRGN hrgn ) { int nRet = ERROR; - WND *wndPtr = WIN_GetPtr( hwnd ); + HRGN win_rgn = 0; + RGNDATA *data; + size_t size = 256; + BOOL retry = FALSE; - if (wndPtr == WND_OTHER_PROCESS) + do { - if (IsWindow( hwnd )) - FIXME( "not supported on other process window %p\n", hwnd ); - wndPtr = NULL; - } - if (!wndPtr) + if (!(data = HeapAlloc( GetProcessHeap(), 0, sizeof(*data) + size - 1 ))) + { + SetLastError( ERROR_OUTOFMEMORY ); + return ERROR; + } + SERVER_START_REQ( get_window_region ) + { + req->window = hwnd; + wine_server_set_reply( req, data->Buffer, size ); + if (!wine_server_call_err( req )) + { + if (!reply->total_size) retry = FALSE; /* no region at all */ + else 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; + win_rgn = ExtCreateRegion( NULL, size, data ); + retry = FALSE; + } + else + { + size = reply->total_size; + retry = TRUE; + } + } + } + SERVER_END_REQ; + HeapFree( GetProcessHeap(), 0, data ); + } while (retry); + + if (win_rgn) { - SetLastError( ERROR_INVALID_WINDOW_HANDLE ); - return ERROR; + nRet = CombineRgn( hrgn, win_rgn, 0, RGN_COPY ); + DeleteObject( win_rgn ); } - if (wndPtr->hrgnWnd) nRet = CombineRgn( hrgn, wndPtr->hrgnWnd, 0, RGN_COPY ); - WIN_ReleasePtr( wndPtr ); return nRet; } @@ -196,21 +226,49 @@ int WINAPI GetWindowRgn ( HWND hwnd, HRGN hrgn ) */ int WINAPI SetWindowRgn( HWND hwnd, HRGN hrgn, BOOL bRedraw ) { - RECT rect; + static const RECT empty_rect; WND *wndPtr; + BOOL ret; - if (hrgn) /* verify that region really exists */ + if (hrgn) { - if (GetRgnBox( hrgn, &rect ) == ERROR) return FALSE; + RGNDATA *data; + DWORD size; + + if (!(size = GetRegionData( hrgn, 0, NULL ))) return FALSE; + if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE; + if (!GetRegionData( hrgn, size, data )) + { + HeapFree( GetProcessHeap(), 0, data ); + return FALSE; + } + SERVER_START_REQ( set_window_region ) + { + req->window = hwnd; + if (data->rdh.nCount) + wine_server_add_data( req, data->Buffer, data->rdh.nCount * sizeof(RECT) ); + else + wine_server_add_data( req, &empty_rect, sizeof(empty_rect) ); + ret = !wine_server_call_err( req ); + } + SERVER_END_REQ; + } + else /* clear existing region */ + { + SERVER_START_REQ( set_window_region ) + { + req->window = hwnd; + ret = !wine_server_call_err( req ); + } + SERVER_END_REQ; } - if (USER_Driver.pSetWindowRgn) - return USER_Driver.pSetWindowRgn( hwnd, hrgn, bRedraw ); + if (!ret) return FALSE; if ((wndPtr = WIN_GetPtr( hwnd )) == WND_OTHER_PROCESS) { if (IsWindow( hwnd )) - FIXME( "not supported on other process window %p\n", hwnd ); + FIXME( "not properly supported on other process window %p\n", hwnd ); wndPtr = NULL; } if (!wndPtr) @@ -224,7 +282,6 @@ int WINAPI SetWindowRgn( HWND hwnd, HRGN hrgn, BOOL bRedraw ) WIN_ReleasePtr( wndPtr ); return TRUE; } - if (wndPtr->hrgnWnd) { /* delete previous region */ @@ -234,12 +291,11 @@ int WINAPI SetWindowRgn( HWND hwnd, HRGN hrgn, BOOL bRedraw ) wndPtr->hrgnWnd = hrgn; WIN_ReleasePtr( wndPtr ); - /* Size the window to the rectangle of the new region (if it isn't NULL) */ - if (hrgn) SetWindowPos( hwnd, 0, rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, - SWP_NOSIZE | SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOACTIVATE | - SWP_NOZORDER | (bRedraw ? 0 : SWP_NOREDRAW) ); - return TRUE; + if (USER_Driver.pSetWindowRgn) + ret = USER_Driver.pSetWindowRgn( hwnd, hrgn, bRedraw ); + + if (ret && bRedraw) RedrawWindow( hwnd, NULL, 0, RDW_FRAME | RDW_INVALIDATE | RDW_ERASE ); + return ret; }