server: Add a separate request to set the window visible rect.
diff --git a/dlls/winex11.drv/winpos.c b/dlls/winex11.drv/winpos.c
index ba91e8b..37c36ec 100644
--- a/dlls/winex11.drv/winpos.c
+++ b/dlls/winex11.drv/winpos.c
@@ -241,7 +241,7 @@
 {
     Display *display = thread_display();
     struct x11drv_win_data *data;
-    RECT new_whole_rect, old_client_rect;
+    RECT new_whole_rect, old_client_rect, visible_rect;
     WND *win;
     DWORD old_style, new_style, new_ex_style;
     BOOL ret, make_managed = FALSE;
@@ -262,15 +262,12 @@
         }
     }
 
-    new_whole_rect = *rectWindow;
-    X11DRV_window_to_X_rect( data, &new_whole_rect );
-
     old_client_rect = data->client_rect;
 
     if (!data->whole_window) swp_flags |= SWP_NOCOPYBITS;  /* we can't rely on X11 to move the bits */
 
     if (!(win = WIN_GetPtr( hwnd ))) return FALSE;
-    if (win == WND_OTHER_PROCESS)
+    if (win == WND_DESKTOP || win == WND_OTHER_PROCESS)
     {
         if (IsWindow( hwnd )) ERR( "cannot set rectangles of other process window %p\n", hwnd );
         return FALSE;
@@ -288,34 +285,52 @@
         req->client.top    = rectClient->top;
         req->client.right  = rectClient->right;
         req->client.bottom = rectClient->bottom;
-        if (memcmp( rectWindow, &new_whole_rect, sizeof(RECT) ) || !IsRectEmpty( &valid_rects[0] ))
+        if (!IsRectEmpty( &valid_rects[0] ))
+            wine_server_add_data( req, valid_rects, 2 * sizeof(*valid_rects) );
+        if ((ret = !wine_server_call( req )))
         {
-            wine_server_add_data( req, &new_whole_rect, sizeof(new_whole_rect) );
-            if (!IsRectEmpty( &valid_rects[0] ))
-                wine_server_add_data( req, valid_rects, 2 * sizeof(*valid_rects) );
+            new_style = reply->new_style;
+            new_ex_style = reply->new_ex_style;
+            visible_rect.left   = reply->visible.left;
+            visible_rect.top    = reply->visible.top;
+            visible_rect.right  = reply->visible.right;
+            visible_rect.bottom = reply->visible.bottom;
         }
-        ret = !wine_server_call( req );
-        new_style = reply->new_style;
-        new_ex_style = reply->new_ex_style;
     }
     SERVER_END_REQ;
 
-    if (win == WND_DESKTOP || data->whole_window == DefaultRootWindow(gdi_display))
+    if (ret)
     {
-        data->whole_rect = data->client_rect = data->window_rect = *rectWindow;
-        if (win != WND_DESKTOP)
+        if (data->whole_window == DefaultRootWindow(gdi_display))
         {
+            data->whole_rect = data->client_rect = data->window_rect = *rectWindow;
             win->rectWindow   = *rectWindow;
             win->rectClient   = *rectClient;
             win->dwStyle      = new_style;
             win->dwExStyle    = new_ex_style;
             WIN_ReleasePtr( win );
+            return TRUE;
         }
-        return ret;
-    }
 
