Added an unknown VxD error code.
[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 "wine/winbase16.h"
14 #include "wine/winuser16.h"
15 #include "message.h"
16 #include "winerror.h"
17 #include "server.h"
18 #include "win.h"
19 #include "heap.h"
20 #include "hook.h"
21 #include "input.h"
22 #include "spy.h"
23 #include "winpos.h"
24 #include "dde.h"
25 #include "queue.h"
26 #include "winproc.h"
27 #include "user.h"
28 #include "thread.h"
29 #include "task.h"
30 #include "controls.h"
31 #include "debugtools.h"
32
33 DEFAULT_DEBUG_CHANNEL(msg);
34 DECLARE_DEBUG_CHANNEL(key);
35 DECLARE_DEBUG_CHANNEL(sendmsg);
36
37 #define WM_NCMOUSEFIRST         WM_NCMOUSEMOVE
38 #define WM_NCMOUSELAST          WM_NCMBUTTONDBLCLK
39
40     
41 typedef enum { SYSQ_MSG_ABANDON, SYSQ_MSG_SKIP, 
42                SYSQ_MSG_ACCEPT, SYSQ_MSG_CONTINUE } SYSQ_STATUS;
43
44 extern HQUEUE16 hCursorQueue;                    /* queue.c */
45
46 static UINT doubleClickSpeed = 452;
47
48 /***********************************************************************
49  *           MSG_CheckFilter
50  */
51 static BOOL MSG_CheckFilter(DWORD uMsg, DWORD first, DWORD last)
52 {
53    if( first || last )
54        return (uMsg >= first && uMsg <= last);
55    return TRUE;
56 }
57
58 /***********************************************************************
59  *           MSG_SendParentNotify
60  *
61  * Send a WM_PARENTNOTIFY to all ancestors of the given window, unless
62  * the window has the WS_EX_NOPARENTNOTIFY style.
63  */
64 static void MSG_SendParentNotify(WND* wndPtr, WORD event, WORD idChild, LPARAM lValue)
65 {
66     POINT pt;
67
68     /* pt has to be in the client coordinates of the parent window */
69     WND *tmpWnd = WIN_LockWndPtr(wndPtr);
70
71     pt.x = SLOWORD(lValue);
72     pt.y = SHIWORD(lValue);
73     MapWindowPoints( 0, tmpWnd->hwndSelf, &pt, 1 );
74     while (tmpWnd)
75     {
76         if (!(tmpWnd->dwStyle & WS_CHILD) || (tmpWnd->dwExStyle & WS_EX_NOPARENTNOTIFY))
77         {
78             WIN_ReleaseWndPtr(tmpWnd);
79             break;
80         }
81         pt.x += tmpWnd->rectClient.left;
82         pt.y += tmpWnd->rectClient.top;
83         WIN_UpdateWndPtr(&tmpWnd,tmpWnd->parent);
84         SendMessageA( tmpWnd->hwndSelf, WM_PARENTNOTIFY,
85                       MAKEWPARAM( event, idChild ), 
86                       MAKELONG( pt.x, pt.y ) );
87     }
88 }
89
90
91 /***********************************************************************
92  *           MSG_TranslateMouseMsg
93  *
94  * Translate a mouse hardware event into a real mouse message.
95  *
96  * Returns:
97  *
98  *  SYSQ_MSG_ABANDON  - abandon the peek message loop
99  *  SYSQ_MSG_CONTINUE - leave the message in the queue and
100  *                      continue with the translation loop
101  *  SYSQ_MSG_ACCEPT   - proceed to process the translated message
102  */
103 static DWORD MSG_TranslateMouseMsg( HWND hTopWnd, DWORD first, DWORD last,
104                                     MSG *msg, BOOL remove, WND* pWndScope,
105                                     INT *pHitTest, POINT *screen_pt, BOOL *pmouseClick )
106 {
107     static DWORD   dblclk_time_limit = 0;
108     static UINT16     clk_message = 0;
109     static HWND16     clk_hwnd = 0;
110     static POINT clk_pos;
111
112     WND *pWnd;
113     HWND hWnd;
114     INT16 ht, hittest;
115     UINT message = msg->message;
116     POINT pt = msg->pt;
117     HANDLE16 hQ = GetFastQueue16();
118     MESSAGEQUEUE *queue = QUEUE_Lock(hQ);
119     int mouseClick = ((message == WM_LBUTTONDOWN) ||
120                       (message == WM_RBUTTONDOWN) ||
121                       (message == WM_MBUTTONDOWN));
122     DWORD retvalue;
123
124     /* Find the window to dispatch this mouse message to */
125
126     hWnd = GetCapture();
127
128     /* If no capture HWND, find window which contains the mouse position.
129      * Also find the position of the cursor hot spot (hittest) */
130     if( !hWnd )
131     {
132         ht = hittest = WINPOS_WindowFromPoint( pWndScope, pt, &pWnd );
133         if( !pWnd ) pWnd = WIN_GetDesktop();
134         else WIN_LockWndPtr(pWnd);
135         hWnd = pWnd->hwndSelf;
136     } 
137     else 
138     {
139         ht = hittest = HTCLIENT;
140         pWnd = WIN_FindWndPtr(hWnd);
141         if (queue)
142             ht = PERQDATA_GetCaptureInfo( queue->pQData );
143     }
144
145     /* Save hittest for return */
146     *pHitTest = hittest;
147         
148         /* stop if not the right queue */
149
150     if (pWnd->hmemTaskQ && pWnd->hmemTaskQ != hQ)
151     {
152         /* Not for the current task */
153         if (queue) QUEUE_ClearWakeBit( queue, QS_MOUSE );
154         /* Wake up the other task */
155         QUEUE_Unlock( queue );
156         queue = QUEUE_Lock( pWnd->hmemTaskQ );
157         if (queue) QUEUE_SetWakeBit( queue, QS_MOUSE, 0 );
158
159         QUEUE_Unlock( queue );
160         retvalue = SYSQ_MSG_ABANDON;
161         goto END;
162     }
163
164         /* check if hWnd is within hWndScope */
165
166     if( hTopWnd && hWnd != hTopWnd )
167         if( !IsChild(hTopWnd, hWnd) )
168         {
169             QUEUE_Unlock( queue );
170             retvalue = SYSQ_MSG_CONTINUE;
171             goto END;
172         }
173
174     /* Was it a mouse click message */
175     if( mouseClick )
176     {
177         /* translate double clicks -
178          * note that ...MOUSEMOVEs can slip in between
179          * ...BUTTONDOWN and ...BUTTONDBLCLK messages */
180
181         if(GetClassLongA(hWnd, GCL_STYLE) & CS_DBLCLKS || ht != HTCLIENT )
182         {
183            if ((message == clk_message) && (hWnd == clk_hwnd) &&
184                (msg->time - dblclk_time_limit < doubleClickSpeed) &&
185                (abs(msg->pt.x - clk_pos.x) < GetSystemMetrics(SM_CXDOUBLECLK)/2) &&
186                (abs(msg->pt.y - clk_pos.y) < GetSystemMetrics(SM_CYDOUBLECLK)/2))
187            {
188               message += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
189               mouseClick++;   /* == 2 */
190            }
191         }
192     }
193     /* save mouse position */
194     *screen_pt = pt;
195
196     if (hittest != HTCLIENT)
197     {
198         message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
199         msg->wParam = hittest;
200     }
201     else
202         ScreenToClient( hWnd, &pt );
203
204         /* check message filter */
205
206     if (!MSG_CheckFilter(message, first, last))
207     {
208         QUEUE_Unlock(queue);
209         retvalue = SYSQ_MSG_CONTINUE;
210         goto END;
211     }
212
213     /* Update global hCursorQueue */
214     hCursorQueue = queue->self;
215     
216     /* Update static double click conditions */
217     if( remove && mouseClick )
218     {
219         if( mouseClick == 1 )
220         {
221             /* set conditions */
222             dblclk_time_limit = msg->time;
223             clk_message = msg->message;
224             clk_hwnd = hWnd;
225             clk_pos = *screen_pt;
226         } else 
227             /* got double click - zero them out */
228             dblclk_time_limit = clk_hwnd = 0;
229     }
230
231     QUEUE_Unlock(queue);
232
233     /* Update message params */
234     msg->hwnd    = hWnd;
235     msg->message = message;
236     msg->lParam  = MAKELONG( pt.x, pt.y );
237     retvalue = SYSQ_MSG_ACCEPT;
238
239 END:
240     WIN_ReleaseWndPtr(pWnd);
241
242     /* Return mouseclick flag */
243     *pmouseClick = mouseClick;
244     
245     return retvalue;
246 }
247
248
249 /***********************************************************************
250  *           MSG_ProcessMouseMsg
251  *
252  * Processes a translated mouse hardware event.
253  * The passed in msg structure should contain the updated hWnd, 
254  * lParam, wParam and message fields from MSG_TranslateMouseMsg.
255  *
256  * Returns:
257  *
258  *  SYSQ_MSG_SKIP     - Message should be skipped entirely (in this case
259  *                      HIWORD contains hit test code). Continue translating..
260  *  SYSQ_MSG_ACCEPT   - the translated message must be passed to the user
261  *                      MSG_PeekHardwareMsg should return TRUE.
262  */
263 static DWORD MSG_ProcessMouseMsg( MSG *msg, BOOL remove, INT hittest,
264                                   POINT screen_pt, BOOL mouseClick )
265 {
266     WND *pWnd;
267     HWND hWnd = msg->hwnd;
268     INT16 sendSC = (GetCapture() == 0);
269     UINT message = msg->message;
270     BOOL eatMsg = FALSE;
271     DWORD retvalue;
272
273     pWnd = WIN_FindWndPtr(hWnd);
274     
275         /* call WH_MOUSE */
276
277     if (HOOK_IsHooked( WH_MOUSE ))
278     { 
279         MOUSEHOOKSTRUCT hook;
280         hook.pt           = screen_pt;
281         hook.hwnd         = hWnd;
282         hook.wHitTestCode = hittest;
283         hook.dwExtraInfo  = 0;
284         if (HOOK_CallHooksA( WH_MOUSE, remove ? HC_ACTION : HC_NOREMOVE,
285                              message, (LPARAM)&hook ))
286         {
287             retvalue = MAKELONG((INT16)SYSQ_MSG_SKIP, hittest);
288             goto END;
289         }
290     }
291
292     if (message >= WM_NCMOUSEFIRST && message <= WM_NCMOUSELAST)
293         message += WM_MOUSEFIRST - WM_NCMOUSEFIRST;
294
295     if ((hittest == HTERROR) || (hittest == HTNOWHERE)) 
296         eatMsg = sendSC = 1;
297     else if( remove && mouseClick )
298     {
299         HWND hwndTop = WIN_GetTopParent( hWnd );
300
301         if( sendSC )
302         {
303             /* Send the WM_PARENTNOTIFY,
304              * note that even for double/nonclient clicks
305              * notification message is still WM_L/M/RBUTTONDOWN.
306              */
307
308             MSG_SendParentNotify( pWnd, message, 0, MAKELPARAM(screen_pt.x, screen_pt.y) );
309
310             /* Activate the window if needed */
311
312             if (hWnd != GetActiveWindow() && hwndTop != GetDesktopWindow())
313             {
314                 LONG ret = SendMessageA( hWnd, WM_MOUSEACTIVATE, hwndTop,
315                                           MAKELONG( hittest, message ) );
316
317                 if ((ret == MA_ACTIVATEANDEAT) || (ret == MA_NOACTIVATEANDEAT))
318                          eatMsg = TRUE;
319
320                 if (((ret == MA_ACTIVATE) || (ret == MA_ACTIVATEANDEAT))
321                         && hwndTop != GetForegroundWindow() )
322                 {
323                       if (!WINPOS_SetActiveWindow( hwndTop, TRUE , TRUE ))
324                          eatMsg = TRUE;
325                 }
326             }
327         }
328     } else sendSC = (remove && sendSC);
329
330      /* Send the WM_SETCURSOR message */
331
332     if (sendSC)
333     {
334         /* Windows sends the normal mouse message as the message parameter
335            in the WM_SETCURSOR message even if it's non-client mouse message */
336         SendMessageA( hWnd, WM_SETCURSOR, hWnd,
337                        MAKELONG( hittest, message ));
338     }
339     if (eatMsg)
340     {
341         retvalue = MAKELONG( (UINT16)SYSQ_MSG_SKIP, hittest);
342         goto END;
343     }
344
345     retvalue = SYSQ_MSG_ACCEPT;
346 END:
347     WIN_ReleaseWndPtr(pWnd);
348
349     return retvalue;
350 }
351
352
353 /***********************************************************************
354  *           MSG_TranslateKbdMsg
355  *
356  * Translate a keyboard hardware event into a real message.
357  */
358 static DWORD MSG_TranslateKbdMsg( HWND hTopWnd, DWORD first, DWORD last,
359                                   MSG *msg, BOOL remove )
360 {
361     WORD message = msg->message;
362     HWND hWnd = GetFocus();
363     WND *pWnd;
364
365       /* Should check Ctrl-Esc and PrintScreen here */
366
367     if (!hWnd)
368     {
369           /* Send the message to the active window instead,  */
370           /* translating messages to their WM_SYS equivalent */
371
372         hWnd = GetActiveWindow();
373
374         if( message < WM_SYSKEYDOWN )
375             message += WM_SYSKEYDOWN - WM_KEYDOWN;
376     }
377     if ( !hWnd ) return SYSQ_MSG_ABANDON;
378     pWnd = WIN_FindWndPtr( hWnd );
379
380     if (pWnd && pWnd->hmemTaskQ && (pWnd->hmemTaskQ != GetFastQueue16()))
381     {
382         /* Not for the current task */
383         MESSAGEQUEUE *queue = QUEUE_Lock( GetFastQueue16() );
384         if (queue) QUEUE_ClearWakeBit( queue, QS_KEY );
385         QUEUE_Unlock( queue );
386         
387         /* Wake up the other task */
388         queue = QUEUE_Lock( pWnd->hmemTaskQ );
389         if (queue) QUEUE_SetWakeBit( queue, QS_KEY, 0 );
390         QUEUE_Unlock( queue );
391         WIN_ReleaseWndPtr(pWnd);
392         return SYSQ_MSG_ABANDON;
393     }
394     WIN_ReleaseWndPtr(pWnd);
395
396     if (hTopWnd && hWnd != hTopWnd)
397         if (!IsChild(hTopWnd, hWnd)) return SYSQ_MSG_CONTINUE;
398     if (!MSG_CheckFilter(message, first, last)) return SYSQ_MSG_CONTINUE;
399
400     msg->hwnd = hWnd;
401     msg->message = message;
402
403     return SYSQ_MSG_ACCEPT;
404 }
405
406
407 /***********************************************************************
408  *           MSG_ProcessKbdMsg
409  *
410  *  Processes a translated keyboard message
411  */
412 static DWORD MSG_ProcessKbdMsg( MSG *msg, BOOL remove )
413 {
414     /* Handle F1 key by sending out WM_HELP message */
415     if ((msg->message == WM_KEYUP) && 
416         (msg->wParam == VK_F1) &&
417         remove &&
418         (msg->hwnd != GetDesktopWindow()) &&
419         !MENU_IsMenuActive())
420     {   
421         HELPINFO hi;
422         WND *pWnd = WIN_FindWndPtr(msg->hwnd);
423
424         if (NULL != pWnd)
425         {
426             hi.cbSize = sizeof(HELPINFO);
427             hi.iContextType = HELPINFO_WINDOW;
428             hi.iCtrlId = pWnd->wIDmenu; 
429             hi.hItemHandle = msg->hwnd;
430             hi.dwContextId = pWnd->helpContext;
431             hi.MousePos = msg->pt;
432             SendMessageA(msg->hwnd, WM_HELP, 0, (LPARAM)&hi);
433         }
434         WIN_ReleaseWndPtr(pWnd);
435     }
436
437     return (HOOK_CallHooksA( WH_KEYBOARD, remove ? HC_ACTION : HC_NOREMOVE,
438                              LOWORD (msg->wParam), msg->lParam )
439             ? SYSQ_MSG_SKIP : SYSQ_MSG_ACCEPT);
440 }
441
442
443 /***********************************************************************
444  *           MSG_JournalRecordMsg
445  *
446  * Build an EVENTMSG structure and call JOURNALRECORD hook
447  */
448 static void MSG_JournalRecordMsg( MSG *msg )
449 {
450     EVENTMSG event;
451
452     event.message = msg->message;
453     event.time = msg->time;
454     if ((msg->message >= WM_KEYFIRST) && (msg->message <= WM_KEYLAST))
455     {
456         event.paramL = (msg->wParam & 0xFF) | (HIWORD(msg->lParam) << 8);
457         event.paramH = msg->lParam & 0x7FFF;
458         if (HIWORD(msg->lParam) & 0x0100)
459             event.paramH |= 0x8000;               /* special_key - bit */
460         HOOK_CallHooksA( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event );
461     }
462     else if ((msg->message >= WM_MOUSEFIRST) && (msg->message <= WM_MOUSELAST))
463     {
464         POINT pt;
465         pt.x = SLOWORD(msg->lParam);
466         pt.y = SHIWORD(msg->lParam);
467         ClientToScreen( msg->hwnd, &pt );
468         event.paramL = pt.x;
469         event.paramH = pt.y;
470         HOOK_CallHooksA( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event );
471     }
472     else if ((msg->message >= WM_NCMOUSEFIRST) &&
473              (msg->message <= WM_NCMOUSELAST))
474     {
475         event.paramL = LOWORD(msg->lParam);       /* X pos */
476         event.paramH = HIWORD(msg->lParam);       /* Y pos */
477         event.message += WM_MOUSEMOVE-WM_NCMOUSEMOVE;/* give no info about NC area */
478         HOOK_CallHooksA( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event );
479     }
480 }
481
482 /***********************************************************************
483  *          MSG_JournalPlayBackMsg
484  *
485  * Get an EVENTMSG struct via call JOURNALPLAYBACK hook function 
486  */
487 static int MSG_JournalPlayBackMsg(void)
488 {
489     EVENTMSG tmpMsg;
490     LPARAM lParam;
491     WPARAM wParam;
492     LRESULT wtime;
493     int keyDown,i;
494
495     if (!HOOK_IsHooked( WH_JOURNALPLAYBACK )) return 0;
496
497     wtime=HOOK_CallHooksA( WH_JOURNALPLAYBACK, HC_GETNEXT, 0, (LPARAM)&tmpMsg );
498     /*  TRACE(msg,"Playback wait time =%ld\n",wtime); */
499     if (wtime<=0)
500     {
501         wtime=0;
502         if ((tmpMsg.message >= WM_KEYFIRST) && (tmpMsg.message <= WM_KEYLAST))
503         {
504             wParam=tmpMsg.paramL & 0xFF;
505             lParam=MAKELONG(tmpMsg.paramH&0x7ffff,tmpMsg.paramL>>8);
506             if (tmpMsg.message == WM_KEYDOWN || tmpMsg.message == WM_SYSKEYDOWN)
507             {
508                 for (keyDown=i=0; i<256 && !keyDown; i++)
509                     if (InputKeyStateTable[i] & 0x80)
510                         keyDown++;
511                 if (!keyDown)
512                     lParam |= 0x40000000;
513                 AsyncKeyStateTable[wParam]=InputKeyStateTable[wParam] |= 0x80;
514             }
515             else                                       /* WM_KEYUP, WM_SYSKEYUP */
516             {
517                 lParam |= 0xC0000000;
518                 AsyncKeyStateTable[wParam]=InputKeyStateTable[wParam] &= ~0x80;
519             }
520             if (InputKeyStateTable[VK_MENU] & 0x80)
521                 lParam |= 0x20000000;
522             if (tmpMsg.paramH & 0x8000)              /*special_key bit*/
523                 lParam |= 0x01000000;
524             hardware_event( tmpMsg.message, wParam, lParam, 0, 0, tmpMsg.time, 0 );
525         }
526         else if ((tmpMsg.message>= WM_MOUSEFIRST) && (tmpMsg.message <= WM_MOUSELAST))
527         {
528             switch (tmpMsg.message)
529             {
530             case WM_LBUTTONDOWN:
531                 MouseButtonsStates[0]=AsyncMouseButtonsStates[0]=TRUE;break;
532             case WM_LBUTTONUP:
533                 MouseButtonsStates[0]=AsyncMouseButtonsStates[0]=FALSE;break;
534             case WM_MBUTTONDOWN:
535                 MouseButtonsStates[1]=AsyncMouseButtonsStates[1]=TRUE;break;
536             case WM_MBUTTONUP:
537                 MouseButtonsStates[1]=AsyncMouseButtonsStates[1]=FALSE;break;
538             case WM_RBUTTONDOWN:
539                 MouseButtonsStates[2]=AsyncMouseButtonsStates[2]=TRUE;break;
540             case WM_RBUTTONUP:
541                 MouseButtonsStates[2]=AsyncMouseButtonsStates[2]=FALSE;break;
542             }
543             AsyncKeyStateTable[VK_LBUTTON]= InputKeyStateTable[VK_LBUTTON] = MouseButtonsStates[0] ? 0x80 : 0;
544             AsyncKeyStateTable[VK_MBUTTON]= InputKeyStateTable[VK_MBUTTON] = MouseButtonsStates[1] ? 0x80 : 0;
545             AsyncKeyStateTable[VK_RBUTTON]= InputKeyStateTable[VK_RBUTTON] = MouseButtonsStates[2] ? 0x80 : 0;
546             SetCursorPos(tmpMsg.paramL,tmpMsg.paramH);
547             lParam=MAKELONG(tmpMsg.paramL,tmpMsg.paramH);
548             wParam=0;
549             if (MouseButtonsStates[0]) wParam |= MK_LBUTTON;
550             if (MouseButtonsStates[1]) wParam |= MK_MBUTTON;
551             if (MouseButtonsStates[2]) wParam |= MK_RBUTTON;
552             hardware_event( tmpMsg.message, wParam, lParam,
553                             tmpMsg.paramL, tmpMsg.paramH, tmpMsg.time, 0 );
554         }
555         HOOK_CallHooksA( WH_JOURNALPLAYBACK, HC_SKIP, 0, (LPARAM)&tmpMsg);
556         return 0;
557     }
558     else
559     {
560         if( tmpMsg.message == WM_QUEUESYNC )
561             if (HOOK_IsHooked( WH_CBT ))
562                 HOOK_CallHooksA( WH_CBT, HCBT_QS, 0, 0L);
563
564         return QS_MOUSE | QS_KEY; /* ? */
565     }
566 }
567
568 /***********************************************************************
569  *           MSG_PeekHardwareMsg
570  *
571  * Peek for a hardware message matching the hwnd and message filters.
572  */
573 static BOOL MSG_PeekHardwareMsg( MSG *msg, HWND hwnd, DWORD first, DWORD last,
574                                    BOOL remove )
575 {
576     DWORD status = SYSQ_MSG_ACCEPT;
577     MESSAGEQUEUE *sysMsgQueue = QUEUE_GetSysQueue();
578     enum { MOUSE_MSG = 0, KEYBOARD_MSG, HARDWARE_MSG } msgType;
579     QMSG *nextqmsg, *qmsg = 0;
580     BOOL bRet = FALSE;
581
582     EnterCriticalSection(&sysMsgQueue->cSection);
583
584     /* Loop through the Q and translate the message we wish to process
585      * while we own the lock. Based on the translation status (abandon/cont/accept)
586      * we then process the message accordingly
587      */
588
589     for ( qmsg = sysMsgQueue->firstMsg; qmsg; qmsg = nextqmsg )
590     {
591         INT hittest;
592         POINT screen_pt;
593         BOOL mouseClick;
594
595         *msg = qmsg->msg;
596
597         nextqmsg = qmsg->nextMsg;
598
599           /* Translate message */
600
601         if ((msg->message >= WM_MOUSEFIRST) && (msg->message <= WM_MOUSELAST))
602         {
603             HWND hWndScope = (HWND)qmsg->extraInfo;
604             WND *tmpWnd = IsWindow(hWndScope) ? WIN_FindWndPtr(hWndScope) : WIN_GetDesktop();
605
606             status = MSG_TranslateMouseMsg(hwnd, first, last, msg, remove, tmpWnd,
607                                            &hittest, &screen_pt, &mouseClick );
608             msgType = MOUSE_MSG;
609             
610             WIN_ReleaseWndPtr(tmpWnd);
611
612         }
613         else if ((msg->message >= WM_KEYFIRST) && (msg->message <= WM_KEYLAST))
614         {
615             status = MSG_TranslateKbdMsg(hwnd, first, last, msg, remove);
616             msgType = KEYBOARD_MSG;
617         }
618         else /* Non-standard hardware event */
619         {
620             HARDWAREHOOKSTRUCT16 *hook;
621             msgType = HARDWARE_MSG;
622             if ((hook = SEGPTR_NEW(HARDWAREHOOKSTRUCT16)))
623             {
624                 BOOL ret;
625                 hook->hWnd     = msg->hwnd;
626                 hook->wMessage = msg->message & 0xffff;
627                 hook->wParam   = LOWORD (msg->wParam);
628                 hook->lParam   = msg->lParam;
629                 ret = HOOK_CallHooks16( WH_HARDWARE,
630                                         remove ? HC_ACTION : HC_NOREMOVE,
631                                         0, (LPARAM)SEGPTR_GET(hook) );
632                 SEGPTR_FREE(hook);
633                 if (ret) 
634                 {
635                     QUEUE_RemoveMsg( sysMsgQueue, qmsg );
636                     continue;
637                 }
638                 status = SYSQ_MSG_ACCEPT; 
639             }
640         }
641
642         
643         switch (LOWORD(status))
644         {
645            case SYSQ_MSG_ACCEPT:
646            {
647                /* Remove the message from the system msg Q while it is still locked,
648                 * before accepting it */
649                if (remove)
650                {
651                    if (HOOK_IsHooked( WH_JOURNALRECORD )) MSG_JournalRecordMsg( msg );
652                    QUEUE_RemoveMsg( sysMsgQueue, qmsg );
653                }
654                /* Now actually process the message, after we unlock the system msg Q.
655                 * We should not hold on to the crst since SendMessage calls during processing 
656                 * will potentially cause callbacks to PeekMessage from the application.
657                 * If we're holding the crst and QUEUE_WaitBits is called with a
658                 * QS_SENDMESSAGE mask we will deadlock in hardware_event() when a
659                 * message is being posted to the Q.
660                 */
661                LeaveCriticalSection(&sysMsgQueue->cSection);
662                if( msgType == KEYBOARD_MSG )
663                    status = MSG_ProcessKbdMsg( msg, remove );
664                else if ( msgType == MOUSE_MSG )
665                    status = MSG_ProcessMouseMsg( msg, remove, hittest, screen_pt, mouseClick );
666
667                /* Reclaim the sys msg Q crst */
668                EnterCriticalSection(&sysMsgQueue->cSection);
669                
670                /* Pass the translated message to the user if it was accepted */
671                if (status == SYSQ_MSG_ACCEPT)
672                 break;
673
674                /* If not accepted, fall through into the SYSQ_MSG_SKIP case */
675            }
676                 
677            case SYSQ_MSG_SKIP:
678                 if (HOOK_IsHooked( WH_CBT ))
679                 {
680                    if( msgType == KEYBOARD_MSG )
681                        HOOK_CallHooksA( WH_CBT, HCBT_KEYSKIPPED,
682                                          LOWORD (msg->wParam), msg->lParam );
683                    else if ( msgType == MOUSE_MSG )
684                    {
685                        MOUSEHOOKSTRUCT hook;
686                        hook.pt           = msg->pt;
687                        hook.hwnd         = msg->hwnd;
688                        hook.wHitTestCode = HIWORD(status);
689                        hook.dwExtraInfo  = 0;
690                        HOOK_CallHooksA( WH_CBT, HCBT_CLICKSKIPPED, msg->message, (LPARAM)&hook );
691                    }
692                 }
693
694                 /* If the message was removed earlier set up nextqmsg so that we start 
695                  * at the top of the queue again.  We need to do this since our next pointer
696                  * could be invalid due to us unlocking the system message Q to process the message.
697                  * If not removed just refresh nextqmsg to point to the next msg.
698                  */
699                 if (remove)
700                     nextqmsg = sysMsgQueue->firstMsg;
701                 else
702                     nextqmsg = qmsg->nextMsg;
703
704                 continue;
705                 
706            case SYSQ_MSG_CONTINUE:
707                 continue;
708
709            case SYSQ_MSG_ABANDON: 
710                bRet = FALSE;
711                goto END;
712         }
713
714         bRet = TRUE;
715         goto END;
716     }
717
718 END:
719     LeaveCriticalSection(&sysMsgQueue->cSection);
720     return bRet;
721 }
722
723
724 /**********************************************************************
725  *              SetDoubleClickTime (USER.20)
726  */
727 void WINAPI SetDoubleClickTime16( UINT16 interval )
728 {
729     SetDoubleClickTime( interval );
730 }               
731
732
733 /**********************************************************************
734  *              SetDoubleClickTime (USER32.@)
735  */
736 BOOL WINAPI SetDoubleClickTime( UINT interval )
737 {
738     doubleClickSpeed = interval ? interval : 500;
739     return TRUE;
740 }               
741
742
743 /**********************************************************************
744  *              GetDoubleClickTime (USER.21)
745  */
746 UINT16 WINAPI GetDoubleClickTime16(void)
747 {
748     return doubleClickSpeed;
749 }               
750
751
752 /**********************************************************************
753  *              GetDoubleClickTime (USER32.@)
754  */
755 UINT WINAPI GetDoubleClickTime(void)
756 {
757     return doubleClickSpeed;
758 }               
759
760
761 /***********************************************************************
762  *           MSG_SendMessageInterThread
763  *
764  * Implementation of an inter-task SendMessage.
765  * Return values:
766  *    0 if error or timeout
767  *    1 if successful
768  */
769 static LRESULT MSG_SendMessageInterThread( HQUEUE16 hDestQueue,
770                                            HWND hwnd, UINT msg,
771                                            WPARAM wParam, LPARAM lParam,
772                                            DWORD timeout, WORD type,
773                                            LRESULT *pRes)
774 {
775     MESSAGEQUEUE *destQ;
776     BOOL ret;
777     int iWndsLocks;
778     LRESULT result = 0;
779
780     TRACE( "hwnd %x msg %x (%s) wp %x lp %lx\n", hwnd, msg, SPY_GetMsgName(msg), wParam, lParam );
781
782     if (!(destQ = QUEUE_Lock( hDestQueue ))) return 0;
783
784     SERVER_START_REQ( send_message )
785     {
786         req->posted = FALSE;
787         req->id     = destQ->teb->tid;
788         req->type   = type;
789         req->win    = hwnd;
790         req->msg    = msg;
791         req->wparam = wParam;
792         req->lparam = lParam;
793         req->info   = 0;
794         ret = !SERVER_CALL_ERR();
795     }
796     SERVER_END_REQ;
797     QUEUE_Unlock( destQ );
798     if (!ret) return 0;
799
800     iWndsLocks = WIN_SuspendWndsLock();
801
802     /* wait for the result */
803     QUEUE_WaitBits( QS_SMRESULT, timeout );
804
805     SERVER_START_REQ( get_message_reply )
806     {
807         req->cancel = 1;
808         if ((ret = !SERVER_CALL_ERR())) result = req->result;
809     }
810     SERVER_END_REQ;
811
812     TRACE( "hwnd %x msg %x (%s) wp %x lp %lx got reply %lx (err=%ld)\n",
813            hwnd, msg, SPY_GetMsgName(msg), wParam, lParam, result, GetLastError() );
814
815     if (!ret && (GetLastError() == ERROR_IO_PENDING))
816     {
817         if (timeout == INFINITE) ERR("no timeout but no result\n");
818         SetLastError(0);  /* timeout */
819     }
820
821     if (pRes) *pRes = result;
822
823     WIN_RestoreWndsLock(iWndsLocks);
824     return ret;
825 }
826
827
828 /***********************************************************************
829  *              ReplyMessage (USER.115)
830  */
831 void WINAPI ReplyMessage16( LRESULT result )
832 {
833     ReplyMessage( result );
834 }
835
836 /***********************************************************************
837  *              ReplyMessage (USER32.@)
838  */
839 BOOL WINAPI ReplyMessage( LRESULT result )
840 {
841     BOOL ret;
842     SERVER_START_REQ( reply_message )
843     {
844         req->result = result;
845         req->remove = 0;
846         ret = !SERVER_CALL_ERR();
847     }
848     SERVER_END_REQ;
849     return ret;
850 }
851
852 /***********************************************************************
853  *           MSG_ConvertMsg
854  */
855 static BOOL MSG_ConvertMsg( MSG *msg, int srcType, int dstType )
856 {
857     UINT16 msg16;
858     MSGPARAM16 mp16;
859
860     switch ( MAKELONG( srcType, dstType ) )
861     {
862     case MAKELONG( QMSG_WIN16,  QMSG_WIN16 ):
863     case MAKELONG( QMSG_WIN32A, QMSG_WIN32A ):
864     case MAKELONG( QMSG_WIN32W, QMSG_WIN32W ):
865         return TRUE;
866
867     case MAKELONG( QMSG_WIN16, QMSG_WIN32A ):
868         switch ( WINPROC_MapMsg16To32A( msg->message, msg->wParam,
869                                        &msg->message, &msg->wParam, &msg->lParam ) )
870         {
871         case 0:
872             return TRUE;
873         case 1:
874             /* Pointer messages were mapped --> need to free allocated memory and fail */
875             WINPROC_UnmapMsg16To32A( msg->hwnd, msg->message, msg->wParam, msg->lParam, 0 );
876         default:
877             return FALSE;
878         }
879
880     case MAKELONG( QMSG_WIN16, QMSG_WIN32W ):
881         switch ( WINPROC_MapMsg16To32W( msg->hwnd, msg->message, msg->wParam,
882                                        &msg->message, &msg->wParam, &msg->lParam ) )
883         {
884         case 0:
885             return TRUE;
886         case 1:
887             /* Pointer messages were mapped --> need to free allocated memory and fail */
888             WINPROC_UnmapMsg16To32W( msg->hwnd, msg->message, msg->wParam, msg->lParam, 0 );
889         default:
890             return FALSE;
891         }
892
893     case MAKELONG( QMSG_WIN32A, QMSG_WIN16 ):
894         mp16.lParam = msg->lParam;
895         switch ( WINPROC_MapMsg32ATo16( msg->hwnd, msg->message, msg->wParam,
896                                         &msg16, &mp16.wParam, &mp16.lParam ) )
897         {
898         case 0:
899             msg->message = msg16; 
900             msg->wParam  = mp16.wParam;
901             msg->lParam  = mp16.lParam;
902             return TRUE;
903         case 1:
904             /* Pointer messages were mapped --> need to free allocated memory and fail */
905             WINPROC_UnmapMsg32ATo16( msg->hwnd, msg->message, msg->wParam, msg->lParam, &mp16 );
906         default:
907             return FALSE;
908         }
909
910     case MAKELONG( QMSG_WIN32W, QMSG_WIN16 ):
911         mp16.lParam = msg->lParam;
912         switch ( WINPROC_MapMsg32WTo16( msg->hwnd, msg->message, msg->wParam,
913                                         &msg16, &mp16.wParam, &mp16.lParam ) )
914         {
915         case 0:
916             msg->message = msg16; 
917             msg->wParam  = mp16.wParam;
918             msg->lParam  = mp16.lParam;
919             return TRUE;
920         case 1:
921             /* Pointer messages were mapped --> need to free allocated memory and fail */
922             WINPROC_UnmapMsg32WTo16( msg->hwnd, msg->message, msg->wParam, msg->lParam, &mp16 );
923         default:
924             return FALSE;
925         }
926
927     case MAKELONG( QMSG_WIN32A, QMSG_WIN32W ):
928         switch ( WINPROC_MapMsg32ATo32W( msg->hwnd, msg->message, &msg->wParam, &msg->lParam ) )
929         {
930         case 0:
931             return TRUE;
932         case 1:
933             /* Pointer messages were mapped --> need to free allocated memory and fail */
934             WINPROC_UnmapMsg32ATo32W( msg->hwnd, msg->message, msg->wParam, msg->lParam );
935         default:
936             return FALSE;
937         }
938
939     case MAKELONG( QMSG_WIN32W, QMSG_WIN32A ):
940         switch ( WINPROC_MapMsg32WTo32A( msg->hwnd, msg->message, &msg->wParam, &msg->lParam ) )
941         {
942         case 0:
943             return TRUE;
944         case 1:
945             /* Pointer messages were mapped --> need to free allocated memory and fail */
946             WINPROC_UnmapMsg32WTo32A( msg->hwnd, msg->message, msg->wParam, msg->lParam );
947         default:
948             return FALSE;
949         }
950
951     default:    
952         FIXME( "Invalid message type(s): %d / %d\n", srcType, dstType );
953         return FALSE;
954     }
955 }
956
957 /***********************************************************************
958  *           MSG_PeekMessage
959  */
960 static BOOL MSG_PeekMessage( int type, LPMSG msg_out, HWND hwnd, 
961                              DWORD first, DWORD last, WORD flags, BOOL peek )
962 {
963     int changeBits, mask;
964     MESSAGEQUEUE *msgQueue;
965     HQUEUE16 hQueue;
966     int iWndsLocks;
967     MSG msg;
968
969     mask = QS_POSTMESSAGE | QS_SENDMESSAGE;  /* Always selected */
970     if (first || last)
971     {
972         if ((first <= WM_KEYLAST) && (last >= WM_KEYFIRST)) mask |= QS_KEY;
973         if ( ((first <= WM_MOUSELAST) && (last >= WM_MOUSEFIRST)) ||
974              ((first <= WM_NCMOUSELAST) && (last >= WM_NCMOUSEFIRST)) ) mask |= QS_MOUSE;
975         if ((first <= WM_TIMER) && (last >= WM_TIMER)) mask |= QS_TIMER;
976         if ((first <= WM_SYSTIMER) && (last >= WM_SYSTIMER)) mask |= QS_TIMER;
977         if ((first <= WM_PAINT) && (last >= WM_PAINT)) mask |= QS_PAINT;
978     }
979     else mask |= QS_MOUSE | QS_KEY | QS_TIMER | QS_PAINT;
980
981     if (IsTaskLocked16()) flags |= PM_NOYIELD;
982
983     iWndsLocks = WIN_SuspendWndsLock();
984
985     while(1)
986     {
987         WORD wakeBits = HIWORD(GetQueueStatus( mask ));
988
989         hQueue   = GetFastQueue16();
990         msgQueue = QUEUE_Lock( hQueue );
991         if (!msgQueue)
992         {
993             WIN_RestoreWndsLock(iWndsLocks);
994             return FALSE;
995         }
996
997 #if 0
998         /* First handle a message put by SendMessage() */
999
1000         while ( QUEUE_ReceiveMessage( msgQueue ) )
1001             ;
1002
1003         /* Now handle a WM_QUIT message */
1004
1005         EnterCriticalSection( &msgQueue->cSection );
1006         if (msgQueue->wPostQMsg &&
1007            (!first || WM_QUIT >= first) && 
1008            (!last || WM_QUIT <= last) )
1009         {
1010             msg.hwnd    = hwnd;
1011             msg.message = WM_QUIT;
1012             msg.wParam  = msgQueue->wExitCode;
1013             msg.lParam  = 0;
1014             if (flags & PM_REMOVE) msgQueue->wPostQMsg = 0;
1015             LeaveCriticalSection( &msgQueue->cSection );
1016             break;
1017         }
1018         LeaveCriticalSection( &msgQueue->cSection );
1019 #endif
1020
1021         /* Now find a normal message */
1022
1023   retry:
1024         if (wakeBits & (QS_SENDMESSAGE|QS_POSTMESSAGE|QS_TIMER|QS_PAINT))
1025         {
1026             QMSG qmsg;
1027             if (QUEUE_FindMsg( hwnd, first, last, flags & PM_REMOVE, FALSE, &qmsg ))
1028             {
1029                 /* Try to convert message to requested type */
1030                 MSG tmpMsg = qmsg.msg;
1031                 if ( !MSG_ConvertMsg( &tmpMsg, qmsg.type, type ) )
1032                 {
1033                     ERR( "Message %s of wrong type contains pointer parameters. Skipped!\n",
1034                          SPY_GetMsgName(tmpMsg.message));
1035                     /* remove it (FIXME) */
1036                     if (!(flags & PM_REMOVE)) QUEUE_FindMsg( hwnd, first, last, TRUE, FALSE, &qmsg );
1037                     goto retry;
1038                 }
1039
1040                 msg = tmpMsg;
1041                 msgQueue->GetMessageTimeVal      = msg.time;
1042                 msgQueue->GetMessagePosVal       = MAKELONG( (INT16)msg.pt.x, (INT16)msg.pt.y );
1043                 msgQueue->GetMessageExtraInfoVal = qmsg.extraInfo;
1044
1045                 /* need to fill the window handle for WM_PAINT message */
1046                 if (msg.message == WM_PAINT)
1047                 {
1048                     WND* wndPtr;
1049                     msg.hwnd = WIN_FindWinToRepaint( hwnd , hQueue );
1050                     if ((wndPtr = WIN_FindWndPtr(msg.hwnd)))
1051                     {
1052                         if( wndPtr->dwStyle & WS_MINIMIZE &&
1053                             (HICON) GetClassLongA(wndPtr->hwndSelf, GCL_HICON) )
1054                         {
1055                             msg.message = WM_PAINTICON;
1056                             msg.wParam = 1;
1057                         }
1058
1059                         if( !hwnd || msg.hwnd == hwnd || IsChild16(hwnd,msg.hwnd) )
1060                         {
1061                             if( wndPtr->flags & WIN_INTERNAL_PAINT && !wndPtr->hrgnUpdate)
1062                             {
1063                                 wndPtr->flags &= ~WIN_INTERNAL_PAINT;
1064                                 QUEUE_DecPaintCount( hQueue );
1065                             }
1066                             WIN_ReleaseWndPtr(wndPtr);
1067                             break;
1068                         }
1069                         WIN_ReleaseWndPtr(wndPtr);
1070                     }
1071                 }
1072                 else break;
1073             }
1074         }
1075
1076         changeBits = MSG_JournalPlayBackMsg();
1077 #if 0  /* FIXME */
1078         EnterCriticalSection( &msgQueue->cSection );
1079         msgQueue->changeBits |= changeBits;
1080         LeaveCriticalSection( &msgQueue->cSection );
1081 #endif
1082
1083         /* Now find a hardware event */
1084
1085         if (MSG_PeekHardwareMsg( &msg, hwnd, first, last, flags & PM_REMOVE ))
1086         {
1087             /* Got one */
1088             msgQueue->GetMessageTimeVal      = msg.time;
1089             msgQueue->GetMessagePosVal       = MAKELONG( (INT16)msg.pt.x, (INT16)msg.pt.y );
1090             msgQueue->GetMessageExtraInfoVal = 0;  /* Always 0 for now */
1091             break;
1092         }
1093
1094         if (peek)
1095         {
1096 #if 0  /* FIXME */
1097             if (!(flags & PM_NOYIELD)) UserYield16();
1098 #endif
1099             /* check for graphics events */
1100             if (USER_Driver.pMsgWaitForMultipleObjectsEx)
1101                 USER_Driver.pMsgWaitForMultipleObjectsEx( 0, NULL, 0, 0, 0 );
1102
1103             QUEUE_Unlock( msgQueue );
1104             WIN_RestoreWndsLock(iWndsLocks);
1105             return FALSE;
1106         }
1107
1108         QUEUE_WaitBits( mask, INFINITE );
1109         QUEUE_Unlock( msgQueue );
1110     }
1111
1112     WIN_RestoreWndsLock(iWndsLocks);
1113     
1114     /* instead of unlocking queue for every break condition, all break
1115        condition will fall here */
1116     QUEUE_Unlock( msgQueue );
1117     
1118       /* We got a message */
1119     if (flags & PM_REMOVE)
1120     {
1121         WORD message = msg.message;
1122
1123         if (message == WM_KEYDOWN || message == WM_SYSKEYDOWN)
1124         {
1125             BYTE *p = &QueueKeyStateTable[msg.wParam & 0xff];
1126
1127             if (!(*p & 0x80))
1128                 *p ^= 0x01;
1129             *p |= 0x80;
1130         }
1131         else if (message == WM_KEYUP || message == WM_SYSKEYUP)
1132             QueueKeyStateTable[msg.wParam & 0xff] &= ~0x80;
1133     }
1134
1135     /* copy back our internal safe copy of message data to msg_out.
1136      * msg_out is a variable from the *program*, so it can't be used
1137      * internally as it can get "corrupted" by our use of SendMessage()
1138      * (back to the program) inside the message handling itself. */
1139     *msg_out = msg;
1140     if (peek)
1141         return TRUE;
1142
1143     else
1144         return (msg.message != WM_QUIT);
1145 }
1146
1147 /***********************************************************************
1148  *           MSG_InternalGetMessage
1149  *
1150  * GetMessage() function for internal use. Behave like GetMessage(),
1151  * but also call message filters and optionally send WM_ENTERIDLE messages.
1152  * 'hwnd' must be the handle of the dialog or menu window.
1153  * 'code' is the message filter value (MSGF_??? codes).
1154  */
1155 BOOL MSG_InternalGetMessage( MSG *msg, HWND hwnd, HWND hwndOwner, UINT first, UINT last,
1156                              WPARAM code, WORD flags, BOOL sendIdle, BOOL* idleSent ) 
1157 {
1158     for (;;)
1159     {
1160         if (sendIdle)
1161         {
1162             if (!MSG_PeekMessage( QMSG_WIN32A, msg, 0, first, last, flags, TRUE ))
1163             {
1164                   /* No message present -> send ENTERIDLE and wait */
1165                 if (IsWindow(hwndOwner))
1166                 {
1167                     SendMessageA( hwndOwner, WM_ENTERIDLE,
1168                                    code, (LPARAM)hwnd );
1169
1170                     if (idleSent!=NULL)
1171                       *idleSent=TRUE;
1172                 }
1173                 MSG_PeekMessage( QMSG_WIN32A, msg, 0, first, last, flags, FALSE );
1174             }
1175         }
1176         else  /* Always wait for a message */
1177             MSG_PeekMessage( QMSG_WIN32A, msg, 0, first, last, flags, FALSE );
1178
1179         /* Call message filters */
1180
1181         if (HOOK_IsHooked( WH_SYSMSGFILTER ) || HOOK_IsHooked( WH_MSGFILTER ))
1182         {
1183             MSG *pmsg = HeapAlloc( GetProcessHeap(), 0, sizeof(MSG) );
1184             if (pmsg)
1185             {
1186                 BOOL ret;
1187                 *pmsg = *msg;
1188                 ret = (HOOK_CallHooksA( WH_SYSMSGFILTER, code, 0,
1189                                           (LPARAM) pmsg ) ||
1190                        HOOK_CallHooksA( WH_MSGFILTER, code, 0,
1191                                           (LPARAM) pmsg ));
1192                        
1193                 HeapFree( GetProcessHeap(), 0, pmsg );
1194                 if (ret)
1195                 {
1196                     /* Message filtered -> remove it from the queue */
1197                     /* if it's still there. */
1198                     if (!(flags & PM_REMOVE))
1199                         MSG_PeekMessage( QMSG_WIN32A, msg, 0, first, last, PM_REMOVE, TRUE );
1200                     continue;
1201                 }
1202             }
1203         }
1204
1205         return (msg->message != WM_QUIT);
1206     }
1207 }
1208
1209
1210 /***********************************************************************
1211  *              PeekMessage32 (USER.819)
1212  */
1213 BOOL16 WINAPI PeekMessage32_16( SEGPTR msg16_32, HWND16 hwnd,
1214                                 UINT16 first, UINT16 last, UINT16 flags, 
1215                                 BOOL16 wHaveParamHigh )
1216 {
1217     BOOL ret;
1218     MSG32_16 *lpmsg16_32 = MapSL(msg16_32);
1219     MSG msg;
1220
1221     ret = MSG_PeekMessage( QMSG_WIN16, &msg, hwnd, first, last, flags, TRUE );
1222
1223     lpmsg16_32->msg.hwnd    = msg.hwnd;
1224     lpmsg16_32->msg.message = msg.message;
1225     lpmsg16_32->msg.wParam  = LOWORD(msg.wParam);
1226     lpmsg16_32->msg.lParam  = msg.lParam;
1227     lpmsg16_32->msg.time    = msg.time;
1228     lpmsg16_32->msg.pt.x    = (INT16)msg.pt.x;
1229     lpmsg16_32->msg.pt.y    = (INT16)msg.pt.y;
1230
1231     if ( wHaveParamHigh )
1232         lpmsg16_32->wParamHigh = HIWORD(msg.wParam);
1233
1234     HOOK_CallHooks16( WH_GETMESSAGE, HC_ACTION, flags & PM_REMOVE, (LPARAM)msg16_32 );
1235     return ret;
1236 }
1237
1238 /***********************************************************************
1239  *              PeekMessage (USER.109)
1240  */
1241 BOOL16 WINAPI PeekMessage16( SEGPTR msg, HWND16 hwnd,
1242                              UINT16 first, UINT16 last, UINT16 flags )
1243 {
1244     return PeekMessage32_16( msg, hwnd, first, last, flags, FALSE );
1245 }
1246
1247 /***********************************************************************
1248  *              PeekMessageA (USER32.@)
1249  */
1250 BOOL WINAPI PeekMessageA( LPMSG lpmsg, HWND hwnd,
1251                           UINT min, UINT max, UINT wRemoveMsg)
1252 {
1253     BOOL ret = MSG_PeekMessage( QMSG_WIN32A, lpmsg, hwnd, min, max, wRemoveMsg, TRUE );
1254
1255     TRACE( "peekmessage %04x, hwnd %04x, filter(%04x - %04x)\n", 
1256            lpmsg->message, hwnd, min, max );
1257
1258     if (ret) HOOK_CallHooksA( WH_GETMESSAGE, HC_ACTION,
1259                               wRemoveMsg & PM_REMOVE, (LPARAM)lpmsg );
1260     return ret;
1261 }
1262
1263 /***********************************************************************
1264  *              PeekMessageW (USER32.@) Check queue for messages
1265  *
1266  * Checks for a message in the thread's queue, filtered as for
1267  * GetMessage(). Returns immediately whether a message is available
1268  * or not.
1269  *
1270  * Whether a retrieved message is removed from the queue is set by the
1271  * _wRemoveMsg_ flags, which should be one of the following values:
1272  *
1273  *    PM_NOREMOVE    Do not remove the message from the queue. 
1274  *
1275  *    PM_REMOVE      Remove the message from the queue.
1276  *
1277  * In addition, PM_NOYIELD may be combined into _wRemoveMsg_ to
1278  * request that the system not yield control during PeekMessage();
1279  * however applications may not rely on scheduling behavior.
1280  * 
1281  * RETURNS
1282  *
1283  *  Nonzero if a message is available and is retrieved, zero otherwise.
1284  *
1285  * CONFORMANCE
1286  *
1287  * ECMA-234, Win32
1288  *
1289  */
1290 BOOL WINAPI PeekMessageW( 
1291   LPMSG lpmsg,    /* [out] buffer to receive message */
1292   HWND hwnd,      /* [in] restrict to messages for hwnd */
1293   UINT min,       /* [in] minimum message to receive */
1294   UINT max,       /* [in] maximum message to receive */
1295   UINT wRemoveMsg /* [in] removal flags */ 
1296
1297 {
1298     BOOL ret = MSG_PeekMessage( QMSG_WIN32W, lpmsg, hwnd, min, max, wRemoveMsg, TRUE );
1299     if (ret) HOOK_CallHooksW( WH_GETMESSAGE, HC_ACTION,
1300                               wRemoveMsg & PM_REMOVE, (LPARAM)lpmsg );
1301     return ret;
1302 }
1303
1304
1305 /***********************************************************************
1306  *              GetMessage32 (USER.820)
1307  */
1308 BOOL16 WINAPI GetMessage32_16( SEGPTR msg16_32, HWND16 hWnd, UINT16 first,
1309                                UINT16 last, BOOL16 wHaveParamHigh )
1310 {
1311     MSG32_16 *lpmsg16_32 = MapSL(msg16_32);
1312     MSG msg;
1313
1314     MSG_PeekMessage( QMSG_WIN16, &msg, hWnd, first, last, PM_REMOVE, FALSE );
1315
1316     lpmsg16_32->msg.hwnd    = msg.hwnd;
1317     lpmsg16_32->msg.message = msg.message;
1318     lpmsg16_32->msg.wParam  = LOWORD(msg.wParam);
1319     lpmsg16_32->msg.lParam  = msg.lParam;
1320     lpmsg16_32->msg.time    = msg.time;
1321     lpmsg16_32->msg.pt.x    = (INT16)msg.pt.x;
1322     lpmsg16_32->msg.pt.y    = (INT16)msg.pt.y;
1323
1324     if ( wHaveParamHigh )
1325         lpmsg16_32->wParamHigh = HIWORD(msg.wParam);
1326
1327     TRACE( "message %04x, hwnd %04x, filter(%04x - %04x)\n",
1328            lpmsg16_32->msg.message, hWnd, first, last );
1329
1330     HOOK_CallHooks16( WH_GETMESSAGE, HC_ACTION, PM_REMOVE, (LPARAM)msg16_32 );
1331     return lpmsg16_32->msg.message != WM_QUIT;
1332 }
1333
1334 /***********************************************************************
1335  *              GetMessage (USER.108)
1336  */
1337 BOOL16 WINAPI GetMessage16( SEGPTR msg, HWND16 hwnd, UINT16 first, UINT16 last)
1338 {
1339     return GetMessage32_16( msg, hwnd, first, last, FALSE );
1340 }
1341
1342 /***********************************************************************
1343  *              GetMessageA (USER32.@)
1344  */
1345 BOOL WINAPI GetMessageA( MSG *lpmsg, HWND hwnd, UINT min, UINT max )
1346 {
1347     MSG_PeekMessage( QMSG_WIN32A, lpmsg, hwnd, min, max, PM_REMOVE, FALSE );
1348     
1349     TRACE( "message %04x, hwnd %04x, filter(%04x - %04x)\n", 
1350            lpmsg->message, hwnd, min, max );
1351     
1352     HOOK_CallHooksA( WH_GETMESSAGE, HC_ACTION, PM_REMOVE, (LPARAM)lpmsg );
1353     return lpmsg->message != WM_QUIT;
1354 }
1355
1356 /***********************************************************************
1357  *              GetMessageW (USER32.@) Retrieve next message
1358  *
1359  * GetMessage retrieves the next event from the calling thread's
1360  * queue and deposits it in *lpmsg.
1361  *
1362  * If _hwnd_ is not NULL, only messages for window _hwnd_ and its
1363  * children as specified by IsChild() are retrieved. If _hwnd_ is NULL
1364  * all application messages are retrieved.
1365  *
1366  * _min_ and _max_ specify the range of messages of interest. If
1367  * min==max==0, no filtering is performed. Useful examples are
1368  * WM_KEYFIRST and WM_KEYLAST to retrieve keyboard input, and
1369  * WM_MOUSEFIRST and WM_MOUSELAST to retrieve mouse input.
1370  *
1371  * WM_PAINT messages are not removed from the queue; they remain until
1372  * processed. Other messages are removed from the queue.
1373  *
1374  * RETURNS
1375  *
1376  * -1 on error, 0 if message is WM_QUIT, nonzero otherwise.
1377  *
1378  * CONFORMANCE
1379  *
1380  * ECMA-234, Win32
1381  * 
1382  */
1383 BOOL WINAPI GetMessageW(
1384   MSG* lpmsg, /* [out] buffer to receive message */
1385   HWND hwnd,  /* [in] restrict to messages for hwnd */
1386   UINT min,   /* [in] minimum message to receive */
1387   UINT max    /* [in] maximum message to receive */
1388
1389 {
1390     MSG_PeekMessage( QMSG_WIN32W, lpmsg, hwnd, min, max, PM_REMOVE, FALSE );
1391     
1392     TRACE( "message %04x, hwnd %04x, filter(%04x - %04x)\n", 
1393            lpmsg->message, hwnd, min, max );
1394     
1395     HOOK_CallHooksW( WH_GETMESSAGE, HC_ACTION, PM_REMOVE, (LPARAM)lpmsg );
1396     return lpmsg->message != WM_QUIT;
1397 }
1398
1399 /***********************************************************************
1400  *           MSG_PostToQueue
1401  */
1402 static BOOL MSG_PostToQueue( DWORD tid, int type, HWND hwnd,
1403                              UINT message, WPARAM wParam, LPARAM lParam )
1404 {
1405     unsigned int res;
1406
1407     TRACE( "posting %x %x (%s) %x %lx\n", hwnd, message, SPY_GetMsgName(message), wParam, lParam );
1408
1409     SERVER_START_REQ( send_message )
1410     {
1411         req->posted = TRUE;
1412         req->id     = (void *)tid;
1413         req->type   = type;
1414         req->win    = hwnd;
1415         req->msg    = message;
1416         req->wparam = wParam;
1417         req->lparam = lParam;
1418         req->info   = 0;
1419         res = SERVER_CALL();
1420     }
1421     SERVER_END_REQ;
1422
1423     if (res)
1424     {
1425         if (res == STATUS_INVALID_PARAMETER)
1426             SetLastError( ERROR_INVALID_THREAD_ID );
1427         else
1428             SetLastError( RtlNtStatusToDosError(res) );
1429     }
1430     return !res;
1431 }
1432
1433
1434 /***********************************************************************
1435  *           MSG_IsPointerMessage
1436  *
1437  * Check whether this message (may) contain pointers. 
1438  * Those messages may not be PostMessage()d or GetMessage()d, but are dropped.
1439  *
1440  * FIXME: list of pointer messages might be incomplete.
1441  *
1442  * (We could do a generic !IsBadWritePtr() check, but this would cause too
1443  *  much slow down I think. MM20010206)
1444  */
1445 static BOOL MSG_IsPointerMessage(UINT message, WPARAM wParam, LPARAM lParam) {
1446     switch (message) {
1447     case WM_CREATE:
1448     case WM_NCCREATE:
1449     case WM_COMPAREITEM:
1450     case WM_DELETEITEM:
1451     case WM_MEASUREITEM:
1452     case WM_DRAWITEM:
1453     case WM_GETMINMAXINFO:
1454     case WM_GETTEXT:
1455     case WM_SETTEXT:
1456     case WM_MDICREATE:
1457     case WM_MDIGETACTIVE:
1458     case WM_NCCALCSIZE:
1459     case WM_WINDOWPOSCHANGING:
1460     case WM_WINDOWPOSCHANGED:
1461     case WM_NOTIFY:
1462     case WM_GETDLGCODE:
1463     case WM_WININICHANGE:
1464     case WM_HELP:
1465     case WM_COPYDATA:
1466     case WM_STYLECHANGING:
1467     case WM_STYLECHANGED:
1468     case WM_DROPOBJECT:
1469     case WM_DRAGMOVE:
1470     case WM_DRAGSELECT:
1471     case WM_QUERYDROPOBJECT:
1472
1473     case CB_DIR:
1474     case CB_ADDSTRING:
1475     case CB_INSERTSTRING:
1476     case CB_FINDSTRING:
1477     case CB_FINDSTRINGEXACT:
1478     case CB_SELECTSTRING:
1479     case CB_GETLBTEXT:
1480     case CB_GETDROPPEDCONTROLRECT:
1481
1482     case LB_DIR:
1483     case LB_ADDFILE:
1484     case LB_ADDSTRING:
1485     case LB_INSERTSTRING:
1486     case LB_GETTEXT:
1487     case LB_GETITEMRECT:
1488     case LB_FINDSTRING:
1489     case LB_FINDSTRINGEXACT:
1490     case LB_SELECTSTRING:
1491     case LB_GETSELITEMS:
1492     case LB_SETTABSTOPS:
1493
1494     case EM_REPLACESEL:
1495     case EM_GETSEL:
1496     case EM_GETRECT:
1497     case EM_SETRECT:
1498     case EM_SETRECTNP:
1499     case EM_GETLINE:
1500     case EM_SETTABSTOPS:
1501             return TRUE;
1502     default:
1503             return FALSE;
1504     }
1505 }
1506
1507 /***********************************************************************
1508  *           MSG_PostMessage
1509  */
1510 static BOOL MSG_PostMessage( int type, HWND hwnd, UINT message, 
1511                              WPARAM wParam, LPARAM lParam )
1512 {
1513     WND *wndPtr;
1514
1515     /* See thread on wine-devel around 6.2.2001. Basically posted messages
1516      * that are known to contain pointers are dropped by the Windows 32bit
1517      * PostMessage() with return FALSE; and invalid parameter last error.
1518      * (tested against NT4 by Gerard Patel)
1519      * 16 bit does not care, so we don't either.
1520      */
1521     if ( (type!=QMSG_WIN16) && MSG_IsPointerMessage(message,wParam,lParam)) {
1522         FIXME("Ignoring posted pointer message 0x%04x to hwnd 0x%04x.\n",
1523                 message,hwnd
1524         );
1525         SetLastError(ERROR_INVALID_PARAMETER);
1526         return FALSE;
1527     }
1528
1529     if (hwnd == HWND_BROADCAST)
1530     {
1531         WND *pDesktop = WIN_GetDesktop();
1532         TRACE("HWND_BROADCAST !\n");
1533         
1534         for (wndPtr=WIN_LockWndPtr(pDesktop->child); wndPtr; WIN_UpdateWndPtr(&wndPtr,wndPtr->next))
1535         {
1536             if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION)
1537             {
1538                 TRACE("BROADCAST Message to hWnd=%04x m=%04X w=%04X l=%08lX !\n",
1539                       wndPtr->hwndSelf, message, wParam, lParam);
1540                 MSG_PostToQueue( GetWindowThreadProcessId( wndPtr->hwndSelf, NULL ), type,
1541                                  wndPtr->hwndSelf, message, wParam, lParam );
1542             }
1543         }
1544         WIN_ReleaseDesktop();
1545         TRACE("End of HWND_BROADCAST !\n");
1546         return TRUE;
1547     }
1548
1549     return MSG_PostToQueue( GetWindowThreadProcessId( hwnd, NULL ),
1550                             type, hwnd, message, wParam, lParam );
1551 }
1552
1553 /***********************************************************************
1554  *              PostMessage (USER.110)
1555  */
1556 BOOL16 WINAPI PostMessage16( HWND16 hwnd, UINT16 message, WPARAM16 wParam,
1557                              LPARAM lParam )
1558 {
1559     return (BOOL16) MSG_PostMessage( QMSG_WIN16, hwnd, message, wParam, lParam );
1560 }
1561
1562 /***********************************************************************
1563  *              PostMessageA (USER32.@)
1564  */
1565 BOOL WINAPI PostMessageA( HWND hwnd, UINT message, WPARAM wParam,
1566                           LPARAM lParam )
1567 {
1568     return MSG_PostMessage( QMSG_WIN32A, hwnd, message, wParam, lParam );
1569 }
1570
1571 /***********************************************************************
1572  *              PostMessageW (USER32.@)
1573  */
1574 BOOL WINAPI PostMessageW( HWND hwnd, UINT message, WPARAM wParam,
1575                           LPARAM lParam )
1576 {
1577     return MSG_PostMessage( QMSG_WIN32W, hwnd, message, wParam, lParam );
1578 }
1579
1580 /***********************************************************************
1581  *              PostAppMessage (USER.116)
1582  *              PostAppMessage16 (USER32.@)
1583  */
1584 BOOL16 WINAPI PostAppMessage16( HTASK16 hTask, UINT16 message, 
1585                                 WPARAM16 wParam, LPARAM lParam )
1586 {
1587     TDB *pTask = TASK_GetPtr( hTask );
1588     if (!pTask) return FALSE;
1589     return MSG_PostToQueue( (DWORD)pTask->teb->tid, QMSG_WIN16, 0, message, wParam, lParam );
1590 }
1591
1592 /**********************************************************************
1593  *              PostThreadMessageA (USER32.@)
1594  */
1595 BOOL WINAPI PostThreadMessageA( DWORD idThread, UINT message,
1596                                 WPARAM wParam, LPARAM lParam )
1597 {
1598     return MSG_PostToQueue( idThread, QMSG_WIN32A, 0, message, wParam, lParam );
1599 }
1600
1601 /**********************************************************************
1602  *              PostThreadMessageW (USER32.@)
1603  */
1604 BOOL WINAPI PostThreadMessageW( DWORD idThread, UINT message,
1605                                  WPARAM wParam, LPARAM lParam )
1606 {
1607     return MSG_PostToQueue( idThread, QMSG_WIN32W, 0, message, wParam, lParam );
1608 }
1609
1610
1611 /************************************************************************
1612  *           MSG_CallWndProcHook32
1613  */
1614 static void  MSG_CallWndProcHook( LPMSG pmsg, BOOL bUnicode )
1615 {
1616    CWPSTRUCT cwp;
1617
1618    cwp.lParam = pmsg->lParam;
1619    cwp.wParam = pmsg->wParam;
1620    cwp.message = pmsg->message;
1621    cwp.hwnd = pmsg->hwnd;
1622
1623    if (bUnicode) HOOK_CallHooksW(WH_CALLWNDPROC, HC_ACTION, 1, (LPARAM)&cwp);
1624    else HOOK_CallHooksA( WH_CALLWNDPROC, HC_ACTION, 1, (LPARAM)&cwp );
1625
1626    pmsg->lParam = cwp.lParam;
1627    pmsg->wParam = cwp.wParam;
1628    pmsg->message = cwp.message;
1629    pmsg->hwnd = cwp.hwnd;
1630 }
1631
1632
1633 /***********************************************************************
1634  *           MSG_SendMessage
1635  *
1636  * return values: 0 if timeout occurs
1637  *                1 otherwise
1638  */
1639 static LRESULT MSG_SendMessage( HWND hwnd, UINT msg, WPARAM wParam,
1640                                 LPARAM lParam, DWORD timeout, WORD type,
1641                          LRESULT *pRes)
1642 {
1643     WND * wndPtr = 0;
1644     WND **list, **ppWnd;
1645     LRESULT ret = 1;
1646
1647     if (pRes) *pRes = 0;
1648
1649     if (hwnd == HWND_BROADCAST|| hwnd == HWND_TOPMOST)
1650     {
1651         if (pRes) *pRes = 1;
1652         
1653         if (!(list = WIN_BuildWinArray( WIN_GetDesktop(), 0, NULL )))
1654         {
1655             WIN_ReleaseDesktop();
1656             return 1;
1657         }
1658         WIN_ReleaseDesktop();
1659
1660         TRACE("HWND_BROADCAST !\n");
1661         for (ppWnd = list; *ppWnd; ppWnd++)
1662         {
1663             WIN_UpdateWndPtr(&wndPtr,*ppWnd);
1664             if (!IsWindow(wndPtr->hwndSelf)) continue;
1665             if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION)
1666             {
1667                 TRACE("BROADCAST Message to hWnd=%04x m=%04X w=%04lX l=%08lX !\n",
1668                       wndPtr->hwndSelf, msg, (DWORD)wParam, lParam);
1669                 MSG_SendMessage( wndPtr->hwndSelf, msg, wParam, lParam,
1670                                timeout, type, pRes);
1671             }
1672         }
1673         WIN_ReleaseWndPtr(wndPtr);
1674         WIN_ReleaseWinArray(list);
1675         TRACE("End of HWND_BROADCAST !\n");
1676         return 1;
1677     }
1678
1679     if (HOOK_IsHooked( WH_CALLWNDPROC ))
1680     {
1681         switch(type)
1682         {
1683         case QMSG_WIN16:
1684             {
1685                 LPCWPSTRUCT16 pmsg;
1686
1687                 if ((pmsg = SEGPTR_NEW(CWPSTRUCT16)))
1688                 {
1689                     pmsg->hwnd   = hwnd & 0xffff;
1690                     pmsg->message= msg & 0xffff;
1691                     pmsg->wParam = wParam & 0xffff;
1692                     pmsg->lParam = lParam;
1693                     HOOK_CallHooks16( WH_CALLWNDPROC, HC_ACTION, 1,
1694                                       (LPARAM)SEGPTR_GET(pmsg) );
1695                     hwnd   = pmsg->hwnd;
1696                     msg    = pmsg->message;
1697                     wParam = pmsg->wParam;
1698                     lParam = pmsg->lParam;
1699                     SEGPTR_FREE( pmsg );
1700                 }
1701             }
1702             break;
1703         case QMSG_WIN32A:
1704             MSG_CallWndProcHook( (LPMSG)&hwnd, FALSE);
1705             break;
1706         case QMSG_WIN32W:
1707             MSG_CallWndProcHook( (LPMSG)&hwnd, TRUE);
1708             break;
1709         }
1710     }
1711
1712     if (!(wndPtr = WIN_FindWndPtr( hwnd )))
1713     {
1714         WARN("invalid hwnd %04x\n", hwnd );
1715         return 0;
1716     }
1717     if (QUEUE_IsExitingQueue(wndPtr->hmemTaskQ))
1718     {
1719         ret = 0;  /* Don't send anything if the task is dying */
1720         goto END;
1721     }
1722     if (type != QMSG_WIN16)
1723         SPY_EnterMessage( SPY_SENDMESSAGE, hwnd, msg, wParam, lParam );
1724     else
1725         SPY_EnterMessage( SPY_SENDMESSAGE16, hwnd, msg, wParam, lParam );
1726
1727     if (wndPtr->hmemTaskQ && wndPtr->hmemTaskQ != GetFastQueue16())
1728         ret = MSG_SendMessageInterThread( wndPtr->hmemTaskQ, hwnd, msg,
1729                                           wParam, lParam, timeout, type, pRes );
1730     else
1731     {
1732         LRESULT res = 0;
1733
1734         /* Call the right CallWindowProc flavor */
1735         switch(type)
1736         {
1737         case QMSG_WIN16:
1738             res = CallWindowProc16( (WNDPROC16)wndPtr->winproc, hwnd, msg, wParam, lParam );
1739             break;
1740         case QMSG_WIN32A:
1741             res = CallWindowProcA( (WNDPROC)wndPtr->winproc, hwnd, msg, wParam, lParam );
1742             break;
1743         case QMSG_WIN32W:
1744             res = CallWindowProcW( (WNDPROC)wndPtr->winproc, hwnd, msg, wParam, lParam );
1745             break;
1746         }
1747         if (pRes) *pRes = res;
1748     }
1749
1750     if (type != QMSG_WIN16)
1751         SPY_ExitMessage( SPY_RESULT_OK, hwnd, msg, pRes?*pRes:0, wParam, lParam );
1752     else
1753         SPY_ExitMessage( SPY_RESULT_OK16, hwnd, msg, pRes?*pRes:0, wParam, lParam );
1754 END:
1755     WIN_ReleaseWndPtr(wndPtr);
1756     return ret;
1757 }
1758
1759
1760 /***********************************************************************
1761  *              SendMessage (USER.111)
1762  */
1763 LRESULT WINAPI SendMessage16( HWND16 hwnd, UINT16 msg, WPARAM16 wParam,
1764                               LPARAM lParam)
1765 {
1766     LRESULT res;
1767     MSG_SendMessage(hwnd, msg, wParam, lParam, INFINITE, QMSG_WIN16, &res);
1768     return res;
1769 }
1770
1771
1772 /***********************************************************************
1773  *              SendMessageA (USER32.@)
1774  */
1775 LRESULT WINAPI SendMessageA( HWND hwnd, UINT msg, WPARAM wParam,
1776                                LPARAM lParam )
1777         {
1778     LRESULT res;
1779
1780     MSG_SendMessage(hwnd, msg, wParam, lParam, INFINITE, QMSG_WIN32A, &res);
1781
1782     return res;
1783 }
1784
1785
1786 /***********************************************************************
1787  *              SendMessageW (USER32.@) Send Window Message
1788  *
1789  *  Sends a message to the window procedure of the specified window.
1790  *  SendMessage() will not return until the called window procedure
1791  *  either returns or calls ReplyMessage().
1792  *
1793  *  Use PostMessage() to send message and return immediately. A window
1794  *  procedure may use InSendMessage() to detect
1795  *  SendMessage()-originated messages.
1796  *
1797  *  Applications which communicate via HWND_BROADCAST may use
1798  *  RegisterWindowMessage() to obtain a unique message to avoid conflicts
1799  *  with other applications.
1800  *
1801  * CONFORMANCE
1802  * 
1803  *  ECMA-234, Win32 
1804  */
1805 LRESULT WINAPI SendMessageW( 
1806   HWND hwnd,     /* [in] Window to send message to. If HWND_BROADCAST, 
1807                          the message will be sent to all top-level windows. */
1808
1809   UINT msg,      /* [in] message */
1810   WPARAM wParam, /* [in] message parameter */
1811   LPARAM lParam  /* [in] additional message parameter */
1812 ) {
1813     LRESULT res;
1814
1815     MSG_SendMessage(hwnd, msg, wParam, lParam, INFINITE, QMSG_WIN32W, &res);
1816
1817     return res;
1818 }
1819
1820
1821 /***********************************************************************
1822  *              SendMessageTimeout (not a WINAPI)
1823  */
1824 LRESULT WINAPI SendMessageTimeout16( HWND16 hwnd, UINT16 msg, WPARAM16 wParam,
1825                                      LPARAM lParam, UINT16 flags,
1826                                      UINT16 timeout, LPWORD resultp)
1827 {
1828     LRESULT ret;
1829     LRESULT msgRet;
1830     
1831     /* FIXME: need support for SMTO_BLOCK */
1832     
1833     ret = MSG_SendMessage(hwnd, msg, wParam, lParam, timeout, QMSG_WIN16, &msgRet);
1834     if (resultp) *resultp = (WORD) msgRet;
1835     return ret;
1836 }
1837
1838
1839 /***********************************************************************
1840  *              SendMessageTimeoutA (USER32.@)
1841  */
1842 LRESULT WINAPI SendMessageTimeoutA( HWND hwnd, UINT msg, WPARAM wParam,
1843                                       LPARAM lParam, UINT flags,
1844                                       UINT timeout, LPDWORD resultp)
1845 {
1846     LRESULT ret;
1847     LRESULT msgRet;
1848
1849     /* FIXME: need support for SMTO_BLOCK */
1850     
1851     ret = MSG_SendMessage(hwnd, msg, wParam, lParam, timeout, QMSG_WIN32A, &msgRet);
1852
1853     if (resultp) *resultp = (DWORD) msgRet;
1854     return ret;
1855 }
1856
1857
1858 /***********************************************************************
1859  *              SendMessageTimeoutW (USER32.@)
1860  */
1861 LRESULT WINAPI SendMessageTimeoutW( HWND hwnd, UINT msg, WPARAM wParam,
1862                                       LPARAM lParam, UINT flags,
1863                                       UINT timeout, LPDWORD resultp)
1864 {
1865     LRESULT ret;
1866     LRESULT msgRet;
1867     
1868     /* FIXME: need support for SMTO_BLOCK */
1869
1870     ret = MSG_SendMessage(hwnd, msg, wParam, lParam, timeout, QMSG_WIN32W, &msgRet);
1871     
1872     if (resultp) *resultp = (DWORD) msgRet;
1873     return ret;
1874 }
1875
1876
1877 /***********************************************************************
1878  *              WaitMessage (USER.112) (USER32.@) Suspend thread pending messages
1879  *
1880  * WaitMessage() suspends a thread until events appear in the thread's
1881  * queue.
1882  */
1883 BOOL WINAPI WaitMessage(void)
1884 {
1885     return (MsgWaitForMultipleObjectsEx( 0, NULL, INFINITE, QS_ALLINPUT, 0 ) != WAIT_FAILED);
1886 }
1887
1888
1889 /***********************************************************************
1890  *              MsgWaitForMultipleObjectsEx   (USER32.@)
1891  */
1892 DWORD WINAPI MsgWaitForMultipleObjectsEx( DWORD count, CONST HANDLE *pHandles,
1893                                           DWORD timeout, DWORD mask, DWORD flags )
1894 {
1895     HANDLE handles[MAXIMUM_WAIT_OBJECTS];
1896     DWORD i, ret;
1897     HQUEUE16 hQueue = GetFastQueue16();
1898     MESSAGEQUEUE *msgQueue;
1899
1900     if (count > MAXIMUM_WAIT_OBJECTS-1)
1901     {
1902         SetLastError( ERROR_INVALID_PARAMETER );
1903         return WAIT_FAILED;
1904     }
1905
1906     if (!(msgQueue = QUEUE_Lock( hQueue ))) return WAIT_FAILED;
1907
1908     /* set the queue mask */
1909     SERVER_START_REQ( set_queue_mask )
1910     {
1911         req->wake_mask    = (flags & MWMO_INPUTAVAILABLE) ? mask : 0;
1912         req->changed_mask = mask;
1913         req->skip_wait    = 0;
1914         SERVER_CALL();
1915     }
1916     SERVER_END_REQ;
1917
1918     /* Add the thread event to the handle list */
1919     for (i = 0; i < count; i++) handles[i] = pHandles[i];
1920     handles[count] = msgQueue->server_queue;
1921
1922
1923     if (USER_Driver.pMsgWaitForMultipleObjectsEx)
1924     {
1925         ret = USER_Driver.pMsgWaitForMultipleObjectsEx( count+1, handles, timeout, mask, flags );
1926         if (ret == count+1) ret = count; /* pretend the msg queue is ready */
1927     }
1928     else
1929         ret = WaitForMultipleObjectsEx( count+1, handles, flags & MWMO_WAITALL,
1930                                         timeout, flags & MWMO_ALERTABLE );
1931     QUEUE_Unlock( msgQueue );
1932     return ret;
1933 }
1934
1935
1936 /***********************************************************************
1937  *              MsgWaitForMultipleObjects (USER32.@)
1938  */
1939 DWORD WINAPI MsgWaitForMultipleObjects( DWORD count, CONST HANDLE *handles,
1940                                         BOOL wait_all, DWORD timeout, DWORD mask )
1941 {
1942     return MsgWaitForMultipleObjectsEx( count, handles, timeout, mask,
1943                                         wait_all ? MWMO_WAITALL : 0 );
1944 }
1945
1946
1947 /***********************************************************************
1948  *              MsgWaitForMultipleObjects16  (USER.640)
1949  */
1950 DWORD WINAPI MsgWaitForMultipleObjects16( DWORD count, CONST HANDLE *handles,
1951                                           BOOL wait_all, DWORD timeout, DWORD mask )
1952 {
1953     return MsgWaitForMultipleObjectsEx( count, handles, timeout, mask,
1954                                         wait_all ? MWMO_WAITALL : 0 );
1955 }
1956
1957
1958 struct accent_char
1959 {
1960     BYTE ac_accent;
1961     BYTE ac_char;
1962     BYTE ac_result;
1963 };
1964
1965 static const struct accent_char accent_chars[] =
1966 {
1967 /* A good idea should be to read /usr/X11/lib/X11/locale/iso8859-x/Compose */
1968     {'`', 'A', '\300'},  {'`', 'a', '\340'},
1969     {'\'', 'A', '\301'}, {'\'', 'a', '\341'},
1970     {'^', 'A', '\302'},  {'^', 'a', '\342'},
1971     {'~', 'A', '\303'},  {'~', 'a', '\343'},
1972     {'"', 'A', '\304'},  {'"', 'a', '\344'},
1973     {'O', 'A', '\305'},  {'o', 'a', '\345'},
1974     {'0', 'A', '\305'},  {'0', 'a', '\345'},
1975     {'A', 'A', '\305'},  {'a', 'a', '\345'},
1976     {'A', 'E', '\306'},  {'a', 'e', '\346'},
1977     {',', 'C', '\307'},  {',', 'c', '\347'},
1978     {'`', 'E', '\310'},  {'`', 'e', '\350'},
1979     {'\'', 'E', '\311'}, {'\'', 'e', '\351'},
1980     {'^', 'E', '\312'},  {'^', 'e', '\352'},
1981     {'"', 'E', '\313'},  {'"', 'e', '\353'},
1982     {'`', 'I', '\314'},  {'`', 'i', '\354'},
1983     {'\'', 'I', '\315'}, {'\'', 'i', '\355'},
1984     {'^', 'I', '\316'},  {'^', 'i', '\356'},
1985     {'"', 'I', '\317'},  {'"', 'i', '\357'},
1986     {'-', 'D', '\320'},  {'-', 'd', '\360'},
1987     {'~', 'N', '\321'},  {'~', 'n', '\361'},
1988     {'`', 'O', '\322'},  {'`', 'o', '\362'},
1989     {'\'', 'O', '\323'}, {'\'', 'o', '\363'},
1990     {'^', 'O', '\324'},  {'^', 'o', '\364'},
1991     {'~', 'O', '\325'},  {'~', 'o', '\365'},
1992     {'"', 'O', '\326'},  {'"', 'o', '\366'},
1993     {'/', 'O', '\330'},  {'/', 'o', '\370'},
1994     {'`', 'U', '\331'},  {'`', 'u', '\371'},
1995     {'\'', 'U', '\332'}, {'\'', 'u', '\372'},
1996     {'^', 'U', '\333'},  {'^', 'u', '\373'},
1997     {'"', 'U', '\334'},  {'"', 'u', '\374'},
1998     {'\'', 'Y', '\335'}, {'\'', 'y', '\375'},
1999     {'T', 'H', '\336'},  {'t', 'h', '\376'},
2000     {'s', 's', '\337'},  {'"', 'y', '\377'},
2001     {'s', 'z', '\337'},  {'i', 'j', '\377'},
2002         /* iso-8859-2 uses this */
2003     {'<', 'L', '\245'},  {'<', 'l', '\265'},    /* caron */
2004     {'<', 'S', '\251'},  {'<', 's', '\271'},
2005     {'<', 'T', '\253'},  {'<', 't', '\273'},
2006     {'<', 'Z', '\256'},  {'<', 'z', '\276'},
2007     {'<', 'C', '\310'},  {'<', 'c', '\350'},
2008     {'<', 'E', '\314'},  {'<', 'e', '\354'},
2009     {'<', 'D', '\317'},  {'<', 'd', '\357'},
2010     {'<', 'N', '\322'},  {'<', 'n', '\362'},
2011     {'<', 'R', '\330'},  {'<', 'r', '\370'},
2012     {';', 'A', '\241'},  {';', 'a', '\261'},    /* ogonek */
2013     {';', 'E', '\312'},  {';', 'e', '\332'},
2014     {'\'', 'Z', '\254'}, {'\'', 'z', '\274'},   /* acute */
2015     {'\'', 'R', '\300'}, {'\'', 'r', '\340'},
2016     {'\'', 'L', '\305'}, {'\'', 'l', '\345'},
2017     {'\'', 'C', '\306'}, {'\'', 'c', '\346'},
2018     {'\'', 'N', '\321'}, {'\'', 'n', '\361'},
2019 /*  collision whith S, from iso-8859-9 !!! */
2020     {',', 'S', '\252'},  {',', 's', '\272'},    /* cedilla */
2021     {',', 'T', '\336'},  {',', 't', '\376'},
2022     {'.', 'Z', '\257'},  {'.', 'z', '\277'},    /* dot above */
2023     {'/', 'L', '\243'},  {'/', 'l', '\263'},    /* slash */
2024     {'/', 'D', '\320'},  {'/', 'd', '\360'},
2025     {'(', 'A', '\303'},  {'(', 'a', '\343'},    /* breve */
2026     {'\275', 'O', '\325'}, {'\275', 'o', '\365'},       /* double acute */
2027     {'\275', 'U', '\334'}, {'\275', 'u', '\374'},
2028     {'0', 'U', '\332'},  {'0', 'u', '\372'},    /* ring above */
2029         /* iso-8859-3 uses this */
2030     {'/', 'H', '\241'},  {'/', 'h', '\261'},    /* slash */
2031     {'>', 'H', '\246'},  {'>', 'h', '\266'},    /* circumflex */
2032     {'>', 'J', '\254'},  {'>', 'j', '\274'},
2033     {'>', 'C', '\306'},  {'>', 'c', '\346'},
2034     {'>', 'G', '\330'},  {'>', 'g', '\370'},
2035     {'>', 'S', '\336'},  {'>', 's', '\376'},
2036 /*  collision whith G( from iso-8859-9 !!!   */
2037     {'(', 'G', '\253'},  {'(', 'g', '\273'},    /* breve */
2038     {'(', 'U', '\335'},  {'(', 'u', '\375'},
2039 /*  collision whith I. from iso-8859-3 !!!   */
2040     {'.', 'I', '\251'},  {'.', 'i', '\271'},    /* dot above */
2041     {'.', 'C', '\305'},  {'.', 'c', '\345'},
2042     {'.', 'G', '\325'},  {'.', 'g', '\365'},
2043         /* iso-8859-4 uses this */
2044     {',', 'R', '\243'},  {',', 'r', '\263'},    /* cedilla */
2045     {',', 'L', '\246'},  {',', 'l', '\266'},
2046     {',', 'G', '\253'},  {',', 'g', '\273'},
2047     {',', 'N', '\321'},  {',', 'n', '\361'},
2048     {',', 'K', '\323'},  {',', 'k', '\363'},
2049     {'~', 'I', '\245'},  {'~', 'i', '\265'},    /* tilde */
2050     {'-', 'E', '\252'},  {'-', 'e', '\272'},    /* macron */
2051     {'-', 'A', '\300'},  {'-', 'a', '\340'},
2052     {'-', 'I', '\317'},  {'-', 'i', '\357'},
2053     {'-', 'O', '\322'},  {'-', 'o', '\362'},
2054     {'-', 'U', '\336'},  {'-', 'u', '\376'},
2055     {'/', 'T', '\254'},  {'/', 't', '\274'},    /* slash */
2056     {'.', 'E', '\314'},  {'.', 'e', '\344'},    /* dot above */
2057     {';', 'I', '\307'},  {';', 'i', '\347'},    /* ogonek */
2058     {';', 'U', '\331'},  {';', 'u', '\371'},
2059         /* iso-8859-9 uses this */
2060         /* iso-8859-9 has really bad choosen G( S, and I. as they collide
2061          * whith the same letters on other iso-8859-x (that is they are on
2062          * different places :-( ), if you use turkish uncomment these and
2063          * comment out the lines in iso-8859-2 and iso-8859-3 sections
2064          * FIXME: should be dynamic according to chosen language
2065          *        if/when Wine has turkish support.  
2066          */ 
2067 /*  collision whith G( from iso-8859-3 !!!   */
2068 /*  {'(', 'G', '\320'},  {'(', 'g', '\360'}, */ /* breve */
2069 /*  collision whith S, from iso-8859-2 !!! */
2070 /*  {',', 'S', '\336'},  {',', 's', '\376'}, */ /* cedilla */
2071 /*  collision whith I. from iso-8859-3 !!!   */
2072 /*  {'.', 'I', '\335'},  {'.', 'i', '\375'}, */ /* dot above */
2073 };
2074
2075
2076 /***********************************************************************
2077  *           MSG_DoTranslateMessage
2078  *
2079  * Implementation of TranslateMessage.
2080  *
2081  * TranslateMessage translates virtual-key messages into character-messages,
2082  * as follows :
2083  * WM_KEYDOWN/WM_KEYUP combinations produce a WM_CHAR or WM_DEADCHAR message.
2084  * ditto replacing WM_* with WM_SYS*
2085  * This produces WM_CHAR messages only for keys mapped to ASCII characters
2086  * by the keyboard driver.
2087  */
2088 static BOOL MSG_DoTranslateMessage( UINT message, HWND hwnd,
2089                                       WPARAM wParam, LPARAM lParam )
2090 {
2091     static int dead_char;
2092     WCHAR wp[2];
2093     
2094     if (message != WM_MOUSEMOVE && message != WM_TIMER)
2095         TRACE("(%s, %04X, %08lX)\n",
2096                      SPY_GetMsgName(message), wParam, lParam );
2097     if(message >= WM_KEYFIRST && message <= WM_KEYLAST)
2098         TRACE_(key)("(%s, %04X, %08lX)\n",
2099                      SPY_GetMsgName(message), wParam, lParam );
2100
2101     if ((message != WM_KEYDOWN) && (message != WM_SYSKEYDOWN))  return FALSE;
2102
2103     TRACE_(key)("Translating key %s (%04x), scancode %02x\n",
2104                  SPY_GetVKeyName(wParam), wParam, LOBYTE(HIWORD(lParam)));
2105
2106     /* FIXME : should handle ToUnicode yielding 2 */
2107     switch (ToUnicode(wParam, HIWORD(lParam), QueueKeyStateTable, wp, 2, 0)) 
2108     {
2109     case 1:
2110         message = (message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
2111         /* Should dead chars handling go in ToAscii ? */
2112         if (dead_char)
2113         {
2114             int i;
2115
2116             if (wp[0] == ' ') wp[0] =  dead_char;
2117             if (dead_char == 0xa2) dead_char = '(';
2118             else if (dead_char == 0xa8) dead_char = '"';
2119             else if (dead_char == 0xb2) dead_char = ';';
2120             else if (dead_char == 0xb4) dead_char = '\'';
2121             else if (dead_char == 0xb7) dead_char = '<';
2122             else if (dead_char == 0xb8) dead_char = ',';
2123             else if (dead_char == 0xff) dead_char = '.';
2124             for (i = 0; i < sizeof(accent_chars)/sizeof(accent_chars[0]); i++)
2125                 if ((accent_chars[i].ac_accent == dead_char) &&
2126                     (accent_chars[i].ac_char == wp[0]))
2127                 {
2128                     wp[0] = accent_chars[i].ac_result;
2129                     break;
2130                 }
2131             dead_char = 0;
2132         }
2133         TRACE_(key)("1 -> PostMessage(%s)\n", SPY_GetMsgName(message));
2134         PostMessageW( hwnd, message, wp[0], lParam );
2135         return TRUE;
2136
2137     case -1:
2138         message = (message == WM_KEYDOWN) ? WM_DEADCHAR : WM_SYSDEADCHAR;
2139         dead_char = wp[0];
2140         TRACE_(key)("-1 -> PostMessage(%s)\n", SPY_GetMsgName(message));
2141         PostMessageW( hwnd, message, wp[0], lParam );
2142         return TRUE;
2143     }
2144     return FALSE;
2145 }
2146
2147
2148 /***********************************************************************
2149  *              TranslateMessage (USER.113)
2150  */
2151 BOOL16 WINAPI TranslateMessage16( const MSG16 *msg )
2152 {
2153     return MSG_DoTranslateMessage( msg->message, msg->hwnd,
2154                                    msg->wParam, msg->lParam );
2155 }
2156
2157
2158 /***********************************************************************
2159  *              TranslateMessage32 (USER.821)
2160  */
2161 BOOL16 WINAPI TranslateMessage32_16( const MSG32_16 *msg, BOOL16 wHaveParamHigh )
2162 {
2163     WPARAM wParam;
2164
2165     if (wHaveParamHigh)
2166         wParam = MAKELONG(msg->msg.wParam, msg->wParamHigh);
2167     else
2168         wParam = (WPARAM)msg->msg.wParam;
2169
2170     return MSG_DoTranslateMessage( msg->msg.message, msg->msg.hwnd,
2171                                    wParam, msg->msg.lParam );
2172 }
2173
2174 /***********************************************************************
2175  *              TranslateMessage (USER32.@)
2176  */
2177 BOOL WINAPI TranslateMessage( const MSG *msg )
2178 {
2179     return MSG_DoTranslateMessage( msg->message, msg->hwnd,
2180                                    msg->wParam, msg->lParam );
2181 }
2182
2183
2184 /***********************************************************************
2185  *              DispatchMessage (USER.114)
2186  */
2187 LONG WINAPI DispatchMessage16( const MSG16* msg )
2188 {
2189     WND * wndPtr;
2190     LONG retval;
2191     int painting;
2192     
2193       /* Process timer messages */
2194     if ((msg->message == WM_TIMER) || (msg->message == WM_SYSTIMER))
2195     {
2196         if (msg->lParam)
2197         {
2198             /* before calling window proc, verify whether timer is still valid;
2199                there's a slim chance that the application kills the timer
2200                between GetMessage and DispatchMessage API calls */
2201             if (!TIMER_IsTimerValid(msg->hwnd, (UINT) msg->wParam, (HWINDOWPROC) msg->lParam))
2202                 return 0; /* invalid winproc */
2203
2204             return CallWindowProc16( (WNDPROC16)msg->lParam, msg->hwnd,
2205                                    msg->message, msg->wParam, GetTickCount() );
2206         }
2207     }
2208
2209     if (!msg->hwnd) return 0;
2210     if (!(wndPtr = WIN_FindWndPtr( msg->hwnd ))) return 0;
2211     if (!wndPtr->winproc)
2212     {
2213         retval = 0;
2214         goto END;
2215     }
2216     painting = (msg->message == WM_PAINT);
2217     if (painting) wndPtr->flags |= WIN_NEEDS_BEGINPAINT;
2218
2219     SPY_EnterMessage( SPY_DISPATCHMESSAGE16, msg->hwnd, msg->message,
2220                       msg->wParam, msg->lParam );
2221     retval = CallWindowProc16( (WNDPROC16)wndPtr->winproc,
2222                                msg->hwnd, msg->message,
2223                                msg->wParam, msg->lParam );
2224     SPY_ExitMessage( SPY_RESULT_OK16, msg->hwnd, msg->message, retval, 
2225                      msg->wParam, msg->lParam );
2226
2227     WIN_ReleaseWndPtr(wndPtr);
2228     wndPtr = WIN_FindWndPtr(msg->hwnd);
2229     if (painting && wndPtr &&
2230         (wndPtr->flags & WIN_NEEDS_BEGINPAINT) && wndPtr->hrgnUpdate)
2231     {
2232         ERR("BeginPaint not called on WM_PAINT for hwnd %04x!\n", 
2233             msg->hwnd);
2234         wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
2235         /* Validate the update region to avoid infinite WM_PAINT loop */
2236         ValidateRect( msg->hwnd, NULL );
2237     }
2238 END:
2239     WIN_ReleaseWndPtr(wndPtr);
2240     return retval;
2241 }
2242
2243
2244 /***********************************************************************
2245  *              DispatchMessage32 (USER.822)
2246  */
2247 LONG WINAPI DispatchMessage32_16( const MSG32_16* lpmsg16_32, BOOL16 wHaveParamHigh )
2248 {
2249     if (wHaveParamHigh == FALSE)
2250         return DispatchMessage16(&(lpmsg16_32->msg));
2251     else
2252     {
2253         MSG msg;
2254
2255         msg.hwnd = lpmsg16_32->msg.hwnd;
2256         msg.message = lpmsg16_32->msg.message;
2257         msg.wParam = MAKELONG(lpmsg16_32->msg.wParam, lpmsg16_32->wParamHigh);
2258         msg.lParam = lpmsg16_32->msg.lParam;
2259         msg.time = lpmsg16_32->msg.time;
2260         msg.pt.x = (INT)lpmsg16_32->msg.pt.x;
2261         msg.pt.y = (INT)lpmsg16_32->msg.pt.y;
2262         return DispatchMessageA(&msg);
2263     }
2264 }
2265
2266 /***********************************************************************
2267  *              DispatchMessageA (USER32.@)
2268  */
2269 LONG WINAPI DispatchMessageA( const MSG* msg )
2270 {
2271     WND * wndPtr;
2272     LONG retval;
2273     int painting;
2274     
2275       /* Process timer messages */
2276     if ((msg->message == WM_TIMER) || (msg->message == WM_SYSTIMER))
2277     {
2278         if (msg->lParam)
2279         {
2280 /*            HOOK_CallHooks32A( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */
2281
2282             /* before calling window proc, verify whether timer is still valid;
2283                there's a slim chance that the application kills the timer
2284                between GetMessage and DispatchMessage API calls */
2285             if (!TIMER_IsTimerValid(msg->hwnd, (UINT) msg->wParam, (HWINDOWPROC) msg->lParam))
2286                 return 0; /* invalid winproc */
2287
2288             return CallWindowProcA( (WNDPROC)msg->lParam, msg->hwnd,
2289                                    msg->message, msg->wParam, GetTickCount() );
2290         }
2291     }
2292
2293     if (!msg->hwnd) return 0;
2294     if (!(wndPtr = WIN_FindWndPtr( msg->hwnd ))) return 0;
2295     if (!wndPtr->winproc)
2296     {
2297         retval = 0;
2298         goto END;
2299     }
2300     painting = (msg->message == WM_PAINT);
2301     if (painting) wndPtr->flags |= WIN_NEEDS_BEGINPAINT;
2302 /*    HOOK_CallHooks32A( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */
2303
2304     SPY_EnterMessage( SPY_DISPATCHMESSAGE, msg->hwnd, msg->message,
2305                       msg->wParam, msg->lParam );
2306     retval = CallWindowProcA( (WNDPROC)wndPtr->winproc,
2307                                 msg->hwnd, msg->message,
2308                                 msg->wParam, msg->lParam );
2309     SPY_ExitMessage( SPY_RESULT_OK, msg->hwnd, msg->message, retval,
2310                      msg->wParam, msg->lParam );
2311
2312     WIN_ReleaseWndPtr(wndPtr);
2313     wndPtr = WIN_FindWndPtr(msg->hwnd);
2314
2315     if (painting && wndPtr &&
2316         (wndPtr->flags & WIN_NEEDS_BEGINPAINT) && wndPtr->hrgnUpdate)
2317     {
2318         ERR("BeginPaint not called on WM_PAINT for hwnd %04x!\n", 
2319             msg->hwnd);
2320         wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
2321         /* Validate the update region to avoid infinite WM_PAINT loop */
2322         RedrawWindow( wndPtr->hwndSelf, NULL, 0,
2323                       RDW_FRAME | RDW_VALIDATE | RDW_NOCHILDREN | RDW_NOINTERNALPAINT );
2324     }
2325 END:
2326     WIN_ReleaseWndPtr(wndPtr);
2327     return retval;
2328 }
2329
2330
2331 /***********************************************************************
2332  *              DispatchMessageW (USER32.@) Process Message
2333  *
2334  * Process the message specified in the structure *_msg_.
2335  *
2336  * If the lpMsg parameter points to a WM_TIMER message and the
2337  * parameter of the WM_TIMER message is not NULL, the lParam parameter
2338  * points to the function that is called instead of the window
2339  * procedure.
2340  *  
2341  * The message must be valid.
2342  *
2343  * RETURNS
2344  *
2345  *   DispatchMessage() returns the result of the window procedure invoked.
2346  *
2347  * CONFORMANCE
2348  *
2349  *   ECMA-234, Win32 
2350  *
2351  */
2352 LONG WINAPI DispatchMessageW( const MSG* msg )
2353 {
2354     WND * wndPtr;
2355     LONG retval;
2356     int painting;
2357     
2358       /* Process timer messages */
2359     if ((msg->message == WM_TIMER) || (msg->message == WM_SYSTIMER))
2360     {
2361         if (msg->lParam)
2362         {
2363 /*            HOOK_CallHooks32W( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */
2364
2365             /* before calling window proc, verify whether timer is still valid;
2366                there's a slim chance that the application kills the timer
2367                between GetMessage and DispatchMessage API calls */
2368             if (!TIMER_IsTimerValid(msg->hwnd, (UINT) msg->wParam, (HWINDOWPROC) msg->lParam))
2369                 return 0; /* invalid winproc */
2370
2371             return CallWindowProcW( (WNDPROC)msg->lParam, msg->hwnd,
2372                                    msg->message, msg->wParam, GetTickCount() );
2373         }
2374     }
2375
2376     if (!msg->hwnd) return 0;
2377     if (!(wndPtr = WIN_FindWndPtr( msg->hwnd ))) return 0;
2378     if (!wndPtr->winproc)
2379     {
2380         retval = 0;
2381         goto END;
2382     }
2383     painting = (msg->message == WM_PAINT);
2384     if (painting) wndPtr->flags |= WIN_NEEDS_BEGINPAINT;
2385 /*    HOOK_CallHooks32W( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */
2386
2387     SPY_EnterMessage( SPY_DISPATCHMESSAGE, msg->hwnd, msg->message,
2388                       msg->wParam, msg->lParam );
2389     retval = CallWindowProcW( (WNDPROC)wndPtr->winproc,
2390                                 msg->hwnd, msg->message,
2391                                 msg->wParam, msg->lParam );
2392     SPY_ExitMessage( SPY_RESULT_OK, msg->hwnd, msg->message, retval,
2393                      msg->wParam, msg->lParam );
2394
2395     WIN_ReleaseWndPtr(wndPtr);
2396     wndPtr = WIN_FindWndPtr(msg->hwnd);
2397
2398     if (painting && wndPtr &&
2399         (wndPtr->flags & WIN_NEEDS_BEGINPAINT) && wndPtr->hrgnUpdate)
2400     {
2401         ERR("BeginPaint not called on WM_PAINT for hwnd %04x!\n", 
2402             msg->hwnd);
2403         wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
2404         /* Validate the update region to avoid infinite WM_PAINT loop */
2405         ValidateRect( msg->hwnd, NULL );
2406     }
2407 END:
2408     WIN_ReleaseWndPtr(wndPtr);
2409     return retval;
2410 }
2411
2412
2413 /***********************************************************************
2414  *              RegisterWindowMessage (USER.118)
2415  *              RegisterWindowMessageA (USER32.@)
2416  */
2417 WORD WINAPI RegisterWindowMessageA( LPCSTR str )
2418 {
2419     TRACE("%s\n", str );
2420     return GlobalAddAtomA( str );
2421 }
2422
2423
2424 /***********************************************************************
2425  *              RegisterWindowMessageW (USER32.@)
2426  */
2427 WORD WINAPI RegisterWindowMessageW( LPCWSTR str )
2428 {
2429     TRACE("%p\n", str );
2430     return GlobalAddAtomW( str );
2431 }
2432
2433
2434 /***********************************************************************
2435  *              InSendMessage (USER.192)
2436  */
2437 BOOL16 WINAPI InSendMessage16(void)
2438 {
2439     return InSendMessage();
2440 }
2441
2442
2443 /***********************************************************************
2444  *              InSendMessage (USER32.@)
2445  */
2446 BOOL WINAPI InSendMessage(void)
2447 {
2448     return (InSendMessageEx(NULL) & (ISMEX_SEND|ISMEX_REPLIED)) == ISMEX_SEND;
2449 }
2450
2451
2452 /***********************************************************************
2453  *              InSendMessageEx  (USER32.@)
2454  */
2455 DWORD WINAPI InSendMessageEx( LPVOID reserved )
2456 {
2457     DWORD ret = 0;
2458     SERVER_START_REQ( in_send_message )
2459     {
2460         if (!SERVER_CALL_ERR()) ret = req->flags;
2461     }
2462     SERVER_END_REQ;
2463     return ret;
2464 }
2465
2466
2467 /***********************************************************************
2468  *              BroadcastSystemMessage (USER32.@)
2469  */
2470 LONG WINAPI BroadcastSystemMessage(
2471         DWORD dwFlags,LPDWORD recipients,UINT uMessage,WPARAM wParam,
2472         LPARAM lParam
2473 ) {
2474         FIXME_(sendmsg)("(%08lx,%08lx,%08x,%08x,%08lx): stub!\n",
2475               dwFlags,*recipients,uMessage,wParam,lParam
2476         );
2477         return 0;
2478 }
2479
2480 /***********************************************************************
2481  *              SendNotifyMessageA (USER32.@)
2482  */
2483 BOOL WINAPI SendNotifyMessageA(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
2484 {
2485    return MSG_SendMessage(hwnd, msg, wParam, lParam, INFINITE, QMSG_WIN32A, NULL);
2486 }
2487
2488 /***********************************************************************
2489  *              SendNotifyMessageW (USER32.@)
2490  */
2491 BOOL WINAPI SendNotifyMessageW(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
2492 {
2493    return MSG_SendMessage(hwnd, msg, wParam, lParam, INFINITE, QMSG_WIN32W, NULL);
2494 }
2495
2496 /***********************************************************************
2497  *              SendMessageCallbackA (USER32.@)
2498  * FIXME: It's like PostMessage. The callback gets called when the message
2499  * is processed. We have to modify the message processing for an exact
2500  * implementation...
2501  * The callback is only called when the thread that called us calls one of
2502  * Get/Peek/WaitMessage.
2503  */
2504 BOOL WINAPI SendMessageCallbackA(
2505         HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam,
2506         FARPROC lpResultCallBack,DWORD dwData)
2507 {       
2508         FIXME("(0x%04x,0x%04x,0x%08x,0x%08lx,%p,0x%08lx),stub!\n",
2509               hWnd,Msg,wParam,lParam,lpResultCallBack,dwData);
2510         if ( hWnd == HWND_BROADCAST)
2511         {       PostMessageA( hWnd, Msg, wParam, lParam);
2512                 FIXME("Broadcast: Callback will not be called!\n");
2513                 return TRUE;
2514         }
2515         (lpResultCallBack)( hWnd, Msg, dwData, SendMessageA ( hWnd, Msg, wParam, lParam ));
2516                 return TRUE;
2517 }
2518 /***********************************************************************
2519  *              SendMessageCallbackW (USER32.@)
2520  * FIXME: see SendMessageCallbackA.
2521  */
2522 BOOL WINAPI SendMessageCallbackW(
2523         HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam,
2524         FARPROC lpResultCallBack,DWORD dwData)
2525 {       
2526         FIXME("(0x%04x,0x%04x,0x%08x,0x%08lx,%p,0x%08lx),stub!\n",
2527               hWnd,Msg,wParam,lParam,lpResultCallBack,dwData);
2528         if ( hWnd == HWND_BROADCAST)
2529         {       PostMessageW( hWnd, Msg, wParam, lParam);
2530                 FIXME("Broadcast: Callback will not be called!\n");
2531                 return TRUE;
2532         }
2533         (lpResultCallBack)( hWnd, Msg, dwData, SendMessageA ( hWnd, Msg, wParam, lParam ));
2534                 return TRUE;
2535 }