Added management and allocation of the PERQUEUEDATA structure.

diff --git a/include/message.h b/include/message.h
index 0251997..bf72126 100644
--- a/include/message.h
+++ b/include/message.h
@@ -59,7 +59,6 @@
 /* input.c */
 
 extern HWND32 EVENT_Capture( HWND32, INT16 );
-extern INT16 EVENT_GetCaptureInfo(void);
 extern BOOL32 EVENT_QueryPointer( DWORD *posX, DWORD *posY, DWORD *state );
 
 extern void joySendMessages(void);
diff --git a/include/queue.h b/include/queue.h
index e31f1fd..5a088ce 100644
--- a/include/queue.h
+++ b/include/queue.h
@@ -28,17 +28,26 @@
   BOOL16    bPending;
 } QSMCTRL;
 
-/* Per-queue system windows */
+/* Per-queue data for the message queue
+ * Note that we currently only store the current values for
+ * Active, Capture and Focus windows currently.
+ * It might be necessary to store a pointer to the system message queue
+ * as well since windows 9x maintains per thread system message queues
+ */
 typedef struct tagPERQUEUEDATA      
 {
-  HWND32    hWndCapture;
-  HWND32    hWndFocus;
-  HWND32    hWndActive;
+  HWND32    hWndFocus;              /* Focus window */
+  HWND32    hWndActive;             /* Active window */
+  HWND32    hWndCapture;            /* Capture window */
+  INT16     nCaptureHT;             /* Capture info (hit-test) */
+  ULONG     ulRefCount;             /* Reference count */
+  CRITICAL_SECTION cSection;        /* Critical section for thread safe access */
 } PERQUEUEDATA;
 
+/* Message queue */
 typedef struct tagMESSAGEQUEUE
 {
-  HQUEUE16  next;                   /* NNext queue */
+  HQUEUE16  next;                   /* Next queue */
   HQUEUE16  self;                   /* Handle to self (was: reserved) */
   THDB*     thdb;                   /* Thread owning queue */
   HANDLE32  hEvent;                 /* Event handle */
@@ -84,7 +93,7 @@
   HANDLE16  hCurHook;               /* Current hook */
   HANDLE16  hooks[WH_NB_HOOKS];     /* Task hooks list */
 
-  HANDLE16  hPerQueue;              /* handle on PERQUEUEDATA structure */
+  PERQUEUEDATA *pQData;             /* pointer to (shared) PERQUEUEDATA structure */
   
 } MESSAGEQUEUE;
 
@@ -99,6 +108,20 @@
 
 #define QUEUE_MAGIC        0xD46E80AF
 
+/* Per queue data management methods */
+PERQUEUEDATA* PERQDATA_CreateInstance( );
+ULONG PERQDATA_Addref( PERQUEUEDATA* pQData );
+ULONG PERQDATA_Release( PERQUEUEDATA* pQData );
+HWND32 PERQDATA_GetFocusWnd( PERQUEUEDATA *pQData );
+HWND32 PERQDATA_SetFocusWnd( PERQUEUEDATA *pQData, HWND32 hWndFocus );
+HWND32 PERQDATA_GetActiveWnd( PERQUEUEDATA *pQData );
+HWND32 PERQDATA_SetActiveWnd( PERQUEUEDATA *pQData, HWND32 hWndActive );
+HWND32 PERQDATA_GetCaptureWnd( PERQUEUEDATA *pQData );
+HWND32 PERQDATA_SetCaptureWnd( PERQUEUEDATA *pQData, HWND32 hWndCapture );
+INT16  PERQDATA_GetCaptureInfo( PERQUEUEDATA *pQData );
+INT16 PERQDATA_SetCaptureInfo( PERQUEUEDATA *pQData, INT16 nCaptureHT );
+    
+/* Message queue management methods */
 extern MESSAGEQUEUE *QUEUE_Lock( HQUEUE16 hQueue );
 extern void QUEUE_Unlock( MESSAGEQUEUE *queue );
 extern void QUEUE_DumpQueue( HQUEUE16 hQueue );
diff --git a/include/win.h b/include/win.h
index 9af5967..5fe5431 100644
--- a/include/win.h
+++ b/include/win.h
@@ -11,6 +11,8 @@
 #include "ldt.h"
 #include "windows.h"
 #include "winproc.h"
+#include "queue.h"
+
 
 #define WND_MAGIC     0x444e4957  /* 'WIND' */
 
@@ -172,6 +174,6 @@
 extern BOOL32 ICONTITLE_Init( void );
 
 /* windows/focus.c */
-extern void FOCUS_SwitchFocus( HWND32 , HWND32 );
+extern void FOCUS_SwitchFocus( MESSAGEQUEUE *pMsgQ, HWND32 , HWND32 );
 
 #endif  /* __WINE_WIN_H */
diff --git a/include/winpos.h b/include/winpos.h
index a572d93..0779a23 100644
--- a/include/winpos.h
+++ b/include/winpos.h
@@ -41,7 +41,7 @@
 extern LONG WINPOS_HandleWindowPosChanging16(WND *wndPtr, WINDOWPOS16 *winpos);
 extern LONG WINPOS_HandleWindowPosChanging32(WND *wndPtr, WINDOWPOS32 *winpos);
 extern INT16 WINPOS_WindowFromPoint( WND* scopeWnd, POINT16 pt, WND **ppWnd );
-extern void WINPOS_CheckInternalPos( HWND32 hwnd );
+extern void WINPOS_CheckInternalPos( WND* wndPtr );
 extern BOOL32 WINPOS_ActivateOtherWindow(WND* pWnd);
 extern BOOL32 WINPOS_CreateInternalPosAtom(void);
 
diff --git a/scheduler/thread.c b/scheduler/thread.c
index 0bbd009..b68a3f6 100644
--- a/scheduler/thread.c
+++ b/scheduler/thread.c
@@ -20,6 +20,8 @@
 #include "server.h"
 #include "stackframe.h"
 #include "debug.h"
