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