Release 940201
[wine] / windows / message.c
1 /*
2  * Message queues related functions
3  *
4  * Copyright 1993 Alexandre Julliard
5  */
6
7 /*
8  * This code assumes that there is only one Windows task (hence
9  * one message queue).
10  */
11
12 static char Copyright[] = "Copyright  Alexandre Julliard, 1993";
13
14 #include <stdlib.h>
15 #include <sys/time.h>
16 #include <sys/types.h>
17
18 #include "message.h"
19 #include "win.h"
20 #include "wineopts.h"
21 #include "sysmetrics.h"
22
23 #define MAX_QUEUE_SIZE   120  /* Max. size of a message queue */
24
25 extern BOOL TIMER_CheckTimer( DWORD *next );      /* timer.c */
26 extern void EVENT_ProcessEvent( XEvent *event );  /* event.c */
27 extern void WINPOS_ChangeActiveWindow( HWND hwnd, BOOL mouseMsg ); /*winpos.c*/
28   
29 extern Display * display;
30
31   /* System message queue (for hardware events) */
32 static HANDLE hmemSysMsgQueue = 0;
33 static MESSAGEQUEUE * sysMsgQueue = NULL;
34
35   /* Application message queue (should be a list, one queue per task) */
36 static HANDLE hmemAppMsgQueue = 0;
37 static MESSAGEQUEUE * appMsgQueue = NULL;
38
39   /* Double-click time */
40 static int doubleClickSpeed = 452;
41
42
43 /***********************************************************************
44  *           MSG_CreateMsgQueue
45  *
46  * Create a message queue.
47  */
48 static HANDLE MSG_CreateMsgQueue( int size )
49 {
50     HANDLE hQueue;
51     MESSAGEQUEUE * msgQueue;
52     int queueSize;
53
54     queueSize = sizeof(MESSAGEQUEUE) + size * sizeof(QMSG);
55     if (!(hQueue = GlobalAlloc( GMEM_FIXED, queueSize ))) return 0;
56     msgQueue = (MESSAGEQUEUE *) GlobalLock( hQueue );
57     msgQueue->next = 0;
58     msgQueue->hTask = 0;
59     msgQueue->msgSize = sizeof(QMSG);
60     msgQueue->msgCount = 0;
61     msgQueue->nextMessage = 0;
62     msgQueue->nextFreeMessage = 0;
63     msgQueue->queueSize = size;
64     msgQueue->GetMessageTimeVal = 0;
65     msgQueue->GetMessagePosVal = 0;
66     msgQueue->GetMessageExtraInfoVal = 0;
67     msgQueue->lParam = 0;
68     msgQueue->wParam = 0;
69     msgQueue->msg = 0;
70     msgQueue->hWnd = 0;
71     msgQueue->wPostQMsg = 0;
72     msgQueue->wExitCode = 0;
73     msgQueue->InSendMessageHandle = 0;
74     msgQueue->wPaintCount = 0;
75     msgQueue->wTimerCount = 0;
76     msgQueue->tempStatus = 0;
77     msgQueue->status = 0;
78     GlobalUnlock( hQueue );
79     return hQueue;
80 }
81
82
83 /***********************************************************************
84  *           MSG_CreateSysMsgQueue
85  *
86  * Create the system message queue, and set the double-click speed.
87  * Must be called only once.
88  */
89 BOOL MSG_CreateSysMsgQueue( int size )
90 {
91     if (size > MAX_QUEUE_SIZE) size = MAX_QUEUE_SIZE;
92     else if (size <= 0) size = 1;
93     if (!(hmemSysMsgQueue = MSG_CreateMsgQueue( size ))) return FALSE;
94     sysMsgQueue = (MESSAGEQUEUE *) GlobalLock( hmemSysMsgQueue );
95     doubleClickSpeed = GetProfileInt( "windows", "DoubleClickSpeed", 452 );
96     return TRUE;
97 }
98
99
100 /***********************************************************************
101  *           MSG_AddMsg
102  *
103  * Add a message to the queue. Return FALSE if queue is full.
104  */
105 static int MSG_AddMsg( MESSAGEQUEUE * msgQueue, MSG * msg, DWORD extraInfo )
106 {
107     int pos;
108   
109     SpyMessage(msg->hwnd, msg->message, msg->wParam, msg->lParam);
110     
111     if (!msgQueue) return FALSE;
112     pos = msgQueue->nextFreeMessage;
113
114       /* Check if queue is full */
115     if ((pos == msgQueue->nextMessage) && (msgQueue->msgCount > 0))
116         return FALSE;
117
118       /* Store message */
119     msgQueue->messages[pos].msg = *msg;
120     msgQueue->messages[pos].extraInfo = extraInfo;
121     if (pos < msgQueue->queueSize-1) pos++;
122     else pos = 0;
123     msgQueue->nextFreeMessage = pos;
124     msgQueue->msgCount++;
125     msgQueue->status |= QS_POSTMESSAGE;
126     msgQueue->tempStatus |= QS_POSTMESSAGE;
127     return TRUE;
128 }
129
130
131 /***********************************************************************
132  *           MSG_FindMsg
133  *
134  * Find a message matching the given parameters. Return -1 if none available.
135  */
136 static int MSG_FindMsg(MESSAGEQUEUE * msgQueue, HWND hwnd, int first, int last)
137 {
138     int i, pos = msgQueue->nextMessage;
139
140     if (!msgQueue->msgCount) return -1;
141     if (!hwnd && !first && !last) return pos;
142         
143     for (i = 0; i < msgQueue->msgCount; i++)
144     {
145         MSG * msg = &msgQueue->messages[pos].msg;
146
147         if (!hwnd || (msg->hwnd == hwnd))
148         {
149             if (!first && !last) return pos;
150             if ((msg->message >= first) && (msg->message <= last)) return pos;
151         }
152         if (pos < msgQueue->queueSize-1) pos++;
153         else pos = 0;
154     }
155     return -1;
156 }
157
158
159 /***********************************************************************
160  *           MSG_RemoveMsg
161  *
162  * Remove a message from the queue (pos must be a valid position).
163  */
164 static void MSG_RemoveMsg( MESSAGEQUEUE * msgQueue, int pos )
165 {
166     QMSG * qmsg;
167     
168     if (!msgQueue) return;
169     qmsg = &msgQueue->messages[pos];
170
171     if (pos >= msgQueue->nextMessage)
172     {
173         int count = pos - msgQueue->nextMessage;
174         if (count) memmove( &msgQueue->messages[msgQueue->nextMessage+1],
175                             &msgQueue->messages[msgQueue->nextMessage],
176                             count * sizeof(QMSG) );
177         msgQueue->nextMessage++;
178         if (msgQueue->nextMessage >= msgQueue->queueSize)
179             msgQueue->nextMessage = 0;
180     }
181     else
182     {
183         int count = msgQueue->nextFreeMessage - pos;
184         if (count) memmove( &msgQueue->messages[pos],
185                             &msgQueue->messages[pos+1], count * sizeof(QMSG) );
186         if (msgQueue->nextFreeMessage) msgQueue->nextFreeMessage--;
187         else msgQueue->nextFreeMessage = msgQueue->queueSize-1;
188     }
189     msgQueue->msgCount--;
190     if (!msgQueue->msgCount) msgQueue->status &= ~QS_POSTMESSAGE;
191     msgQueue->tempStatus = 0;
192 }
193
194
195 /***********************************************************************
196  *           MSG_TranslateMouseMsg
197  *
198  * Translate an mouse hardware event into a real mouse message.
199  * Return value indicates whether the translated message must be passed
200  * to the user.
201  * Actions performed:
202  * - Translate button-down messages in double-clicks.
203  * - Send the WM_NCHITTEST message to find where the cursor is.
204  * - Activate the window if needed.
205  * - Translate the message into a non-client message, or translate
206  *   the coordinates to client coordinates.
207  * - Send the WM_SETCURSOR message.
208  */
209 static BOOL MSG_TranslateMouseMsg( MSG *msg )
210 {
211     BOOL eatMsg = FALSE;
212     static DWORD lastClickTime = 0;
213     static WORD  lastClickMsg = 0;
214     static POINT lastClickPos = { 0, 0 };
215
216     BOOL mouseClick = ((msg->message == WM_LBUTTONDOWN) ||
217                        (msg->message == WM_RBUTTONDOWN) ||
218                        (msg->message == WM_MBUTTONDOWN));
219
220       /* Send the WM_NCHITTEST message */
221
222     LONG hittest_result = SendMessage( msg->hwnd, WM_NCHITTEST, 0,
223                                        MAKELONG( msg->pt.x, msg->pt.y ) );
224
225       /* Activate the window if needed */
226
227     if (mouseClick)
228     {
229         HWND parent, hwndTop = msg->hwnd;       
230         while ((parent = GetParent(hwndTop)) != 0) hwndTop = parent;
231         if (hwndTop != GetActiveWindow())
232         {
233             LONG ret = SendMessage( msg->hwnd, WM_MOUSEACTIVATE, hwndTop,
234                                     MAKELONG( hittest_result, msg->message ) );
235             if ((ret == MA_ACTIVATEANDEAT) || (ret == MA_NOACTIVATEANDEAT))
236                 eatMsg = TRUE;
237             if ((ret == MA_ACTIVATE) || (ret == MA_ACTIVATEANDEAT))
238             {
239                 SetWindowPos( hwndTop, HWND_TOP, 0, 0, 0, 0,
240                               SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
241                 WINPOS_ChangeActiveWindow( hwndTop, TRUE );
242             }
243         }
244     }
245
246       /* Send the WM_SETCURSOR message */
247
248     SendMessage( msg->hwnd, WM_SETCURSOR, msg->hwnd,
249                  MAKELONG( hittest_result, msg->message ));
250     if (eatMsg) return FALSE;
251
252       /* Check for double-click */
253
254     if (mouseClick)
255     {
256         BOOL dbl_click = FALSE;
257
258         if ((msg->message == lastClickMsg) &&
259             (msg->time - lastClickTime < doubleClickSpeed) &&
260             (abs(msg->pt.x - lastClickPos.x) < SYSMETRICS_CXDOUBLECLK/2) &&
261             (abs(msg->pt.y - lastClickPos.y) < SYSMETRICS_CYDOUBLECLK/2))
262             dbl_click = TRUE;
263
264         if (dbl_click && (hittest_result == HTCLIENT))
265         {
266             /* Check whether window wants the double click message. */
267             WND * wndPtr = WIN_FindWndPtr( msg->hwnd );
268             if (!wndPtr || !(wndPtr->flags & WIN_DOUBLE_CLICKS))
269                 dbl_click = FALSE;
270         }
271
272         if (dbl_click) switch(msg->message)
273         {
274             case WM_LBUTTONDOWN: msg->message = WM_LBUTTONDBLCLK; break;
275             case WM_RBUTTONDOWN: msg->message = WM_RBUTTONDBLCLK; break;
276             case WM_MBUTTONDOWN: msg->message = WM_MBUTTONDBLCLK; break;
277         }
278
279         lastClickTime = msg->time;
280         lastClickMsg  = msg->message;
281         lastClickPos  = msg->pt;
282     }
283
284       /* Build the translated message */
285
286     msg->lParam = MAKELONG( msg->pt.x, msg->pt.y );
287     if (hittest_result == HTCLIENT)
288     {
289         ScreenToClient( msg->hwnd, (LPPOINT)&msg->lParam );
290     }
291     else
292     {
293         msg->wParam = hittest_result;
294         msg->message += WM_NCLBUTTONDOWN - WM_LBUTTONDOWN;
295     }
296     return TRUE;
297 }
298
299
300 /**********************************************************************
301  *              SetDoubleClickTime  (USER.20)
302  */
303 void SetDoubleClickTime( WORD interval )
304 {
305     if (interval == 0)
306         doubleClickSpeed = 500;
307     else
308         doubleClickSpeed = interval;
309 }               
310
311
312 /**********************************************************************
313  *              GetDoubleClickTime  (USER.21)
314  */
315 WORD GetDoubleClickTime()
316 {
317         return (WORD)doubleClickSpeed;
318 }               
319
320
321 /***********************************************************************
322  *           MSG_IncPaintCount
323  */
324 void MSG_IncPaintCount( HANDLE hQueue )
325 {
326     if (hQueue != hmemAppMsgQueue) return;
327     appMsgQueue->wPaintCount++;
328     appMsgQueue->status |= QS_PAINT;
329     appMsgQueue->tempStatus |= QS_PAINT;    
330 }
331
332
333 /***********************************************************************
334  *           MSG_DecPaintCount
335  */
336 void MSG_DecPaintCount( HANDLE hQueue )
337 {
338     if (hQueue != hmemAppMsgQueue) return;
339     appMsgQueue->wPaintCount--;
340     if (!appMsgQueue->wPaintCount) appMsgQueue->status &= ~QS_PAINT;
341 }
342
343
344 /***********************************************************************
345  *           MSG_IncTimerCount
346  */
347 void MSG_IncTimerCount( HANDLE hQueue )
348 {
349     if (hQueue != hmemAppMsgQueue) return;
350     appMsgQueue->wTimerCount++;
351     appMsgQueue->status |= QS_TIMER;
352     appMsgQueue->tempStatus |= QS_TIMER;
353 }
354
355
356 /***********************************************************************
357  *           MSG_DecTimerCount
358  */
359 void MSG_DecTimerCount( HANDLE hQueue )
360 {
361     if (hQueue != hmemAppMsgQueue) return;
362     appMsgQueue->wTimerCount--;
363     if (!appMsgQueue->wTimerCount) appMsgQueue->status &= ~QS_TIMER;
364 }
365
366
367 /***********************************************************************
368  *           hardware_event
369  *
370  * Add an event to the system message queue.
371  * Note: the position is in screen coordinates.
372  */
373 void hardware_event( HWND hwnd, WORD message, WORD wParam, LONG lParam,
374                      WORD xPos, WORD yPos, DWORD time, DWORD extraInfo )
375 {
376     MSG msg;
377
378     msg.hwnd    = hwnd;
379     msg.message = message;
380     msg.wParam  = wParam;
381     msg.lParam  = lParam;
382     msg.time    = time;
383     msg.pt.x    = xPos;
384     msg.pt.y    = yPos;    
385     if (!MSG_AddMsg( sysMsgQueue, &msg, extraInfo ))
386         printf( "hardware_event: Queue is full\n" );
387 }
388
389                     
390 /***********************************************************************
391  *           MSG_GetHardwareMessage
392  *
393  * Like GetMessage(), but only return mouse and keyboard events.
394  * Used internally for window moving and resizing. Mouse messages
395  * are not translated.
396  */
397 BOOL MSG_GetHardwareMessage( LPMSG msg )
398 {
399     int pos;
400     XEvent event;
401
402     while(1)
403     {    
404         if ((pos = MSG_FindMsg( sysMsgQueue, 0, 0, 0 )) != -1)
405         {
406             *msg = sysMsgQueue->messages[pos].msg;
407             MSG_RemoveMsg( sysMsgQueue, pos );
408             break;
409         }
410         XNextEvent( display, &event );
411         EVENT_ProcessEvent( &event );
412     }
413     return TRUE;
414 }
415
416
417 /***********************************************************************
418  *           SetTaskQueue  (KERNEL.34)
419  */
420 WORD SetTaskQueue( HANDLE hTask, HANDLE hQueue )
421 {
422     HANDLE prev = hmemAppMsgQueue;
423     hmemAppMsgQueue = hQueue;
424     return prev;
425 }
426
427
428 /***********************************************************************
429  *           GetTaskQueue  (KERNEL.35)
430  */
431 WORD GetTaskQueue( HANDLE hTask )
432 {
433     return hmemAppMsgQueue;
434 }
435
436
437 /***********************************************************************
438  *           SetMessageQueue  (USER.266)
439  */
440 BOOL SetMessageQueue( int size )
441 {
442     HANDLE hQueue;
443
444     if ((size > MAX_QUEUE_SIZE) || (size <= 0)) return TRUE;
445
446       /* Free the old message queue */
447     if ((hQueue = GetTaskQueue(0)) != 0)
448     {
449         GlobalUnlock( hQueue );
450         GlobalFree( hQueue );
451     }
452   
453     if (!(hQueue = MSG_CreateMsgQueue( size ))) return FALSE;
454     SetTaskQueue( 0, hQueue );
455     appMsgQueue = (MESSAGEQUEUE *)GlobalLock( hQueue );
456     return TRUE;
457 }
458
459
460 /***********************************************************************
461  *           PostQuitMessage   (USER.6)
462  */
463 void PostQuitMessage( int exitCode )
464 {
465     if (!appMsgQueue) return;
466     appMsgQueue->wPostQMsg = TRUE;
467     appMsgQueue->wExitCode = exitCode;
468 }
469
470
471 /***********************************************************************
472  *           GetQueueStatus   (USER.334)
473  */
474 DWORD GetQueueStatus( int flags )
475 {
476     unsigned long ret = (appMsgQueue->status << 16) | appMsgQueue->tempStatus;
477     appMsgQueue->tempStatus = 0;
478     return ret & ((flags << 16) | flags);
479 }
480
481
482 /***********************************************************************
483  *           GetInputState   (USER.335)
484  */
485 BOOL GetInputState()
486 {
487     return appMsgQueue->status & (QS_KEY | QS_MOUSEBUTTON);
488 }
489
490
491 /***********************************************************************
492  *           MSG_PeekMessage
493  */
494 static BOOL MSG_PeekMessage( MESSAGEQUEUE * msgQueue, LPMSG msg, HWND hwnd,
495                              WORD first, WORD last, WORD flags, BOOL peek )
496 {
497     int pos, mask;
498     DWORD nextExp;  /* Next timer expiration time */
499     XEvent event;
500
501     if (first || last)
502     {
503         mask = QS_POSTMESSAGE;  /* Always selectioned */
504         if ((first <= WM_KEYLAST) && (last >= WM_KEYFIRST)) mask |= QS_KEY;
505         if ((first <= WM_MOUSELAST) && (last >= WM_MOUSEFIRST)) mask |= QS_MOUSE;
506         if ((first <= WM_TIMER) && (last >= WM_TIMER)) mask |= WM_TIMER;
507         if ((first <= WM_SYSTIMER) && (last >= WM_SYSTIMER)) mask |= WM_TIMER;
508         if ((first <= WM_PAINT) && (last >= WM_PAINT)) mask |= WM_PAINT;
509     }
510     else mask = QS_MOUSE | QS_KEY | QS_POSTMESSAGE | QS_TIMER | QS_PAINT;
511
512     while (XPending( display ))
513     {
514         XNextEvent( display, &event );
515         EVENT_ProcessEvent( &event );
516     }    
517
518     while(1)
519     {    
520           /* First handle a WM_QUIT message */
521         if (msgQueue->wPostQMsg)
522         {
523             msg->hwnd    = hwnd;
524             msg->message = WM_QUIT;
525             msg->wParam  = msgQueue->wExitCode;
526             msg->lParam  = 0;
527             break;
528         }
529
530           /* Then handle a message put by SendMessage() */
531         if (msgQueue->status & QS_SENDMESSAGE)
532         {
533             if (!hwnd || (msgQueue->hWnd == hwnd))
534             {
535                 if ((!first && !last) || 
536                     ((msgQueue->msg >= first) && (msgQueue->msg <= last)))
537                 {
538                     msg->hwnd    = msgQueue->hWnd;
539                     msg->message = msgQueue->msg;
540                     msg->wParam  = msgQueue->wParam;
541                     msg->lParam  = msgQueue->lParam;
542                     if (flags & PM_REMOVE) msgQueue->status &= ~QS_SENDMESSAGE;
543                     break;
544                 }
545             }
546         }
547     
548           /* Now find a normal message */
549         pos = MSG_FindMsg( msgQueue, hwnd, first, last );
550         if (pos != -1)
551         {
552             QMSG *qmsg = &msgQueue->messages[pos];
553             *msg = qmsg->msg;
554             msgQueue->GetMessageTimeVal      = msg->time;
555             msgQueue->GetMessagePosVal       = *(DWORD *)&msg->pt;
556             msgQueue->GetMessageExtraInfoVal = qmsg->extraInfo;
557
558             if (flags & PM_REMOVE) MSG_RemoveMsg( msgQueue, pos );
559             break;
560         }
561
562           /* Now find a hardware event */
563         pos = MSG_FindMsg( sysMsgQueue, hwnd, first, last );
564         if (pos != -1)
565         {
566             QMSG *qmsg = &sysMsgQueue->messages[pos];
567             *msg = qmsg->msg;
568             msgQueue->GetMessageTimeVal      = msg->time;
569             msgQueue->GetMessagePosVal       = *(DWORD *)&msg->pt;
570             msgQueue->GetMessageExtraInfoVal = qmsg->extraInfo;
571
572             if ((msg->message >= WM_MOUSEFIRST) &&
573                 (msg->message <= WM_MOUSELAST))
574                 if (!MSG_TranslateMouseMsg( msg )) 
575                 {
576                     MSG_RemoveMsg( sysMsgQueue, pos );
577                     continue;
578                 }
579             if (flags & PM_REMOVE) MSG_RemoveMsg( sysMsgQueue, pos );
580             break;
581         }
582
583           /* Now find a WM_PAINT message */
584         if ((msgQueue->status & QS_PAINT) && (mask & QS_PAINT))
585         {
586             msg->hwnd = WIN_FindWinToRepaint( hwnd );
587             msg->message = WM_PAINT;
588             msg->wParam = 0;
589             msg->lParam = 0;
590             if (msg->hwnd != 0) break;
591         }
592
593           /* Finally handle WM_TIMER messages */
594         if ((msgQueue->status & QS_TIMER) && (mask & QS_TIMER))
595             if (TIMER_CheckTimer( &nextExp ))
596                 continue;  /* Restart the whole search */
597
598           /* Wait until something happens */
599         if (peek) return FALSE;
600         if (!XPending( display ) && (nextExp != -1))
601         {
602             fd_set read_set;
603             struct timeval timeout;
604             int fd = ConnectionNumber(display);
605             FD_ZERO( &read_set );
606             FD_SET( fd, &read_set );
607             timeout.tv_sec = nextExp / 1000;
608             timeout.tv_usec = (nextExp % 1000) * 1000;
609             if (select( fd+1, &read_set, NULL, NULL, &timeout ) != 1)
610                 continue;  /* On timeout or error, restart from the start */
611         }
612         XNextEvent( display, &event );
613         EVENT_ProcessEvent( &event );
614     }
615
616       /* We got a message */
617     if (peek) return TRUE;
618     else return (msg->message != WM_QUIT);
619 }
620
621
622 /***********************************************************************
623  *           PeekMessage   (USER.109)
624  */
625 BOOL PeekMessage( LPMSG msg, HWND hwnd, WORD first, WORD last, WORD flags )
626 {
627     return MSG_PeekMessage( appMsgQueue, msg, hwnd, first, last, flags, TRUE );
628 }
629
630
631 /***********************************************************************
632  *           GetMessage   (USER.108)
633  */
634 BOOL GetMessage( LPMSG msg, HWND hwnd, WORD first, WORD last ) 
635 {
636     return MSG_PeekMessage( appMsgQueue, msg, hwnd, first, last, PM_REMOVE, FALSE );
637 }
638
639
640 /***********************************************************************
641  *           PostMessage   (USER.110)
642  */
643 BOOL PostMessage( HWND hwnd, WORD message, WORD wParam, LONG lParam )
644 {
645     MSG msg;
646     
647     msg.hwnd    = hwnd;
648     msg.message = message;
649     msg.wParam  = wParam;
650     msg.lParam  = lParam;
651     msg.time    = GetTickCount();
652     msg.pt.x    = 0;
653     msg.pt.y    = 0;
654     
655     return MSG_AddMsg( appMsgQueue, &msg, 0 );
656 }
657
658
659 /***********************************************************************
660  *           SendMessage   (USER.111)
661  */
662 LONG SendMessage( HWND hwnd, WORD msg, WORD wParam, LONG lParam )
663 {
664     WND * wndPtr;
665
666     SpyMessage(hwnd, msg, wParam, lParam);
667     
668     wndPtr = WIN_FindWndPtr( hwnd );
669     if (!wndPtr) return 0;
670     return CallWindowProc( wndPtr->lpfnWndProc, hwnd, msg, wParam, lParam );
671 }
672
673
674 /***********************************************************************
675  *           TranslateMessage   (USER.113)
676  */
677 BOOL TranslateMessage( LPMSG msg )
678 {
679     int message = msg->message;
680     
681     if ((message == WM_KEYDOWN) || (message == WM_KEYUP) ||
682         (message == WM_SYSKEYDOWN) || (message == WM_SYSKEYUP))
683     {
684 #ifdef DEBUG_MSG
685         printf( "Translating key message\n" );
686 #endif
687         return TRUE;
688     }
689     return FALSE;
690 }
691
692
693 /***********************************************************************
694  *           DispatchMessage   (USER.114)
695  */
696 LONG DispatchMessage( LPMSG msg )
697 {
698     WND * wndPtr;
699     LONG retval;
700     int painting;
701     
702 #ifdef DEBUG_MSG
703     printf( "Dispatch message hwnd=%08x msg=0x%x w=%d l=%d time=%u pt=%d,%d\n",
704             msg->hwnd, msg->message, msg->wParam, msg->lParam, 
705             msg->time, msg->pt.x, msg->pt.y );
706 #endif
707
708       /* Process timer messages */
709     if ((msg->message == WM_TIMER) || (msg->message == WM_SYSTIMER))
710     {
711         if (msg->lParam)
712             return CallWindowProc( (FARPROC)msg->lParam, msg->hwnd,
713                                    msg->message, msg->wParam, GetTickCount() );
714     }
715
716     if (!msg->hwnd) return 0;
717     if (!(wndPtr = WIN_FindWndPtr( msg->hwnd ))) return 0;
718     if (!wndPtr->lpfnWndProc) return 0;
719     painting = (msg->message == WM_PAINT);
720     if (painting) wndPtr->flags |= WIN_NEEDS_BEGINPAINT;
721     retval = CallWindowProc( wndPtr->lpfnWndProc, msg->hwnd, msg->message,
722                              msg->wParam, msg->lParam );
723     if (painting && (wndPtr->flags & WIN_NEEDS_BEGINPAINT))
724     {
725 #ifdef DEBUG_WIN
726         printf( "BeginPaint not called on WM_PAINT for hwnd %d!\n", msg->hwnd);
727 #endif
728         wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
729     }
730     return retval;
731 }
732
733
734 /***********************************************************************
735  *           GetMessagePos   (USER.119)
736  */
737 DWORD GetMessagePos(void)
738 {
739     return appMsgQueue->GetMessagePosVal;
740 }
741
742
743 /***********************************************************************
744  *           GetMessageTime   (USER.120)
745  */
746 LONG GetMessageTime(void)
747 {
748     return appMsgQueue->GetMessageTimeVal;
749 }
750
751
752 /***********************************************************************
753  *           GetMessageExtraInfo   (USER.288)
754  */
755 LONG GetMessageExtraInfo(void)
756 {
757     return appMsgQueue->GetMessageExtraInfoVal;
758 }
759
760
761 /***********************************************************************
762  *           RegisterWindowMessage   (USER.118)
763  */
764 WORD RegisterWindowMessage( LPCSTR str )
765 {
766 #ifdef DEBUG_MSG
767     printf( "RegisterWindowMessage: '%s'\n", str );
768 #endif
769     return GlobalAddAtom( str );
770 }
771
772
773 /***********************************************************************
774  *           GetTickCount    (USER.13)
775  */
776 DWORD GetTickCount()
777 {
778     struct timeval t;
779     gettimeofday( &t, NULL );
780     return (t.tv_sec * 1000) + (t.tv_usec / 1000);
781 }