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