-    if (ret)
-    {
+        new_whole_rect = *rectWindow;
+        X11DRV_window_to_X_rect( data, &new_whole_rect );
+        if (memcmp( &visible_rect, &new_whole_rect, sizeof(RECT) ))
+        {
+            TRACE( "%p: need to update visible rect %s -> %s\n", hwnd,
+                   wine_dbgstr_rect(&visible_rect), wine_dbgstr_rect(&new_whole_rect) );
+            SERVER_START_REQ( set_window_visible_rect )
+            {
+                req->handle         = hwnd;
+                req->flags          = swp_flags;
+                req->visible.left   = new_whole_rect.left;
+                req->visible.top    = new_whole_rect.top;
+                req->visible.right  = new_whole_rect.right;
+                req->visible.bottom = new_whole_rect.bottom;
+                wine_server_call( req );
+            }
+            SERVER_END_REQ;
+        }
+
         /* invalidate DCEs */
 
         if ((((swp_flags & SWP_AGG_NOPOSCHANGE) != SWP_AGG_NOPOSCHANGE) && (new_style & WS_VISIBLE)) ||
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index b26ac15..a659d82 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -2965,6 +2965,21 @@
     struct reply_header __header;
     unsigned int   new_style;
     unsigned int   new_ex_style;
+    rectangle_t    visible;
+};
+
+
+
+struct set_window_visible_rect_request
+{
+    struct request_header __header;
+    unsigned int   flags;
+    user_handle_t  handle;
+    rectangle_t    visible;
+};
+struct set_window_visible_rect_reply
+{
+    struct reply_header __header;
 };
 
 
@@ -4416,6 +4431,7 @@
     REQ_get_window_children_from_point,
     REQ_get_window_tree,
     REQ_set_window_pos,
+    REQ_set_window_visible_rect,
     REQ_get_window_rectangles,
     REQ_get_window_text,
     REQ_set_window_text,
@@ -4655,6 +4671,7 @@
     struct get_window_children_from_point_request get_window_children_from_point_request;
     struct get_window_tree_request get_window_tree_request;
     struct set_window_pos_request set_window_pos_request;
+    struct set_window_visible_rect_request set_window_visible_rect_request;
     struct get_window_rectangles_request get_window_rectangles_request;
     struct get_window_text_request get_window_text_request;
     struct set_window_text_request set_window_text_request;
@@ -4892,6 +4909,7 @@
     struct get_window_children_from_point_reply get_window_children_from_point_reply;
     struct get_window_tree_reply get_window_tree_reply;
     struct set_window_pos_reply set_window_pos_reply;
+    struct set_window_visible_rect_reply set_window_visible_rect_reply;
     struct get_window_rectangles_reply get_window_rectangles_reply;
     struct get_window_text_reply get_window_text_reply;
     struct set_window_text_reply set_window_text_reply;
@@ -4976,6 +4994,6 @@
     struct add_fd_completion_reply add_fd_completion_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 336
+#define SERVER_PROTOCOL_VERSION 337
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/protocol.def b/server/protocol.def
index 1dff790..ff8bf52 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -2185,6 +2185,15 @@
 @REPLY
     unsigned int   new_style;     /* new window style */
     unsigned int   new_ex_style;  /* new window extended style */
+    rectangle_t    visible;       /* new visible rectangle */
+@END
+
+
+/* Set the visible rectangle of a window */
+@REQ(set_window_visible_rect)
+    unsigned int   flags;         /* SWP_* flags */
+    user_handle_t  handle;        /* handle to the window */
+    rectangle_t    visible;       /* visible rectangle */
 @END
 
 
diff --git a/server/request.h b/server/request.h
index 3f035db..283ad97 100644
--- a/server/request.h
+++ b/server/request.h
@@ -260,6 +260,7 @@
 DECL_HANDLER(get_window_children_from_point);
 DECL_HANDLER(get_window_tree);
 DECL_HANDLER(set_window_pos);
+DECL_HANDLER(set_window_visible_rect);
 DECL_HANDLER(get_window_rectangles);
 DECL_HANDLER(get_window_text);
 DECL_HANDLER(set_window_text);
@@ -498,6 +499,7 @@
     (req_handler)req_get_window_children_from_point,
     (req_handler)req_get_window_tree,
     (req_handler)req_set_window_pos,
+    (req_handler)req_set_window_visible_rect,
     (req_handler)req_get_window_rectangles,
     (req_handler)req_get_window_text,
     (req_handler)req_set_window_text,
diff --git a/server/trace.c b/server/trace.c
index e188b3f..61d0238 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -2719,7 +2719,17 @@
 static void dump_set_window_pos_reply( const struct set_window_pos_reply *req )
 {
     fprintf( stderr, " new_style=%08x,", req->new_style );
-    fprintf( stderr, " new_ex_style=%08x", req->new_ex_style );
+    fprintf( stderr, " new_ex_style=%08x,", req->new_ex_style );
+    fprintf( stderr, " visible=" );
+    dump_rectangle( &req->visible );
+}
+
+static void dump_set_window_visible_rect_request( const struct set_window_visible_rect_request *req )
+{
+    fprintf( stderr, " flags=%08x,", req->flags );
+    fprintf( stderr, " handle=%p,", req->handle );
+    fprintf( stderr, " visible=" );
+    dump_rectangle( &req->visible );
 }
 
 static void dump_get_window_rectangles_request( const struct get_window_rectangles_request *req )
@@ -3921,6 +3931,7 @@
     (dump_func)dump_get_window_children_from_point_request,
     (dump_func)dump_get_window_tree_request,
     (dump_func)dump_set_window_pos_request,
+    (dump_func)dump_set_window_visible_rect_request,
     (dump_func)dump_get_window_rectangles_request,
     (dump_func)dump_get_window_text_request,
     (dump_func)dump_set_window_text_request,
@@ -4156,6 +4167,7 @@
     (dump_func)dump_get_window_children_from_point_reply,
     (dump_func)dump_get_window_tree_reply,
     (dump_func)dump_set_window_pos_reply,
+    (dump_func)0,
     (dump_func)dump_get_window_rectangles_reply,
     (dump_func)dump_get_window_text_reply,
     (dump_func)0,
@@ -4391,6 +4403,7 @@
     "get_window_children_from_point",
     "get_window_tree",
     "set_window_pos",
+    "set_window_visible_rect",
     "get_window_rectangles",
     "get_window_text",
     "set_window_text",
diff --git a/server/window.c b/server/window.c
index 201f804..6c1b816 100644
--- a/server/window.c
+++ b/server/window.c
@@ -1409,12 +1409,10 @@
 /* set the window and client rectangles, updating the update region if necessary */
 static void set_window_pos( struct window *win, struct window *previous,
                             unsigned int swp_flags, const rectangle_t *window_rect,
-                            const rectangle_t *client_rect, const rectangle_t *visible_rect,
-                            const rectangle_t *valid_rects )
+                            const rectangle_t *client_rect, const rectangle_t *valid_rects )
 {
     struct region *old_vis_rgn = NULL, *exposed_rgn = NULL;
     const rectangle_t old_window_rect = win->window_rect;
-    const rectangle_t old_visible_rect = win->visible_rect;
     const rectangle_t old_client_rect = win->client_rect;
     int client_changed, frame_changed;
     int visible = (win->style & WS_VISIBLE) || (swp_flags & SWP_SHOWWINDOW);
@@ -1426,12 +1424,26 @@
     /* set the new window info before invalidating anything */
 
     win->window_rect  = *window_rect;
-    win->visible_rect = *visible_rect;
     win->client_rect  = *client_rect;
     if (!(swp_flags & SWP_NOZORDER) && win->parent) link_window( win, previous );
     if (swp_flags & SWP_SHOWWINDOW) win->style |= WS_VISIBLE;
     else if (swp_flags & SWP_HIDEWINDOW) win->style &= ~WS_VISIBLE;
 
+    /* assume the visible rect stays at the same offset from the window rect */
+    win->visible_rect.left   += window_rect->left   - old_window_rect.left;
+    win->visible_rect.top    += window_rect->top    - old_window_rect.top;
+    win->visible_rect.right  += window_rect->right  - old_window_rect.right;
+    win->visible_rect.bottom += window_rect->bottom - old_window_rect.bottom;
+    /* but don't make it smaller than the client rect */
+    if (win->visible_rect.left > client_rect->left)
+        win->visible_rect.left = max( window_rect->left, client_rect->left );
+    if (win->visible_rect.top > client_rect->top)
+        win->visible_rect.top = max( window_rect->top, client_rect->top );
+    if (win->visible_rect.right < client_rect->right)
+        win->visible_rect.right = min( window_rect->right, client_rect->right );
+    if (win->visible_rect.bottom < client_rect->bottom)
+        win->visible_rect.bottom = min( window_rect->bottom, client_rect->bottom );
+
     /* if the window is not visible, everything is easy */
     if (!visible) return;
 
@@ -1474,8 +1486,7 @@
     if (swp_flags & SWP_NOCOPYBITS)
     {
         frame_changed = ((swp_flags & SWP_FRAMECHANGED) ||
-                         memcmp( window_rect, &old_window_rect, sizeof(old_window_rect) ) ||
-                         memcmp( visible_rect, &old_visible_rect, sizeof(old_visible_rect) ));
+                         memcmp( window_rect, &old_window_rect, sizeof(old_window_rect) ));
         client_changed = memcmp( client_rect, &old_client_rect, sizeof(old_client_rect) );
     }
     else
