- New implementation of SendMessage, ReceiveMessage, ReplyMessage functions
  to support thread-safeness, and nested SendMessage.
- Addition of ReplyMessage32.

diff --git a/windows/message.c b/windows/message.c
index 1723efd..b0f4ab9 100644
--- a/windows/message.c
+++ b/windows/message.c
@@ -43,7 +43,7 @@
 DWORD MSG_WineStartTicks; /* Ticks at Wine startup */
 
 static UINT32 doubleClickSpeed = 452;
-static INT32 debugSMRL = 0;       /* intertask SendMessage() recursion level */
+
 
 /***********************************************************************
  *           MSG_CheckFilter
@@ -621,16 +621,20 @@
  *
  * Implementation of an inter-task SendMessage.
  */
-static LRESULT MSG_SendMessage( HQUEUE16 hDestQueue, HWND16 hwnd, UINT16 msg,
+static LRESULT MSG_SendMessage( HQUEUE16 hDestQueue, HWND32 hwnd, UINT32 msg,
                                 WPARAM32 wParam, LPARAM lParam, WORD flags )
 {
-    INT32	  prevSMRL = debugSMRL;
-    QSMCTRL 	  qCtrl = { 0, 1};
     MESSAGEQUEUE *queue, *destQ;
+    SMSG         *smsg;
+    LRESULT      lResult = 0;
 
     if (IsTaskLocked() || !IsWindow32(hwnd))
         return 0;
 
+    /* create a SMSG structure to hold SendMessage() parameters */
+    if (! (smsg = (SMSG *) HeapAlloc( SystemHeap, 0, sizeof(SMSG) )) )
+        return 0;
+          
     if (!(queue = (MESSAGEQUEUE*)QUEUE_Lock( GetFastQueue() ))) return 0;
 
     if (!(destQ = (MESSAGEQUEUE*)QUEUE_Lock( hDestQueue )))
@@ -639,66 +643,84 @@
         return 0;
     }
 
-    debugSMRL+=4;
-    TRACE(sendmsg,"%*sSM: %s [%04x] (%04x -> %04x)\n", 
-		    prevSMRL, "", SPY_GetMsgName(msg), msg, queue->self, hDestQueue );
+    TRACE(sendmsg,"SM: %s [%04x] (%04x -> %04x)\n",
+		    SPY_GetMsgName(msg), msg, queue->self, hDestQueue );
 
-    if( !(queue->wakeBits & QS_SMPARAMSFREE) )
-    {
-      TRACE(sendmsg,"\tIntertask SendMessage: sleeping since unreplied SendMessage pending\n");
-      QUEUE_WaitBits( QS_SMPARAMSFREE );
-    }
+    /* fill up SMSG structure */
+    smsg->hWnd = hwnd;
+    smsg->msg = msg;
+    smsg->wParam = wParam;
+    smsg->lParam = lParam;
+    
+    smsg->lResult = 0;
+    smsg->hSrcQueue = GetFastQueue();
+    smsg->hDstQueue = hDestQueue;
+    smsg->flags = flags;
 
-    /* resume sending */ 
-    queue->hWnd32     = hwnd;
-    queue->msg32      = msg;
-    queue->wParam32   = wParam;
-    queue->lParam     = lParam;
-    queue->hPrevSendingTask = destQ->hSendingTask;
-    destQ->hSendingTask = GetFastQueue();
+    /* add smsg struct in the processing SM list of the source queue */
+    QUEUE_AddSMSG(queue, SM_PROCESSING_LIST, smsg);
 
-    QUEUE_ClearWakeBit( queue, QS_SMPARAMSFREE );
-    queue->flags = (queue->flags & ~(QUEUE_SM_WIN32|QUEUE_SM_UNICODE)) | flags;
-
-    TRACE(sendmsg,"%*ssm: smResultInit = %08x\n", prevSMRL, "", (unsigned)&qCtrl);
-
-    queue->smResultInit = &qCtrl;
-
-    QUEUE_SetWakeBit( destQ, QS_SENDMESSAGE );
+    /* add smsg struct in the pending list of the destination queue */
+    if (QUEUE_AddSMSG(destQ, SM_PENDING_LIST, smsg) == FALSE)
+        return 0;
 
     /* perform task switch and wait for the result */
-
-    while( qCtrl.bPending )
+    while( (smsg->flags & SMSG_HAVE_RESULT) == 0 )
     {
-      if (!(queue->wakeBits & QS_SMRESULT))
-      {
-        if (THREAD_IsWin16( THREAD_Current() ))
+        /* force destination task to run next, if 16 bit threads */
+        if (THREAD_IsWin16(THREAD_Current()) && THREAD_IsWin16(destQ->thdb) )
             DirectedYield( destQ->thdb->teb.htask16 );
+
         QUEUE_WaitBits( QS_SMRESULT );
-	TRACE(sendmsg,"\tsm: have result!\n");
-      }
-      /* got something */
 
-      TRACE(sendmsg,"%*ssm: smResult = %08x\n", prevSMRL, "", (unsigned)queue->smResult );
-
-      if (queue->smResult) { /* FIXME, smResult should always be set */
-        queue->smResult->lResult = queue->SendMessageReturn;
-        queue->smResult->bPending = FALSE;
+        if (! (smsg->flags & SMSG_HAVE_RESULT) )
+        {
+            /* not supposed to happen */
+            ERR(sendmsg, "SMSG_HAVE_RESULT not set smsg->flags=%x\n", smsg->flags);
       }
+        else
+        {
+            lResult = smsg->lResult;
+            TRACE(sendmsg,"smResult = %08x\n", (unsigned)lResult );
+        }
+
       QUEUE_ClearWakeBit( queue, QS_SMRESULT );
-
-      if( queue->smResult != &qCtrl )
-	  ERR(sendmsg, "%*ssm: weird scenes inside the goldmine!\n", prevSMRL, "");
     }
-    queue->smResultInit = NULL;
+
+    /* remove the smsg from the processingg list of the source queue */
+    QUEUE_RemoveSMSG( queue, SM_PROCESSING_LIST, smsg );
+
+    /* Note: the destination thread is in charge of removing the smsg from
+       the pending list */
+
+    /* sender thread is in charge of releasing smsg if it's not an
+     early reply */
+    if ( !(smsg->flags & SMSG_EARLY_REPLY) )
+    {
+        HeapFree(SystemHeap, 0, smsg);
+    }
+    else
+    {
+        /* In the case of an early reply, sender thread will released the
+         smsg structure if the receiver thread is done (SMSG_RECEIVED set).
+         If the receiver thread isn't done, SMSG_RECEIVER_CLEANS_UP flag
+         is set, and it will be the receiver responsability to released
+         smsg */
+        EnterCriticalSection( &queue->cSection );
     
-    TRACE(sendmsg,"%*sSM: [%04x] returning %08lx\n", prevSMRL, "", msg, qCtrl.lResult);
-    debugSMRL-=4;
+        if (smsg->flags & SMSG_RECEIVED)
+            HeapFree(SystemHeap, 0, smsg);
+        else
+            smsg->flags |= SMSG_RECEIVER_CLEANS;
+        
+        LeaveCriticalSection( &queue->cSection );
+    }
 
     QUEUE_Unlock( queue );
     QUEUE_Unlock( destQ );
     
-    return qCtrl.lResult;
+    TRACE(sendmsg,"done!\n");
+    return lResult;
 }
 
 
@@ -707,52 +729,81 @@
  */
 void WINAPI ReplyMessage16( LRESULT result )
 {
-    MESSAGEQUEUE *senderQ;
-    MESSAGEQUEUE *queue;
+    ReplyMessage32( result );
+}
 
-    if (!(queue = (MESSAGEQUEUE*)QUEUE_Lock( GetFastQueue() ))) return;
+/***********************************************************************
+ *           ReplyMessage   (USER.115)
+ */
+BOOL32 WINAPI ReplyMessage32( LRESULT result )
+{
+    MESSAGEQUEUE *senderQ = 0;
+    MESSAGEQUEUE *queue = 0;
+    SMSG         *smsg;
+    BOOL32       ret = FALSE;
 
-    TRACE(msg,"ReplyMessage, queue %04x\n", queue->self);
+    if (!(queue = (MESSAGEQUEUE*)QUEUE_Lock( GetFastQueue() ))) return FALSE;
 
-    while( (senderQ = (MESSAGEQUEUE*)QUEUE_Lock( queue->InSendMessageHandle)))
+    TRACE(sendmsg,"ReplyMessage, queue %04x\n", queue->self);
+
+    while ((smsg = queue->smWaiting) != 0)
     {
-      TRACE(msg,"\trpm: replying to %08x (%04x -> %04x)\n",
-            queue->msg32, queue->self, senderQ->self);
+        /* if message has already been reply, continue the loop of receving
+         message */
+        if ( smsg->flags & SMSG_ALREADY_REPLIED )
+            goto ReplyMessageDone;
 
+        senderQ = (MESSAGEQUEUE*)QUEUE_Lock( smsg->hSrcQueue );
+        if ( !senderQ )
+            goto ReplyMessageDone;
+
+        /* if send message pending, processed it */
       if( queue->wakeBits & QS_SENDMESSAGE )
       {
+            /* Note: QUEUE_ReceiveMessage() and ReplyMessage call each other */
 	QUEUE_ReceiveMessage( queue );
         QUEUE_Unlock( senderQ );
 	continue; /* ReceiveMessage() already called us */
       }
-
-      if(!(senderQ->wakeBits & QS_SMRESULT) ) break;
-      if (THREAD_IsWin16(THREAD_Current())) OldYield();
-      
-      QUEUE_Unlock( senderQ );
-    } 
-    if( !senderQ )
-    {
-      TRACE(msg,"\trpm: done\n");
-      QUEUE_Unlock( queue );
-      return;
+        break;   /* message to reply is in smsg */
     }
 
-    senderQ->SendMessageReturn = result;
-    TRACE(msg,"\trpm: smResult = %08x, result = %08lx\n", 
-			(unsigned)queue->smResultCurrent, result );
+    if ( !smsg )
+        goto ReplyMessageDone;
+      
+    smsg->lResult = result;
+    smsg->flags |= SMSG_ALREADY_REPLIED | SMSG_HAVE_RESULT;
 
-    senderQ->smResult = queue->smResultCurrent;
-    queue->InSendMessageHandle = 0;
+    /* check if it's an early reply (called by the application) or
+       a regular reply (called by ReceiveMessage) */
+    if ( !(smsg->flags & SMSG_SENDING_REPLY) )
+        smsg->flags |= SMSG_EARLY_REPLY;
 
+    TRACE( sendmsg,"\trpm: smResult = %08lx\n", (long) result );
+
+    /* remove smsg from the waiting list, if it's not an early reply */
+    /* it is important to leave it in the waiting list if it's an early
+     reply, to be protected aginst multiple call to ReplyMessage() */
+    if ( !(smsg->flags & SMSG_EARLY_REPLY) )
+        QUEUE_RemoveSMSG( queue, SM_WAITING_LIST, smsg );
+
+    /* tell the sending task that its reply is ready */
     QUEUE_SetWakeBit( senderQ, QS_SMRESULT );
+
+    /* switch directly to sending task (16 bit thread only) */
     if (THREAD_IsWin16( THREAD_Current() ))
         DirectedYield( senderQ->thdb->teb.htask16 );
 
+    ret = TRUE;
+    
+ReplyMessageDone:
+    if ( senderQ )
     QUEUE_Unlock( senderQ );
+    if ( queue )
     QUEUE_Unlock( queue );
-}
 
+    return ret;
+}
 
 /***********************************************************************
  *           MSG_PeekMessage
@@ -1454,7 +1505,7 @@
 
     if (wndPtr->hmemTaskQ != GetFastQueue())
         ret = MSG_SendMessage( wndPtr->hmemTaskQ, hwnd, msg, wParam, lParam,
-                               QUEUE_SM_WIN32 );
+                               SMSG_WIN32 );
     else
         ret = CallWindowProc32A( (WNDPROC32)wndPtr->winproc,
                                  hwnd, msg, wParam, lParam );
@@ -1525,7 +1576,7 @@
 
     if (wndPtr->hmemTaskQ != GetFastQueue())
         ret = MSG_SendMessage( wndPtr->hmemTaskQ, hwnd, msg, wParam, lParam,
-                                QUEUE_SM_WIN32 | QUEUE_SM_UNICODE );
+                                SMSG_WIN32 | SMSG_UNICODE );
     else
         ret = CallWindowProc32W( (WNDPROC32)wndPtr->winproc,
                                  hwnd, msg, wParam, lParam );
@@ -2115,7 +2166,7 @@
 
     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() )))
         return 0;
-    ret = (BOOL32)queue->InSendMessageHandle;
+    ret = (BOOL32)queue->smProcessing;
 
     QUEUE_Unlock( queue );
     return ret;