Release 960606
[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 <string.h>
9 #include <sys/time.h>
10 #include <sys/types.h>
11
12 #include "message.h"
13 #include "win.h"
14 #include "gdi.h"
15 #include "sysmetrics.h"
16 #include "hook.h"
17 #include "spy.h"
18 #include "winpos.h"
19 #include "atom.h"
20 #include "dde.h"
21 #include "queue.h"
22 #include "stddebug.h"
23 /* #define DEBUG_MSG */
24 #include "debug.h"
25
26 #define HWND_BROADCAST16  ((HWND16)0xffff)
27 #define HWND_BROADCAST32  ((HWND32)0xffffffff)
28
29 extern BYTE*    KeyStateTable;                           /* event.c */
30 extern WPARAM   lastEventChar;                           /* event.c */
31
32 extern BOOL TIMER_CheckTimer( LONG *next, MSG *msg,
33                               HWND hwnd, BOOL remove );  /* timer.c */
34
35 DWORD MSG_WineStartTicks;                                /* Ticks at Wine startup */
36
37 static WORD doubleClickSpeed = 452;
38
39 /***********************************************************************
40  *           MSG_TranslateMouseMsg
41  *
42  * Translate an mouse hardware event into a real mouse message.
43  * Return value indicates whether the translated message must be passed
44  * to the user.
45  * Actions performed:
46  * - Find the window for this message.
47  * - Translate button-down messages in double-clicks.
48  * - Send the WM_NCHITTEST message to find where the cursor is.
49  * - Activate the window if needed.
50  * - Translate the message into a non-client message, or translate
51  *   the coordinates to client coordinates.
52  * - Send the WM_SETCURSOR message.
53  */
54 static BOOL MSG_TranslateMouseMsg( MSG *msg, BOOL remove )
55 {
56     WND *pWnd;
57     BOOL eatMsg = FALSE;
58     INT16 hittest;
59     static DWORD lastClickTime = 0;
60     static WORD  lastClickMsg = 0;
61     static POINT16 lastClickPos = { 0, 0 };
62     POINT16 pt = msg->pt;
63     MOUSEHOOKSTRUCT hook = { msg->pt, 0, HTCLIENT, 0 };
64
65     BOOL mouseClick = ((msg->message == WM_LBUTTONDOWN) ||
66                        (msg->message == WM_RBUTTONDOWN) ||
67                        (msg->message == WM_MBUTTONDOWN));
68
69       /* Find the window */
70
71     if (GetCapture())
72     {
73         msg->hwnd = GetCapture();
74         ScreenToClient16( msg->hwnd, &pt );
75         msg->lParam = MAKELONG( pt.x, pt.y );
76         /* No need to further process the message */
77         hook.hwnd = msg->hwnd;
78         return !HOOK_CallHooks( WH_MOUSE, remove ? HC_ACTION : HC_NOREMOVE,
79                                 msg->message, (LPARAM)MAKE_SEGPTR(&hook));
80     }
81    
82     hittest = WINPOS_WindowFromPoint( msg->pt, &pWnd );
83     msg->hwnd = pWnd->hwndSelf;
84     if ((hittest != HTERROR) && mouseClick)
85     {
86         HWND hwndTop = WIN_GetTopParent( msg->hwnd );
87
88         /* Send the WM_PARENTNOTIFY message */
89
90         WIN_SendParentNotify( msg->hwnd, msg->message, 0,
91                               MAKELONG( msg->pt.x, msg->pt.y ) );
92
93         /* Activate the window if needed */
94
95         if (msg->hwnd != GetActiveWindow() && msg->hwnd != GetDesktopWindow())
96         {
97             LONG ret = SendMessage16( msg->hwnd, WM_MOUSEACTIVATE, hwndTop,
98                                     MAKELONG( hittest, msg->message ) );
99
100             if ((ret == MA_ACTIVATEANDEAT) || (ret == MA_NOACTIVATEANDEAT))
101                 eatMsg = TRUE;
102
103             if (((ret == MA_ACTIVATE) || (ret == MA_ACTIVATEANDEAT)) 
104                 && hwndTop != GetActiveWindow() )
105                 WINPOS_SetActiveWindow( hwndTop, TRUE , TRUE );
106         }
107     }
108
109       /* Send the WM_SETCURSOR message */
110
111     SendMessage16( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd,
112                    MAKELONG( hittest, msg->message ));
113     if (eatMsg) return FALSE;
114
115       /* Check for double-click */
116
117     if (mouseClick)
118     {
119         BOOL dbl_click = FALSE;
120
121         if ((msg->message == lastClickMsg) &&
122             (msg->time - lastClickTime < doubleClickSpeed) &&
123             (abs(msg->pt.x - lastClickPos.x) < SYSMETRICS_CXDOUBLECLK/2) &&
124             (abs(msg->pt.y - lastClickPos.y) < SYSMETRICS_CYDOUBLECLK/2))
125             dbl_click = TRUE;
126
127         if (dbl_click && (hittest == HTCLIENT))
128         {
129             /* Check whether window wants the double click message. */
130             dbl_click = (pWnd->class->style & CS_DBLCLKS) != 0;
131         }
132
133         if (dbl_click) switch(msg->message)
134         {
135             case WM_LBUTTONDOWN: msg->message = WM_LBUTTONDBLCLK; break;
136             case WM_RBUTTONDOWN: msg->message = WM_RBUTTONDBLCLK; break;
137             case WM_MBUTTONDOWN: msg->message = WM_MBUTTONDBLCLK; break;
138         }
139
140         if (remove)
141         {
142             lastClickTime = msg->time;
143             lastClickMsg  = msg->message;
144             lastClickPos  = msg->pt;
145         }
146     }
147
148       /* Build the translated message */
149
150     if (hittest == HTCLIENT)
151         ScreenToClient16( msg->hwnd, &pt );
152     else
153     {
154         msg->wParam = hittest;
155         msg->message += WM_NCLBUTTONDOWN - WM_LBUTTONDOWN;
156     }
157     msg->lParam = MAKELONG( pt.x, pt.y );
158     
159     hook.hwnd = msg->hwnd;
160     hook.wHitTestCode = hittest;
161     return !HOOK_CallHooks( WH_MOUSE, remove ? HC_ACTION : HC_NOREMOVE,
162                             msg->message, (LPARAM)MAKE_SEGPTR(&hook));
163 }
164
165
166 /***********************************************************************
167  *           MSG_TranslateKeyboardMsg
168  *
169  * Translate an keyboard hardware event into a real message.
170  * Return value indicates whether the translated message must be passed
171  * to the user.
172  */
173 static BOOL MSG_TranslateKeyboardMsg( MSG *msg, BOOL remove )
174 {
175       /* Should check Ctrl-Esc and PrintScreen here */
176
177     msg->hwnd = GetFocus();
178     if (!msg->hwnd)
179     {
180           /* Send the message to the active window instead,  */
181           /* translating messages to their WM_SYS equivalent */
182
183         msg->hwnd = GetActiveWindow();
184
185         if( msg->message < WM_SYSKEYDOWN )
186             msg->message += WM_SYSKEYDOWN - WM_KEYDOWN;
187     }
188     return !HOOK_CallHooks( WH_KEYBOARD, remove ? HC_ACTION : HC_NOREMOVE,
189                             msg->wParam, msg->lParam );
190 }
191
192
193 /***********************************************************************
194  *           MSG_PeekHardwareMsg
195  *
196  * Peek for a hardware message matching the hwnd and message filters.
197  */
198 static BOOL MSG_PeekHardwareMsg( MSG *msg, HWND hwnd, WORD first, WORD last,
199                                  BOOL remove )
200 {
201     MESSAGEQUEUE *sysMsgQueue = QUEUE_GetSysQueue();
202     int i, pos = sysMsgQueue->nextMessage;
203
204     /* If the queue is empty, attempt to fill it */
205     if (!sysMsgQueue->msgCount && XPending(display)) EVENT_WaitXEvent( 0 );
206
207     for (i = 0; i < sysMsgQueue->msgCount; i++, pos++)
208     {
209         if (pos >= sysMsgQueue->queueSize) pos = 0;
210         *msg = sysMsgQueue->messages[pos].msg;
211
212           /* Translate message */
213
214         if ((msg->message >= WM_MOUSEFIRST) && (msg->message <= WM_MOUSELAST))
215         {
216             if (!MSG_TranslateMouseMsg( msg, remove )) continue;
217         }
218         else if ((msg->message >= WM_KEYFIRST) && (msg->message <= WM_KEYLAST))
219         {
220             if (!MSG_TranslateKeyboardMsg( msg, remove )) continue;
221         }
222         else  /* Non-standard hardware event */
223         {
224             HARDWAREHOOKSTRUCT hook = { msg->hwnd, msg->message,
225                                         msg->wParam, msg->lParam };
226             if (HOOK_CallHooks( WH_HARDWARE, remove ? HC_ACTION : HC_NOREMOVE,
227                                 0, (LPARAM)MAKE_SEGPTR(&hook) )) continue;
228         }
229
230           /* Check message against filters */
231
232         if (hwnd && (msg->hwnd != hwnd)) continue;
233         if ((first || last) && 
234             ((msg->message < first) || (msg->message > last))) continue;
235         if ((msg->hwnd != GetDesktopWindow()) && 
236             (GetWindowTask(msg->hwnd) != GetCurrentTask()))
237             continue;  /* Not for this task */
238         if (remove)
239         {
240             MSG tmpMsg = *msg; /* FIXME */
241             HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION,
242                             0, (LPARAM)MAKE_SEGPTR(&tmpMsg) );
243             QUEUE_RemoveMsg( sysMsgQueue, pos );
244         }
245         return TRUE;
246     }
247     return FALSE;
248 }
249
250
251 /**********************************************************************
252  *           SetDoubleClickTime   (USER.20)
253  */
254 void SetDoubleClickTime( WORD interval )
255 {
256     doubleClickSpeed = interval ? interval : 500;
257 }               
258
259
260 /**********************************************************************
261  *           GetDoubleClickTime   (USER.21)
262  */
263 WORD GetDoubleClickTime()
264 {
265     return doubleClickSpeed;
266 }               
267
268
269 /***********************************************************************
270  *           MSG_GetHardwareMessage
271  *
272  * Like GetMessage(), but only return mouse and keyboard events.
273  * Used internally for window moving and resizing. Mouse messages
274  * are not translated.
275  * Warning: msg->hwnd is always 0.
276  */
277 BOOL MSG_GetHardwareMessage( LPMSG msg )
278 {
279 #if 0
280     int pos;
281     XEvent event;
282     MESSAGEQUEUE *sysMsgQueue = QUEUE_GetSysQueue();
283
284     while(1)
285     {    
286         if ((pos = QUEUE_FindMsg( sysMsgQueue, 0, 0, 0 )) != -1)
287         {
288             *msg = sysMsgQueue->messages[pos].msg;
289             QUEUE_RemoveMsg( sysMsgQueue, pos );
290             break;
291         }
292         XNextEvent( display, &event );
293         EVENT_ProcessEvent( &event );
294     }
295 #endif
296     MSG_PeekMessage( msg, 0, WM_KEYFIRST, WM_MOUSELAST, PM_REMOVE, 0 );
297     return TRUE;
298 }
299
300
301 /***********************************************************************
302  *           MSG_SendMessage
303  *
304  * Implementation of an inter-task SendMessage.
305  */
306 LRESULT MSG_SendMessage( HQUEUE hDestQueue, HWND hwnd, UINT msg,
307                          WPARAM wParam, LPARAM lParam )
308 {
309     MESSAGEQUEUE *queue, *destQ;
310
311     if (!(queue = (MESSAGEQUEUE*)GlobalLock16( GetTaskQueue(0) ))) return 0;
312     if (!(destQ = (MESSAGEQUEUE*)GlobalLock16( hDestQueue ))) return 0;
313
314     if (IsTaskLocked())
315     {
316         fprintf( stderr, "SendMessage: task is locked\n" );
317         return 0;
318     }
319
320     if (queue->hWnd)
321     {
322         fprintf( stderr, "Nested SendMessage() not supported\n" );
323         return 0;
324     }
325     queue->hWnd   = hwnd;
326     queue->msg    = msg;
327     queue->wParam = wParam;
328     queue->lParam = lParam;
329     queue->hPrevSendingTask = destQ->hSendingTask;
330     destQ->hSendingTask = GetTaskQueue(0);
331     QUEUE_SetWakeBit( destQ, QS_SENDMESSAGE );
332
333     /* Wait for the result */
334
335     printf( "SendMessage %04x to %04x\n", msg, hDestQueue );
336
337     if (!(queue->wakeBits & QS_SMRESULT))
338     {
339         DirectedYield( hDestQueue );
340         QUEUE_WaitBits( QS_SMRESULT );
341     }
342     printf( "SendMessage %04x to %04x: got %08x\n",
343             msg, hDestQueue, queue->SendMessageReturn );
344     queue->wakeBits &= ~QS_SMRESULT;
345     return queue->SendMessageReturn;
346 }
347
348
349 /***********************************************************************
350  *           ReplyMessage   (USER.115)
351  */
352 void ReplyMessage( LRESULT result )
353 {
354     MESSAGEQUEUE *senderQ;
355     MESSAGEQUEUE *queue;
356
357     printf( "ReplyMessage\n " );
358     if (!(queue = (MESSAGEQUEUE*)GlobalLock16( GetTaskQueue(0) ))) return;
359     if (!(senderQ = (MESSAGEQUEUE*)GlobalLock16( queue->InSendMessageHandle)))
360         return;
361     for (;;)
362     {
363         if (queue->wakeBits & QS_SENDMESSAGE) QUEUE_ReceiveMessage( queue );
364         else if (senderQ->wakeBits & QS_SMRESULT) Yield();
365         else break;
366     }
367     printf( "ReplyMessage: res = %08x\n", result );
368     senderQ->SendMessageReturn = result;
369     queue->InSendMessageHandle = 0;
370     QUEUE_SetWakeBit( senderQ, QS_SMRESULT );
371     DirectedYield( queue->hSendingTask );
372 }
373
374
375 /***********************************************************************
376  *           MSG_PeekMessage
377  */
378 static BOOL MSG_PeekMessage( LPMSG msg, HWND hwnd, WORD first, WORD last,
379                              WORD flags, BOOL peek )
380 {
381     int pos, mask;
382     MESSAGEQUEUE *msgQueue;
383     HQUEUE        hQueue;
384     LONG nextExp;  /* Next timer expiration time */
385
386 #ifdef CONFIG_IPC
387     DDE_TestDDE(hwnd);  /* do we have dde handling in the window ?*/
388     DDE_GetRemoteMessage();
389 #endif  /* CONFIG_IPC */
390
391     mask = QS_POSTMESSAGE | QS_SENDMESSAGE;  /* Always selected */
392     if (first || last)
393     {
394         if ((first <= WM_KEYLAST) && (last >= WM_KEYFIRST)) mask |= QS_KEY;
395         if ((first <= WM_MOUSELAST) && (last >= WM_MOUSEFIRST)) mask |= QS_MOUSE;
396         if ((first <= WM_TIMER) && (last >= WM_TIMER)) mask |= QS_TIMER;
397         if ((first <= WM_SYSTIMER) && (last >= WM_SYSTIMER)) mask |= QS_TIMER;
398         if ((first <= WM_PAINT) && (last >= WM_PAINT)) mask |= QS_PAINT;
399     }
400     else mask |= QS_MOUSE | QS_KEY | QS_TIMER | QS_PAINT;
401
402     if (IsTaskLocked()) flags |= PM_NOYIELD;
403
404     while(1)
405     {    
406         hQueue   = GetTaskQueue(0);
407         msgQueue = (MESSAGEQUEUE *)GlobalLock16( hQueue );
408         if (!msgQueue) return FALSE;
409         msgQueue->changeBits = 0;
410
411         /* First handle a message put by SendMessage() */
412
413         if (msgQueue->wakeBits & QS_SENDMESSAGE)
414             QUEUE_ReceiveMessage( msgQueue );
415     
416         /* Now find a normal message */
417
418         if (((msgQueue->wakeBits & mask) & QS_POSTMESSAGE) &&
419             ((pos = QUEUE_FindMsg( msgQueue, hwnd, first, last )) != -1))
420         {
421             QMSG *qmsg = &msgQueue->messages[pos];
422             *msg = qmsg->msg;
423             msgQueue->GetMessageTimeVal      = msg->time;
424             msgQueue->GetMessagePosVal       = *(DWORD *)&msg->pt;
425             msgQueue->GetMessageExtraInfoVal = qmsg->extraInfo;
426
427             if (flags & PM_REMOVE) QUEUE_RemoveMsg( msgQueue, pos );
428             break;
429         }
430
431         /* Now find a hardware event */
432
433         if (((msgQueue->wakeBits & mask) & (QS_MOUSE | QS_KEY)) &&
434             MSG_PeekHardwareMsg( msg, hwnd, first, last, flags & PM_REMOVE ))
435         {
436             /* Got one */
437             msgQueue->GetMessageTimeVal      = msg->time;
438             msgQueue->GetMessagePosVal       = *(DWORD *)&msg->pt;
439             msgQueue->GetMessageExtraInfoVal = 0;  /* Always 0 for now */
440             break;
441         }
442
443         /* Now handle a WM_QUIT message */
444
445         if (msgQueue->wPostQMsg)
446         {
447             msg->hwnd    = hwnd;
448             msg->message = WM_QUIT;
449             msg->wParam  = msgQueue->wExitCode;
450             msg->lParam  = 0;
451             break;
452         }
453
454         /* Check again for SendMessage */
455
456         if (msgQueue->wakeBits & QS_SENDMESSAGE)
457             QUEUE_ReceiveMessage( msgQueue );
458
459         /* Now find a WM_PAINT message */
460
461         if ((msgQueue->wakeBits & mask) & QS_PAINT)
462         {
463             msg->hwnd = WIN_FindWinToRepaint( hwnd , hQueue );
464             msg->message = WM_PAINT;
465             msg->wParam = 0;
466             msg->lParam = 0;
467             if( msg->hwnd &&
468               (!hwnd || msg->hwnd == hwnd || IsChild(hwnd,msg->hwnd)) )
469               {
470                 WND* wndPtr = WIN_FindWndPtr(msg->hwnd);
471
472                 /* FIXME: WM_PAINTICON should be sent sometimes */
473
474                 if( wndPtr->flags & WIN_INTERNAL_PAINT && !wndPtr->hrgnUpdate)
475                   {
476                     wndPtr->flags &= ~WIN_INTERNAL_PAINT;
477                     QUEUE_DecPaintCount( hQueue );
478                   }
479                 break;
480               }
481         }
482
483         /* Check for timer messages, but yield first */
484
485         if (!(flags & PM_NOYIELD))
486         {
487             UserYield();
488             if (msgQueue->wakeBits & QS_SENDMESSAGE)
489                 QUEUE_ReceiveMessage( msgQueue );
490         }
491         if ((msgQueue->wakeBits & mask) & QS_TIMER)
492         {
493             if (TIMER_CheckTimer( &nextExp, msg, hwnd, flags & PM_REMOVE ))
494                 break;  /* Got a timer msg */
495         }
496         else nextExp = -1;  /* No timeout needed */
497
498         if (peek)
499         {
500             if (!(flags & PM_NOYIELD)) UserYield();
501             return FALSE;
502         }
503         msgQueue->wakeMask = mask;
504         QUEUE_WaitBits( mask );
505     }
506
507       /* We got a message */
508     if (peek) return TRUE;
509     else return (msg->message != WM_QUIT);
510 }
511
512
513 /***********************************************************************
514  *           MSG_InternalGetMessage
515  *
516  * GetMessage() function for internal use. Behave like GetMessage(),
517  * but also call message filters and optionally send WM_ENTERIDLE messages.
518  * 'hwnd' must be the handle of the dialog or menu window.
519  * 'code' is the message filter value (MSGF_??? codes).
520  */
521 BOOL MSG_InternalGetMessage( SEGPTR msg, HWND hwnd, HWND hwndOwner, short code,
522                              WORD flags, BOOL sendIdle ) 
523 {
524     for (;;)
525     {
526         if (sendIdle)
527         {
528             if (!MSG_PeekMessage( (MSG *)PTR_SEG_TO_LIN(msg),
529                                   0, 0, 0, flags, TRUE ))
530             {
531                   /* No message present -> send ENTERIDLE and wait */
532                 if (IsWindow(hwndOwner))
533                     SendMessage16( hwndOwner, WM_ENTERIDLE,
534                                    code, (LPARAM)hwnd );
535                 MSG_PeekMessage( (MSG *)PTR_SEG_TO_LIN(msg),
536                                  0, 0, 0, flags, FALSE );
537             }
538         }
539         else  /* Always wait for a message */
540             MSG_PeekMessage( (MSG *)PTR_SEG_TO_LIN(msg),
541                              0, 0, 0, flags, FALSE );
542
543         if (!CallMsgFilter( msg, code ))
544             return (((MSG *)PTR_SEG_TO_LIN(msg))->message != WM_QUIT);
545
546           /* Message filtered -> remove it from the queue */
547           /* if it's still there. */
548         if (!(flags & PM_REMOVE))
549             MSG_PeekMessage( (MSG *)PTR_SEG_TO_LIN(msg),
550                              0, 0, 0, PM_REMOVE, TRUE );
551     }
552 }
553
554
555 /***********************************************************************
556  *           PeekMessage   (USER.109)
557  */
558 BOOL PeekMessage( LPMSG msg, HWND hwnd, WORD first, WORD last, WORD flags )
559 {
560     return MSG_PeekMessage( msg, hwnd, first, last, flags, TRUE );
561 }
562
563
564 /***********************************************************************
565  *           GetMessage   (USER.108)
566  */
567 BOOL GetMessage( SEGPTR msg, HWND hwnd, UINT first, UINT last ) 
568 {
569     MSG* lpmsg = (MSG *)PTR_SEG_TO_LIN(msg);
570     MSG_PeekMessage( lpmsg,
571                      hwnd, first, last, PM_REMOVE, FALSE );
572
573     dprintf_msg(stddeb,"message %04x, hwnd %04x, filter(%04x - %04x)\n", lpmsg->message,
574                                                                  hwnd, first, last );
575     HOOK_CallHooks( WH_GETMESSAGE, HC_ACTION, 0, (LPARAM)msg );
576     return (lpmsg->message != WM_QUIT);
577 }
578
579
580 /***********************************************************************
581  *           PostMessage   (USER.110)
582  */
583 BOOL PostMessage( HWND hwnd, WORD message, WORD wParam, LONG lParam )
584 {
585     MSG         msg;
586     WND         *wndPtr;
587
588     msg.hwnd    = hwnd;
589     msg.message = message;
590     msg.wParam  = wParam;
591     msg.lParam  = lParam;
592     msg.time    = GetTickCount();
593     msg.pt.x    = 0;
594     msg.pt.y    = 0;
595
596 #ifdef CONFIG_IPC
597     if (DDE_PostMessage(&msg))
598        return TRUE;
599 #endif  /* CONFIG_IPC */
600     
601     if (hwnd == HWND_BROADCAST16)
602     {
603         dprintf_msg(stddeb,"PostMessage // HWND_BROADCAST !\n");
604         for (wndPtr = WIN_GetDesktop()->child; wndPtr; wndPtr = wndPtr->next)
605         {
606             if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION)
607             {
608                 dprintf_msg(stddeb,"BROADCAST Message to hWnd=%04x m=%04X w=%04X l=%08lX !\n",
609                             wndPtr->hwndSelf, message, wParam, lParam);
610                 PostMessage( wndPtr->hwndSelf, message, wParam, lParam );
611             }
612         }
613         dprintf_msg(stddeb,"PostMessage // End of HWND_BROADCAST !\n");
614         return TRUE;
615     }
616
617     wndPtr = WIN_FindWndPtr( hwnd );
618     if (!wndPtr || !wndPtr->hmemTaskQ) return FALSE;
619
620     return QUEUE_AddMsg( wndPtr->hmemTaskQ, &msg, 0 );
621 }
622
623 /***********************************************************************
624  *           PostAppMessage   (USER.116)
625  */
626 BOOL PostAppMessage( HTASK hTask, WORD message, WORD wParam, LONG lParam )
627 {
628     MSG         msg;
629
630     if (GetTaskQueue(hTask) == 0) return FALSE;
631     msg.hwnd    = 0;
632     msg.message = message;
633     msg.wParam  = wParam;
634     msg.lParam  = lParam;
635     msg.time    = GetTickCount();
636     msg.pt.x    = 0;
637     msg.pt.y    = 0;
638
639     return QUEUE_AddMsg( GetTaskQueue(hTask), &msg, 0 );
640 }
641
642
643 /***********************************************************************
644  *           SendMessage16   (USER.111)
645  */
646 LRESULT SendMessage16( HWND16 hwnd, UINT16 msg, WPARAM16 wParam, LPARAM lParam)
647 {
648     WND * wndPtr;
649     LRESULT ret;
650     struct
651     {
652         LPARAM   lParam;
653         WPARAM16 wParam;
654         UINT16   wMsg;
655         HWND16   hWnd;
656     } msgstruct = { lParam, wParam, msg, hwnd };
657
658 #ifdef CONFIG_IPC
659     MSG DDE_msg = { hwnd, msg, wParam, lParam };
660     if (DDE_SendMessage(&DDE_msg)) return TRUE;
661 #endif  /* CONFIG_IPC */
662
663     if (hwnd == HWND_BROADCAST16)
664     {
665         dprintf_msg(stddeb,"SendMessage // HWND_BROADCAST !\n");
666         for (wndPtr = WIN_GetDesktop()->child; wndPtr; wndPtr = wndPtr->next)
667         {
668             if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION)
669             {
670                 dprintf_msg(stddeb,"BROADCAST Message to hWnd=%04x m=%04X w=%04lX l=%08lX !\n",
671                             wndPtr->hwndSelf, msg, (DWORD)wParam, lParam);
672                 SendMessage16( wndPtr->hwndSelf, msg, wParam, lParam );
673             }
674         }
675         dprintf_msg(stddeb,"SendMessage // End of HWND_BROADCAST !\n");
676         return TRUE;
677     }
678
679
680     HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, 1,
681                     (LPARAM)MAKE_SEGPTR(&msgstruct) );
682     hwnd   = msgstruct.hWnd;
683     msg    = msgstruct.wMsg;
684     wParam = msgstruct.wParam;
685     lParam = msgstruct.lParam;
686
687     if (!(wndPtr = WIN_FindWndPtr( hwnd )))
688     {
689         fprintf( stderr, "SendMessage16: invalid hwnd %04x\n", hwnd );
690         return 0;
691     }
692     if (wndPtr->hmemTaskQ != GetTaskQueue(0))
693     {
694 #if 0
695         fprintf( stderr, "SendMessage16: intertask message not supported\n" );
696         return 0;
697 #endif
698         return MSG_SendMessage( wndPtr->hmemTaskQ, hwnd, msg, wParam, lParam );
699     }
700
701     SPY_EnterMessage( SPY_SENDMESSAGE16, hwnd, msg, wParam, lParam );
702     ret = CallWindowProc16( (WNDPROC16)wndPtr->winproc,
703                             hwnd, msg, wParam, lParam );
704     SPY_ExitMessage( SPY_RESULT_OK16, hwnd, msg, ret );
705     return ret;
706 }
707
708
709 /***********************************************************************
710  *           SendMessage32A   (USER32.453)
711  */
712 LRESULT SendMessage32A(HWND32 hwnd, UINT32 msg, WPARAM32 wParam, LPARAM lParam)
713 {
714     WND * wndPtr;
715     LRESULT ret;
716
717     if (hwnd == HWND_BROADCAST32)
718     {
719         for (wndPtr = WIN_GetDesktop()->child; wndPtr; wndPtr = wndPtr->next)
720         {
721             /* FIXME: should use something like EnumWindows here */
722             if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION)
723                 SendMessage32A( wndPtr->hwndSelf, msg, wParam, lParam );
724         }
725         return TRUE;
726     }
727
728     /* FIXME: call hooks */
729
730     if (!(wndPtr = WIN_FindWndPtr( hwnd )))
731     {
732         fprintf( stderr, "SendMessage32A: invalid hwnd %08x\n", hwnd );
733         return 0;
734     }
735     if (wndPtr->hmemTaskQ != GetTaskQueue(0))
736     {
737         fprintf( stderr, "SendMessage32A: intertask message not supported\n" );
738         return 0;
739     }
740
741     SPY_EnterMessage( SPY_SENDMESSAGE32, hwnd, msg, wParam, lParam );
742     ret = CallWindowProc32A( (WNDPROC32)wndPtr->winproc,
743                              hwnd, msg, wParam, lParam );
744     SPY_ExitMessage( SPY_RESULT_OK32, hwnd, msg, ret );
745     return ret;
746 }
747
748
749 /***********************************************************************
750  *           SendMessage32W   (USER32.458)
751  */
752 LRESULT SendMessage32W(HWND32 hwnd, UINT32 msg, WPARAM32 wParam, LPARAM lParam)
753 {
754     WND * wndPtr;
755     LRESULT ret;
756
757     if (hwnd == HWND_BROADCAST32)
758     {
759         for (wndPtr = WIN_GetDesktop()->child; wndPtr; wndPtr = wndPtr->next)
760         {
761             /* FIXME: should use something like EnumWindows here */
762             if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION)
763                 SendMessage32W( wndPtr->hwndSelf, msg, wParam, lParam );
764         }
765         return TRUE;
766     }
767
768     /* FIXME: call hooks */
769
770     if (!(wndPtr = WIN_FindWndPtr( hwnd )))
771     {
772         fprintf( stderr, "SendMessage32W: invalid hwnd %08x\n", hwnd );
773         return 0;
774     }
775     if (wndPtr->hmemTaskQ != GetTaskQueue(0))
776     {
777         fprintf( stderr, "SendMessage32W: intertask message not supported\n" );
778         return 0;
779     }
780
781     SPY_EnterMessage( SPY_SENDMESSAGE32, hwnd, msg, wParam, lParam );
782     ret = CallWindowProc32W( (WNDPROC32)wndPtr->winproc,
783                              hwnd, msg, wParam, lParam );
784     SPY_ExitMessage( SPY_RESULT_OK32, hwnd, msg, ret );
785     return ret;
786 }
787
788
789 /***********************************************************************
790  *           WaitMessage    (USER.112)
791  */
792 void WaitMessage( void )
793 {
794     MSG msg;
795     MESSAGEQUEUE *queue;
796     LONG nextExp = -1;  /* Next timer expiration time */
797
798 #ifdef CONFIG_IPC
799     DDE_GetRemoteMessage();
800 #endif  /* CONFIG_IPC */
801     
802     if (!(queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) ))) return;
803     if ((queue->wPostQMsg) || 
804         (queue->wakeBits & (QS_SENDMESSAGE | QS_PAINT)) ||
805         (queue->msgCount) || (QUEUE_GetSysQueue()->msgCount) )
806         return;
807     if ((queue->wakeBits & QS_TIMER) && 
808         TIMER_CheckTimer( &nextExp, &msg, 0, FALSE))
809         return;
810     /* FIXME: (dde) must check DDE & X-events simultaneously */
811     EVENT_WaitXEvent( nextExp );
812 }
813
814
815 /***********************************************************************
816  *           TranslateMessage   (USER.113)
817  *
818  * This should call ToAscii but it is currently broken
819  */
820
821 #define ASCII_CHAR_HACK 0x0800
822
823 BOOL TranslateMessage( LPMSG msg )
824 {
825     UINT message = msg->message;
826     /* BYTE wparam[2]; */
827     
828     if ((message == WM_KEYDOWN) || (message == WM_KEYUP) ||
829         (message == WM_SYSKEYDOWN) || (message == WM_SYSKEYUP))
830     {
831         dprintf_msg(stddeb, "Translating key %04x, scancode %04x\n", msg->wParam, 
832                                                               HIWORD(msg->lParam) );
833
834         if( HIWORD(msg->lParam) & ASCII_CHAR_HACK )
835
836         /*  if( ToAscii( msg->wParam, HIWORD(msg->lParam), (LPSTR)&KeyStateTable,
837                                       wparam, 0 ) ) 
838          */
839               {
840                 message += 2 - (message & 0x0001); 
841
842                 PostMessage( msg->hwnd, message, lastEventChar, msg->lParam );
843
844                 return TRUE;
845               }
846     }
847     return FALSE;
848 }
849
850
851 /***********************************************************************
852  *           DispatchMessage   (USER.114)
853  */
854 LONG DispatchMessage( const MSG* msg )
855 {
856     WND * wndPtr;
857     LONG retval;
858     int painting;
859     
860       /* Process timer messages */
861     if ((msg->message == WM_TIMER) || (msg->message == WM_SYSTIMER))
862     {
863         if (msg->lParam)
864         {
865 /*            HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */
866             return CallWindowProc16( (WNDPROC)msg->lParam, msg->hwnd,
867                                    msg->message, msg->wParam, GetTickCount() );
868         }
869     }
870
871     if (!msg->hwnd) return 0;
872     if (!(wndPtr = WIN_FindWndPtr( msg->hwnd ))) return 0;
873     if (!wndPtr->winproc) return 0;
874     painting = (msg->message == WM_PAINT);
875     if (painting) wndPtr->flags |= WIN_NEEDS_BEGINPAINT;
876 /*    HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */
877
878     SPY_EnterMessage( SPY_DISPATCHMESSAGE16, msg->hwnd, msg->message,
879                       msg->wParam, msg->lParam );
880     retval = CallWindowProc16( wndPtr->winproc, msg->hwnd, msg->message,
881                                msg->wParam, msg->lParam );
882     SPY_ExitMessage( SPY_RESULT_OK16, msg->hwnd, msg->message, retval );
883
884     if (painting && (wndPtr = WIN_FindWndPtr( msg->hwnd )) &&
885         (wndPtr->flags & WIN_NEEDS_BEGINPAINT) && wndPtr->hrgnUpdate)
886     {
887         fprintf(stderr, "BeginPaint not called on WM_PAINT for hwnd %04x!\n", 
888                 msg->hwnd);
889         wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
890         /* Validate the update region to avoid infinite WM_PAINT loop */
891         ValidateRect32( msg->hwnd, NULL );
892     }
893     return retval;
894 }
895
896
897 /***********************************************************************
898  *           RegisterWindowMessage16   (USER.118)
899  */
900 WORD RegisterWindowMessage16( SEGPTR str )
901 {
902     dprintf_msg(stddeb, "RegisterWindowMessage16: %08lx\n", (DWORD)str );
903     return GlobalAddAtom16( str );
904 }
905
906
907 /***********************************************************************
908  *           RegisterWindowMessage32A   (USER32.436)
909  */
910 WORD RegisterWindowMessage32A( LPCSTR str )
911 {
912     dprintf_msg(stddeb, "RegisterWindowMessage32A: %s\n", str );
913     return GlobalAddAtom32A( str );
914 }
915
916
917 /***********************************************************************
918  *           RegisterWindowMessage32W   (USER32.437)
919  */
920 WORD RegisterWindowMessage32W( LPCWSTR str )
921 {
922     dprintf_msg(stddeb, "RegisterWindowMessage32W: %p\n", str );
923     return GlobalAddAtom32W( str );
924 }
925
926
927 /***********************************************************************
928  *           GetTickCount    (USER.13) (KERNEL32.299)
929  */
930 DWORD GetTickCount(void)
931 {
932     struct timeval t;
933     gettimeofday( &t, NULL );
934     return ((t.tv_sec * 1000) + (t.tv_usec / 1000)) - MSG_WineStartTicks;
935 }
936
937
938 /***********************************************************************
939  *           GetCurrentTime    (USER.15)
940  *
941  * (effectively identical to GetTickCount)
942  */
943 DWORD GetCurrentTime(void)
944 {
945     return GetTickCount();
946 }
947
948
949 /***********************************************************************
950  *           InSendMessage    (USER.192)
951  */
952 BOOL InSendMessage()
953 {
954     MESSAGEQUEUE *queue;
955
956     if (!(queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) )))
957         return 0;
958     return (BOOL)queue->InSendMessageHandle;
959 }