Release 960302
[wine] / windows / message.c
1 /*
2  * Message queues related functions
3  *
4  * Copyright 1993, 1994 Alexandre Julliard
5  */
6
7 #include <stdlib.h>
8 #include <sys/time.h>
9 #include <sys/types.h>
10 #include <errno.h>
11
12 #include "message.h"
13 #include "win.h"
14 #include "gdi.h"
15 #include "sysmetrics.h"
16 #include "hook.h"
17 #include "event.h"
18 #include "spy.h"
19 #include "winpos.h"
20 #include "atom.h"
21 #include "dde.h"
22 #include "stddebug.h"
23 /* #define DEBUG_MSG */
24 #include "debug.h"
25
26 #define HWND_BROADCAST  ((HWND)0xffff)
27 #define MAX_QUEUE_SIZE   120  /* Max. size of a message queue */
28
29
30 extern BOOL TIMER_CheckTimer( LONG *next, MSG *msg,
31                               HWND hwnd, BOOL remove );  /* timer.c */
32
33 DWORD MSG_WineStartTicks;  /* Ticks at Wine startup */
34
35 /* ------- Internal Queues ------ */
36
37 static HANDLE hmemSysMsgQueue = 0;
38 static MESSAGEQUEUE *sysMsgQueue = NULL;
39 static HANDLE hFirstQueue = 0;
40
41 /* ------- Miscellaneous ------ */
42 static int doubleClickSpeed = 452;
43
44
45 /***********************************************************************
46  *           MSG_CreateMsgQueue
47  *
48  * Creates a message queue. Doesn't link it into queue list!
49  */
50 static HANDLE MSG_CreateMsgQueue( int size )
51 {
52     HANDLE hQueue;
53     MESSAGEQUEUE * msgQueue;
54     int queueSize;
55
56     queueSize = sizeof(MESSAGEQUEUE) + size * sizeof(QMSG);
57     if (!(hQueue = GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, queueSize )))
58         return 0;
59     msgQueue = (MESSAGEQUEUE *) GlobalLock( hQueue );
60     msgQueue->msgSize = sizeof(QMSG);
61     msgQueue->queueSize = size;
62     msgQueue->wWinVersion = 0;  /* FIXME? */
63     GlobalUnlock( hQueue );
64     return hQueue;
65 }
66
67
68 /***********************************************************************
69  *           MSG_DeleteMsgQueue
70  *
71  * Unlinks and deletes a message queue.
72  */
73 BOOL MSG_DeleteMsgQueue( HANDLE hQueue )
74 {
75     MESSAGEQUEUE * msgQueue = (MESSAGEQUEUE*)GlobalLock(hQueue);
76     HANDLE *pPrev;
77
78     if (!hQueue || !msgQueue)
79     {
80         dprintf_msg(stddeb,"DeleteMsgQueue: invalid argument.\n");
81         return 0;
82     }
83
84     pPrev = &hFirstQueue;
85     while (*pPrev && (*pPrev != hQueue))
86     {
87         MESSAGEQUEUE *msgQ = (MESSAGEQUEUE*)GlobalLock(*pPrev);
88         pPrev = &msgQ->next;
89     }
90     if (*pPrev) *pPrev = msgQueue->next;
91     GlobalFree( hQueue );
92     return 1;
93 }
94
95
96 /***********************************************************************
97  *           MSG_CreateSysMsgQueue
98  *
99  * Create the system message queue, and set the double-click speed.
100  * Must be called only once.
101  */
102 BOOL MSG_CreateSysMsgQueue( int size )
103 {
104     if (size > MAX_QUEUE_SIZE) size = MAX_QUEUE_SIZE;
105     else if (size <= 0) size = 1;
106     if (!(hmemSysMsgQueue = MSG_CreateMsgQueue( size ))) return FALSE;
107     sysMsgQueue = (MESSAGEQUEUE *) GlobalLock( hmemSysMsgQueue );
108     doubleClickSpeed = GetProfileInt( "windows", "DoubleClickSpeed", 452 );
109     return TRUE;
110 }
111
112
113 /***********************************************************************
114  *           MSG_AddMsg
115  *
116  * Add a message to the queue. Return FALSE if queue is full.
117  */
118 static int MSG_AddMsg( HANDLE hQueue, MSG * msg, DWORD extraInfo )
119 {
120     int pos;
121     MESSAGEQUEUE *msgQueue;
122
123     if (!(msgQueue = (MESSAGEQUEUE *)GlobalLock( hQueue ))) return FALSE;
124     pos = msgQueue->nextFreeMessage;
125
126       /* Check if queue is full */
127     if ((pos == msgQueue->nextMessage) && (msgQueue->msgCount > 0)) {
128                 fprintf(stderr,"MSG_AddMsg // queue is full !\n");
129                 return FALSE;
130                 }
131
132       /* Store message */
133     msgQueue->messages[pos].msg = *msg;
134     msgQueue->messages[pos].extraInfo = extraInfo;
135     if (pos < msgQueue->queueSize-1) pos++;
136     else pos = 0;
137     msgQueue->nextFreeMessage = pos;
138     msgQueue->msgCount++;
139     msgQueue->status |= QS_POSTMESSAGE;
140     msgQueue->tempStatus |= QS_POSTMESSAGE;
141     return TRUE;
142 }
143
144
145 /***********************************************************************
146  *           MSG_FindMsg
147  *
148  * Find a message matching the given parameters. Return -1 if none available.
149  */
150 static int MSG_FindMsg(MESSAGEQUEUE * msgQueue, HWND hwnd, int first, int last)
151 {
152     int i, pos = msgQueue->nextMessage;
153
154     dprintf_msg(stddeb,"MSG_FindMsg: hwnd=0x"NPFMT"\n\n", hwnd );
155
156     if (!msgQueue->msgCount) return -1;
157     if (!hwnd && !first && !last) return pos;
158         
159     for (i = 0; i < msgQueue->msgCount; i++)
160     {
161         MSG * msg = &msgQueue->messages[pos].msg;
162
163         if (!hwnd || (msg->hwnd == hwnd))
164         {
165             if (!first && !last) return pos;
166             if ((msg->message >= first) && (msg->message <= last)) return pos;
167         }
168         if (pos < msgQueue->queueSize-1) pos++;
169         else pos = 0;
170     }
171     return -1;
172 }
173
174
175 /***********************************************************************
176  *           MSG_RemoveMsg
177  *
178  * Remove a message from the queue (pos must be a valid position).
179  */
180 static void MSG_RemoveMsg( MESSAGEQUEUE * msgQueue, int pos )
181 {
182     if (pos >= msgQueue->nextMessage)
183     {
184         for ( ; pos > msgQueue->nextMessage; pos--)
185             msgQueue->messages[pos] = msgQueue->messages[pos-1];
186         msgQueue->nextMessage++;
187         if (msgQueue->nextMessage >= msgQueue->queueSize)
188             msgQueue->nextMessage = 0;
189     }
190     else
191     {
192         for ( ; pos < msgQueue->nextFreeMessage; pos++)
193             msgQueue->messages[pos] = msgQueue->messages[pos+1];
194         if (msgQueue->nextFreeMessage) msgQueue->nextFreeMessage--;
195         else msgQueue->nextFreeMessage = msgQueue->queueSize-1;
196     }
197     msgQueue->msgCount--;
198     if (!msgQueue->msgCount) msgQueue->status &= ~QS_POSTMESSAGE;
199     msgQueue->tempStatus = 0;
200 }
201
202 /***********************************************************************
203  *           MSG_GetQueueTask
204  */
205 HTASK MSG_GetQueueTask( HANDLE hQueue )
206 {
207     MESSAGEQUEUE *msgQ = GlobalLock( hQueue );
208
209     return (msgQ) ? msgQ->hTask : 0 ;
210 }
211
212 /***********************************************************************
213  *           MSG_TranslateMouseMsg
214  *
215  * Translate an mouse hardware event into a real mouse message.
216  * Return value indicates whether the translated message must be passed
217  * to the user.
218  * Actions performed:
219  * - Find the window for this message.
220  * - Translate button-down messages in double-clicks.
221  * - Send the WM_NCHITTEST message to find where the cursor is.
222  * - Activate the window if needed.
223  * - Translate the message into a non-client message, or translate
224  *   the coordinates to client coordinates.
225  * - Send the WM_SETCURSOR message.
226  */
227 static BOOL MSG_TranslateMouseMsg( MSG *msg, BOOL remove )
228 {
229     BOOL eatMsg = FALSE;
230     INT hittest;
231     static DWORD lastClickTime = 0;
232     static WORD  lastClickMsg = 0;
233     static POINT lastClickPos = { 0, 0 };
234     POINT pt = msg->pt;
235     MOUSEHOOKSTRUCT hook = { msg->pt, 0, HTCLIENT, 0 };
236
237     BOOL mouseClick = ((msg->message == WM_LBUTTONDOWN) ||
238                        (msg->message == WM_RBUTTONDOWN) ||
239                        (msg->message == WM_MBUTTONDOWN));
240
241       /* Find the window */
242
243     if (GetCapture())
244     {
245         msg->hwnd = GetCapture();
246         ScreenToClient( msg->hwnd, &pt );
247         msg->lParam = MAKELONG( pt.x, pt.y );
248         /* No need to further process the message */
249         hook.hwnd = msg->hwnd;
250         return !HOOK_CallHooks( WH_MOUSE, remove ? HC_ACTION : HC_NOREMOVE,
251                                 msg->message, (LPARAM)MAKE_SEGPTR(&hook));
252     }
253    
254     if ((hittest = WINPOS_WindowFromPoint( msg->pt, &msg->hwnd )) != HTERROR)
255     {
256
257         /* Send the WM_PARENTNOTIFY message */
258
259         if (mouseClick) WIN_SendParentNotify( msg->hwnd, msg->message, 0,
260                                             MAKELONG( msg->pt.x, msg->pt.y ) );
261
262         /* Activate the window if needed */
263
264         if (mouseClick)
265         {
266             HWND hwndTop = WIN_GetTopParent( msg->hwnd );
267             if (hwndTop != GetActiveWindow())
268             {
269                 LONG ret = SendMessage( msg->hwnd, WM_MOUSEACTIVATE,
270                                         (WPARAM)hwndTop,
271                                         MAKELONG( hittest, msg->message ) );
272                 if ((ret == MA_ACTIVATEANDEAT) || (ret == MA_NOACTIVATEANDEAT))
273                     eatMsg = TRUE;
274                 if ((ret == MA_ACTIVATE) || (ret == MA_ACTIVATEANDEAT))
275                 {
276                     SetWindowPos( hwndTop, HWND_TOP, 0, 0, 0, 0,
277                                  SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
278                     WINPOS_ChangeActiveWindow( hwndTop, TRUE );
279                 }
280             }
281         }
282     }
283
284       /* Send the WM_SETCURSOR message */
285
286     SendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd,
287                  MAKELONG( hittest, msg->message ));
288     if (eatMsg) return FALSE;
289
290       /* Check for double-click */
291
292     if (mouseClick)
293     {
294         BOOL dbl_click = FALSE;
295
296         if ((msg->message == lastClickMsg) &&
297             (msg->time - lastClickTime < doubleClickSpeed) &&
298             (abs(msg->pt.x - lastClickPos.x) < SYSMETRICS_CXDOUBLECLK/2) &&
299             (abs(msg->pt.y - lastClickPos.y) < SYSMETRICS_CYDOUBLECLK/2))
300             dbl_click = TRUE;
301
302         if (dbl_click && (hittest == HTCLIENT))
303         {
304             /* Check whether window wants the double click message. */
305             WND * wndPtr = WIN_FindWndPtr( msg->hwnd );
306             if (!wndPtr || !(WIN_CLASS_STYLE(wndPtr) & CS_DBLCLKS))
307                 dbl_click = FALSE;
308         }
309
310         if (dbl_click) switch(msg->message)
311         {
312             case WM_LBUTTONDOWN: msg->message = WM_LBUTTONDBLCLK; break;
313             case WM_RBUTTONDOWN: msg->message = WM_RBUTTONDBLCLK; break;
314             case WM_MBUTTONDOWN: msg->message = WM_MBUTTONDBLCLK; break;
315         }
316
317         if (remove)
318         {
319             lastClickTime = msg->time;
320             lastClickMsg  = msg->message;
321             lastClickPos  = msg->pt;
322         }
323     }
324
325       /* Build the translated message */
326
327     if (hittest == HTCLIENT)
328         ScreenToClient( msg->hwnd, &pt );
329     else
330     {
331         msg->wParam = hittest;
332         msg->message += WM_NCLBUTTONDOWN - WM_LBUTTONDOWN;
333     }
334     msg->lParam = MAKELONG( pt.x, pt.y );
335     
336     hook.hwnd = msg->hwnd;
337     hook.wHitTestCode = hittest;
338     return !HOOK_CallHooks( WH_MOUSE, remove ? HC_ACTION : HC_NOREMOVE,
339                             msg->message, (LPARAM)MAKE_SEGPTR(&hook));
340 }
341
342
343 /***********************************************************************
344  *           MSG_TranslateKeyboardMsg
345  *
346  * Translate an keyboard hardware event into a real message.
347  * Return value indicates whether the translated message must be passed
348  * to the user.
349  */
350 static BOOL MSG_TranslateKeyboardMsg( MSG *msg, BOOL remove )
351 {
352       /* Should check Ctrl-Esc and PrintScreen here */
353
354     msg->hwnd = GetFocus();
355     if (!msg->hwnd)
356     {
357           /* Send the message to the active window instead,  */
358           /* translating messages to their WM_SYS equivalent */
359         msg->hwnd = GetActiveWindow();
360         msg->message += WM_SYSKEYDOWN - WM_KEYDOWN;
361     }
362     return !HOOK_CallHooks( WH_KEYBOARD, remove ? HC_ACTION : HC_NOREMOVE,
363                             msg->wParam, msg->lParam );
364 }
365
366
367 /***********************************************************************
368  *           MSG_PeekHardwareMsg
369  *
370  * Peek for a hardware message matching the hwnd and message filters.
371  */
372 static BOOL MSG_PeekHardwareMsg( MSG *msg, HWND hwnd, WORD first, WORD last,
373                                  BOOL remove )
374 {
375     int i, pos = sysMsgQueue->nextMessage;
376
377     for (i = 0; i < sysMsgQueue->msgCount; i++, pos++)
378     {
379         if (pos >= sysMsgQueue->queueSize) pos = 0;
380         *msg = sysMsgQueue->messages[pos].msg;
381
382           /* Translate message */
383
384         if ((msg->message >= WM_MOUSEFIRST) && (msg->message <= WM_MOUSELAST))
385         {
386             if (!MSG_TranslateMouseMsg( msg, remove )) continue;
387         }
388         else if ((msg->message >= WM_KEYFIRST) && (msg->message <= WM_KEYLAST))
389         {
390             if (!MSG_TranslateKeyboardMsg( msg, remove )) continue;
391         }
392         else  /* Non-standard hardware event */
393         {
394             HARDWAREHOOKSTRUCT hook = { msg->hwnd, msg->message,
395                                         msg->wParam, msg->lParam };
396             if (HOOK_CallHooks( WH_HARDWARE, remove ? HC_ACTION : HC_NOREMOVE,
397                                 0, (LPARAM)MAKE_SEGPTR(&hook) )) continue;
398         }
399
400           /* Check message against filters */
401
402         if (hwnd && (msg->hwnd != hwnd)) continue;
403         if ((first || last) && 
404             ((msg->message < first) || (msg->message > last))) continue;
405         if ((msg->hwnd != GetDesktopWindow()) && 
406             (GetWindowTask(msg->hwnd) != GetCurrentTask()))
407             continue;  /* Not for this task */
408         if (remove)
409         {
410             MSG tmpMsg = *msg; /* FIXME */
411             HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION,
412                             0, (LPARAM)MAKE_SEGPTR(&tmpMsg) );
413             MSG_RemoveMsg( sysMsgQueue, pos );
414         }
415         return TRUE;
416     }
417     return FALSE;
418 }
419
420
421 /**********************************************************************
422  *              SetDoubleClickTime  (USER.20)
423  */
424 void SetDoubleClickTime( WORD interval )
425 {
426     if (interval == 0)
427         doubleClickSpeed = 500;
428     else
429         doubleClickSpeed = interval;
430 }               
431
432
433 /**********************************************************************
434  *              GetDoubleClickTime  (USER.21)
435  */
436 WORD GetDoubleClickTime()
437 {
438         return (WORD)doubleClickSpeed;
439 }               
440
441
442 /***********************************************************************
443  *           MSG_IncPaintCount
444  */
445 void MSG_IncPaintCount( HANDLE hQueue )
446 {
447     MESSAGEQUEUE *queue;
448
449     if (!(queue = (MESSAGEQUEUE *)GlobalLock( hQueue ))) return;
450     queue->wPaintCount++;
451     queue->status |= QS_PAINT;
452     queue->tempStatus |= QS_PAINT;    
453 }
454
455
456 /***********************************************************************
457  *           MSG_DecPaintCount
458  */
459 void MSG_DecPaintCount( HANDLE hQueue )
460 {
461     MESSAGEQUEUE *queue;
462
463     if (!(queue = (MESSAGEQUEUE *)GlobalLock( hQueue ))) return;
464     queue->wPaintCount--;
465     if (!queue->wPaintCount) queue->status &= ~QS_PAINT;
466 }
467
468
469 /***********************************************************************
470  *           MSG_IncTimerCount
471  */
472 void MSG_IncTimerCount( HANDLE hQueue )
473 {
474     MESSAGEQUEUE *queue;
475
476     if (!(queue = (MESSAGEQUEUE *)GlobalLock( hQueue ))) return;
477     queue->wTimerCount++;
478     queue->status |= QS_TIMER;
479     queue->tempStatus |= QS_TIMER;
480 }
481
482
483 /***********************************************************************
484  *           MSG_DecTimerCount
485  */
486 void MSG_DecTimerCount( HANDLE hQueue )
487 {
488     MESSAGEQUEUE *queue;
489
490     if (!(queue = (MESSAGEQUEUE *)GlobalLock( hQueue ))) return;
491     queue->wTimerCount--;
492     if (!queue->wTimerCount) queue->status &= ~QS_TIMER;
493 }
494
495
496 /***********************************************************************
497  *           hardware_event
498  *
499  * Add an event to the system message queue.
500  * Note: the position is relative to the desktop window.
501  */
502 void hardware_event( WORD message, WORD wParam, LONG lParam,
503                      int xPos, int yPos, DWORD time, DWORD extraInfo )
504 {
505     MSG *msg;
506     int pos;
507   
508     if (!sysMsgQueue) return;
509     pos = sysMsgQueue->nextFreeMessage;
510
511       /* Merge with previous event if possible */
512
513     if ((message == WM_MOUSEMOVE) && sysMsgQueue->msgCount)
514     {
515         if (pos > 0) pos--;
516         else pos = sysMsgQueue->queueSize - 1;
517         msg = &sysMsgQueue->messages[pos].msg;
518         if ((msg->message == message) && (msg->wParam == wParam))
519             sysMsgQueue->msgCount--;  /* Merge events */
520         else
521             pos = sysMsgQueue->nextFreeMessage;  /* Don't merge */
522     }
523
524       /* Check if queue is full */
525
526     if ((pos == sysMsgQueue->nextMessage) && sysMsgQueue->msgCount)
527     {
528         /* Queue is full, beep (but not on every mouse motion...) */
529         if (message != WM_MOUSEMOVE) MessageBeep(0);
530         return;
531     }
532
533       /* Store message */
534
535     msg = &sysMsgQueue->messages[pos].msg;
536     msg->hwnd    = 0;
537     msg->message = message;
538     msg->wParam  = wParam;
539     msg->lParam  = lParam;
540     msg->time    = time;
541     msg->pt.x    = xPos & 0xffff;
542     msg->pt.y    = yPos & 0xffff;
543     sysMsgQueue->messages[pos].extraInfo = extraInfo;
544     if (pos < sysMsgQueue->queueSize - 1) pos++;
545     else pos = 0;
546     sysMsgQueue->nextFreeMessage = pos;
547     sysMsgQueue->msgCount++;
548 }
549
550                     
551 /***********************************************************************
552  *           MSG_GetHardwareMessage
553  *
554  * Like GetMessage(), but only return mouse and keyboard events.
555  * Used internally for window moving and resizing. Mouse messages
556  * are not translated.
557  * Warning: msg->hwnd is always 0.
558  */
559 BOOL MSG_GetHardwareMessage( LPMSG msg )
560 {
561     int pos;
562     XEvent event;
563
564     while(1)
565     {    
566         if ((pos = MSG_FindMsg( sysMsgQueue, 0, 0, 0 )) != -1)
567         {
568             *msg = sysMsgQueue->messages[pos].msg;
569             MSG_RemoveMsg( sysMsgQueue, pos );
570             break;
571         }
572         XNextEvent( display, &event );
573         EVENT_ProcessEvent( &event );
574     }
575     return TRUE;
576 }
577
578
579 /***********************************************************************
580  *           SetMessageQueue  (USER.266)
581  */
582 BOOL SetMessageQueue( int size )
583 {
584     HANDLE hQueue, hNewQueue;
585     MESSAGEQUEUE *queuePtr;
586
587     if ((size > MAX_QUEUE_SIZE) || (size <= 0)) return TRUE;
588
589     if( !(hNewQueue = MSG_CreateMsgQueue( size ))) 
590     {
591         dprintf_msg(stddeb,"SetMessageQueue: failed!\n");
592         return FALSE;
593     }
594
595     /* Free the old message queue */
596     if ((hQueue = GetTaskQueue(0)) != 0) MSG_DeleteMsgQueue( hQueue );
597
598     /* Link new queue into list */
599     queuePtr = (MESSAGEQUEUE *)GlobalLock( hNewQueue );
600     queuePtr->hTask = GetCurrentTask();
601     queuePtr->next  = hFirstQueue;
602     hFirstQueue = hNewQueue;
603
604     SetTaskQueue( 0, hNewQueue );
605     return TRUE;
606 }
607
608
609 /***********************************************************************
610  *           GetWindowTask  (USER.224)
611  */
612 HTASK GetWindowTask( HWND hwnd )
613 {
614     WND *wndPtr = WIN_FindWndPtr( hwnd );
615     MESSAGEQUEUE *queuePtr;
616
617     if (!wndPtr) return 0;
618     queuePtr = (MESSAGEQUEUE *)GlobalLock( wndPtr->hmemTaskQ );
619     if (!queuePtr) return 0;
620     return queuePtr->hTask;
621 }
622
623
624 /***********************************************************************
625  *           PostQuitMessage   (USER.6)
626  */
627 void PostQuitMessage( int exitCode )
628 {
629     MESSAGEQUEUE *queue;
630
631     if (!(queue = (MESSAGEQUEUE *)GlobalLock( GetTaskQueue(0) ))) return;
632     queue->wPostQMsg = TRUE;
633     queue->wExitCode = exitCode;
634 }
635
636
637 /***********************************************************************
638  *           GetQueueStatus   (USER.334)
639  */
640 DWORD GetQueueStatus( UINT flags )
641 {
642     MESSAGEQUEUE *queue;
643     DWORD ret;
644
645     if (!(queue = (MESSAGEQUEUE *)GlobalLock( GetTaskQueue(0) ))) return 0;
646     ret = MAKELONG( queue->tempStatus, queue->status );
647     queue->tempStatus = 0;
648     return ret & MAKELONG( flags, flags );
649 }
650
651
652 /***********************************************************************
653  *           GetInputState   (USER.335)
654  */
655 BOOL GetInputState()
656 {
657     MESSAGEQUEUE *queue;
658
659     if (!(queue = (MESSAGEQUEUE *)GlobalLock( GetTaskQueue(0) ))) return FALSE;
660     return queue->status & (QS_KEY | QS_MOUSEBUTTON);
661 }
662
663
664 /***********************************************************************
665  *           MSG_Synchronize
666  *
667  * Synchronize with the X server. Should not be used too often.
668  */
669 void MSG_Synchronize()
670 {
671     XEvent event;
672
673     XSync( display, False );
674     while (XPending( display ))
675     {
676         XNextEvent( display, &event );
677         EVENT_ProcessEvent( &event );
678     }    
679 }
680
681
682 /***********************************************************************
683  *           MSG_WaitXEvent
684  *
685  * Wait for an X event, but at most maxWait milliseconds (-1 for no timeout).
686  * Return TRUE if an event is pending, FALSE on timeout or error
687  * (for instance lost connection with the server).
688  */
689 BOOL MSG_WaitXEvent( LONG maxWait )
690 {
691     fd_set read_set;
692     struct timeval timeout;
693     XEvent event;
694     int fd = ConnectionNumber(display);
695
696     if (!XPending(display) && (maxWait != -1))
697     {
698         FD_ZERO( &read_set );
699         FD_SET( fd, &read_set );
700
701         timeout.tv_usec = (maxWait % 1000) * 1000;
702         timeout.tv_sec = maxWait / 1000;
703
704 #ifdef CONFIG_IPC
705         sigsetjmp(env_wait_x, 1);
706         stop_wait_op= CONT;
707             
708         if (DDE_GetRemoteMessage()) {
709             while(DDE_GetRemoteMessage())
710                 ;
711             return TRUE;
712         }
713         stop_wait_op= STOP_WAIT_X;
714         /* The code up to the next "stop_wait_op= CONT" must be reentrant  */
715         if (select( fd+1, &read_set, NULL, NULL, &timeout ) != 1 &&
716             !XPending(display)) {
717             stop_wait_op= CONT;
718             return FALSE;
719         } else {
720             stop_wait_op= CONT;
721         }
722 #else  /* CONFIG_IPC */
723         if (select( fd+1, &read_set, NULL, NULL, &timeout ) != 1)
724             return FALSE;  /* Timeout or error */
725 #endif  /* CONFIG_IPC */
726
727     }
728
729     /* Process the event (and possibly others that occurred in the meantime) */
730     do
731     {
732
733 #ifdef CONFIG_IPC
734         if (DDE_GetRemoteMessage())
735         {
736             while(DDE_GetRemoteMessage()) ;
737             return TRUE;
738         }
739 #endif  /* CONFIG_IPC */
740
741         XNextEvent( display, &event );
742         EVENT_ProcessEvent( &event );
743     }
744     while (XPending( display ));
745     return TRUE;
746 }
747
748
749 /***********************************************************************
750  *           MSG_PeekMessage
751  */
752 static BOOL MSG_PeekMessage( LPMSG msg, HWND hwnd, WORD first, WORD last,
753                              WORD flags, BOOL peek )
754 {
755     int pos, mask;
756     MESSAGEQUEUE *msgQueue;
757     LONG nextExp;  /* Next timer expiration time */
758
759 #ifdef CONFIG_IPC
760     DDE_TestDDE(hwnd);  /* do we have dde handling in the window ?*/
761     DDE_GetRemoteMessage();
762 #endif  /* CONFIG_IPC */
763     
764     if (first || last)
765     {
766         mask = QS_POSTMESSAGE;  /* Always selectioned */
767         if ((first <= WM_KEYLAST) && (last >= WM_KEYFIRST)) mask |= QS_KEY;
768         if ((first <= WM_MOUSELAST) && (last >= WM_MOUSEFIRST)) mask |= QS_MOUSE;
769         if ((first <= WM_TIMER) && (last >= WM_TIMER)) mask |= QS_TIMER;
770         if ((first <= WM_SYSTIMER) && (last >= WM_SYSTIMER)) mask |= QS_TIMER;
771         if ((first <= WM_PAINT) && (last >= WM_PAINT)) mask |= QS_PAINT;
772     }
773     else mask = QS_MOUSE | QS_KEY | QS_POSTMESSAGE | QS_TIMER | QS_PAINT;
774
775     while(1)
776     {    
777         msgQueue = (MESSAGEQUEUE *)GlobalLock( GetTaskQueue(0) );
778         if (!msgQueue) return FALSE;
779
780           /* First handle a message put by SendMessage() */
781         if (msgQueue->status & QS_SENDMESSAGE)
782         {
783             if (!hwnd || (msgQueue->hWnd == hwnd))
784             {
785                 if ((!first && !last) || 
786                     ((msgQueue->msg >= first) && (msgQueue->msg <= last)))
787                 {
788                     msg->hwnd    = msgQueue->hWnd;
789                     msg->message = msgQueue->msg;
790                     msg->wParam  = msgQueue->wParam;
791                     msg->lParam  = msgQueue->lParam;
792                     if (flags & PM_REMOVE) msgQueue->status &= ~QS_SENDMESSAGE;
793                     break;
794                 }
795             }
796         }
797     
798           /* Now find a normal message */
799         pos = MSG_FindMsg( msgQueue, hwnd, first, last );
800         if (pos != -1)
801         {
802             QMSG *qmsg = &msgQueue->messages[pos];
803             *msg = qmsg->msg;
804             msgQueue->GetMessageTimeVal      = msg->time;
805             msgQueue->GetMessagePosVal       = *(DWORD *)&msg->pt;
806             msgQueue->GetMessageExtraInfoVal = qmsg->extraInfo;
807
808             if (flags & PM_REMOVE) MSG_RemoveMsg( msgQueue, pos );
809             break;
810         }
811
812           /* Now find a hardware event */
813         if (MSG_PeekHardwareMsg( msg, hwnd, first, last, flags & PM_REMOVE ))
814         {
815             /* Got one */
816             msgQueue->GetMessageTimeVal      = msg->time;
817             msgQueue->GetMessagePosVal       = *(DWORD *)&msg->pt;
818             msgQueue->GetMessageExtraInfoVal = 0;  /* Always 0 for now */
819             break;
820         }
821
822           /* Now handle a WM_QUIT message */
823         if (msgQueue->wPostQMsg)
824         {
825             msg->hwnd    = hwnd;
826             msg->message = WM_QUIT;
827             msg->wParam  = msgQueue->wExitCode;
828             msg->lParam  = 0;
829             break;
830         }
831
832           /* Now find a WM_PAINT message */
833         if ((msgQueue->status & QS_PAINT) && (mask & QS_PAINT))
834         {
835             msg->hwnd = WIN_FindWinToRepaint( hwnd );
836             msg->message = WM_PAINT;
837             msg->wParam = 0;
838             msg->lParam = 0;
839             if (msg->hwnd != 0) break;
840         }
841
842           /* Finally handle WM_TIMER messages */
843         if ((msgQueue->status & QS_TIMER) && (mask & QS_TIMER))
844         {
845             if (TIMER_CheckTimer( &nextExp, msg, hwnd, flags & PM_REMOVE ))
846                 break;  /* Got a timer msg */
847         }
848         else nextExp = -1;  /* No timeout needed */
849
850         Yield();
851
852           /* Wait until something happens */
853         if (peek)
854         {
855             if (!MSG_WaitXEvent( 0 )) return FALSE;  /* No pending event */
856         }
857         else  /* Wait for an event, then restart the loop */
858             MSG_WaitXEvent( nextExp );
859     }
860
861       /* We got a message */
862     if (peek) return TRUE;
863     else return (msg->message != WM_QUIT);
864 }
865
866
867 /***********************************************************************
868  *           MSG_InternalGetMessage
869  *
870  * GetMessage() function for internal use. Behave like GetMessage(),
871  * but also call message filters and optionally send WM_ENTERIDLE messages.
872  * 'hwnd' must be the handle of the dialog or menu window.
873  * 'code' is the message filter value (MSGF_??? codes).
874  */
875 BOOL MSG_InternalGetMessage( SEGPTR msg, HWND hwnd, HWND hwndOwner, short code,
876                              WORD flags, BOOL sendIdle ) 
877 {
878     for (;;)
879     {
880         if (sendIdle)
881         {
882             if (!MSG_PeekMessage( (MSG *)PTR_SEG_TO_LIN(msg),
883                                   0, 0, 0, flags, TRUE ))
884             {
885                   /* No message present -> send ENTERIDLE and wait */
886                 SendMessage( hwndOwner, WM_ENTERIDLE, code, (LPARAM)hwnd );
887                 MSG_PeekMessage( (MSG *)PTR_SEG_TO_LIN(msg),
888                                  0, 0, 0, flags, FALSE );
889             }
890         }
891         else  /* Always wait for a message */
892             MSG_PeekMessage( (MSG *)PTR_SEG_TO_LIN(msg),
893                              0, 0, 0, flags, FALSE );
894
895         if (!CallMsgFilter( msg, code ))
896             return (((MSG *)PTR_SEG_TO_LIN(msg))->message != WM_QUIT);
897
898           /* Message filtered -> remove it from the queue */
899           /* if it's still there. */
900         if (!(flags & PM_REMOVE))
901             MSG_PeekMessage( (MSG *)PTR_SEG_TO_LIN(msg),
902                              0, 0, 0, PM_REMOVE, TRUE );
903     }
904 }
905
906
907 /***********************************************************************
908  *           PeekMessage   (USER.109)
909  */
910 BOOL PeekMessage( LPMSG msg, HWND hwnd, WORD first, WORD last, WORD flags )
911 {
912     return MSG_PeekMessage( msg, hwnd, first, last, flags, TRUE );
913 }
914
915
916 /***********************************************************************
917  *           GetMessage   (USER.108)
918  */
919 BOOL GetMessage( SEGPTR msg, HWND hwnd, UINT first, UINT last ) 
920 {
921     MSG_PeekMessage( (MSG *)PTR_SEG_TO_LIN(msg),
922                      hwnd, first, last, PM_REMOVE, FALSE );
923     HOOK_CallHooks( WH_GETMESSAGE, HC_ACTION, 0, (LPARAM)msg );
924     return (((MSG *)PTR_SEG_TO_LIN(msg))->message != WM_QUIT);
925 }
926
927
928
929 /***********************************************************************
930  *           PostMessage   (USER.110)
931  */
932 BOOL PostMessage( HWND hwnd, WORD message, WORD wParam, LONG lParam )
933 {
934     MSG         msg;
935     WND         *wndPtr;
936
937     msg.hwnd    = hwnd;
938     msg.message = message;
939     msg.wParam  = wParam;
940     msg.lParam  = lParam;
941     msg.time    = GetTickCount();
942     msg.pt.x    = 0;
943     msg.pt.y    = 0;
944
945 #ifdef CONFIG_IPC
946     if (DDE_PostMessage(&msg))
947        return TRUE;
948 #endif  /* CONFIG_IPC */
949     
950     if (hwnd == HWND_BROADCAST) {
951       dprintf_msg(stddeb,"PostMessage // HWND_BROADCAST !\n");
952       hwnd = GetTopWindow(GetDesktopWindow());
953       while (hwnd) {
954         if (!(wndPtr = WIN_FindWndPtr(hwnd))) break;
955         if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION) {
956           dprintf_msg(stddeb,"BROADCAST Message to hWnd="NPFMT" m=%04X w=%04X l=%08lX !\n",
957                       hwnd, message, wParam, lParam);
958           PostMessage(hwnd, message, wParam, lParam);
959         }
960         hwnd = wndPtr->hwndNext;
961       }
962       dprintf_msg(stddeb,"PostMessage // End of HWND_BROADCAST !\n");
963       return TRUE;
964     }
965
966     wndPtr = WIN_FindWndPtr( hwnd );
967     if (!wndPtr || !wndPtr->hmemTaskQ) return FALSE;
968
969     return MSG_AddMsg( wndPtr->hmemTaskQ, &msg, 0 );
970 }
971
972 /***********************************************************************
973  *           PostAppMessage   (USER.116)
974  */
975 BOOL PostAppMessage( HTASK hTask, WORD message, WORD wParam, LONG lParam )
976 {
977     MSG         msg;
978
979     if (GetTaskQueue(hTask) == 0) return FALSE;
980     msg.hwnd    = 0;
981     msg.message = message;
982     msg.wParam  = wParam;
983     msg.lParam  = lParam;
984     msg.time    = GetTickCount();
985     msg.pt.x    = 0;
986     msg.pt.y    = 0;
987
988     return MSG_AddMsg( GetTaskQueue(hTask), &msg, 0 );
989 }
990
991
992 /***********************************************************************
993  *           SendMessage   (USER.111)
994  */
995 LRESULT SendMessage( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
996 {
997     WND * wndPtr;
998     LONG ret;
999     struct
1000     {
1001         LPARAM lParam;
1002         WPARAM wParam;
1003         UINT wMsg;
1004         HWND hWnd;
1005     } msgstruct = { lParam, wParam, msg, hwnd };
1006
1007 #ifdef CONFIG_IPC
1008     MSG DDE_msg = { hwnd, msg, wParam, lParam };
1009     if (DDE_SendMessage(&DDE_msg)) return TRUE;
1010 #endif  /* CONFIG_IPC */
1011
1012     if (hwnd == HWND_BROADCAST)
1013     {
1014         dprintf_msg(stddeb,"SendMessage // HWND_BROADCAST !\n");
1015         hwnd = GetTopWindow(GetDesktopWindow());
1016         while (hwnd)
1017         {
1018             if (!(wndPtr = WIN_FindWndPtr(hwnd))) break;
1019             if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION)
1020             {
1021                 dprintf_msg(stddeb,"BROADCAST Message to hWnd="NPFMT" m=%04X w=%04lX l=%08lX !\n",
1022                             hwnd, msg, (DWORD)wParam, lParam);
1023                  ret |= SendMessage( hwnd, msg, wParam, lParam );
1024             }
1025             hwnd = wndPtr->hwndNext;
1026         }
1027         dprintf_msg(stddeb,"SendMessage // End of HWND_BROADCAST !\n");
1028         return TRUE;
1029     }
1030
1031     EnterSpyMessage(SPY_SENDMESSAGE, hwnd, msg, wParam, lParam);
1032
1033     HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, 1, (LPARAM)MAKE_SEGPTR(&msgstruct) );
1034     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) 
1035     {
1036         ExitSpyMessage(SPY_RESULT_INVALIDHWND,hwnd,msg,0);
1037         return 0;
1038     }
1039     ret = CallWindowProc( wndPtr->lpfnWndProc, msgstruct.hWnd, msgstruct.wMsg,
1040                           msgstruct.wParam, msgstruct.lParam );
1041     ExitSpyMessage(SPY_RESULT_OK,hwnd,msg,ret);
1042     return ret;
1043 }
1044
1045
1046 /***********************************************************************
1047  *           WaitMessage    (USER.112)
1048  */
1049 void WaitMessage( void )
1050 {
1051     MSG msg;
1052     MESSAGEQUEUE *queue;
1053     LONG nextExp = -1;  /* Next timer expiration time */
1054
1055 #ifdef CONFIG_IPC
1056     DDE_GetRemoteMessage();
1057 #endif  /* CONFIG_IPC */
1058     
1059     if (!(queue = (MESSAGEQUEUE *)GlobalLock( GetTaskQueue(0) ))) return;
1060     if ((queue->wPostQMsg) || 
1061         (queue->status & (QS_SENDMESSAGE | QS_PAINT)) ||
1062         (queue->msgCount) || (sysMsgQueue->msgCount) )
1063         return;
1064     if ((queue->status & QS_TIMER) && 
1065         TIMER_CheckTimer( &nextExp, &msg, 0, FALSE))
1066         return;
1067     /* FIXME: (dde) must check DDE & X-events simultaneously */
1068     MSG_WaitXEvent( nextExp );
1069 }
1070
1071
1072 /***********************************************************************
1073  *           TranslateMessage   (USER.113)
1074  */
1075 BOOL TranslateMessage( LPMSG msg )
1076 {
1077     int message = msg->message;
1078     
1079     if ((message == WM_KEYDOWN) || (message == WM_KEYUP) ||
1080         (message == WM_SYSKEYDOWN) || (message == WM_SYSKEYUP))
1081     {
1082         dprintf_msg(stddeb, "Translating key message\n" );
1083         return TRUE;
1084     }
1085     return FALSE;
1086 }
1087
1088
1089 /***********************************************************************
1090  *           DispatchMessage   (USER.114)
1091  */
1092 LONG DispatchMessage( const MSG* msg )
1093 {
1094     WND * wndPtr;
1095     LONG retval;
1096     int painting;
1097     
1098     EnterSpyMessage( SPY_DISPATCHMESSAGE, msg->hwnd, msg->message,
1099                      msg->wParam, msg->lParam );
1100
1101       /* Process timer messages */
1102     if ((msg->message == WM_TIMER) || (msg->message == WM_SYSTIMER))
1103     {
1104         if (msg->lParam)
1105         {
1106 #ifndef WINELIB32
1107             HINSTANCE ds = msg->hwnd ? WIN_GetWindowInstance( msg->hwnd )
1108                                      : (HINSTANCE)CURRENT_DS;
1109 #endif
1110 /*            HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */
1111             return CallWndProc( (WNDPROC)msg->lParam, ds, msg->hwnd,
1112                                 msg->message, msg->wParam, GetTickCount() );
1113         }
1114     }
1115
1116     if (!msg->hwnd) return 0;
1117     if (!(wndPtr = WIN_FindWndPtr( msg->hwnd ))) return 0;
1118     if (!wndPtr->lpfnWndProc) return 0;
1119     painting = (msg->message == WM_PAINT);
1120     if (painting) wndPtr->flags |= WIN_NEEDS_BEGINPAINT;
1121 /*    HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */
1122     retval = CallWindowProc( wndPtr->lpfnWndProc, msg->hwnd, msg->message,
1123                              msg->wParam, msg->lParam );
1124     if (painting && (wndPtr = WIN_FindWndPtr( msg->hwnd )) &&
1125         (wndPtr->flags & WIN_NEEDS_BEGINPAINT) && wndPtr->hrgnUpdate)
1126     {
1127         fprintf(stderr, "BeginPaint not called on WM_PAINT for hwnd "NPFMT"!\n", 
1128                 msg->hwnd);
1129         wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
1130         /* Validate the update region to avoid infinite WM_PAINT loop */
1131         ValidateRect( msg->hwnd, NULL );
1132     }
1133     return retval;
1134 }
1135
1136
1137 /***********************************************************************
1138  *           GetMessagePos   (USER.119)
1139  */
1140 DWORD GetMessagePos(void)
1141 {
1142     MESSAGEQUEUE *queue;
1143
1144     if (!(queue = (MESSAGEQUEUE *)GlobalLock( GetTaskQueue(0) ))) return 0;
1145     return queue->GetMessagePosVal;
1146 }
1147
1148
1149 /***********************************************************************
1150  *           GetMessageTime   (USER.120)
1151  */
1152 LONG GetMessageTime(void)
1153 {
1154     MESSAGEQUEUE *queue;
1155
1156     if (!(queue = (MESSAGEQUEUE *)GlobalLock( GetTaskQueue(0) ))) return 0;
1157     return queue->GetMessageTimeVal;
1158 }
1159
1160
1161 /***********************************************************************
1162  *           GetMessageExtraInfo   (USER.288)
1163  */
1164 LONG GetMessageExtraInfo(void)
1165 {
1166     MESSAGEQUEUE *queue;
1167
1168     if (!(queue = (MESSAGEQUEUE *)GlobalLock( GetTaskQueue(0) ))) return 0;
1169     return queue->GetMessageExtraInfoVal;
1170 }
1171
1172
1173 /***********************************************************************
1174  *           RegisterWindowMessage   (USER.118)
1175  */
1176 WORD RegisterWindowMessage( SEGPTR str )
1177 {
1178     dprintf_msg(stddeb, "RegisterWindowMessage: '"SPFMT"'\n", str );
1179     return GlobalAddAtom( str );
1180 }
1181
1182
1183 /***********************************************************************
1184  *           GetTickCount    (USER.13) (KERNEL32.299)
1185  */
1186 DWORD GetTickCount(void)
1187 {
1188     struct timeval t;
1189     gettimeofday( &t, NULL );
1190     return ((t.tv_sec * 1000) + (t.tv_usec / 1000)) - MSG_WineStartTicks;
1191 }
1192
1193 /***********************************************************************
1194  *           GetCurrentTime  (effectively identical to GetTickCount)
1195  */
1196 DWORD GetCurrentTime(void)
1197 {
1198   return GetTickCount();
1199 }
1200
1201 /***********************************************************************
1202  *                      InSendMessage   (USER.192
1203  *
1204  * According to the book, this should return true iff the current message
1205  * was send from another application. In that case, the application should
1206  * invoke ReplyMessage before calling message relevant API.
1207  * Currently, Wine will always return FALSE, as there is no other app.
1208  */
1209 BOOL InSendMessage()
1210 {
1211         return FALSE;
1212 }