@@ -1485,11 +1496,7 @@
         int y_offset = window_rect->top - old_window_rect.top;
         frame_changed = ((swp_flags & SWP_FRAMECHANGED) ||
                          window_rect->right  - old_window_rect.right != x_offset ||
-                         window_rect->bottom - old_window_rect.bottom != y_offset ||
-                         visible_rect->left   - old_visible_rect.left   != x_offset ||
-                         visible_rect->right  - old_visible_rect.right  != x_offset ||
-                         visible_rect->top    - old_visible_rect.top    != y_offset ||
-                         visible_rect->bottom - old_visible_rect.bottom != y_offset);
+                         window_rect->bottom - old_window_rect.bottom != y_offset);
         client_changed = (client_rect->left   - old_client_rect.left   != x_offset ||
                           client_rect->right  - old_client_rect.right  != x_offset ||
                           client_rect->top    - old_client_rect.top    != y_offset ||
@@ -1533,6 +1540,45 @@
     clear_error();  /* we ignore out of memory errors once the new rects have been set */
 }
 
+/* set the window visible rect */
+static void set_window_visible_rect( struct window *win, const rectangle_t *visible_rect,
+                                     unsigned int swp_flags )
+{
+    struct region *old_vis_rgn = NULL, *exposed_rgn = NULL;
+    const rectangle_t old_visible_rect = win->visible_rect;
+
+    if (!memcmp( visible_rect, &old_visible_rect, sizeof(old_visible_rect) )) return;
+
+    /* if the window is not visible, everything is easy */
+    if (!is_visible( win ) || (swp_flags & SWP_NOREDRAW))
+    {
+        win->visible_rect = *visible_rect;
+        return;
+    }
+
+    if (!(old_vis_rgn = get_visible_region( win, DCX_WINDOW ))) return;
+    win->visible_rect = *visible_rect;
+
+    /* expose anything revealed by the change */
+
+    exposed_rgn = expose_window( win, &win->window_rect, old_vis_rgn );
+    if (exposed_rgn)
+    {
+        /* subtract the client rect from the total window rect */
+        set_region_rect( exposed_rgn, &win->window_rect );
+        set_region_rect( old_vis_rgn, &win->client_rect );
+        if (subtract_region( exposed_rgn, exposed_rgn, old_vis_rgn ))
+        {
+            if (!is_desktop_window(win))
+                offset_region( exposed_rgn, -win->client_rect.left, -win->client_rect.top );
+            redraw_window( win, exposed_rgn, 1, RDW_INVALIDATE | RDW_FRAME | RDW_NOCHILDREN );
+        }
+        free_region( exposed_rgn );
+    }
+    free_region( old_vis_rgn );
+    clear_error();  /* we ignore out of memory errors once the new rect has been set */
+}
+
 
 /* set the window region, updating the update region if necessary */
 static void set_window_region( struct window *win, struct region *region, int redraw )