+#include "queue.h"
+#include "hook.h"
 
 #ifndef __i386__
 THDB *pCurrentThread;
@@ -788,25 +790,79 @@
  * RETURNS
  *    Success: TRUE
  *    Failure: FALSE
+ *
+ * TODO:
+ *    1. Reset the Key State (currenly per thread key state is not maintained)
  */
 BOOL32 WINAPI AttachThreadInput( 
     DWORD idAttach,   /* [in] Thread to attach */
     DWORD idAttachTo, /* [in] Thread to attach to */
     BOOL32 fAttach)   /* [in] Attach or detach */
 {
-    BOOL32 ret;
+    MESSAGEQUEUE *pSrcMsgQ = 0, *pTgtMsgQ = 0;
+    BOOL16 bRet = 0;
 
-    FIXME(thread, "(0x%08lx,0x%08lx,%d): stub\n",idAttach,idAttachTo,fAttach);
     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    if (fAttach) {
-        /* Attach threads */
-        ret = FALSE;
+
+    /* A thread cannot attach to itself */
+    if ( idAttach == idAttachTo )
+        goto CLEANUP;
+
+    /* According to the docs this method should fail if a
+     * "Journal record" hook is installed. (attaches all input queues together)
+     */
+    if ( HOOK_IsHooked( WH_JOURNALRECORD ) )
+        goto CLEANUP;
+        
+    /* Retrieve message queues corresponding to the thread id's */
+    pTgtMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetThreadQueue( idAttach ) );
+    pSrcMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetThreadQueue( idAttachTo ) );
+
+    /* Ensure we have message queues and that Src and Tgt threads
+     * are not system threads.
+     */
+    if ( !pSrcMsgQ || !pTgtMsgQ || !pSrcMsgQ->pQData || !pTgtMsgQ->pQData )
+        goto CLEANUP;
+
+    if (fAttach)   /* Attach threads */
+    {
+        /* Only attach if currently detached  */
+        if ( pTgtMsgQ->pQData != pSrcMsgQ->pQData )
+        {
+            /* First release the target threads perQData */
+            PERQDATA_Release( pTgtMsgQ->pQData );
+        
+            /* Share a reference to the source threads perQDATA */
+            PERQDATA_Addref( pSrcMsgQ->pQData );
+            pTgtMsgQ->pQData = pSrcMsgQ->pQData;
+        }
     }
-    else {
-        /* Detach threads */
-        ret = FALSE;
-    };
-    return ret;
+    else    /* Detach threads */
+    {
+        /* Only detach if currently attached */
+        if ( pTgtMsgQ->pQData == pSrcMsgQ->pQData )
+        {
+            /* First release the target threads perQData */
+            PERQDATA_Release( pTgtMsgQ->pQData );
+        
+            /* Give the target thread its own private perQDATA once more */
+            pTgtMsgQ->pQData = PERQDATA_CreateInstance();
+        }
+    }
+
+    /* TODO: Reset the Key State */
+
+    bRet = 1;      // Success
+    
+CLEANUP:
+
+    /* Unlock the queues before returning */
+    if ( pSrcMsgQ )
+        QUEUE_Unlock( pSrcMsgQ );
+    if ( pTgtMsgQ )
+        QUEUE_Unlock( pTgtMsgQ );
+    
+    return bRet;
 }
 
 /**********************************************************************
diff --git a/windows/focus.c b/windows/focus.c
index 4ff9faf..55b6430 100644
--- a/windows/focus.c
+++ b/windows/focus.c
@@ -11,17 +11,20 @@
 #include "winpos.h"
 #include "hook.h"
 #include "message.h"
+#include "task.h"
+#include "debug.h"
 
-static HWND32 hwndFocus = 0;
 
 /*****************************************************************
  *	         FOCUS_SwitchFocus 
+ * pMsgQ is the queue whose perQData focus is to be modified
  */
-void FOCUS_SwitchFocus( HWND32 hFocusFrom, HWND32 hFocusTo )
+void FOCUS_SwitchFocus( MESSAGEQUEUE *pMsgQ, HWND32 hFocusFrom, HWND32 hFocusTo )
 {
     WND *pFocusTo = WIN_FindWndPtr( hFocusTo );
-    hwndFocus = hFocusTo;
 
+    PERQDATA_SetFocusWnd( pMsgQ->pQData, hFocusTo );
+    
 #if 0
     if (hFocusFrom) SendMessage32A( hFocusFrom, WM_KILLFOCUS, hFocusTo, 0 );
 #else
@@ -29,7 +32,8 @@
      * intertask at this time */
     if (hFocusFrom) SendMessage16( hFocusFrom, WM_KILLFOCUS, hFocusTo, 0 );
 #endif
-    if( !pFocusTo || hFocusTo != hwndFocus )
+
+    if( !pFocusTo || hFocusTo != PERQDATA_GetFocusWnd( pMsgQ->pQData ) )
 	return;
 
     /* According to API docs, the WM_SETFOCUS message is sent AFTER the window
@@ -59,8 +63,17 @@
  */
 HWND32 WINAPI SetFocus32( HWND32 hwnd )
 {
-    HWND32 hWndPrevFocus, hwndTop = hwnd;
+    HWND32 hWndFocus = 0, hwndTop = hwnd;
     WND *wndPtr = WIN_FindWndPtr( hwnd );
+    MESSAGEQUEUE *pMsgQ = 0, *pCurMsgQ = 0;
+    BOOL16 bRet = 0;
+
+    /* Get the messageQ for the current thread */
+    if (!(pCurMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() )))
+    {
+        WARN( win, "\tCurrent message queue not found. Exiting!\n" );
+        goto CLEANUP;
+    }
 
     if (wndPtr)
     {
@@ -69,35 +82,79 @@
 	while ( (wndPtr->dwStyle & (WS_CHILD | WS_POPUP)) == WS_CHILD  )
 	{
 	    if ( wndPtr->dwStyle & ( WS_MINIMIZE | WS_DISABLED) )
-		 return 0;
-            if (!(wndPtr = wndPtr->parent)) return 0;
+		 goto CLEANUP;
+            if (!(wndPtr = wndPtr->parent)) goto CLEANUP;
 	    hwndTop = wndPtr->hwndSelf;
 	}
 
-	if( hwnd == hwndFocus ) return hwnd;
+        /* 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 focus to
+         * shares the same perQ data as the current threads message queue.
+         * In other words you can't set focus to a window owned by a different
+         * thread unless AttachThreadInput has been called previously.
+         * (see AttachThreadInput and SetFocus docs)
+         */
+        if ( pCurMsgQ->pQData != pMsgQ->pQData )
+            goto CLEANUP;
+
+        /* Get the current focus window from the perQ data */
+        hWndFocus = PERQDATA_GetFocusWnd( pMsgQ->pQData );
+        
+        if( hwnd == hWndFocus )
+        {
+            bRet = 1;      // Success
+            goto CLEANUP;  // Nothing to do
+        }
+        
 	/* call hooks */
 	if( HOOK_CallHooks16( WH_CBT, HCBT_SETFOCUS, (WPARAM16)hwnd,
-			      (LPARAM)hwndFocus) )
-	    return 0;
+			      (LPARAM)hWndFocus) )
+	    goto CLEANUP;
 
         /* activate hwndTop if needed. */
 	if (hwndTop != GetActiveWindow32())
 	{
-	    if (!WINPOS_SetActiveWindow(hwndTop, 0, 0)) return 0;
+	    if (!WINPOS_SetActiveWindow(hwndTop, 0, 0)) goto CLEANUP;
 
-	    if (!IsWindow32( hwnd )) return 0;  /* Abort if window destroyed */
+	    if (!IsWindow32( hwnd )) goto CLEANUP;  /* Abort if window destroyed */
 	}
