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