Replace the link_window request by a set_parent request since that is
all link_window is used for at this point.
Get rid of the WIN_LinkWindow/UnlinkWindow functions.

diff --git a/dlls/user/user32.spec b/dlls/user/user32.spec
index 895238a..c9a6efb 100644
--- a/dlls/user/user32.spec
+++ b/dlls/user/user32.spec
@@ -732,6 +732,4 @@
 @ cdecl WINPOS_GetMinMaxInfo(long ptr ptr ptr ptr)
 @ cdecl WINPOS_ShowIconTitle(long long)
 @ cdecl WIN_GetPtr(long)
-@ cdecl WIN_LinkWindow(long long long)
 @ cdecl WIN_SetStyle(long long long)
-@ cdecl WIN_UnlinkWindow(long)
diff --git a/dlls/x11drv/window.c b/dlls/x11drv/window.c
index f987d87..b4a7fe4 100644
--- a/dlls/x11drv/window.c
+++ b/dlls/x11drv/window.c
@@ -39,8 +39,9 @@
 #include "winuser.h"
 #include "wine/unicode.h"
 
-#include "wine/debug.h"
 #include "x11drv.h"
+#include "wine/debug.h"
+#include "wine/server.h"
 #include "win.h"
 #include "winpos.h"
 #include "mwm.h"
@@ -1003,11 +1004,7 @@
     else
         ret = (SendMessageA( hwnd, WM_CREATE, 0, (LPARAM)cs ) != -1);
 
-    if (!ret)
-    {
-        WIN_UnlinkWindow( hwnd );
-        return FALSE;
-    }
+    if (!ret) return FALSE;
 
     NotifyWinEvent(EVENT_OBJECT_CREATE, hwnd, OBJID_WINDOW, 0);
 