+
+        /* Get the current focus window from the perQ data */
+        hWndFocus = PERQDATA_GetFocusWnd( pMsgQ->pQData );
+        
+        /* Change focus and send messages */
+        FOCUS_SwitchFocus( pMsgQ, hWndFocus, hwnd );
     }
-    else if( HOOK_CallHooks16( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)hwndFocus ) )
-             return 0;
+    else /* NULL hwnd passed in */
+    {
+        if( HOOK_CallHooks16( WH_CBT, HCBT_SETFOCUS, 0, (LPARAM)hWndFocus ) )
+            goto CLEANUP;
+
+        /* Get the current focus from the perQ data of the current message Q */
+        hWndFocus = PERQDATA_GetFocusWnd( pCurMsgQ->pQData );
 
       /* Change focus and send messages */
-    hWndPrevFocus = hwndFocus;
+        FOCUS_SwitchFocus( pCurMsgQ, hWndFocus, hwnd );
+    }
 
-    FOCUS_SwitchFocus( hwndFocus , hwnd );
+    bRet = 1;      // Success
+    
+CLEANUP:
 
-    return hWndPrevFocus;
+    /* Unlock the queues before returning */
+    if ( pMsgQ )
+        QUEUE_Unlock( pMsgQ );
+    if ( pCurMsgQ )
+        QUEUE_Unlock( pCurMsgQ );
+
+    return bRet ? hWndFocus : 0;
 }
 
 
@@ -106,7 +163,7 @@
  */
 HWND16 WINAPI GetFocus16(void)
 {
-    return (HWND16)hwndFocus;
+    return (HWND16)GetFocus32();
 }
 
 
@@ -115,5 +172,20 @@
  */
 HWND32 WINAPI GetFocus32(void)
 {
+    MESSAGEQUEUE *pCurMsgQ = 0;
+    HWND32 hwndFocus = 0;
+
+    /* Get the messageQ for the current thread */
+    if (!(pCurMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() )))
+    {
+        WARN( win, "\tCurrent message queue not found. Exiting!\n" );
+        return 0;
+    }
+
+    /* Get the current focus from the perQ data of the current message Q */
+    hwndFocus = PERQDATA_GetFocusWnd( pCurMsgQ->pQData );
+
+    QUEUE_Unlock( pCurMsgQ );
+
     return hwndFocus;
 }
diff --git a/windows/input.c b/windows/input.c
index e09c7e0..5f84174 100644
--- a/windows/input.c
+++ b/windows/input.c
@@ -27,9 +27,8 @@
 #include "debugtools.h"
 #include "struct32.h"
 #include "winerror.h"
+#include "task.h"
 
-static INT16  captureHT = HTCLIENT;
-static HWND32 captureWnd = 0;
 static BOOL32 InputEnabled = TRUE;
 static BOOL32 SwappedButtons = FALSE;
 
@@ -274,7 +273,20 @@
  */
 HWND32 EVENT_Capture(HWND32 hwnd, INT16 ht)
 {
-    HWND32 capturePrev = captureWnd;
+    HWND32 capturePrev = 0, captureWnd = 0;
+    MESSAGEQUEUE *pMsgQ = 0, *pCurMsgQ = 0;
+    WND* wndPtr = 0;
+    INT16 captureHT = 0;
+
+    /* Get the messageQ for the current thread */
+    if (!(pCurMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() )))
+    {
+        WARN( win, "\tCurrent message queue not found. Exiting!\n" );
+        goto CLEANUP;
+    }
+    
+    /* Get the current capture window from the perQ data of the current message Q */
+    capturePrev = PERQDATA_GetCaptureWnd( pCurMsgQ->pQData );
 
     if (!hwnd)
     {
@@ -283,7 +295,7 @@
     }
     else
     {
-        WND* wndPtr = WIN_FindWndPtr( hwnd );
+        wndPtr = WIN_FindWndPtr( hwnd );
         if (wndPtr)
         {
             TRACE(win, "(0x%04x)\n", hwnd );
@@ -292,22 +304,47 @@
         }
     }
 
