- Made the message queue access to be thread safe. (Using two new
[wine] / windows / queue.c
1 /*
2  * Message queues related functions
3  *
4  * Copyright 1993, 1994 Alexandre Julliard
5  */
6
7 #include <stdlib.h>
8 #include <signal.h>
9 #include "miscemu.h"
10 #include "module.h"
11 #include "queue.h"
12 #include "task.h"
13 #include "win.h"
14 #include "clipboard.h"
15 #include "hook.h"
16 #include "heap.h"
17 #include "thread.h"
18 #include "process.h"
19 #include "debug.h"
20
21 #define MAX_QUEUE_SIZE   120  /* Max. size of a message queue */
22
23 static HQUEUE16 hFirstQueue = 0;
24 static HQUEUE16 hExitingQueue = 0;
25 static HQUEUE16 hmemSysMsgQueue = 0;
26 static MESSAGEQUEUE *sysMsgQueue = NULL;
27
28 static MESSAGEQUEUE *pMouseQueue = NULL;  /* Queue for last mouse message */
29 static MESSAGEQUEUE *pKbdQueue = NULL;    /* Queue for last kbd message */
30
31 HQUEUE16 hCursorQueue = 0;
32 HQUEUE16 hActiveQueue = 0;
33
34
35 /***********************************************************************
36  *           QUEUE_Lock
37  *
38  * Fonction for getting a 32 bit pointer on queue strcture. For thread
39  * safeness programmers should use this function instead of GlobalLock to
40  * retrieve a pointer on the structure. QUEUE_Unlock should also be called
41  * when access to the queue structure is not required anymore.
42  */
43 MESSAGEQUEUE *QUEUE_Lock( HQUEUE16 hQueue )
44 {
45     MESSAGEQUEUE *queue;
46
47     SYSTEM_LOCK();
48     queue = GlobalLock16( hQueue );
49     if ( !queue || (queue->magic != QUEUE_MAGIC) )
50     {
51         SYSTEM_UNLOCK();
52         return NULL;
53     }
54
55     queue->lockCount++;
56     SYSTEM_UNLOCK();
57
58     return queue;
59 }
60
61 /***********************************************************************
62  *           QUEUE_Unlock
63  *
64  * Use with QUEUE_Lock to get a thread safe acces to message queue
65  * structure
66  */
67 void QUEUE_Unlock( MESSAGEQUEUE *queue )
68 {
69     if (queue)
70     {
71         SYSTEM_LOCK();
72
73         if ( --queue->lockCount == 0 )
74         {
75             DeleteCriticalSection ( &queue->cSection );
76             GlobalFree16( queue->self );
77         }
78     
79         SYSTEM_UNLOCK();
80     }
81 }
82
83
84 /***********************************************************************
85  *           QUEUE_DumpQueue
86  */
87 void QUEUE_DumpQueue( HQUEUE16 hQueue )
88 {
89     MESSAGEQUEUE *pq; 
90
91     if (!(pq = (MESSAGEQUEUE*) QUEUE_Lock( hQueue )) )
92     {
93         WARN(msg, "%04x is not a queue handle\n", hQueue );
94         return;
95     }
96
97     DUMP(    "next: %12.4x  Intertask SendMessage:\n"
98              "thread: %10p  ----------------------\n"
99              "hWnd: %12.8x\n"
100              "firstMsg: %8p   msg:     %11.8x\n"
101              "lastMsg:  %8p   wParam:   %10.8x\n"
102              "msgCount: %8.4x   lParam:   %10.8x\n"
103              "lockCount: %7.4x   lRet:   %12.8x\n"
104              "wWinVer: %9.4x  ISMH: %10.4x\n"
105              "paints: %10.4x  hSendTask: %5.4x\n"
106              "timers: %10.4x  hPrevSend: %5.4x\n"
107              "wakeBits: %8.4x\n"
108              "wakeMask: %8.4x\n"
109              "hCurHook: %8.4x\n",
110              pq->next, pq->thdb, pq->hWnd32, pq->firstMsg, pq->msg32,
111              pq->lastMsg, pq->wParam32, pq->msgCount, (unsigned)pq->lParam,
112              (unsigned)pq->lockCount, (unsigned)pq->SendMessageReturn,
113              pq->wWinVersion, pq->InSendMessageHandle,
114              pq->wPaintCount, pq->hSendingTask, pq->wTimerCount,
115              pq->hPrevSendingTask, pq->wakeBits, pq->wakeMask, pq->hCurHook);
116
117     QUEUE_Unlock( pq );
118 }
119
120
121 /***********************************************************************
122  *           QUEUE_WalkQueues
123  */
124 void QUEUE_WalkQueues(void)
125 {
126     char module[10];
127     HQUEUE16 hQueue = hFirstQueue;
128
129     DUMP( "Queue Msgs Thread   Task Module\n" );
130     while (hQueue)
131     {
132         MESSAGEQUEUE *queue = (MESSAGEQUEUE *)QUEUE_Lock( hQueue );
133         if (!queue)
134         {
135             WARN( msg, "Bad queue handle %04x\n", hQueue );
136             return;
137         }
138         if (!GetModuleName( queue->thdb->process->task, module, sizeof(module )))
139             strcpy( module, "???" );
140         DUMP( "%04x %4d %p %04x %s\n", hQueue,queue->msgCount,
141               queue->thdb, queue->thdb->process->task, module );
142         hQueue = queue->next;
143         QUEUE_Unlock( queue );
144     }
145     DUMP( "\n" );
146 }
147
148
149 /***********************************************************************
150  *           QUEUE_IsExitingQueue
151  */
152 BOOL32 QUEUE_IsExitingQueue( HQUEUE16 hQueue )
153 {
154     return (hExitingQueue && (hQueue == hExitingQueue));
155 }
156
157
158 /***********************************************************************
159  *           QUEUE_SetExitingQueue
160  */
161 void QUEUE_SetExitingQueue( HQUEUE16 hQueue )
162 {
163     hExitingQueue = hQueue;
164 }
165
166
167 /***********************************************************************
168  *           QUEUE_CreateMsgQueue
169  *
170  * Creates a message queue. Doesn't link it into queue list!
171  */
172 static HQUEUE16 QUEUE_CreateMsgQueue( )
173 {
174     HQUEUE16 hQueue;
175     MESSAGEQUEUE * msgQueue;
176     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
177
178     TRACE(msg,"Creating message queue...\n");
179
180     if (!(hQueue = GlobalAlloc16( GMEM_FIXED | GMEM_ZEROINIT,
181                                   sizeof(MESSAGEQUEUE) )))
182         return 0;
183
184     msgQueue = (MESSAGEQUEUE *) GlobalLock16( hQueue );
185     if ( !msgQueue )
186         return 0;
187
188     msgQueue->self        = hQueue;
189     msgQueue->wakeBits    = msgQueue->changeBits = QS_SMPARAMSFREE;
190     msgQueue->wWinVersion = pTask ? pTask->version : 0;
191     
192     InitializeCriticalSection( &msgQueue->cSection );
193     msgQueue->lockCount = 1;
194     msgQueue->magic = QUEUE_MAGIC;
195     
196     return hQueue;
197 }
198
199
200 /***********************************************************************
201  *           QUEUE_DeleteMsgQueue
202  *
203  * Unlinks and deletes a message queue.
204  *
205  * Note: We need to mask asynchronous events to make sure PostMessage works
206  * even in the signal handler.
207  */
208 BOOL32 QUEUE_DeleteMsgQueue( HQUEUE16 hQueue )
209 {
210     MESSAGEQUEUE * msgQueue = (MESSAGEQUEUE*)QUEUE_Lock(hQueue);
211     HQUEUE16  senderQ;
212     HQUEUE16 *pPrev;
213
214     TRACE(msg,"Deleting message queue %04x\n", hQueue);
215
216     if (!hQueue || !msgQueue)
217     {
218         WARN(msg, "invalid argument.\n");
219         return 0;
220     }
221
222     msgQueue->magic = 0;
223     
224     if( hCursorQueue == hQueue ) hCursorQueue = 0;
225     if( hActiveQueue == hQueue ) hActiveQueue = 0;
226
227     /* flush sent messages */
228     senderQ = msgQueue->hSendingTask;
229     while( senderQ )
230     {
231       MESSAGEQUEUE* sq = (MESSAGEQUEUE*)QUEUE_Lock(senderQ);
232       if( !sq ) break;
233       sq->SendMessageReturn = 0L;
234       QUEUE_SetWakeBit( sq, QS_SMRESULT );
235       senderQ = sq->hPrevSendingTask;
236       QUEUE_Unlock(sq);
237     }
238
239     SYSTEM_LOCK();
240
241     /* remove the message queue from the global link list */
242     pPrev = &hFirstQueue;
243     while (*pPrev && (*pPrev != hQueue))
244     {
245         MESSAGEQUEUE *msgQ = (MESSAGEQUEUE*)GlobalLock16(*pPrev);
246         pPrev = &msgQ->next;
247     }
248     if (*pPrev) *pPrev = msgQueue->next;
249     msgQueue->self = 0;
250
251     SYSTEM_UNLOCK();
252
253     /* free up resource used by MESSAGEQUEUE strcture */
254     msgQueue->lockCount--;
255     QUEUE_Unlock( msgQueue );
256     
257     return 1;
258 }
259
260
261 /***********************************************************************
262  *           QUEUE_CreateSysMsgQueue
263  *
264  * Create the system message queue, and set the double-click speed.
265  * Must be called only once.
266  */
267 BOOL32 QUEUE_CreateSysMsgQueue( int size )
268 {
269     if (!(hmemSysMsgQueue = QUEUE_CreateMsgQueue( ))) return FALSE;
270     sysMsgQueue = (MESSAGEQUEUE *) GlobalLock16( hmemSysMsgQueue );
271     return TRUE;
272 }
273
274
275 /***********************************************************************
276  *           QUEUE_GetSysQueue
277  */
278 MESSAGEQUEUE *QUEUE_GetSysQueue(void)
279 {
280     return sysMsgQueue;
281 }
282
283 /***********************************************************************
284  *           QUEUE_Signal
285  */
286 void QUEUE_Signal( THDB *thdb  )
287 {
288     /* Wake up thread waiting for message */
289     SetEvent( thdb->event );
290
291     PostEvent( thdb->process->task );
292 }
293
294 /***********************************************************************
295  *           QUEUE_Wait
296  */
297 static void QUEUE_Wait( DWORD wait_mask )
298 {
299     if ( THREAD_IsWin16( THREAD_Current() ) )
300         WaitEvent( 0 );
301     else
302     {
303         TRACE(msg, "current task is 32-bit, calling SYNC_DoWait\n");
304         MsgWaitForMultipleObjects( 0, NULL, FALSE, INFINITE32, wait_mask );
305     }
306 }
307
308
309 /***********************************************************************
310  *           QUEUE_SetWakeBit               `
311  *
312  * See "Windows Internals", p.449
313  */
314 void QUEUE_SetWakeBit( MESSAGEQUEUE *queue, WORD bit )
315 {
316     TRACE(msg,"queue = %04x (wm=%04x), bit = %04x\n", 
317                         queue->self, queue->wakeMask, bit );
318
319     if (bit & QS_MOUSE) pMouseQueue = queue;
320     if (bit & QS_KEY) pKbdQueue = queue;
321     queue->changeBits |= bit;
322     queue->wakeBits   |= bit;
323     if (queue->wakeMask & bit)
324     {
325         queue->wakeMask = 0;
326         QUEUE_Signal( queue->thdb );
327     }
328 }
329
330
331 /***********************************************************************
332  *           QUEUE_ClearWakeBit
333  */
334 void QUEUE_ClearWakeBit( MESSAGEQUEUE *queue, WORD bit )
335 {
336     queue->changeBits &= ~bit;
337     queue->wakeBits   &= ~bit;
338 }
339
340
341 /***********************************************************************
342  *           QUEUE_WaitBits
343  *
344  * See "Windows Internals", p.447
345  */
346 void QUEUE_WaitBits( WORD bits )
347 {
348     MESSAGEQUEUE *queue;
349
350     TRACE(msg,"q %04x waiting for %04x\n", GetFastQueue(), bits);
351
352     for (;;)
353     {
354         if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) return;
355
356         if (queue->changeBits & bits)
357         {
358             /* One of the bits is set; we can return */
359             queue->wakeMask = 0;
360             QUEUE_Unlock( queue );
361             return;
362         }
363         if (queue->wakeBits & QS_SENDMESSAGE)
364         {
365             /* Process the sent message immediately */
366
367             queue->wakeMask = 0;
368             QUEUE_ReceiveMessage( queue );
369             QUEUE_Unlock( queue );
370             continue;                           /* nested sm crux */
371         }
372
373         queue->wakeMask = bits | QS_SENDMESSAGE;
374         if(queue->changeBits & bits)
375         {
376             QUEUE_Unlock( queue );
377             continue;
378         }
379         
380         TRACE(msg,"%04x) wakeMask is %04x, waiting\n", queue->self, queue->wakeMask);
381
382         QUEUE_Wait( queue->wakeMask );
383
384         QUEUE_Unlock( queue );
385     }
386 }
387
388
389 /***********************************************************************
390  *           QUEUE_ReceiveMessage
391  *
392  * This routine is called when a sent message is waiting for the queue.
393  */
394 void QUEUE_ReceiveMessage( MESSAGEQUEUE *queue )
395 {
396     MESSAGEQUEUE *senderQ = NULL;
397     HQUEUE16      prevSender = 0;
398     QSMCTRL*      prevCtrlPtr = NULL;
399     LRESULT       result = 0;
400
401     TRACE(msg, "ReceiveMessage, queue %04x\n", queue->self );
402     if (!(queue->wakeBits & QS_SENDMESSAGE) ||
403         !(senderQ = (MESSAGEQUEUE*)QUEUE_Lock( queue->hSendingTask)))
404         { TRACE(msg,"\trcm: nothing to do\n"); return; }
405
406     if( !senderQ->hPrevSendingTask )
407         QUEUE_ClearWakeBit( queue, QS_SENDMESSAGE );   /* no more sent messages */
408
409     /* Save current state on stack */
410     prevSender                 = queue->InSendMessageHandle;
411     prevCtrlPtr                = queue->smResultCurrent;
412
413     /* Remove sending queue from the list */
414     queue->InSendMessageHandle = queue->hSendingTask;
415     queue->smResultCurrent     = senderQ->smResultInit;
416     queue->hSendingTask        = senderQ->hPrevSendingTask;
417
418     TRACE(msg, "\trcm: smResultCurrent = %08x, prevCtrl = %08x\n", 
419                                 (unsigned)queue->smResultCurrent, (unsigned)prevCtrlPtr );
420     QUEUE_SetWakeBit( senderQ, QS_SMPARAMSFREE );
421
422     TRACE(msg, "\trcm: calling wndproc - %08x %08x %08x %08x\n",
423                 senderQ->hWnd32, senderQ->msg32,
424                 senderQ->wParam32, (unsigned)senderQ->lParam );
425
426     if (IsWindow32( senderQ->hWnd32 ))
427     {
428         WND *wndPtr = WIN_FindWndPtr( senderQ->hWnd32 );
429         DWORD extraInfo = queue->GetMessageExtraInfoVal;
430         queue->GetMessageExtraInfoVal = senderQ->GetMessageExtraInfoVal;
431
432         if (senderQ->flags & QUEUE_SM_WIN32)
433         {
434             TRACE(msg, "\trcm: msg is Win32\n" );
435             if (senderQ->flags & QUEUE_SM_UNICODE)
436                 result = CallWindowProc32W( wndPtr->winproc,
437                                             senderQ->hWnd32, senderQ->msg32,
438                                             senderQ->wParam32, senderQ->lParam );
439             else
440                 result = CallWindowProc32A( wndPtr->winproc,
441                                             senderQ->hWnd32, senderQ->msg32,
442                                             senderQ->wParam32, senderQ->lParam );
443         }
444         else  /* Win16 message */
445             result = CallWindowProc16( (WNDPROC16)wndPtr->winproc,
446                                        (HWND16) senderQ->hWnd32,
447                                        (UINT16) senderQ->msg32,
448                                        LOWORD (senderQ->wParam32),
449                                        senderQ->lParam );
450
451         queue->GetMessageExtraInfoVal = extraInfo;  /* Restore extra info */
452         TRACE(msg,"\trcm: result =  %08x\n", (unsigned)result );
453     }
454     else WARN(msg, "\trcm: bad hWnd\n");
455
456     QUEUE_Unlock( senderQ );
457     
458     /* Return the result to the sender task */
459     ReplyMessage16( result );
460
461     queue->InSendMessageHandle = prevSender;
462     queue->smResultCurrent     = prevCtrlPtr;
463
464     TRACE(msg,"done!\n");
465 }
466
467 /***********************************************************************
468  *           QUEUE_FlushMessage
469  * 
470  * Try to reply to all pending sent messages on exit.
471  */
472 void QUEUE_FlushMessages( HQUEUE16 hQueue )
473 {
474   MESSAGEQUEUE *queue = (MESSAGEQUEUE*)QUEUE_Lock( hQueue );
475
476   if( queue )
477   {
478     MESSAGEQUEUE *senderQ = (MESSAGEQUEUE*)QUEUE_Lock( queue->hSendingTask );
479     QSMCTRL*      CtrlPtr = queue->smResultCurrent;
480
481     TRACE(msg,"Flushing queue %04x:\n", hQueue );
482
483     while( senderQ )
484     {
485       if( !CtrlPtr )
486            CtrlPtr = senderQ->smResultInit;
487
488       TRACE(msg,"\tfrom queue %04x, smResult %08x\n", queue->hSendingTask, (unsigned)CtrlPtr );
489
490       if( !(queue->hSendingTask = senderQ->hPrevSendingTask) )
491         QUEUE_ClearWakeBit( queue, QS_SENDMESSAGE );
492
493       QUEUE_SetWakeBit( senderQ, QS_SMPARAMSFREE );
494       
495       queue->smResultCurrent = CtrlPtr;
496       while( senderQ->wakeBits & QS_SMRESULT ) OldYield();
497
498       senderQ->SendMessageReturn = 0;
499       senderQ->smResult = queue->smResultCurrent;
500       QUEUE_SetWakeBit( senderQ, QS_SMRESULT);
501
502       QUEUE_Unlock( senderQ );
503
504       senderQ = (MESSAGEQUEUE*)QUEUE_Lock( queue->hSendingTask );
505       CtrlPtr = NULL;
506     }
507     queue->InSendMessageHandle = 0;
508     
509     QUEUE_Unlock( queue );
510   }  
511
512 }
513
514 /***********************************************************************
515  *           QUEUE_AddMsg
516  *
517  * Add a message to the queue. Return FALSE if queue is full.
518  */
519 BOOL32 QUEUE_AddMsg( HQUEUE16 hQueue, MSG32 *msg, DWORD extraInfo )
520 {
521     MESSAGEQUEUE *msgQueue;
522     QMSG         *qmsg;
523
524
525     if (!(msgQueue = (MESSAGEQUEUE *)QUEUE_Lock( hQueue ))) return FALSE;
526
527     /* allocate new message in global heap for now */
528     if (!(qmsg = (QMSG *) HeapAlloc( SystemHeap, 0, sizeof(QMSG) ) ))
529     {
530         QUEUE_Unlock( msgQueue );
531         return 0;
532     }
533
534     EnterCriticalSection( &msgQueue->cSection );
535
536       /* Store message */
537     qmsg->msg = *msg;
538     qmsg->extraInfo = extraInfo;
539
540     /* insert the message in the link list */
541     qmsg->nextMsg = 0;
542     qmsg->prevMsg = msgQueue->lastMsg;
543
544     if (msgQueue->lastMsg)
545         msgQueue->lastMsg->nextMsg = qmsg;
546
547     /* update first and last anchor in message queue */
548     msgQueue->lastMsg = qmsg;
549     if (!msgQueue->firstMsg)
550         msgQueue->firstMsg = qmsg;
551     
552     msgQueue->msgCount++;
553
554     LeaveCriticalSection( &msgQueue->cSection );
555
556     QUEUE_SetWakeBit( msgQueue, QS_POSTMESSAGE );
557     QUEUE_Unlock( msgQueue );
558     
559     return TRUE;
560 }
561
562
563
564 /***********************************************************************
565  *           QUEUE_FindMsg
566  *
567  * Find a message matching the given parameters. Return -1 if none available.
568  */
569 QMSG* QUEUE_FindMsg( MESSAGEQUEUE * msgQueue, HWND32 hwnd, int first, int last )
570 {
571     QMSG* qmsg;
572
573     EnterCriticalSection( &msgQueue->cSection );
574
575     if (!msgQueue->msgCount)
576         qmsg = 0;
577     else if (!hwnd && !first && !last)
578         qmsg = msgQueue->firstMsg;
579     else
580     {
581         /* look in linked list for message matching first and last criteria */
582         for (qmsg = msgQueue->firstMsg; qmsg; qmsg = qmsg->nextMsg)
583     {
584             MSG32 *msg = &(qmsg->msg);
585
586         if (!hwnd || (msg->hwnd == hwnd))
587         {
588                 if (!first && !last)
589                     break;   /* found it */
590                 
591                 if ((msg->message >= first) && (msg->message <= last))
592                     break;   /* found it */
593             }
594         }
595     }
596     
597     LeaveCriticalSection( &msgQueue->cSection );
598
599     return qmsg;
600 }
601
602
603
604 /***********************************************************************
605  *           QUEUE_RemoveMsg
606  *
607  * Remove a message from the queue (pos must be a valid position).
608  */
609 void QUEUE_RemoveMsg( MESSAGEQUEUE * msgQueue, QMSG *qmsg )
610 {
611     EnterCriticalSection( &msgQueue->cSection );
612
613     /* set the linked list */
614     if (qmsg->prevMsg)
615         qmsg->prevMsg->nextMsg = qmsg->nextMsg;
616
617     if (qmsg->nextMsg)
618         qmsg->nextMsg->prevMsg = qmsg->prevMsg;
619
620     if (msgQueue->firstMsg == qmsg)
621         msgQueue->firstMsg = qmsg->nextMsg;
622
623     if (msgQueue->lastMsg == qmsg)
624         msgQueue->lastMsg = qmsg->prevMsg;
625
626     /* deallocate the memory for the message */
627     HeapFree( SystemHeap, 0, qmsg );
628     
629     msgQueue->msgCount--;
630     if (!msgQueue->msgCount) msgQueue->wakeBits &= ~QS_POSTMESSAGE;
631
632     LeaveCriticalSection( &msgQueue->cSection );
633 }
634
635
636 /***********************************************************************
637  *           QUEUE_WakeSomeone
638  *
639  * Wake a queue upon reception of a hardware event.
640  */
641 static void QUEUE_WakeSomeone( UINT32 message )
642 {
643     WND*          wndPtr = NULL;
644     WORD          wakeBit;
645     HWND32 hwnd;
646     HQUEUE16     hQueue = 0;
647     MESSAGEQUEUE *queue = NULL;
648
649     if (hCursorQueue)
650         hQueue = hCursorQueue;
651
652     if( (message >= WM_KEYFIRST) && (message <= WM_KEYLAST) )
653     {
654        wakeBit = QS_KEY;
655        if( hActiveQueue )
656            hQueue = hActiveQueue;
657     }
658     else 
659     {
660        wakeBit = (message == WM_MOUSEMOVE) ? QS_MOUSEMOVE : QS_MOUSEBUTTON;
661        if( (hwnd = GetCapture32()) )
662          if( (wndPtr = WIN_FindWndPtr( hwnd )) ) 
663            {
664                hQueue = wndPtr->hmemTaskQ;
665            }
666     }
667
668     if( (hwnd = GetSysModalWindow16()) )
669     {
670       if( (wndPtr = WIN_FindWndPtr( hwnd )) )
671             hQueue = wndPtr->hmemTaskQ;
672     }
673
674     if (hQueue)
675         queue = QUEUE_Lock( hQueue );
676     
677     if( !queue ) 
678     {
679         queue = QUEUE_Lock( hFirstQueue );
680       while( queue )
681       {
682         if (queue->wakeMask & wakeBit) break;
683           
684             QUEUE_Unlock(queue);
685             queue = QUEUE_Lock( queue->next );
686       }
687       if( !queue )
688       { 
689         WARN(msg, "couldn't find queue\n"); 
690         return; 
691       }
692     }
693
694     QUEUE_SetWakeBit( queue, wakeBit );
695
696     QUEUE_Unlock( queue );
697 }
698
699
700 /***********************************************************************
701  *           hardware_event
702  *
703  * Add an event to the system message queue.
704  * Note: the position is relative to the desktop window.
705  */
706 void hardware_event( WORD message, WORD wParam, LONG lParam,
707                      int xPos, int yPos, DWORD time, DWORD extraInfo )
708 {
709     MSG32 *msg;
710     QMSG  *qmsg = sysMsgQueue->lastMsg;
711     int  mergeMsg = 0;
712
713     if (!sysMsgQueue) return;
714
715       /* Merge with previous event if possible */
716
717     if ((message == WM_MOUSEMOVE) && sysMsgQueue->lastMsg)
718     {
719         msg = &(sysMsgQueue->lastMsg->msg);
720         
721         if ((msg->message == message) && (msg->wParam == wParam))
722         {
723             /* Merge events */
724             qmsg = sysMsgQueue->lastMsg;
725             mergeMsg = 1;
726     }
727     }
728
729     if (!mergeMsg)
730     {
731         /* Should I limit the number of message in
732           the system message queue??? */
733
734         /* Don't merge allocate a new msg in the global heap */
735         
736         if (!(qmsg = (QMSG *) HeapAlloc( SystemHeap, 0, sizeof(QMSG) ) ))
737         return;
738         
739         /* put message at the end of the linked list */
740         qmsg->nextMsg = 0;
741         qmsg->prevMsg = sysMsgQueue->lastMsg;
742
743         if (sysMsgQueue->lastMsg)
744             sysMsgQueue->lastMsg->nextMsg = qmsg;
745
746         /* set last and first anchor index in system message queue */
747         sysMsgQueue->lastMsg = qmsg;
748         if (!sysMsgQueue->firstMsg)
749             sysMsgQueue->firstMsg = qmsg;
750         
751         sysMsgQueue->msgCount++;
752     }
753
754       /* Store message */
755     msg = &(qmsg->msg);
756     msg->hwnd    = 0;
757     msg->message = message;
758     msg->wParam  = wParam;
759     msg->lParam  = lParam;
760     msg->time    = time;
761     msg->pt.x    = xPos;
762     msg->pt.y    = yPos;
763     qmsg->extraInfo = extraInfo;
764
765     QUEUE_WakeSomeone( message );
766 }
767
768                     
769 /***********************************************************************
770  *           QUEUE_GetQueueTask
771  */
772 HTASK16 QUEUE_GetQueueTask( HQUEUE16 hQueue )
773 {
774     HTASK16 hTask = 0;
775     
776     MESSAGEQUEUE *queue = QUEUE_Lock( hQueue );
777
778     if (queue)
779 {
780         hTask = queue->thdb->process->task;
781         QUEUE_Unlock( queue );
782 }
783
784     return hTask;
785 }
786
787
788
789 /***********************************************************************
790  *           QUEUE_IncPaintCount
791  */
792 void QUEUE_IncPaintCount( HQUEUE16 hQueue )
793 {
794     MESSAGEQUEUE *queue;
795
796     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( hQueue ))) return;
797     queue->wPaintCount++;
798     QUEUE_SetWakeBit( queue, QS_PAINT );
799     QUEUE_Unlock( queue );
800 }
801
802
803 /***********************************************************************
804  *           QUEUE_DecPaintCount
805  */
806 void QUEUE_DecPaintCount( HQUEUE16 hQueue )
807 {
808     MESSAGEQUEUE *queue;
809
810     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( hQueue ))) return;
811     queue->wPaintCount--;
812     if (!queue->wPaintCount) queue->wakeBits &= ~QS_PAINT;
813     QUEUE_Unlock( queue );
814 }
815
816
817 /***********************************************************************
818  *           QUEUE_IncTimerCount
819  */
820 void QUEUE_IncTimerCount( HQUEUE16 hQueue )
821 {
822     MESSAGEQUEUE *queue;
823
824     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( hQueue ))) return;
825     queue->wTimerCount++;
826     QUEUE_SetWakeBit( queue, QS_TIMER );
827     QUEUE_Unlock( queue );
828 }
829
830
831 /***********************************************************************
832  *           QUEUE_DecTimerCount
833  */
834 void QUEUE_DecTimerCount( HQUEUE16 hQueue )
835 {
836     MESSAGEQUEUE *queue;
837
838     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( hQueue ))) return;
839     queue->wTimerCount--;
840     if (!queue->wTimerCount) queue->wakeBits &= ~QS_TIMER;
841     QUEUE_Unlock( queue );
842 }
843
844
845 /***********************************************************************
846  *           PostQuitMessage16   (USER.6)
847  */
848 void WINAPI PostQuitMessage16( INT16 exitCode )
849 {
850     PostQuitMessage32( exitCode );
851 }
852
853
854 /***********************************************************************
855  *           PostQuitMessage32   (USER32.421)
856  *
857  * PostQuitMessage() posts a message to the system requesting an
858  * application to terminate execution. As a result of this function,
859  * the WM_QUIT message is posted to the application, and
860  * PostQuitMessage() returns immediately.  The exitCode parameter
861  * specifies an application-defined exit code, which appears in the
862  * _wParam_ parameter of the WM_QUIT message posted to the application.  
863  *
864  * CONFORMANCE
865  *
866  *  ECMA-234, Win32
867  */
868 void WINAPI PostQuitMessage32( INT32 exitCode )
869 {
870     MESSAGEQUEUE *queue;
871
872     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) return;
873     queue->wPostQMsg = TRUE;
874     queue->wExitCode = (WORD)exitCode;
875     QUEUE_Unlock( queue );
876 }
877
878
879 /***********************************************************************
880  *           GetWindowTask16   (USER.224)
881  */
882 HTASK16 WINAPI GetWindowTask16( HWND16 hwnd )
883 {
884     WND *wndPtr = WIN_FindWndPtr( hwnd );
885
886     if (!wndPtr) return 0;
887     return QUEUE_GetQueueTask( wndPtr->hmemTaskQ );
888 }
889
890 /***********************************************************************
891  *           GetWindowThreadProcessId   (USER32.313)
892  */
893 DWORD WINAPI GetWindowThreadProcessId( HWND32 hwnd, LPDWORD process )
894 {
895     HTASK16 htask;
896     TDB *tdb;
897
898     WND *wndPtr = WIN_FindWndPtr( hwnd );
899
900     if (!wndPtr) return 0;
901     htask=QUEUE_GetQueueTask( wndPtr->hmemTaskQ );
902     tdb = (TDB*)GlobalLock16(htask);
903     if (!tdb || !tdb->thdb) return 0;
904     if (process) *process = PDB_TO_PROCESS_ID( tdb->thdb->process );
905     return THDB_TO_THREAD_ID( tdb->thdb );
906 }
907
908
909 /***********************************************************************
910  *           SetMessageQueue16   (USER.266)
911  */
912 BOOL16 WINAPI SetMessageQueue16( INT16 size )
913 {
914     return SetMessageQueue32( size );
915 }
916
917
918 /***********************************************************************
919  *           SetMessageQueue32   (USER32.494)
920  */
921 BOOL32 WINAPI SetMessageQueue32( INT32 size )
922 {
923     /* now obsolete the message queue will be expanded dynamically
924      as necessary */
925
926     /* access the queue to create it if it's not existing */
927     GetFastQueue();
928
929     return TRUE;
930 }
931
932 /***********************************************************************
933  *           InitThreadInput   (USER.409)
934  */
935 HQUEUE16 WINAPI InitThreadInput( WORD unknown, WORD flags )
936 {
937     HQUEUE16 hQueue;
938     MESSAGEQUEUE *queuePtr;
939
940     THDB *thdb = THREAD_Current();
941
942     if (!thdb)
943         return 0;
944
945     hQueue = thdb->teb.queue;
946     
947     if ( !hQueue )
948     {
949         /* Create thread message queue */
950         if( !(hQueue = QUEUE_CreateMsgQueue( 0 )))
951         {
952             WARN(msg, "failed!\n");
953             return FALSE;
954     }
955         
956         /* Link new queue into list */
957         queuePtr = (MESSAGEQUEUE *)QUEUE_Lock( hQueue );
958         queuePtr->thdb = THREAD_Current();
959
960         SYSTEM_LOCK();
961         SetThreadQueue( 0, hQueue );
962         thdb->teb.queue = hQueue;
963             
964         queuePtr->next  = hFirstQueue;
965         hFirstQueue = hQueue;
966         SYSTEM_UNLOCK();
967         
968         QUEUE_Unlock( queuePtr );
969     }
970
971     return hQueue;
972 }
973
974 /***********************************************************************
975  *           GetQueueStatus16   (USER.334)
976  */
977 DWORD WINAPI GetQueueStatus16( UINT16 flags )
978 {
979     MESSAGEQUEUE *queue;
980     DWORD ret;
981
982     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) return 0;
983     ret = MAKELONG( queue->changeBits, queue->wakeBits );
984     queue->changeBits = 0;
985     QUEUE_Unlock( queue );
986     
987     return ret & MAKELONG( flags, flags );
988 }
989
990 /***********************************************************************
991  *           GetQueueStatus32   (USER32.283)
992  */
993 DWORD WINAPI GetQueueStatus32( UINT32 flags )
994 {
995     MESSAGEQUEUE *queue;
996     DWORD ret;
997
998     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) return 0;
999     ret = MAKELONG( queue->changeBits, queue->wakeBits );
1000     queue->changeBits = 0;
1001     QUEUE_Unlock( queue );
1002     
1003     return ret & MAKELONG( flags, flags );
1004 }
1005
1006
1007 /***********************************************************************
1008  *           GetInputState16   (USER.335)
1009  */
1010 BOOL16 WINAPI GetInputState16(void)
1011 {
1012     return GetInputState32();
1013 }
1014
1015 /***********************************************************************
1016  *           WaitForInputIdle   (USER32.577)
1017  */
1018 DWORD WINAPI WaitForInputIdle (HANDLE32 hProcess, DWORD dwTimeOut)
1019 {
1020   FIXME (msg, "(hProcess=%d, dwTimeOut=%ld): stub\n", hProcess, dwTimeOut);
1021
1022   return WAIT_TIMEOUT;
1023 }
1024
1025
1026 /***********************************************************************
1027  *           GetInputState32   (USER32.244)
1028  */
1029 BOOL32 WINAPI GetInputState32(void)
1030 {
1031     MESSAGEQUEUE *queue;
1032     BOOL32 ret;
1033
1034     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() )))
1035         return FALSE;
1036     ret = queue->wakeBits & (QS_KEY | QS_MOUSEBUTTON);
1037     QUEUE_Unlock( queue );
1038
1039     return ret;
1040 }
1041
1042 /***********************************************************************
1043  *           UserYield  (USER.332)
1044  */
1045 void WINAPI UserYield(void)
1046 {
1047     TDB *pCurTask = (TDB *)GlobalLock16( GetCurrentTask() );
1048     MESSAGEQUEUE *queue = (MESSAGEQUEUE *)QUEUE_Lock( pCurTask->hQueue );
1049
1050     if ( !THREAD_IsWin16( THREAD_Current() ) )
1051     {
1052         FIXME(task, "called for Win32 thread (%04x)!\n", THREAD_Current()->teb_sel);
1053         QUEUE_Unlock( queue );
1054         return;
1055     }
1056
1057     /* Handle sent messages */
1058     while (queue && (queue->wakeBits & QS_SENDMESSAGE))
1059         QUEUE_ReceiveMessage( queue );
1060
1061     QUEUE_Unlock( queue );
1062     
1063     OldYield();
1064
1065     queue = (MESSAGEQUEUE *)QUEUE_Lock( pCurTask->hQueue );
1066     while (queue && (queue->wakeBits & QS_SENDMESSAGE))
1067         QUEUE_ReceiveMessage( queue );
1068
1069     QUEUE_Unlock( queue );
1070 }
1071
1072 /***********************************************************************
1073  *           GetMessagePos   (USER.119) (USER32.272)
1074  * 
1075  * The GetMessagePos() function returns a long value representing a
1076  * cursor position, in screen coordinates, when the last message
1077  * retrieved by the GetMessage() function occurs. The x-coordinate is
1078  * in the low-order word of the return value, the y-coordinate is in
1079  * the high-order word. The application can use the MAKEPOINT()
1080  * macro to obtain a POINT structure from the return value. 
1081  *
1082  * For the current cursor position, use GetCursorPos().
1083  *
1084  * RETURNS
1085  *
1086  * Cursor position of last message on success, zero on failure.
1087  *
1088  * CONFORMANCE
1089  *
1090  * ECMA-234, Win32
1091  *
1092  */
1093 DWORD WINAPI GetMessagePos(void)
1094 {
1095     MESSAGEQUEUE *queue;
1096     DWORD ret;
1097
1098     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) return 0;
1099     ret = queue->GetMessagePosVal;
1100     QUEUE_Unlock( queue );
1101
1102     return ret;
1103 }
1104
1105
1106 /***********************************************************************
1107  *           GetMessageTime   (USER.120) (USER32.273)
1108  *
1109  * GetMessageTime() returns the message time for the last message
1110  * retrieved by the function. The time is measured in milliseconds with
1111  * the same offset as GetTickCount().
1112  *
1113  * Since the tick count wraps, this is only useful for moderately short
1114  * relative time comparisons.
1115  *
1116  * RETURNS
1117  *
1118  * Time of last message on success, zero on failure.
1119  *
1120  * CONFORMANCE
1121  *
1122  * ECMA-234, Win32
1123  *  
1124  */
1125 LONG WINAPI GetMessageTime(void)
1126 {
1127     MESSAGEQUEUE *queue;
1128     LONG ret;
1129
1130     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) return 0;
1131     ret = queue->GetMessageTimeVal;
1132     QUEUE_Unlock( queue );
1133     
1134     return ret;
1135 }
1136
1137
1138 /***********************************************************************
1139  *           GetMessageExtraInfo   (USER.288) (USER32.271)
1140  */
1141 LONG WINAPI GetMessageExtraInfo(void)
1142 {
1143     MESSAGEQUEUE *queue;
1144     LONG ret;
1145
1146     if (!(queue = (MESSAGEQUEUE *)QUEUE_Lock( GetFastQueue() ))) return 0;
1147     ret = queue->GetMessageExtraInfoVal;
1148     QUEUE_Unlock( queue );
1149
1150     return ret;
1151 }