@@ -1850,7 +1896,7 @@
 /* set the position and Z order of a window */
 DECL_HANDLER(set_window_pos)
 {
-    const rectangle_t *visible_rect = NULL, *valid_rects = NULL;
+    const rectangle_t *valid_rects = NULL;
     struct window *previous = NULL;
     struct window *win = get_window( req->handle );
     unsigned int flags = req->flags;
@@ -1894,13 +1940,19 @@
         return;
     }
 
-    if (get_req_data_size() >= sizeof(rectangle_t)) visible_rect = get_req_data();
-    if (get_req_data_size() >= 3 * sizeof(rectangle_t)) valid_rects = visible_rect + 1;
-
-    if (!visible_rect) visible_rect = &req->window;
-    set_window_pos( win, previous, flags, &req->window, &req->client, visible_rect, valid_rects );
+    if (get_req_data_size() >= 2 * sizeof(rectangle_t)) valid_rects = get_req_data();
+    set_window_pos( win, previous, flags, &req->window, &req->client, valid_rects );
     reply->new_style = win->style;
     reply->new_ex_style = win->ex_style;
+    reply->visible = win->visible_rect;
+}
+
+
+/* set the visible rectangle of a window */
+DECL_HANDLER(set_window_visible_rect)
+{
+    struct window *win = get_window( req->handle );
+    if (win) set_window_visible_rect( win, &req->visible, req->flags );
 }