Release 960811
[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 "module.h"
9 #include "queue.h"
10 #include "task.h"
11 #include "win.h"
12 #include "stddebug.h"
13 #include "debug.h"
14
15 #define MAX_QUEUE_SIZE   120  /* Max. size of a message queue */
16
17 static HQUEUE16 hFirstQueue = 0;
18 static HQUEUE16 hmemSysMsgQueue = 0;
19 static MESSAGEQUEUE *pMouseQueue = NULL;  /* Queue for last mouse message */
20 static MESSAGEQUEUE *pKbdQueue = NULL;    /* Queue for last kbd message */
21 static HQUEUE16 hDoomedQueue = 0;
22 static MESSAGEQUEUE *sysMsgQueue = NULL;
23
24 /***********************************************************************
25  *           QUEUE_DumpQueue
26  */
27 void QUEUE_DumpQueue( HQUEUE16 hQueue )
28 {
29     MESSAGEQUEUE *pq; 
30
31     if (!(pq = (MESSAGEQUEUE*) GlobalLock16( hQueue )) ||
32         GlobalSize16(hQueue) < sizeof(MESSAGEQUEUE)+pq->queueSize*sizeof(QMSG))
33     {
34         fprintf( stderr, "%04x is not a queue handle\n", hQueue );
35         return;
36     }
37
38     fprintf( stderr,
39              "next: %12.4x  Intertask SendMessage:\n"
40              "hTask: %11.4x  ----------------------\n"
41              "msgSize: %9.4x  hWnd: %10.4x\n"
42              "msgCount: %8.4x  msg: %11.4x\n"
43              "msgNext: %9.4x  wParam: %8.4x\n"
44              "msgFree: %9.4x  lParam: %8.8x\n"
45              "qSize: %11.4x  lRet: %10.8x\n"
46              "wWinVer: %9.4x  ISMH: %10.4x\n"
47              "paints: %10.4x  hSendTask: %5.4x\n"
48              "timers: %10.4x  hPrevSend: %5.4x\n"
49              "wakeBits: %8.4x\n"
50              "wakeMask: %8.4x\n"
51              "hCurHook: %8.4x\n",
52              pq->next, pq->hTask, pq->msgSize, pq->hWnd, 
53              pq->msgCount, pq->msg, pq->nextMessage, pq->wParam,
54              pq->nextFreeMessage, (unsigned)pq->lParam, pq->queueSize,
55              (unsigned)pq->SendMessageReturn, pq->wWinVersion, pq->InSendMessageHandle,
56              pq->wPaintCount, pq->hSendingTask, pq->wTimerCount,
57              pq->hPrevSendingTask, pq->wakeBits, pq->wakeMask, pq->hCurHook);
58 }
59
60
61 /***********************************************************************
62  *           QUEUE_WalkQueues
63  */
64 void QUEUE_WalkQueues(void)
65 {
66     HQUEUE16 hQueue = hFirstQueue;
67
68     fprintf( stderr, "Queue Size Msgs Task\n" );
69     while (hQueue)
70     {
71         MESSAGEQUEUE *queue = (MESSAGEQUEUE *)GlobalLock16( hQueue );
72         if (!queue)
73         {
74             fprintf( stderr, "*** Bad queue handle %04x\n", hQueue );
75             return;
76         }
77         fprintf( stderr, "%04x %5d %4d %04x %s\n",
78                  hQueue, queue->msgSize, queue->msgCount, queue->hTask,
79                  MODULE_GetModuleName( GetExePtr(queue->hTask) ) );
80         hQueue = queue->next;
81     }
82     fprintf( stderr, "\n" );
83 }
84
85
86 /***********************************************************************
87  *           QUEUE_GetDoomedQueue/QUEUE_SetDoomedQueue
88  */
89 HQUEUE16 QUEUE_GetDoomedQueue()
90 {
91     return hDoomedQueue;
92 }
93
94
95 /***********************************************************************
96  *           QUEUE_SetDoomedQueue
97  */
98 void QUEUE_SetDoomedQueue( HQUEUE16 hQueue )
99 {
100     hDoomedQueue = hQueue;
101 }
102
103
104 /***********************************************************************
105  *           QUEUE_CreateMsgQueue
106  *
107  * Creates a message queue. Doesn't link it into queue list!
108  */
109 static HQUEUE16 QUEUE_CreateMsgQueue( int size )
110 {
111     HQUEUE16 hQueue;
112     MESSAGEQUEUE * msgQueue;
113     int queueSize;
114     TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
115
116     dprintf_msg(stddeb,"Creating message queue...\n");
117
118     queueSize = sizeof(MESSAGEQUEUE) + size * sizeof(QMSG);
119     if (!(hQueue = GlobalAlloc16( GMEM_FIXED | GMEM_ZEROINIT, queueSize )))
120         return 0;
121     msgQueue = (MESSAGEQUEUE *) GlobalLock16( hQueue );
122     msgQueue->self = hQueue;
123     msgQueue->msgSize = sizeof(QMSG);
124     msgQueue->queueSize = size;
125     msgQueue->wWinVersion = pTask ? pTask->version : 0;
126     GlobalUnlock16( hQueue );
127     return hQueue;
128 }
129
130
131 /***********************************************************************
132  *           QUEUE_DeleteMsgQueue
133  *
134  * Unlinks and deletes a message queue.
135  */
136 BOOL32 QUEUE_DeleteMsgQueue( HQUEUE16 hQueue )
137 {
138     MESSAGEQUEUE * msgQueue = (MESSAGEQUEUE*)GlobalLock16(hQueue);
139     HQUEUE16 *pPrev;
140
141     dprintf_msg(stddeb,"Deleting message queue %04x\n", hQueue);
142
143     if (!hQueue || !msgQueue)
144     {
145         dprintf_msg(stddeb,"DeleteMsgQueue: invalid argument.\n");
146         return 0;
147     }
148
149     pPrev = &hFirstQueue;
150     while (*pPrev && (*pPrev != hQueue))
151     {
152         MESSAGEQUEUE *msgQ = (MESSAGEQUEUE*)GlobalLock16(*pPrev);
153         pPrev = &msgQ->next;
154     }
155     if (*pPrev) *pPrev = msgQueue->next;
156     msgQueue->self = 0;
157     GlobalFree16( hQueue );
158     return 1;
159 }
160
161
162 /***********************************************************************
163  *           QUEUE_CreateSysMsgQueue
164  *
165  * Create the system message queue, and set the double-click speed.
166  * Must be called only once.
167  */
168 BOOL32 QUEUE_CreateSysMsgQueue( int size )
169 {
170     if (size > MAX_QUEUE_SIZE) size = MAX_QUEUE_SIZE;
171     else if (size <= 0) size = 1;
172     if (!(hmemSysMsgQueue = QUEUE_CreateMsgQueue( size ))) return FALSE;
173     sysMsgQueue = (MESSAGEQUEUE *) GlobalLock16( hmemSysMsgQueue );
174     return TRUE;
175 }
176
177
178 /***********************************************************************
179  *           QUEUE_GetSysQueue
180  */
181 MESSAGEQUEUE *QUEUE_GetSysQueue(void)
182 {
183     return sysMsgQueue;
184 }
185
186
187 /***********************************************************************
188  *           QUEUE_SetWakeBit
189  *
190  * See "Windows Internals", p.449
191  */
192 void QUEUE_SetWakeBit( MESSAGEQUEUE *queue, WORD bit )
193 {
194     if (bit & QS_MOUSE) pMouseQueue = queue;
195     if (bit & QS_KEY) pKbdQueue = queue;
196     queue->changeBits |= bit;
197     queue->wakeBits   |= bit;
198     if (queue->wakeMask & bit)
199     {
200         queue->wakeMask = 0;
201         PostEvent( queue->hTask );
202     }
203 }
204
205
206 /***********************************************************************
207  *           QUEUE_ClearWakeBit
208  */
209 void QUEUE_ClearWakeBit( MESSAGEQUEUE *queue, WORD bit )
210 {
211     queue->changeBits &= ~bit;
212     queue->wakeBits   &= ~bit;
213 }
214
215
216 /***********************************************************************
217  *           QUEUE_WaitBits
218  *
219  * See "Windows Internals", p.447
220  */
221 void QUEUE_WaitBits( WORD bits )
222 {
223     MESSAGEQUEUE *queue;
224
225     for (;;)
226     {
227         if (!(queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) ))) return;
228         if (queue->changeBits & bits)
229         {
230             /* One of the bits is set; we can return */
231             queue->wakeMask = 0;
232             return;
233         }
234         if (queue->wakeBits & QS_SENDMESSAGE)
235         {
236             /* Process the sent message immediately */
237             QUEUE_ReceiveMessage( queue );
238         }
239         queue->wakeMask = bits | QS_SENDMESSAGE;
240         WaitEvent( 0 );
241     }
242 }
243
244
245 /***********************************************************************
246  *           QUEUE_ReceiveMessage
247  *
248  * This routine is called when a sent message is waiting for the queue.
249  */
250 void QUEUE_ReceiveMessage( MESSAGEQUEUE *queue )
251 {
252     MESSAGEQUEUE *senderQ;
253     HWND hwnd;
254     UINT msg;
255     WPARAM wParam;
256     LPARAM lParam;
257     LRESULT result = 0;
258     HQUEUE16 oldSender;
259
260     printf( "ReceiveMessage\n" );
261     if (!(queue->wakeBits & QS_SENDMESSAGE)) return;
262     if (!(senderQ = (MESSAGEQUEUE*)GlobalLock16( queue->hSendingTask))) return;
263
264     /* Remove sending queue from the list */
265     oldSender                  = queue->InSendMessageHandle;
266     queue->InSendMessageHandle = queue->hSendingTask;
267     queue->hSendingTask        = senderQ->hPrevSendingTask;
268     senderQ->hPrevSendingTask  = 0;
269     if (!queue->hSendingTask)
270     {
271         queue->wakeBits &= ~QS_SENDMESSAGE;
272         queue->changeBits &= ~QS_SENDMESSAGE;
273     }
274
275     /* Get the parameters from the sending task */
276     hwnd   = senderQ->hWnd;
277     msg    = senderQ->msg;
278     wParam = senderQ->wParam;
279     lParam = senderQ->lParam;
280     senderQ->hWnd = 0;
281     QUEUE_SetWakeBit( senderQ, QS_SMPARAMSFREE );
282
283     printf( "ReceiveMessage: calling wnd proc %04x %04x %04x %08x\n",
284             hwnd, msg, wParam, lParam );
285
286     /* Call the window procedure */
287     /* FIXME: should we use CallWindowProc here? */
288     if (IsWindow( hwnd ))
289     {
290         DWORD extraInfo = queue->GetMessageExtraInfoVal;
291         queue->GetMessageExtraInfoVal = senderQ->GetMessageExtraInfoVal;
292         result = SendMessage16( hwnd, msg, wParam, lParam );
293         queue->GetMessageExtraInfoVal = extraInfo;  /* Restore extra info */
294     }
295
296     printf( "ReceiveMessage: wnd proc %04x %04x %04x %08x ret = %08x\n",
297             hwnd, msg, wParam, lParam, result );
298
299     /* Return the result to the sender task */
300     ReplyMessage( result );
301
302     queue->InSendMessageHandle = oldSender;
303 }
304
305
306 /***********************************************************************
307  *           QUEUE_AddMsg
308  *
309  * Add a message to the queue. Return FALSE if queue is full.
310  */
311 BOOL32 QUEUE_AddMsg( HQUEUE16 hQueue, MSG16 * msg, DWORD extraInfo )
312 {
313     int pos;
314     MESSAGEQUEUE *msgQueue;
315
316     if (!(msgQueue = (MESSAGEQUEUE *)GlobalLock16( hQueue ))) return FALSE;
317     pos = msgQueue->nextFreeMessage;
318
319       /* Check if queue is full */
320     if ((pos == msgQueue->nextMessage) && (msgQueue->msgCount > 0))
321     {
322         fprintf(stderr,"MSG_AddMsg // queue is full !\n");
323         return FALSE;
324     }
325
326       /* Store message */
327     msgQueue->messages[pos].msg = *msg;
328     msgQueue->messages[pos].extraInfo = extraInfo;
329     if (pos < msgQueue->queueSize-1) pos++;
330     else pos = 0;
331     msgQueue->nextFreeMessage = pos;
332     msgQueue->msgCount++;
333     QUEUE_SetWakeBit( msgQueue, QS_POSTMESSAGE );
334     return TRUE;
335 }
336
337
338 /***********************************************************************
339  *           QUEUE_FindMsg
340  *
341  * Find a message matching the given parameters. Return -1 if none available.
342  */
343 int QUEUE_FindMsg( MESSAGEQUEUE * msgQueue, HWND32 hwnd, int first, int last )
344 {
345     int i, pos = msgQueue->nextMessage;
346
347     dprintf_msg(stddeb,"QUEUE_FindMsg: hwnd=%04x pos=%d\n", hwnd, pos );
348
349     if (!msgQueue->msgCount) return -1;
350     if (!hwnd && !first && !last) return pos;
351         
352     for (i = 0; i < msgQueue->msgCount; i++)
353     {
354         MSG16 * msg = &msgQueue->messages[pos].msg;
355
356         if (!hwnd || (msg->hwnd == hwnd))
357         {
358             if (!first && !last) return pos;
359             if ((msg->message >= first) && (msg->message <= last)) return pos;
360         }
361         if (pos < msgQueue->queueSize-1) pos++;
362         else pos = 0;
363     }
364     return -1;
365 }
366
367
368 /***********************************************************************
369  *           QUEUE_RemoveMsg
370  *
371  * Remove a message from the queue (pos must be a valid position).
372  */
373 void QUEUE_RemoveMsg( MESSAGEQUEUE * msgQueue, int pos )
374 {
375     if (pos >= msgQueue->nextMessage)
376     {
377         for ( ; pos > msgQueue->nextMessage; pos--)
378             msgQueue->messages[pos] = msgQueue->messages[pos-1];
379         msgQueue->nextMessage++;
380         if (msgQueue->nextMessage >= msgQueue->queueSize)
381             msgQueue->nextMessage = 0;
382     }
383     else
384     {
385         for ( ; pos < msgQueue->nextFreeMessage; pos++)
386             msgQueue->messages[pos] = msgQueue->messages[pos+1];
387         if (msgQueue->nextFreeMessage) msgQueue->nextFreeMessage--;
388         else msgQueue->nextFreeMessage = msgQueue->queueSize-1;
389     }
390     msgQueue->msgCount--;
391     if (!msgQueue->msgCount) msgQueue->wakeBits &= ~QS_POSTMESSAGE;
392 }
393
394
395 /***********************************************************************
396  *           QUEUE_WakeSomeone
397  *
398  * Wake a queue upon reception of a hardware event.
399  */
400 static void QUEUE_WakeSomeone( UINT message )
401 {
402     HWND hwnd;
403     WORD wakeBit;
404     HQUEUE16 hQueue;
405     MESSAGEQUEUE *queue = NULL;
406
407     if ((message >= WM_KEYFIRST) && (message <= WM_KEYLAST)) wakeBit = QS_KEY;
408     else wakeBit = (message == WM_MOUSEMOVE) ? QS_MOUSEMOVE : QS_MOUSEBUTTON;
409
410     if (!(hwnd = GetSysModalWindow16()))
411     {
412         if (wakeBit == QS_KEY)
413         {
414             if (!(hwnd = GetFocus())) hwnd = GetActiveWindow();
415         }
416         else hwnd = GetCapture();
417     }
418     if (hwnd)
419     {
420         WND *wndPtr = WIN_FindWndPtr( hwnd );
421         if (wndPtr) queue = (MESSAGEQUEUE *)GlobalLock16( wndPtr->hmemTaskQ );
422     }
423     else if (!(queue = pMouseQueue))
424     {
425         hQueue = hFirstQueue;
426         while (hQueue)
427         {
428             queue = GlobalLock16( hQueue );
429             if (queue->wakeBits & wakeBit) break;
430             hQueue = queue->next;
431         }
432     }
433     if (!queue) printf( "WakeSomeone: no one found\n" );
434     if (queue) QUEUE_SetWakeBit( queue, wakeBit );
435 }
436
437
438 /***********************************************************************
439  *           hardware_event
440  *
441  * Add an event to the system message queue.
442  * Note: the position is relative to the desktop window.
443  */
444 void hardware_event( WORD message, WORD wParam, LONG lParam,
445                      int xPos, int yPos, DWORD time, DWORD extraInfo )
446 {
447     MSG16 *msg;
448     int pos;
449
450     if (!sysMsgQueue) return;
451     pos = sysMsgQueue->nextFreeMessage;
452
453       /* Merge with previous event if possible */
454
455     if ((message == WM_MOUSEMOVE) && sysMsgQueue->msgCount)
456     {
457         if (pos > 0) pos--;
458         else pos = sysMsgQueue->queueSize - 1;
459         msg = &sysMsgQueue->messages[pos].msg;
460         if ((msg->message == message) && (msg->wParam == wParam))
461             sysMsgQueue->msgCount--;  /* Merge events */
462         else
463             pos = sysMsgQueue->nextFreeMessage;  /* Don't merge */
464     }
465
466       /* Check if queue is full */
467
468     if ((pos == sysMsgQueue->nextMessage) && sysMsgQueue->msgCount)
469     {
470         /* Queue is full, beep (but not on every mouse motion...) */
471         if (message != WM_MOUSEMOVE) MessageBeep(0);
472         return;
473     }
474
475       /* Store message */
476
477     msg = &sysMsgQueue->messages[pos].msg;
478     msg->hwnd    = 0;
479     msg->message = message;
480     msg->wParam  = wParam;
481     msg->lParam  = lParam;
482     msg->time    = time;
483     msg->pt.x    = xPos & 0xffff;
484     msg->pt.y    = yPos & 0xffff;
485     sysMsgQueue->messages[pos].extraInfo = extraInfo;
486     if (pos < sysMsgQueue->queueSize - 1) pos++;
487     else pos = 0;
488     sysMsgQueue->nextFreeMessage = pos;
489     sysMsgQueue->msgCount++;
490     QUEUE_WakeSomeone( message );
491 }
492
493                     
494 /***********************************************************************
495  *           QUEUE_GetQueueTask
496  */
497 HTASK16 QUEUE_GetQueueTask( HQUEUE16 hQueue )
498 {
499     MESSAGEQUEUE *queue = GlobalLock16( hQueue );
500     return (queue) ? queue->hTask : 0 ;
501 }
502
503
504 /***********************************************************************
505  *           QUEUE_IncPaintCount
506  */
507 void QUEUE_IncPaintCount( HQUEUE16 hQueue )
508 {
509     MESSAGEQUEUE *queue;
510
511     if (!(queue = (MESSAGEQUEUE *)GlobalLock16( hQueue ))) return;
512     queue->wPaintCount++;
513     QUEUE_SetWakeBit( queue, QS_PAINT );
514 }
515
516
517 /***********************************************************************
518  *           QUEUE_DecPaintCount
519  */
520 void QUEUE_DecPaintCount( HQUEUE16 hQueue )
521 {
522     MESSAGEQUEUE *queue;
523
524     if (!(queue = (MESSAGEQUEUE *)GlobalLock16( hQueue ))) return;
525     queue->wPaintCount--;
526     if (!queue->wPaintCount) queue->wakeBits &= ~QS_PAINT;
527 }
528
529
530 /***********************************************************************
531  *           QUEUE_IncTimerCount
532  */
533 void QUEUE_IncTimerCount( HQUEUE16 hQueue )
534 {
535     MESSAGEQUEUE *queue;
536
537     if (!(queue = (MESSAGEQUEUE *)GlobalLock16( hQueue ))) return;
538     queue->wTimerCount++;
539     QUEUE_SetWakeBit( queue, QS_TIMER );
540 }
541
542
543 /***********************************************************************
544  *           QUEUE_DecTimerCount
545  */
546 void QUEUE_DecTimerCount( HQUEUE16 hQueue )
547 {
548     MESSAGEQUEUE *queue;
549
550     if (!(queue = (MESSAGEQUEUE *)GlobalLock16( hQueue ))) return;
551     queue->wTimerCount--;
552     if (!queue->wTimerCount) queue->wakeBits &= ~QS_TIMER;
553 }
554
555
556 /***********************************************************************
557  *           PostQuitMessage   (USER.6)
558  */
559 void PostQuitMessage( INT exitCode )
560 {
561     MESSAGEQUEUE *queue;
562
563     if (!(queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) ))) return;
564     queue->wPostQMsg = TRUE;
565     queue->wExitCode = (WORD)exitCode;
566 }
567
568
569 /***********************************************************************
570  *           GetWindowTask16   (USER.224)
571  */
572 HTASK16 GetWindowTask16( HWND16 hwnd )
573 {
574     WND *wndPtr = WIN_FindWndPtr( hwnd );
575
576     if (!wndPtr) return 0;
577     return QUEUE_GetQueueTask( wndPtr->hmemTaskQ );
578 }
579
580
581 /***********************************************************************
582  *           SetMessageQueue   (USER.266)
583  */
584 BOOL SetMessageQueue( int size )
585 {
586     HQUEUE16 hQueue, hNewQueue;
587     MESSAGEQUEUE *queuePtr;
588
589     dprintf_msg(stddeb,"SetMessageQueue: task %04x size %i\n", GetCurrentTask(), size); 
590
591     if ((size > MAX_QUEUE_SIZE) || (size <= 0)) return TRUE;
592
593     if( !(hNewQueue = QUEUE_CreateMsgQueue( size ))) 
594     {
595         dprintf_msg(stddeb,"SetMessageQueue: failed!\n");
596         return FALSE;
597     }
598
599     /* Free the old message queue */
600     if ((hQueue = GetTaskQueue(0)) != 0) QUEUE_DeleteMsgQueue( hQueue );
601
602     /* Link new queue into list */
603     queuePtr = (MESSAGEQUEUE *)GlobalLock16( hNewQueue );
604     queuePtr->hTask = GetCurrentTask();
605     queuePtr->next  = hFirstQueue;
606     hFirstQueue = hNewQueue;
607
608     SetTaskQueue( 0, hNewQueue );
609     return TRUE;
610 }
611
612
613 /***********************************************************************
614  *           GetQueueStatus   (USER.334)
615  */
616 DWORD GetQueueStatus( UINT flags )
617 {
618     MESSAGEQUEUE *queue;
619     DWORD ret;
620
621     if (!(queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) ))) return 0;
622     ret = MAKELONG( queue->changeBits, queue->wakeBits );
623     queue->changeBits = 0;
624     return ret & MAKELONG( flags, flags );
625 }
626
627
628 /***********************************************************************
629  *           GetInputState   (USER.335)
630  */
631 BOOL GetInputState()
632 {
633     MESSAGEQUEUE *queue;
634
635     if (!(queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) ))) return FALSE;
636     return queue->wakeBits & (QS_KEY | QS_MOUSEBUTTON);
637 }
638
639
640 /***********************************************************************
641  *           GetMessagePos   (USER.119)
642  */
643 DWORD GetMessagePos(void)
644 {
645     MESSAGEQUEUE *queue;
646
647     if (!(queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) ))) return 0;
648     return queue->GetMessagePosVal;
649 }
650
651
652 /***********************************************************************
653  *           GetMessageTime   (USER.120)
654  */
655 LONG GetMessageTime(void)
656 {
657     MESSAGEQUEUE *queue;
658
659     if (!(queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) ))) return 0;
660     return queue->GetMessageTimeVal;
661 }
662
663
664 /***********************************************************************
665  *           GetMessageExtraInfo   (USER.288)
666  */
667 LONG GetMessageExtraInfo(void)
668 {
669     MESSAGEQUEUE *queue;
670
671     if (!(queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) ))) return 0;
672     return queue->GetMessageExtraInfoVal;
673 }