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