Added support for window regions in the server.
diff --git a/dlls/x11drv/winpos.c b/dlls/x11drv/winpos.c
index 6570e7b..738c515 100644
--- a/dlls/x11drv/winpos.c
+++ b/dlls/x11drv/winpos.c
@@ -1657,20 +1657,6 @@
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 @@
#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 f942f24..db99ae3 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -2630,6 +2630,33 @@
+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 @@
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 @@
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 @@
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 @@
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 08eb476..041dc6d 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1849,6 +1849,22 @@
@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 7ecefe9..ab6645c 100644
--- a/server/region.c
+++ b/server/region.c
@@ -99,6 +99,8 @@
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 @@
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 @@
/* 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_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 43dc9a9..86c7419 100644
--- a/server/request.h
+++ b/server/request.h
@@ -252,6 +252,8 @@
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 @@
(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 8a85720..0f293e4 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -2190,6 +2190,25 @@
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 @@
(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 @@
(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 @@
"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 f6b960d..80ccddf 100644
--- a/server/user.h
+++ b/server/user.h
@@ -68,6 +68,7 @@
/* 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 dbfde58..f93106e 100644
--- a/server/window.c
+++ b/server/window.c
@@ -68,6 +68,7 @@
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 @@
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 @@
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 @@
}
+/* 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 @@
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 @@
{
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 @@
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 @@
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 @@
}
+/* 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 d64a269..4140a8d 100644
--- a/windows/winpos.c
+++ b/windows/winpos.c
@@ -172,21 +172,51 @@
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 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 @@
WIN_ReleasePtr( wndPtr );
return TRUE;
}
-
if (wndPtr->hrgnWnd)
{
/* delete previous region */
@@ -234,12 +291,11 @@
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;
}