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;
 }