-    if( capturePrev && capturePrev != captureWnd )
+    /* 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( pCurMsgQ->pQData, captureWnd );
+        PERQDATA_SetCaptureInfo( pCurMsgQ->pQData, captureHT );
+        
+        if( capturePrev )
     {
         WND* wndPtr = WIN_FindWndPtr( capturePrev );
         if( wndPtr && (wndPtr->flags & WIN_ISWIN32) )
             SendMessage32A( capturePrev, WM_CAPTURECHANGED, 0L, hwnd);
     }
+}
+
+CLEANUP:
+    /* Unlock the queues before returning */
+    if ( pMsgQ )
+        QUEUE_Unlock( pMsgQ );
+    if ( pCurMsgQ )
+        QUEUE_Unlock( pCurMsgQ );
+    
     return capturePrev;
 }
 
-/**********************************************************************
- *              EVENT_GetCaptureInfo
- */
-INT16 EVENT_GetCaptureInfo()
-{
-    return captureHT;
-}
 
 /**********************************************************************
  *              SetCapture16   (USER.18)
@@ -332,8 +369,7 @@
  */
 void WINAPI ReleaseCapture(void)
 {
-    TRACE(win, "captureWnd=%04x\n", captureWnd );
-    if( captureWnd ) EVENT_Capture( 0, 0 );
+    EVENT_Capture( 0, 0 );
 }
 
 
@@ -342,7 +378,7 @@
  */
 HWND16 WINAPI GetCapture16(void)
 {
-    return captureWnd;
+    return (HWND16)GetCapture32();
 }
 
 /**********************************************************************
@@ -350,7 +386,21 @@
  */
 HWND32 WINAPI GetCapture32(void)
 {
-    return captureWnd;
+    MESSAGEQUEUE *pCurMsgQ = 0;
+    HWND32 hwndCapture = 0;
+
+    /* Get the messageQ for the current thread */
+    if (!(pCurMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() )))
+{
+        TRACE( win, "GetCapture32:  Current message queue not found. Exiting!\n" );
+        return 0;
+    }
+    
+    /* Get the current capture window from the perQ data of the current message Q */
+    hwndCapture = PERQDATA_GetCaptureWnd( pCurMsgQ->pQData );
+
+    QUEUE_Unlock( pCurMsgQ );
+    return hwndCapture;
 }
 
 /**********************************************************************
diff --git a/windows/message.c b/windows/message.c
index 5b4586c..a783e8f 100644
--- a/windows/message.c
+++ b/windows/message.c
@@ -122,7 +122,8 @@
     else 
     {
 	pWnd = WIN_FindWndPtr(hWnd);
-	ht = EVENT_GetCaptureInfo();
+        if (queue)
+            ht = PERQDATA_GetCaptureInfo( queue->pQData );
     }
 
 	/* stop if not the right queue */
diff --git a/windows/queue.c b/windows/queue.c
index 463d78c..fcaff97 100644
--- a/windows/queue.c
+++ b/windows/queue.c
@@ -1,5 +1,4 @@
-/*
- * Message queues related functions
+/* * Message queues related functions
  *
  * Copyright 1993, 1994 Alexandre Julliard
  */
@@ -16,6 +15,7 @@
 #include "heap.h"
 #include "thread.h"
 #include "process.h"
+#include <assert.h>
 #include "debug.h"
 
 #define MAX_QUEUE_SIZE   120  /* Max. size of a message queue */
@@ -24,6 +24,7 @@
 static HQUEUE16 hExitingQueue = 0;
 static HQUEUE16 hmemSysMsgQueue = 0;
 static MESSAGEQUEUE *sysMsgQueue = NULL;
+static PERQUEUEDATA *pQDataWin16 = NULL;  /* Global perQData for Win16 tasks */
 
 static MESSAGEQUEUE *pMouseQueue = NULL;  /* Queue for last mouse message */
 static MESSAGEQUEUE *pKbdQueue = NULL;    /* Queue for last kbd message */
