Moved mouse capture handling into the server.

diff --git a/controls/menu.c b/controls/menu.c
index 13f7d85..52742a5 100644
--- a/controls/menu.c
+++ b/controls/menu.c
@@ -39,13 +39,12 @@
 #include "wingdi.h"
 #include "wine/winbase16.h"
 #include "wine/winuser16.h"
+#include "wine/server.h"
 #include "wine/unicode.h"
 #include "win.h"
 #include "controls.h"
 #include "nonclient.h"
 #include "user.h"
-#include "message.h"
-
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(menu);
@@ -2366,6 +2365,30 @@
 
 
 /***********************************************************************
+ *           MENU_SetCapture
+ */
+static void MENU_SetCapture( HWND hwnd )
+{
+    HWND previous = 0;
+
+    SERVER_START_REQ( set_capture_window )
+    {
+        req->handle = hwnd;
+        req->flags  = CAPTURE_MENU;
+        if (!wine_server_call_err( req ))
+        {
+            previous = reply->previous;
+            hwnd = reply->full_handle;
+        }
+    }
+    SERVER_END_REQ;
+
+    if (previous && previous != hwnd)
+        SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
+}
+
+
+/***********************************************************************
  *           MENU_DoNextMenu
  *
  * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
@@ -2450,9 +2473,8 @@
 
 	if( hNewWnd != pmt->hOwnerWnd )
 	{
-	    ReleaseCapture();
 	    pmt->hOwnerWnd = hNewWnd;
-	    EVENT_Capture( pmt->hOwnerWnd, HTMENU );
+	    MENU_SetCapture( pmt->hOwnerWnd );
 	}
 
 	pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
@@ -2681,7 +2703,7 @@
 	fEndMenu = !fRemove;
     }
 
-    EVENT_Capture( mt.hOwnerWnd, HTMENU );
+    MENU_SetCapture( mt.hOwnerWnd );
 
     while (!fEndMenu)
     {
@@ -2735,14 +2757,12 @@
 	if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
 	{
             /*
-             * use the mouse coordinates in lParam instead of those in the MSG
-             * struct to properly handle synthetic messages. lParam coords are
-             * relative to client area, so they must be converted; since they can
-             * be negative, we must use SLOWORD/SHIWORD instead of LOWORD/HIWORD.
+             * Use the mouse coordinates in lParam instead of those in the MSG
+             * struct to properly handle synthetic messages. They are already
+             * in screen coordinates.
              */
             mt.pt.x = SLOWORD(msg.lParam);
             mt.pt.y = SHIWORD(msg.lParam);
-            ClientToScreen(msg.hwnd,&mt.pt);
 
 	    /* Find a menu for this mouse event */
 	    hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
@@ -2911,7 +2931,7 @@
 	else mt.trackFlags &= ~TF_SKIPREMOVE;
     }
 
-    ReleaseCapture();
+    MENU_SetCapture(0);  /* release the capture */
 
     /* If dropdown is still painted and the close box is clicked on
        then the menu will be destroyed as part of the DispatchMessage above.
@@ -3967,7 +3987,7 @@
     if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
 
     hWnd = WIN_GetFullHandle( hWnd );
-    if (GetCapture() == hWnd) ReleaseCapture();
+    if (GetCapture() == hWnd) MENU_SetCapture(0);  /* release the capture */
 
     if (hMenu != 0)
     {
diff --git a/dlls/x11drv/winpos.c b/dlls/x11drv/winpos.c
index 6f0f977..7edeec3 100644
--- a/dlls/x11drv/winpos.c
+++ b/dlls/x11drv/winpos.c
@@ -41,6 +41,7 @@
 #include "nonclient.h"
 #include "message.h"
 
+#include "wine/server.h"
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
@@ -1829,6 +1830,29 @@
 
 
 /***********************************************************************
+ *           set_movesize_capture
+ */
+static void set_movesize_capture( HWND hwnd )
+{
+    HWND previous = 0;
+
+    SERVER_START_REQ( set_capture_window )
+    {
+        req->handle = hwnd;
+        req->flags  = CAPTURE_MOVESIZE;
+        if (!wine_server_call_err( req ))
+        {
+            previous = reply->previous;
+            hwnd = reply->full_handle;
+        }
+    }
+    SERVER_END_REQ;
+    if (previous && previous != hwnd)
+        SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
+}
+
+
+/***********************************************************************
  *           SysCommandSizeMove   (X11DRV.@)
  *
  * Perform SC_MOVE and SC_SIZE commands.
@@ -1874,11 +1898,11 @@
         if ( hittest && hittest != HTSYSMENU ) hittest += 2;
         else
         {
-            SetCapture(hwnd);
+            set_movesize_capture( hwnd );
             hittest = start_size_move( hwnd, wParam, &capturePoint, style );
             if (!hittest)
             {
-                ReleaseCapture();
+                set_movesize_capture(0);
                 return;
             }
         }
@@ -1938,7 +1962,7 @@
     RedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN );
 
     SendMessageA( hwnd, WM_ENTERSIZEMOVE, 0, 0 );
-    SetCapture( hwnd );
+    set_movesize_capture( hwnd );
 
     /* grab the server only when moving top-level windows without desktop */
     grab = (!DragFullWindows && !parent && (root_window == DefaultRootWindow(gdi_display)));
@@ -2048,7 +2072,7 @@
         }
     }
 
