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