@@ -33,9 +34,266 @@
 
 
 /***********************************************************************
+ *           PERQDATA_CreateInstance
+ *
+ * Creates an instance of a reference counted PERQUEUEDATA element
+ * for the message queue. perQData is stored globally for 16 bit tasks.
+ *
+ * Note: We don't implement perQdata exactly the same way Windows does.
+ * Each perQData element is reference counted since it may be potentially
+ * shared by multiple message Queues (via AttachThreadInput).
+ * We only store the current values for Active, Capture and focus windows
+ * currently.
+ */
+PERQUEUEDATA * PERQDATA_CreateInstance( )
+{
+    PERQUEUEDATA *pQData;
+    
+    BOOL16 bIsWin16 = 0;
+    
+    TRACE(msg,"()\n");
+
+    /* Share a single instance of perQData for all 16 bit tasks */
+    if ( ( bIsWin16 = THREAD_IsWin16( THREAD_Current() ) ) )
+    {
+        /* If previously allocated, just bump up ref count */
+        if ( pQDataWin16 )
+        {
+            PERQDATA_Addref( pQDataWin16 );
+            return pQDataWin16;
+        }
+    }
+
+    /* Allocate PERQUEUEDATA from the system heap */
+    if (!( pQData = (PERQUEUEDATA *) HeapAlloc( SystemHeap, 0,
+                                                    sizeof(PERQUEUEDATA) ) ))
+        return 0;
+
+    /* Initialize */
+    pQData->hWndCapture = pQData->hWndFocus = pQData->hWndActive = 0;
+    pQData->ulRefCount = 1;
+    pQData->nCaptureHT = HTCLIENT;
+
+    /* Note: We have an independent critical section for the per queue data
+     * since this may be shared by different threads. see AttachThreadInput()
+     */
+    InitializeCriticalSection( &pQData->cSection );
+
+    /* Save perQData globally for 16 bit tasks */
+    if ( bIsWin16 )
+        pQDataWin16 = pQData;
+        
+    return pQData;
+}
+
+
+/***********************************************************************
+ *           PERQDATA_Addref
+ *
+ * Increment reference count for the PERQUEUEDATA instance
+ * Returns reference count for debugging purposes
+ */
+ULONG PERQDATA_Addref( PERQUEUEDATA *pQData )
+{
+    assert(pQData != 0 );
+    TRACE(msg,"(): current refcount %lu ...\n", pQData->ulRefCount);
+
+    EnterCriticalSection( &pQData->cSection );
+    ++pQData->ulRefCount;
+    LeaveCriticalSection( &pQData->cSection );
+
+    return pQData->ulRefCount;
+}
+
+
+/***********************************************************************
+ *           PERQDATA_Release
+ *
+ * Release a reference to a PERQUEUEDATA instance.
+ * Destroy the instance if no more references exist
+ * Returns reference count for debugging purposes
+ */
+ULONG PERQDATA_Release( PERQUEUEDATA *pQData )
+{
+    assert(pQData != 0 );
+    TRACE(msg,"(): current refcount %lu ...\n",
+          (LONG)pQData->ulRefCount );
+
+    EnterCriticalSection( &pQData->cSection );
+    if ( --pQData->ulRefCount == 0 )
+    {
+        LeaveCriticalSection( &pQData->cSection );
+        DeleteCriticalSection( &pQData->cSection );
+
+        TRACE(msg,"(): deleting PERQUEUEDATA instance ...\n" );
+
+        /* Deleting our global 16 bit perQData? */
+        if ( pQData == pQDataWin16 )
+            pQDataWin16 = 0;
+            
+        /* Free the PERQUEUEDATA instance */
+        HeapFree( SystemHeap, 0, pQData );
+
+        return 0;
+    }
+    LeaveCriticalSection( &pQData->cSection );
+
+    return pQData->ulRefCount;
+}
+
+
+/***********************************************************************
+ *           PERQDATA_GetFocusWnd
+ *
+ * Get the focus hwnd member in a threadsafe manner
+ */
+HWND32 PERQDATA_GetFocusWnd( PERQUEUEDATA *pQData )
+{
+    HWND32 hWndFocus;
+    assert(pQData != 0 );
+
+    EnterCriticalSection( &pQData->cSection );
+    hWndFocus = pQData->hWndFocus;
+    LeaveCriticalSection( &pQData->cSection );
+
+    return hWndFocus;
+}
+
+
+/***********************************************************************
+ *           PERQDATA_SetFocusWnd
+ *
+ * Set the focus hwnd member in a threadsafe manner
+ */
+HWND32 PERQDATA_SetFocusWnd( PERQUEUEDATA *pQData, HWND32 hWndFocus )
+{
+    HWND32 hWndFocusPrv;
+    assert(pQData != 0 );
+
+    EnterCriticalSection( &pQData->cSection );
+    hWndFocusPrv = pQData->hWndFocus;
+    pQData->hWndFocus = hWndFocus;
+    LeaveCriticalSection( &pQData->cSection );
+
+    return hWndFocusPrv;
+}
+
+
+/***********************************************************************
+ *           PERQDATA_GetActiveWnd
+ *
+ * Get the active hwnd member in a threadsafe manner
+ */
+HWND32 PERQDATA_GetActiveWnd( PERQUEUEDATA *pQData )
+{
+    HWND32 hWndActive;
+    assert(pQData != 0 );
+
+    EnterCriticalSection( &pQData->cSection );
+    hWndActive = pQData->hWndActive;
+    LeaveCriticalSection( &pQData->cSection );
+
+    return hWndActive;
+}
+
+
+/***********************************************************************
+ *           PERQDATA_SetActiveWnd
+ *
+ * Set the active focus hwnd member in a threadsafe manner
+ */
+HWND32 PERQDATA_SetActiveWnd( PERQUEUEDATA *pQData, HWND32 hWndActive )
+{
+    HWND32 hWndActivePrv;
+    assert(pQData != 0 );
+
+    EnterCriticalSection( &pQData->cSection );
+    hWndActivePrv = pQData->hWndActive;
+    pQData->hWndActive = hWndActive;
+    LeaveCriticalSection( &pQData->cSection );
+
+    return hWndActivePrv;
+}
+
+
+/***********************************************************************
+ *           PERQDATA_GetCaptureWnd
+ *
+ * Get the capture hwnd member in a threadsafe manner
+ */
+HWND32 PERQDATA_GetCaptureWnd( PERQUEUEDATA *pQData )
+{
+    HWND32 hWndCapture;
+    assert(pQData != 0 );
+
+    EnterCriticalSection( &pQData->cSection );
+    hWndCapture = pQData->hWndCapture;
+    LeaveCriticalSection( &pQData->cSection );
+
+    return hWndCapture;
+}
+
+
+/***********************************************************************
+ *           PERQDATA_SetCaptureWnd
+ *
+ * Set the capture hwnd member in a threadsafe manner
+ */
+HWND32 PERQDATA_SetCaptureWnd( PERQUEUEDATA *pQData, HWND32 hWndCapture )
+{
+    HWND32 hWndCapturePrv;
+    assert(pQData != 0 );
+
+    EnterCriticalSection( &pQData->cSection );
+    hWndCapturePrv = pQData->hWndCapture;
+    pQData->hWndCapture = hWndCapture;
+    LeaveCriticalSection( &pQData->cSection );
+
+    return hWndCapturePrv;
+}
+
+
+/***********************************************************************
+ *           PERQDATA_GetCaptureInfo
+ *
+ * Get the capture info member in a threadsafe manner
+ */
+INT16 PERQDATA_GetCaptureInfo( PERQUEUEDATA *pQData )
+{
+    INT16 nCaptureHT;
+    assert(pQData != 0 );
+
+    EnterCriticalSection( &pQData->cSection );
+    nCaptureHT = pQData->nCaptureHT;
+    LeaveCriticalSection( &pQData->cSection );
+
+    return nCaptureHT;
+}
+
+
+/***********************************************************************
+ *           PERQDATA_SetCaptureInfo
+ *
+ * Set the capture info member in a threadsafe manner
+ */
+INT16 PERQDATA_SetCaptureInfo( PERQUEUEDATA *pQData, INT16 nCaptureHT )
+{
+    INT16 nCaptureHTPrv;
+    assert(pQData != 0 );
+
+    EnterCriticalSection( &pQData->cSection );
+    nCaptureHTPrv = pQData->nCaptureHT;
+    pQData->nCaptureHT = nCaptureHT;
+    LeaveCriticalSection( &pQData->cSection );
+
+    return nCaptureHTPrv;
+}
+
+
+/***********************************************************************
  *	     QUEUE_Lock
  *
- * Fonction for getting a 32 bit pointer on queue strcture. For thread
+ * Function for getting a 32 bit pointer on queue strcture. For thread
  * safeness programmers should use this function instead of GlobalLock to
  * retrieve a pointer on the structure. QUEUE_Unlock should also be called
  * when access to the queue structure is not required anymore.
@@ -58,6 +316,7 @@
     return queue;
 }
 
+
 /***********************************************************************
  *	     QUEUE_Unlock
  *
@@ -169,13 +428,13 @@
  *
  * Creates a message queue. Doesn't link it into queue list!
  */