-    ReleaseCapture();
+    set_movesize_capture(0);
     if( iconic )
     {
         if( moved ) /* restore cursors, show icon title later on */
diff --git a/include/message.h b/include/message.h
index e1aa0c1..b01eea1 100644
--- a/include/message.h
+++ b/include/message.h
@@ -39,8 +39,4 @@
 extern void TIMER_RemoveQueueTimers( HQUEUE16 hqueue );
 extern BOOL TIMER_IsTimerValid( HWND hwnd, UINT id, HWINDOWPROC hProc );
 
-/* input.c */
-
-extern HWND EVENT_Capture( HWND, INT16 );
-
 #endif  /* __WINE_MESSAGE_H */
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 7e0457d..95e538c 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -2802,6 +2802,22 @@
 };
 
 
+struct set_capture_window_request
+{
+    struct request_header __header;
+    user_handle_t  handle;
+    unsigned int   flags;
+};
+struct set_capture_window_reply
+{
+    struct reply_header __header;
+    user_handle_t  previous;
+    user_handle_t  full_handle;
+};
+#define CAPTURE_MENU     0x01
+#define CAPTURE_MOVESIZE 0x02
+
+
 enum request
 {
     REQ_new_process,
@@ -2965,6 +2981,7 @@
     REQ_set_foreground_window,
     REQ_set_focus_window,
     REQ_set_active_window,
+    REQ_set_capture_window,
     REQ_NB_REQUESTS
 };
 
@@ -3133,6 +3150,7 @@
     struct set_foreground_window_request set_foreground_window_request;
     struct set_focus_window_request set_focus_window_request;
     struct set_active_window_request set_active_window_request;
+    struct set_capture_window_request set_capture_window_request;
 };
 union generic_reply
 {
@@ -3299,8 +3317,9 @@
     struct set_foreground_window_reply set_foreground_window_reply;
     struct set_focus_window_reply set_focus_window_reply;
     struct set_active_window_reply set_active_window_reply;
+    struct set_capture_window_reply set_capture_window_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 86
+#define SERVER_PROTOCOL_VERSION 87
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/protocol.def b/server/protocol.def
index 6e72e97..3837f39 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1955,3 +1955,14 @@
 @REPLY
     user_handle_t  previous;      /* handle to the previous active window */
 @END
+
+/* Set the current thread capture window */
+@REQ(set_capture_window)
+    user_handle_t  handle;        /* handle to the capture window */
+    unsigned int   flags;         /* capture flags (see below) */
+@REPLY
+    user_handle_t  previous;      /* handle to the previous capture window */
+    user_handle_t  full_handle;   /* full 32-bit handle of new capture window */
+@END
+#define CAPTURE_MENU     0x01  /* capture is for a menu */
+#define CAPTURE_MOVESIZE 0x02  /* capture is for moving/resizing */
diff --git a/server/queue.c b/server/queue.c
index 5d51e62..5332dcf 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -1369,3 +1369,22 @@
         else set_error( STATUS_INVALID_HANDLE );
     }
 }