@@ -1127,22 +1124,37 @@
 HWND X11DRV_SetParent( HWND hwnd, HWND parent )
 {
     Display *display = thread_display();
-    HWND old_parent;
+    WND *wndPtr;
+    BOOL ret;
+    HWND old_parent = 0;
 
     /* Windows hides the window first, then shows it again
      * including the WM_SHOWWINDOW messages and all */
     BOOL was_visible = ShowWindow( hwnd, SW_HIDE );
 
-    if (!IsWindow( parent )) return 0;
+    wndPtr = WIN_GetPtr( hwnd );
+    if (!wndPtr || wndPtr == WND_OTHER_PROCESS || wndPtr == WND_DESKTOP) return 0;
 
-    old_parent = GetAncestor( hwnd, GA_PARENT );
+    SERVER_START_REQ( set_parent )
+    {
+        req->handle = hwnd;
+        req->parent = parent;
+        if ((ret = !wine_server_call( req )))
+        {
+            old_parent = reply->old_parent;
+            wndPtr->parent = parent = reply->full_parent;
+        }
+
+    }
+    SERVER_END_REQ;
+    WIN_ReleasePtr( wndPtr );
+    if (!ret) return 0;
+
     if (parent != old_parent)
     {
-        struct x11drv_win_data *data;
+        struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
 
-        if (!(data = X11DRV_get_win_data( hwnd ))) return 0;
-
-        WIN_LinkWindow( hwnd, parent, HWND_TOP );
+        if (!data) return 0;
 
         if (parent != GetDesktopWindow()) /* a child window */
         {
diff --git a/include/win.h b/include/win.h
index b55b9f9..7895479 100644
--- a/include/win.h
+++ b/include/win.h
@@ -77,8 +77,6 @@
 extern HWND WIN_Handle32( HWND16 hwnd16 );
 extern HWND WIN_IsCurrentProcess( HWND hwnd );
 extern HWND WIN_IsCurrentThread( HWND hwnd );
-extern void WIN_LinkWindow( HWND hwnd, HWND parent, HWND hwndInsertAfter );
-extern void WIN_UnlinkWindow( HWND hwnd );
 extern HWND WIN_SetOwner( HWND hwnd, HWND owner );
 extern ULONG WIN_SetStyle( HWND hwnd, ULONG set_bits, ULONG clear_bits );
 extern BOOL WIN_GetRectangles( HWND hwnd, RECT *rectWindow, RECT *rectClient );
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index fa632e0..747c05b 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -2435,21 +2435,6 @@
 
 
 
-struct link_window_request
-{
-    struct request_header __header;
-    user_handle_t  handle;
-    user_handle_t  parent;
-    user_handle_t  previous;
-};
-struct link_window_reply
-{
-    struct reply_header __header;
-    user_handle_t  full_parent;
-};
-
-
-
 struct destroy_window_request
 {
     struct request_header __header;
@@ -2527,6 +2512,21 @@
 
 
 
+struct set_parent_request
+{
+    struct request_header __header;
+    user_handle_t  handle;
+    user_handle_t  parent;
+};
+struct set_parent_reply
+{
+    struct reply_header __header;
+    user_handle_t  old_parent;
+    user_handle_t  full_parent;
+};
+
+
+
 struct get_window_parents_request
 {
     struct request_header __header;
@@ -3380,11 +3380,11 @@
     REQ_disconnect_named_pipe,
     REQ_get_named_pipe_info,
     REQ_create_window,
-    REQ_link_window,
     REQ_destroy_window,
     REQ_set_window_owner,
     REQ_get_window_info,
     REQ_set_window_info,
+    REQ_set_parent,
     REQ_get_window_parents,
     REQ_get_window_children,
     REQ_get_window_children_from_point,
@@ -3572,11 +3572,11 @@
     struct disconnect_named_pipe_request disconnect_named_pipe_request;
     struct get_named_pipe_info_request get_named_pipe_info_request;
     struct create_window_request create_window_request;
-    struct link_window_request link_window_request;
     struct destroy_window_request destroy_window_request;
     struct set_window_owner_request set_window_owner_request;
     struct get_window_info_request get_window_info_request;
     struct set_window_info_request set_window_info_request;
+    struct set_parent_request set_parent_request;
     struct get_window_parents_request get_window_parents_request;
     struct get_window_children_request get_window_children_request;
     struct get_window_children_from_point_request get_window_children_from_point_request;
@@ -3762,11 +3762,11 @@
     struct disconnect_named_pipe_reply disconnect_named_pipe_reply;
     struct get_named_pipe_info_reply get_named_pipe_info_reply;
     struct create_window_reply create_window_reply;
-    struct link_window_reply link_window_reply;
     struct destroy_window_reply destroy_window_reply;
     struct set_window_owner_reply set_window_owner_reply;
     struct get_window_info_reply get_window_info_reply;
     struct set_window_info_reply set_window_info_reply;
+    struct set_parent_reply set_parent_reply;
     struct get_window_parents_reply get_window_parents_reply;
     struct get_window_children_reply get_window_children_reply;
     struct get_window_children_from_point_reply get_window_children_from_point_reply;
@@ -3812,6 +3812,6 @@
     struct duplicate_token_reply duplicate_token_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 163
+#define SERVER_PROTOCOL_VERSION 164
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/protocol.def b/server/protocol.def
index b6553e2..da7dfe8 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1718,16 +1718,6 @@
 @END
 
 
-/* Link a window into the tree */
-@REQ(link_window)
-    user_handle_t  handle;      /* handle to the window */
-    user_handle_t  parent;      /* handle to the parent */
-    user_handle_t  previous;    /* previous child in Z-order */
-@REPLY
-    user_handle_t  full_parent; /* full handle of new parent */
-@END
-
-
 /* Destroy a window */
 @REQ(destroy_window)
     user_handle_t  handle;      /* handle to the window */
@@ -1784,6 +1774,16 @@
 #define SET_WIN_EXTRA     0x20
 
 
+/* Set the parent of a window */
+@REQ(set_parent)
+    user_handle_t  handle;      /* handle to the window */
+    user_handle_t  parent;      /* handle to the parent */
+@REPLY
+    user_handle_t  old_parent;  /* old parent window */
+    user_handle_t  full_parent; /* full handle of new parent */
+@END
+
+
 /* Get a list of the window parents, up to the root of the tree */
 @REQ(get_window_parents)
     user_handle_t  handle;        /* handle to the window */
diff --git a/server/request.h b/server/request.h
index 7ce7aa1..5c8ffe2 100644
--- a/server/request.h
+++ b/server/request.h
@@ -240,11 +240,11 @@
 DECL_HANDLER(disconnect_named_pipe);
 DECL_HANDLER(get_named_pipe_info);
 DECL_HANDLER(create_window);
-DECL_HANDLER(link_window);
 DECL_HANDLER(destroy_window);
 DECL_HANDLER(set_window_owner);
 DECL_HANDLER(get_window_info);
 DECL_HANDLER(set_window_info);
+DECL_HANDLER(set_parent);
 DECL_HANDLER(get_window_parents);
 DECL_HANDLER(get_window_children);
 DECL_HANDLER(get_window_children_from_point);
@@ -431,11 +431,11 @@
     (req_handler)req_disconnect_named_pipe,
     (req_handler)req_get_named_pipe_info,
     (req_handler)req_create_window,
-    (req_handler)req_link_window,
     (req_handler)req_destroy_window,
     (req_handler)req_set_window_owner,
     (req_handler)req_get_window_info,
     (req_handler)req_set_window_info,
+    (req_handler)req_set_parent,
     (req_handler)req_get_window_parents,
     (req_handler)req_get_window_children,
     (req_handler)req_get_window_children_from_point,
diff --git a/server/trace.c b/server/trace.c
index 738a1b8..317d22d 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -2069,18 +2069,6 @@
     fprintf( stderr, " class_ptr=%p", req->class_ptr );
 }
 
-static void dump_link_window_request( const struct link_window_request *req )
-{
-    fprintf( stderr, " handle=%p,", req->handle );
-    fprintf( stderr, " parent=%p,", req->parent );
-    fprintf( stderr, " previous=%p", req->previous );
-}
-
-static void dump_link_window_reply( const struct link_window_reply *req )
-{
-    fprintf( stderr, " full_parent=%p", req->full_parent );
-}
-
 static void dump_destroy_window_request( const struct destroy_window_request *req )
 {
     fprintf( stderr, " handle=%p", req->handle );
@@ -2136,6 +2124,18 @@
     fprintf( stderr, " old_extra_value=%08x", req->old_extra_value );
 }
 
+static void dump_set_parent_request( const struct set_parent_request *req )
+{
+    fprintf( stderr, " handle=%p,", req->handle );
+    fprintf( stderr, " parent=%p", req->parent );
+}
+
+static void dump_set_parent_reply( const struct set_parent_reply *req )
+{
+    fprintf( stderr, " old_parent=%p,", req->old_parent );
+    fprintf( stderr, " full_parent=%p", req->full_parent );
+}
+
 static void dump_get_window_parents_request( const struct get_window_parents_request *req )
 {
     fprintf( stderr, " handle=%p", req->handle );
@@ -2834,11 +2834,11 @@
     (dump_func)dump_disconnect_named_pipe_request,
     (dump_func)dump_get_named_pipe_info_request,
     (dump_func)dump_create_window_request,
-    (dump_func)dump_link_window_request,
     (dump_func)dump_destroy_window_request,
     (dump_func)dump_set_window_owner_request,
     (dump_func)dump_get_window_info_request,
     (dump_func)dump_set_window_info_request,
+    (dump_func)dump_set_parent_request,
     (dump_func)dump_get_window_parents_request,
     (dump_func)dump_get_window_children_request,
     (dump_func)dump_get_window_children_from_point_request,
@@ -3022,11 +3022,11 @@
     (dump_func)dump_disconnect_named_pipe_reply,
     (dump_func)dump_get_named_pipe_info_reply,
     (dump_func)dump_create_window_reply,
-    (dump_func)dump_link_window_reply,
     (dump_func)0,
     (dump_func)dump_set_window_owner_reply,
     (dump_func)dump_get_window_info_reply,
     (dump_func)dump_set_window_info_reply,
+    (dump_func)dump_set_parent_reply,
     (dump_func)dump_get_window_parents_reply,
     (dump_func)dump_get_window_children_reply,
     (dump_func)dump_get_window_children_from_point_reply,
@@ -3210,11 +3210,11 @@
     "disconnect_named_pipe",
     "get_named_pipe_info",
     "create_window",
-    "link_window",
     "destroy_window",
     "set_window_owner",
     "get_window_info",
     "set_window_info",
+    "set_parent",
     "get_window_parents",
     "get_window_children",
     "get_window_children_from_point",
diff --git a/server/window.c b/server/window.c
index ae46dc9..ff8c339 100644
--- a/server/window.c
+++ b/server/window.c
@@ -111,21 +111,37 @@
     return ret;
 }
 
-/* link a window into the tree (or unlink it if the new parent is NULL)  */
-static void link_window( struct window *win, struct window *parent, struct window *previous )
+/* change the parent of a window (or unlink the window if the new parent is NULL) */
+static int set_parent_window( struct window *win, struct window *parent )
 {
+    struct window *ptr;
+
+    /* make sure parent is not a child of window */
+    for (ptr = parent; ptr; ptr = ptr->parent)
+    {
+        if (ptr == win)
+        {
+            set_error( STATUS_INVALID_PARAMETER );
+            return 0;
+        }
+    }
+
     list_remove( &win->entry );  /* unlink it from the previous location */
 
     if (parent)
     {
         win->parent = parent;
-        if (previous) list_add_after( &previous->entry, &win->entry );
-        else list_add_head( &parent->children, &win->entry );
+        list_add_head( &parent->children, &win->entry );
+
+        /* if parent belongs to a different thread, attach the two threads */
+        if (parent->thread && parent->thread != win->thread)
+            attach_thread_input( win->thread, parent->thread );
     }
     else  /* move it to parent unlinked list */
     {
         list_add_head( &win->parent->unlinked, &win->entry );
     }
+    return 1;
 }
 
 /* get next window in Z-order list */
@@ -1094,7 +1110,12 @@
     win->window_rect  = *window_rect;
     win->visible_rect = *visible_rect;
     win->client_rect  = *client_rect;
-    if (!(swp_flags & SWP_NOZORDER)) link_window( win, win->parent, previous );
+    if (!(swp_flags & SWP_NOZORDER) && win->parent)
+    {
+        list_remove( &win->entry );  /* unlink it from the previous location */
+        if (previous) list_add_after( &previous->entry, &win->entry );
+        else list_add_head( &win->parent->children, &win->entry );
+    }
     if (swp_flags & SWP_SHOWWINDOW) win->style |= WS_VISIBLE;
     else if (swp_flags & SWP_HIDEWINDOW) win->style &= ~WS_VISIBLE;
 
@@ -1198,10 +1219,10 @@
 }
 
 
-/* link a window into the tree */
-DECL_HANDLER(link_window)
+/* set the parent of a window */
+DECL_HANDLER(set_parent)
 {
-    struct window *win, *parent = NULL, *previous = NULL;
+    struct window *win, *parent = NULL;
 
     if (!(win = get_window( req->handle ))) return;
     if (req->parent && !(parent = get_window( req->parent ))) return;
@@ -1211,26 +1232,9 @@
         set_error( STATUS_INVALID_PARAMETER );
         return;
     }
+    reply->old_parent  = win->parent->handle;
     reply->full_parent = parent ? parent->handle : 0;
-    if (parent && req->previous)
-    {
-        if (req->previous == (user_handle_t)1)  /* special case: HWND_BOTTOM */
-        {
-            previous = get_last_child( parent );
-            if (previous == win) return;  /* nothing to do */
-        }
-        else
-        {
-            if (!(previous = get_window( req->previous ))) return;
-            /* previous must be a child of parent, and not win itself */
-            if (previous->parent != parent || previous == win)
-            {
-                set_error( STATUS_INVALID_PARAMETER );
-                return;
-            }
-        }
-    }
-    link_window( win, parent, previous );
+    set_parent_window( win, parent );
 }
 
 
diff --git a/windows/win.c b/windows/win.c
index 0405749..f3a2dca 100644
--- a/windows/win.c
+++ b/windows/win.c
@@ -395,52 +395,6 @@
 
 
 /***********************************************************************
- *           WIN_UnlinkWindow
- *
- * Remove a window from the siblings linked list.
- */
-void WIN_UnlinkWindow( HWND hwnd )
-{
-    WIN_LinkWindow( hwnd, 0, 0 );
-}
-
-
-/***********************************************************************
- *           WIN_LinkWindow
- *
- * Insert a window into the siblings linked list.
- * The window is inserted after the specified window, which can also
- * be specified as HWND_TOP or HWND_BOTTOM.
- * If parent is 0, window is unlinked from the tree.
- */
-void WIN_LinkWindow( HWND hwnd, HWND parent, HWND hwndInsertAfter )
-{
-    WND *wndPtr = WIN_GetPtr( hwnd );
-
-    if (!wndPtr || wndPtr == WND_DESKTOP) return;
-    if (wndPtr == WND_OTHER_PROCESS)
-    {
-        if (IsWindow(hwnd)) ERR(" cannot link other process window %p\n", hwnd );
-        return;
-    }
-
-    SERVER_START_REQ( link_window )
-    {
-        req->handle   = hwnd;
-        req->parent   = parent;
-        req->previous = hwndInsertAfter;
-        if (!wine_server_call( req ))
-        {
-            if (reply->full_parent) wndPtr->parent = reply->full_parent;
-        }
-
-    }
-    SERVER_END_REQ;
-    WIN_ReleasePtr( wndPtr );
-}
-
-
-/***********************************************************************
  *           WIN_SetOwner
  *
  * Change the owner of a window.
@@ -584,12 +538,6 @@
 
     TRACE("%p\n", hwnd );
 
-    if (!(hwnd = WIN_IsCurrentThread( hwnd )))
-    {
-        ERR( "window doesn't belong to current thread\n" );
-        return 0;
-    }
-
     /* free child windows */
     if ((list = WIN_ListChildren( hwnd )))
     {
@@ -603,7 +551,13 @@
     }
 
     /* Unlink now so we won't bother with the children later on */
-    WIN_UnlinkWindow( hwnd );
+    SERVER_START_REQ( set_parent )
+    {
+        req->handle = hwnd;
+        req->parent = 0;
+        wine_server_call( req );
+    }
+    SERVER_END_REQ;
 
     /*
      * Send the WM_NCDESTROY to the window being destroyed.
@@ -2556,9 +2510,7 @@
  */
 HWND WINAPI SetParent( HWND hwnd, HWND parent )
 {
-    WND *wndPtr;
-    HWND retvalue, full_handle;
-    BOOL was_visible;
+    HWND full_handle;
 
     if (is_broadcast(hwnd) || is_broadcast(parent))
     {
@@ -2585,43 +2537,10 @@
     if (!(full_handle = WIN_IsCurrentThread( hwnd )))
         return (HWND)SendMessageW( hwnd, WM_WINE_SETPARENT, (WPARAM)parent, 0 );
 
-    hwnd = full_handle;
-
     if (USER_Driver.pSetParent)
-        return USER_Driver.pSetParent( hwnd, parent );
+        return USER_Driver.pSetParent( full_handle, parent );
 
-    /* Windows hides the window first, then shows it again
-     * including the WM_SHOWWINDOW messages and all */
-    was_visible = ShowWindow( hwnd, SW_HIDE );
-
-    if (!IsWindow( parent )) return 0;
-    if (!(wndPtr = WIN_GetPtr(hwnd)) || wndPtr == WND_OTHER_PROCESS || wndPtr == WND_DESKTOP) return 0;
-
-    retvalue = wndPtr->parent;  /* old parent */
-    if (parent != retvalue)
-    {
-        WIN_LinkWindow( hwnd, parent, HWND_TOP );
-
-        if (parent != GetDesktopWindow()) /* a child window */
-        {
-            if (!(wndPtr->dwStyle & WS_CHILD))
-            {
-                HMENU menu = (HMENU)SetWindowLongPtrW( hwnd, GWLP_ID, 0 );
-                if (menu) DestroyMenu( menu );
-            }
-        }
-    }
-    WIN_ReleasePtr( wndPtr );
-
-    /* SetParent additionally needs to make hwnd the topmost window
-       in the x-order and send the expected WM_WINDOWPOSCHANGING and
-       WM_WINDOWPOSCHANGED notification messages.
-    */
-    SetWindowPos( hwnd, HWND_TOPMOST, 0, 0, 0, 0,
-                  SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | (was_visible ? SWP_SHOWWINDOW : 0) );
-    /* FIXME: a WM_MOVE is also generated (in the DefWindowProc handler
-     * for WM_WINDOWPOSCHANGED) in Windows, should probably remove SWP_NOMOVE */
-    return retvalue;
+    return 0;
 }