-static HQUEUE16 QUEUE_CreateMsgQueue( )
+static HQUEUE16 QUEUE_CreateMsgQueue( BOOL16 bCreatePerQData )
 {
     HQUEUE16 hQueue;
     MESSAGEQUEUE * msgQueue;
     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
 
-    TRACE(msg,"Creating message queue...\n");
+    TRACE(msg,"(): Creating message queue...\n");
 
     if (!(hQueue = GlobalAlloc16( GMEM_FIXED | GMEM_ZEROINIT,
                                   sizeof(MESSAGEQUEUE) )))
@@ -193,6 +452,9 @@
     msgQueue->lockCount = 1;
     msgQueue->magic = QUEUE_MAGIC;
     
+    /* Create and initialize our per queue data */
+    msgQueue->pQData = bCreatePerQData ? PERQDATA_CreateInstance() : NULL;
+    
     return hQueue;
 }
 
@@ -211,7 +473,7 @@
     HQUEUE16  senderQ;
     HQUEUE16 *pPrev;
 
-    TRACE(msg,"Deleting message queue %04x\n", hQueue);
+    TRACE(msg,"(): Deleting message queue %04x\n", hQueue);
 
     if (!hQueue || !msgQueue)
     {
@@ -238,6 +500,13 @@
 
     SYSTEM_LOCK();
 
+    /* Release per queue data if present */
+    if ( msgQueue->pQData )
+    {
+        PERQDATA_Release( msgQueue->pQData );
+        msgQueue->pQData = 0;
+    }
+    
     /* remove the message queue from the global link list */
     pPrev = &hFirstQueue;
     while (*pPrev && (*pPrev != hQueue))
@@ -266,7 +535,10 @@
  */
 BOOL32 QUEUE_CreateSysMsgQueue( int size )
 {
-    if (!(hmemSysMsgQueue = QUEUE_CreateMsgQueue( ))) return FALSE;
+    /* Note: We dont need perQ data for the system message queue */
+    if (!(hmemSysMsgQueue = QUEUE_CreateMsgQueue( FALSE )))
+        return FALSE;
+    
     sysMsgQueue = (MESSAGEQUEUE *) GlobalLock16( hmemSysMsgQueue );
     return TRUE;
 }
@@ -947,7 +1219,7 @@
     if ( !hQueue )
     {
         /* Create thread message queue */
-        if( !(hQueue = QUEUE_CreateMsgQueue( 0 )))
+        if( !(hQueue = QUEUE_CreateMsgQueue( TRUE )))
         {
             WARN(msg, "failed!\n");
             return FALSE;
diff --git a/windows/win.c b/windows/win.c
index 1754d85..f96c2bd 100644
--- a/windows/win.c
+++ b/windows/win.c
@@ -282,7 +282,7 @@
 
     /* FIXME: do we need to fake QS_MOUSEMOVE wakebit? */
 
-    WINPOS_CheckInternalPos( hwnd );
+    WINPOS_CheckInternalPos( wndPtr );
     if( hwnd == GetCapture32()) ReleaseCapture();
 
     /* free resources associated with the window */
@@ -1322,6 +1322,8 @@
 {
     WND *wndPtr;
 
+    TRACE(win,"EnableWindow32: ( %x, %d )\n", hwnd, enable);
+   
     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return FALSE;
     if (enable && (wndPtr->dwStyle & WS_DISABLED))
     {
@@ -1335,9 +1337,13 @@
 	  /* Disable window */
 	wndPtr->dwStyle |= WS_DISABLED;
 	if ((hwnd == GetFocus32()) || IsChild32( hwnd, GetFocus32() ))
+        {
 	    SetFocus32( 0 );  /* A disabled window can't have the focus */
+        }
 	if ((hwnd == GetCapture32()) || IsChild32( hwnd, GetCapture32() ))
+        {
 	    ReleaseCapture();  /* A disabled window can't capture the mouse */
+        }
 	SendMessage32A( hwnd, WM_ENABLE, FALSE, 0 );
 	return FALSE;
     }
diff --git a/windows/winpos.c b/windows/winpos.c
index dcf3a23..381670d 100644
--- a/windows/winpos.c
+++ b/windows/winpos.c
@@ -53,7 +53,6 @@
 
 /* ----- internal variables ----- */
 
-static HWND32 hwndActive      = 0;  /* Currently active window */
 static HWND32 hwndPrevActive  = 0;  /* Previously active window */
 static HWND32 hGlobalShellWindow=0; /*the shell*/
 
@@ -76,14 +75,27 @@
  *
  * Called when a window is destroyed.
  */
-void WINPOS_CheckInternalPos( HWND32 hwnd )
+void WINPOS_CheckInternalPos( WND* wndPtr )
 {
-    LPINTERNALPOS lpPos = (LPINTERNALPOS) GetProp32A( hwnd, atomInternalPos );
+    LPINTERNALPOS lpPos;
+    MESSAGEQUEUE *pMsgQ = 0;
+    HWND32 hwnd = wndPtr->hwndSelf;
+
+    lpPos = (LPINTERNALPOS) GetProp32A( hwnd, atomInternalPos );
+
+    /* Retrieve the message queue associated with this window */
+    pMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( wndPtr->hmemTaskQ );
+    if ( !pMsgQ )
+    {
+        WARN( win, "\tMessage queue not found. Exiting!\n" );
+        return;
+    }
 
     if( hwnd == hwndPrevActive ) hwndPrevActive = 0;
-    if( hwnd == hwndActive )
+
+    if( hwnd == PERQDATA_GetActiveWnd( pMsgQ->pQData ) )
     {
-	hwndActive = 0; 
+        PERQDATA_SetActiveWnd( pMsgQ->pQData, 0 );
 	WARN(win, "\tattempt to activate destroyed window!\n");
     }
 
@@ -93,6 +105,9 @@
 	    DestroyWindow32( lpPos->hwndIconTitle );
 	HeapFree( SystemHeap, 0, lpPos );
     }
+
+    QUEUE_Unlock( pMsgQ );
+    return;
 }
 
 /***********************************************************************
@@ -680,7 +695,7 @@
  */
 HWND16 WINAPI GetActiveWindow16(void)
 {
-    return (HWND16)hwndActive;
+    return (HWND16)GetActiveWindow32();
 }
 
 /*******************************************************************
@@ -688,7 +703,21 @@
  */
 HWND32 WINAPI GetActiveWindow32(void)
 {
-    return (HWND32)hwndActive;
+    MESSAGEQUEUE *pCurMsgQ = 0;
+    HWND32 hwndActive = 0;
+
+    /* Get the messageQ for the current thread */
+    if (!(pCurMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() )))
+{
+        WARN( win, "\tCurrent message queue not found. Exiting!\n" );
+        return 0;
+    }
+
+    /* Return the current active window from the perQ data of the current message Q */
+    hwndActive = PERQDATA_GetActiveWnd( pCurMsgQ->pQData );
+
+    QUEUE_Unlock( pCurMsgQ );
+    return hwndActive;
 }
 
 
@@ -717,12 +746,45 @@
  */
 HWND32 WINAPI SetActiveWindow32( HWND32 hwnd )
 {
-    HWND32 prev = hwndActive;
+    HWND32 prev = 0;
     WND *wndPtr = WIN_FindWndPtr( hwnd );
+    MESSAGEQUEUE *pMsgQ = 0, *pCurMsgQ = 0;
 
     if ( !WINPOS_CanActivate(wndPtr) ) return 0;
 
+    /* Get the messageQ for the current thread */
+    if (!(pCurMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() )))
+    {
+        WARN( win, "\tCurrent message queue not found. Exiting!\n" );
+        goto CLEANUP;
+    }
+    
+    /* Retrieve the message queue associated with this window */
+    pMsgQ = (MESSAGEQUEUE *)QUEUE_Lock( wndPtr->hmemTaskQ );
+    if ( !pMsgQ )
+    {
+        WARN( win, "\tWindow message queue not found. Exiting!\n" );
+        goto CLEANUP;
+    }
+
+    /* Make sure that the window is associated with the calling threads
+     * message queue. It must share the same perQ data.
+     */
+    if ( pCurMsgQ->pQData != pMsgQ->pQData )
+        goto CLEANUP;
+    
+    /* Save current active window */
+    prev = PERQDATA_GetActiveWnd( pMsgQ->pQData );
+    
     WINPOS_SetActiveWindow( hwnd, 0, 0 );
+
+CLEANUP:
+    /* Unlock the queues before returning */
+    if ( pMsgQ )
+        QUEUE_Unlock( pMsgQ );
+    if ( pCurMsgQ )
+        QUEUE_Unlock( pCurMsgQ );
+    
     return prev;
 }
 
@@ -1460,10 +1522,21 @@
     CBTACTIVATESTRUCT16* cbtStruct;
     WND*     wndPtr, *wndTemp;
     HQUEUE16 hOldActiveQueue, hNewActiveQueue;
+    MESSAGEQUEUE *pOldActiveQueue = 0, *pNewActiveQueue = 0;
     WORD     wIconized = 0;
+    HWND32   hwndActive = 0;
+    BOOL32   bRet = 0;
+
+    /* Get current active window from the active queue */
+    if ( hActiveQueue )
+    {
+        pOldActiveQueue = QUEUE_Lock( hActiveQueue );
+        if ( pOldActiveQueue )
+            hwndActive = PERQDATA_GetActiveWnd( pOldActiveQueue->pQData );
+    }
 
     /* paranoid checks */
-    if( hWnd == GetDesktopWindow32() || hWnd == hwndActive ) return 0;
+    if( hWnd == GetDesktopWindow32() || hWnd == hwndActive ) goto CLEANUP;
 
 /*  if (wndPtr && (GetFastQueue() != wndPtr->hmemTaskQ))
  *	return 0;
@@ -1485,15 +1558,23 @@
         wRet = HOOK_CallHooks16( WH_CBT, HCBT_ACTIVATE, (WPARAM16)hWnd,
                                  (LPARAM)SEGPTR_GET(cbtStruct) );
         SEGPTR_FREE(cbtStruct);
-        if (wRet) return wRet;
+        if (wRet)
+        {
+            /* Unlock the active queue before returning */
+            if ( pOldActiveQueue )
+                QUEUE_Unlock( pOldActiveQueue );
+            return wRet;
+        }
     }
 
     /* set prev active wnd to current active wnd and send notification */
     if ((hwndPrevActive = hwndActive) && IsWindow32(hwndPrevActive))
     {
+        MESSAGEQUEUE *pTempActiveQueue = 0;
+        
         if (!SendMessage32A( hwndPrevActive, WM_NCACTIVATE, FALSE, 0 ))
         {
-	    if (GetSysModalWindow16() != hWnd) return 0;
+	    if (GetSysModalWindow16() != hWnd) goto CLEANUP;
 	    /* disregard refusal if hWnd is sysmodal */
         }
 
@@ -1508,12 +1589,24 @@
 				MAKELPARAM( (HWND16)hWnd, wIconized ) );
 #endif
 