+
+
+/* set the current thread capture window */
+DECL_HANDLER(set_capture_window)
+{
+    struct msg_queue *queue = get_current_queue();
+
+    reply->previous = reply->full_handle = 0;
+    if (queue && check_queue_input_window( queue, req->handle ))
+    {
+        struct thread_input *input = queue->input;
+
+        reply->previous = input->capture;
+        input->capture = get_user_full_handle( req->handle );
+        input->menu_owner = (req->flags & CAPTURE_MENU) ? input->capture : 0;
+        input->move_size = (req->flags & CAPTURE_MOVESIZE) ? input->capture : 0;
+        reply->full_handle = input->capture;
+    }
+}
diff --git a/server/request.h b/server/request.h
index a6076d7..63aea54 100644
--- a/server/request.h
+++ b/server/request.h
@@ -264,6 +264,7 @@
 DECL_HANDLER(set_foreground_window);
 DECL_HANDLER(set_focus_window);
 DECL_HANDLER(set_active_window);
+DECL_HANDLER(set_capture_window);
 
 #ifdef WANT_REQUEST_HANDLERS
 
@@ -431,6 +432,7 @@
     (req_handler)req_set_foreground_window,
     (req_handler)req_set_focus_window,
     (req_handler)req_set_active_window,
+    (req_handler)req_set_capture_window,
 };
 #endif  /* WANT_REQUEST_HANDLERS */
 
diff --git a/server/trace.c b/server/trace.c
index e680e58..9487057 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -2233,6 +2233,18 @@
     fprintf( stderr, " previous=%08x", req->previous );
 }
 
+static void dump_set_capture_window_request( const struct set_capture_window_request *req )
+{
+    fprintf( stderr, " handle=%08x,", req->handle );
+    fprintf( stderr, " flags=%08x", req->flags );
+}
+
+static void dump_set_capture_window_reply( const struct set_capture_window_reply *req )
+{
+    fprintf( stderr, " previous=%08x,", req->previous );
+    fprintf( stderr, " full_handle=%08x", req->full_handle );
+}
+
 static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_new_process_request,
     (dump_func)dump_get_new_process_info_request,
@@ -2395,6 +2407,7 @@
     (dump_func)dump_set_foreground_window_request,
     (dump_func)dump_set_focus_window_request,
     (dump_func)dump_set_active_window_request,
+    (dump_func)dump_set_capture_window_request,
 };
 
 static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
@@ -2559,6 +2572,7 @@
     (dump_func)dump_set_foreground_window_reply,
     (dump_func)dump_set_focus_window_reply,
     (dump_func)dump_set_active_window_reply,
+    (dump_func)dump_set_capture_window_reply,
 };
 
 static const char * const req_names[REQ_NB_REQUESTS] = {
@@ -2723,6 +2737,7 @@
     "set_foreground_window",
     "set_focus_window",
     "set_active_window",
+    "set_capture_window",
 };
 
 /* ### make_requests end ### */