-	/* check if something happened during message processing */
-	if( hwndPrevActive != hwndActive ) return 0;
+        /* check if something happened during message processing
+         * (global active queue may have changed)
+         */
+        pTempActiveQueue = QUEUE_Lock( hActiveQueue );
+        hwndActive = PERQDATA_GetActiveWnd( pTempActiveQueue->pQData );
+        QUEUE_Unlock( pTempActiveQueue );
+        if( hwndPrevActive != hwndActive )
+            goto CLEANUP;
     }
 
-    /* set active wnd */
+    /* Set new active window in the message queue */
     hwndActive = hWnd;
+    if ( wndPtr )
+    {
+        pNewActiveQueue = QUEUE_Lock( wndPtr->hmemTaskQ );
+        if ( pNewActiveQueue )
+            PERQDATA_SetActiveWnd( pNewActiveQueue->pQData, hwndActive );
+    }
 
     /* send palette messages */
     if (hWnd && SendMessage16( hWnd, WM_QUERYNEWPALETTE, 0, 0L))
@@ -1532,9 +1625,10 @@
 	if( wndTemp != wndPtr )
 	    SetWindowPos32(hWnd, HWND_TOP, 0,0,0,0, 
 			   SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
-        if (!IsWindow32(hWnd)) return 0;
+        if (!IsWindow32(hWnd))  goto CLEANUP;
     }
 