diff --git a/windows/input.c b/windows/input.c
index 8857879..a9d63c8 100644
--- a/windows/input.c
+++ b/windows/input.c
@@ -513,83 +513,27 @@
 
 
 /**********************************************************************
- *              EVENT_Capture
- *
- * We need this to be able to generate double click messages
- * when menu code captures mouse in the window without CS_DBLCLK style.
- */
-HWND EVENT_Capture(HWND hwnd, INT16 ht)
-{
-    HWND capturePrev = 0, captureWnd = 0;
-    MESSAGEQUEUE *pMsgQ = 0, *pCurMsgQ = 0;
-    WND* wndPtr = 0;
-    INT16 captureHT = 0;
-
-    capturePrev = GetCapture();
-
-    if (!hwnd)
-    {
-        captureWnd = 0;
-        captureHT = 0;
-    }
-    else
-    {
-        wndPtr = WIN_FindWndPtr( hwnd );
-        if (wndPtr)
-        {
-            TRACE_(win)("(0x%04x)\n", hwnd );
-            captureWnd   = wndPtr->hwndSelf;
-            captureHT    = ht;
-        }
-    }
-
-    /* Get the messageQ for the current thread */
-    if (!(pCurMsgQ = QUEUE_Current()))
-    {
-        WARN_(win)("\tCurrent message queue not found. Exiting!\n" );
-        goto CLEANUP;
-    }
-
-    /* Update the perQ capture window and send messages */
-    if( capturePrev != captureWnd )
-    {
-        if (wndPtr)
-        {
-            /* Retrieve the message queue associated with this window */
-            pMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( wndPtr->hmemTaskQ );
-            if ( !pMsgQ )
-            {
-                WARN_(win)("\tMessage queue not found. Exiting!\n" );
-                goto CLEANUP;
-            }
-
-            /* Make sure that message queue for the window we are setting capture to
-             * shares the same perQ data as the current threads message queue.
-             */
-            if ( pCurMsgQ->pQData != pMsgQ->pQData )
-                goto CLEANUP;
-        }
-
-        PERQDATA_SetCaptureWnd( captureWnd, captureHT );
-        if (capturePrev) SendMessageA( capturePrev, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
-    }
-
-CLEANUP:
-    /* Unlock the queues before returning */
-    if ( pMsgQ )
-        QUEUE_Unlock( pMsgQ );
-
-    WIN_ReleaseWndPtr(wndPtr);
-    return capturePrev;
-}
-
-
-/**********************************************************************
  *		SetCapture (USER32.@)
  */
 HWND WINAPI SetCapture( HWND hwnd )
 {
-    return EVENT_Capture( hwnd, HTCLIENT );
+    HWND previous = 0;
+
+    SERVER_START_REQ( set_capture_window )
+    {
+        req->handle = hwnd;
+        req->flags  = 0;
+        if (!wine_server_call_err( req ))
+        {
+            previous = reply->previous;
+            hwnd = reply->full_handle;
+        }
+    }
+    SERVER_END_REQ;
+
+    if (previous && previous != hwnd)
+        SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
+    return previous;
 }
 
 
@@ -598,7 +542,7 @@
  */
 BOOL WINAPI ReleaseCapture(void)
 {
-    return (EVENT_Capture( 0, 0 ) != 0);
+    return (SetCapture(0) != 0);
 }
 
 
@@ -607,10 +551,18 @@
  */
 HWND WINAPI GetCapture(void)
 {
-    INT hittest;
-    return PERQDATA_GetCaptureWnd( &hittest );
+    HWND ret = 0;
+
+    SERVER_START_REQ( get_thread_input )
+    {
+        req->tid = GetCurrentThreadId();
+        if (!wine_server_call_err( req )) ret = reply->capture;
+    }
+    SERVER_END_REQ;
+    return ret;
 }
 
+
 /**********************************************************************
  *		GetAsyncKeyState (USER32.@)
  *
diff --git a/windows/message.c b/windows/message.c
index f789f26..5fe885f 100644
--- a/windows/message.c
+++ b/windows/message.c
@@ -384,12 +384,14 @@
     static MSG clk_msg;
 
     POINT pt;
-    INT ht, hittest;
+    INT hittest;
+    GUITHREADINFO info;
 
     /* find the window to dispatch this mouse message to */
 
     hittest = HTCLIENT;
-    if (!(msg->hwnd = PERQDATA_GetCaptureWnd( &ht )))
+    GetGUIThreadInfo( GetCurrentThreadId(), &info );
+    if (!(msg->hwnd = info.hwndCapture))
     {
         /* If no capture HWND, find window which contains the mouse position.
          * Also find the position of the cursor hot spot (hittest) */
@@ -398,7 +400,6 @@
         if (!IsWindow(hWndScope)) hWndScope = 0;
         if (!(msg->hwnd = WINPOS_WindowFromPoint( hWndScope, msg->pt, &hittest )))
             msg->hwnd = GetDesktopWindow();
-        ht = hittest;
     }
 
     if (HOOK_IsHooked( WH_JOURNALRECORD ))
@@ -423,7 +424,9 @@
 	 * note that ...MOUSEMOVEs can slip in between
 	 * ...BUTTONDOWN and ...BUTTONDBLCLK messages */
 
-        if (GetClassLongA( msg->hwnd, GCL_STYLE ) & CS_DBLCLKS || ht != HTCLIENT )
+        if ((info.flags & (GUI_INMENUMODE|GUI_INMOVESIZE)) ||
+            hittest != HTCLIENT ||
+            (GetClassLongA( msg->hwnd, GCL_STYLE ) & CS_DBLCLKS))
         {
            if ((msg->message == clk_msg.message) &&
                (msg->hwnd == clk_msg.hwnd) &&
@@ -447,7 +450,12 @@
         msg->message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
         msg->wParam = hittest;
     }
-    else ScreenToClient( msg->hwnd, &pt );
+    else
+    {
+        /* coordinates don't get translated while tracking a menu */
+        /* FIXME: should differentiate popups and top-level menus */
+        if (!(info.flags & GUI_INMENUMODE)) ScreenToClient( msg->hwnd, &pt );
+    }
     msg->lParam = MAKELONG( pt.x, pt.y );
     return TRUE;
 }