+    /* Get a handle to the new active queue */
     hNewActiveQueue = wndPtr ? wndPtr->hmemTaskQ : 0;
 
     /* send WM_ACTIVATEAPP if necessary */
@@ -1570,7 +1664,7 @@
             HeapFree( SystemHeap, 0, list );
         }
         
-	if (!IsWindow32(hWnd)) return 0;
+	if (!IsWindow32(hWnd)) goto CLEANUP;
     }
 
     if (hWnd)
@@ -1592,13 +1686,13 @@
                       MAKELPARAM( (HWND16)hwndPrevActive, wIconized) );
 #endif
 
-        if( !IsWindow32(hWnd) ) return 0;
+        if( !IsWindow32(hWnd) ) goto CLEANUP;
     }
 
     /* change focus if possible */
     if( fChangeFocus && GetFocus32() )
 	if( WIN_GetTopParent(GetFocus32()) != hwndActive )
-	    FOCUS_SwitchFocus( GetFocus32(),
+	    FOCUS_SwitchFocus( pNewActiveQueue, GetFocus32(),
 			       (wndPtr && (wndPtr->dwStyle & WS_MINIMIZE))?
 			       0:
 			       hwndActive
@@ -1610,7 +1704,16 @@
     /* if active wnd is minimized redraw icon title */
     if( IsIconic32(hwndActive) ) WINPOS_RedrawIconTitle(hwndActive);
 
-    return (hWnd == hwndActive);
+    bRet = 1;  // Success
+    
+CLEANUP:
+
+    /* Unlock the message queues before returning */
+    if ( pOldActiveQueue )
+        QUEUE_Unlock( pOldActiveQueue );
+    if ( pNewActiveQueue )
+        QUEUE_Unlock( pNewActiveQueue );
+    return bRet ? (hWnd == hwndActive) : 0;
 }
 
 /*******************************************************************
@@ -1622,6 +1725,18 @@
 {
   BOOL32	bRet = 0;
   WND*  	pWndTo = NULL;
+    HWND32       hwndActive = 0;
+
+    /* Get current active window from the active queue */
+    if ( hActiveQueue )
+    {
+        MESSAGEQUEUE *pActiveQueue = QUEUE_Lock( hActiveQueue );
+        if ( pActiveQueue )
+        {
+            hwndActive = PERQDATA_GetActiveWnd( pActiveQueue->pQData );
+            QUEUE_Unlock( pActiveQueue );
+        }
+    }
 
   if( pWnd->hwndSelf == hwndPrevActive )
       hwndPrevActive = 0;
@@ -1663,6 +1778,18 @@
 BOOL32 WINPOS_ChangeActiveWindow( HWND32 hWnd, BOOL32 mouseMsg )
 {
     WND *wndPtr = WIN_FindWndPtr(hWnd);
+    HWND32 hwndActive = 0;
+
+    /* Get current active window from the active queue */
+    if ( hActiveQueue )
+    {
+        MESSAGEQUEUE *pActiveQueue = QUEUE_Lock( hActiveQueue );
+        if ( pActiveQueue )
+        {
+            hwndActive = PERQDATA_GetActiveWnd( pActiveQueue->pQData );
+            QUEUE_Unlock( pActiveQueue );
+        }
+    }
 
     if (!hWnd) return WINPOS_SetActiveWindow( 0, mouseMsg, TRUE );
 
@@ -2063,6 +2190,18 @@
     int 	result = 0;
     UINT32 	uFlags = 0;
     BOOL32      resync = FALSE;
+    HWND32      hwndActive = 0;
+
+    /* Get current active window from the active queue */
+    if ( hActiveQueue )
+    {
+        MESSAGEQUEUE *pActiveQueue = QUEUE_Lock( hActiveQueue );
+        if ( pActiveQueue )
+        {
+            hwndActive = PERQDATA_GetActiveWnd( pActiveQueue->pQData );
+            QUEUE_Unlock( pActiveQueue );
+        }
+    }
 
     TRACE(win,"hwnd %04x, (%i,%i)-(%i,%i) flags %08x\n", 
 						 hwnd, x, y, x+cx, y+cy, flags);  
@@ -2393,6 +2532,10 @@
 
 	if (hwnd == CARET_GetHwnd()) DestroyCaret32();
 
+        /* FIXME: This will cause the window to be activated irrespective
+         * of whether it is owned by the same thread.
+         * Should this behaviour be allowed in SetWindowPos?
+         */
 	if (winpos.hwnd == hwndActive)
 	    WINPOS_ActivateOtherWindow( wndPtr );
     }