Added mappings for a few messages.
[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 "winbase.h"
14 #include "wingdi.h"
15 #include "winuser.h"
16 #include "message.h"
17 #include "winerror.h"
18 #include "wine/server.h"
19 #include "win.h"
20 #include "heap.h"
21 #include "hook.h"
22 #include "input.h"
23 #include "spy.h"
24 #include "winpos.h"
25 #include "dde.h"
26 #include "queue.h"
27 #include "winproc.h"
28 #include "user.h"
29 #include "thread.h"
30 #include "task.h"
31 #include "controls.h"
32 #include "debugtools.h"
33
34 DEFAULT_DEBUG_CHANNEL(msg);
35 DECLARE_DEBUG_CHANNEL(key);
36
37 #define WM_NCMOUSEFIRST         WM_NCMOUSEMOVE
38 #define WM_NCMOUSELAST          WM_NCMBUTTONDBLCLK
39
40 static UINT doubleClickSpeed = 452;
41
42
43 /***********************************************************************
44  *           is_keyboard_message
45  */
46 inline static BOOL is_keyboard_message( UINT message )
47 {
48     return (message >= WM_KEYFIRST && message <= WM_KEYLAST);
49 }
50
51
52 /***********************************************************************
53  *           is_mouse_message
54  */
55 inline static BOOL is_mouse_message( UINT message )
56 {
57     return ((message >= WM_NCMOUSEFIRST && message <= WM_NCMOUSELAST) ||
58             (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST));
59 }
60
61
62 /***********************************************************************
63  *           check_message_filter
64  */
65 inline static BOOL check_message_filter( const MSG *msg, HWND hwnd, UINT first, UINT last )
66 {
67     if (hwnd)
68     {
69         if (msg->hwnd != hwnd && !IsChild( hwnd, msg->hwnd )) return FALSE;
70     }
71     if (first || last)
72     {
73        return (msg->message >= first && msg->message <= last);
74     }
75     return TRUE;
76 }
77
78
79 /***********************************************************************
80  *           process_sent_messages
81  *
82  * Process all pending sent messages.
83  */
84 inline static void process_sent_messages(void)
85 {
86     MSG msg;
87     MSG_peek_message( &msg, 0, 0, 0, GET_MSG_REMOVE | GET_MSG_SENT_ONLY );
88 }
89
90
91 /***********************************************************************
92  *           queue_hardware_message
93  *
94  * store a hardware message in the thread queue
95  */
96 static void queue_hardware_message( MSG *msg, ULONG_PTR extra_info, enum message_type type )
97 {
98     SERVER_START_REQ( send_message )
99     {
100         req->type   = type;
101         req->id     = (void *)GetWindowThreadProcessId( msg->hwnd, NULL );
102         req->win    = msg->hwnd;
103         req->msg    = msg->message;
104         req->wparam = msg->wParam;
105         req->lparam = msg->lParam;
106         req->x      = msg->pt.x;
107         req->y      = msg->pt.y;
108         req->time   = msg->time;
109         req->info   = extra_info;
110         req->timeout = 0;
111         SERVER_CALL();
112     }
113     SERVER_END_REQ;
114 }
115
116
117 /***********************************************************************
118  *           MSG_SendParentNotify
119  *
120  * Send a WM_PARENTNOTIFY to all ancestors of the given window, unless
121  * the window has the WS_EX_NOPARENTNOTIFY style.
122  */
123 static void MSG_SendParentNotify( HWND hwnd, WORD event, WORD idChild, POINT pt )
124 {
125     WND *tmpWnd = WIN_FindWndPtr(hwnd);
126
127     /* pt has to be in the client coordinates of the parent window */
128     MapWindowPoints( 0, tmpWnd->hwndSelf, &pt, 1 );
129     while (tmpWnd)
130     {
131         if (!(tmpWnd->dwStyle & WS_CHILD) || (tmpWnd->dwExStyle & WS_EX_NOPARENTNOTIFY))
132         {
133             WIN_ReleaseWndPtr(tmpWnd);
134             break;
135         }
136         pt.x += tmpWnd->rectClient.left;
137         pt.y += tmpWnd->rectClient.top;
138         WIN_UpdateWndPtr(&tmpWnd,tmpWnd->parent);
139         SendMessageA( tmpWnd->hwndSelf, WM_PARENTNOTIFY,
140                       MAKEWPARAM( event, idChild ), MAKELPARAM( pt.x, pt.y ) );
141     }
142 }
143
144
145 /***********************************************************************
146  *          MSG_JournalPlayBackMsg
147  *
148  * Get an EVENTMSG struct via call JOURNALPLAYBACK hook function 
149  */
150 void MSG_JournalPlayBackMsg(void)
151 {
152     EVENTMSG tmpMsg;
153     MSG msg;
154     LRESULT wtime;
155     int keyDown,i;
156
157     if (!HOOK_IsHooked( WH_JOURNALPLAYBACK )) return;
158
159     wtime=HOOK_CallHooksA( WH_JOURNALPLAYBACK, HC_GETNEXT, 0, (LPARAM)&tmpMsg );
160     /*  TRACE(msg,"Playback wait time =%ld\n",wtime); */
161     if (wtime<=0)
162     {
163         wtime=0;
164         msg.message = tmpMsg.message;
165         msg.hwnd    = tmpMsg.hwnd;
166         msg.time    = tmpMsg.time;
167         if ((tmpMsg.message >= WM_KEYFIRST) && (tmpMsg.message <= WM_KEYLAST))
168         {
169             msg.wParam  = tmpMsg.paramL & 0xFF;
170             msg.lParam  = MAKELONG(tmpMsg.paramH&0x7ffff,tmpMsg.paramL>>8);
171             if (tmpMsg.message == WM_KEYDOWN || tmpMsg.message == WM_SYSKEYDOWN)
172             {
173                 for (keyDown=i=0; i<256 && !keyDown; i++)
174                     if (InputKeyStateTable[i] & 0x80)
175                         keyDown++;
176                 if (!keyDown)
177                     msg.lParam |= 0x40000000;
178                 AsyncKeyStateTable[msg.wParam]=InputKeyStateTable[msg.wParam] |= 0x80;
179             }
180             else                                       /* WM_KEYUP, WM_SYSKEYUP */
181             {
182                 msg.lParam |= 0xC0000000;
183                 AsyncKeyStateTable[msg.wParam]=InputKeyStateTable[msg.wParam] &= ~0x80;
184             }
185             if (InputKeyStateTable[VK_MENU] & 0x80)
186                 msg.lParam |= 0x20000000;
187             if (tmpMsg.paramH & 0x8000)              /*special_key bit*/
188                 msg.lParam |= 0x01000000;
189
190             msg.pt.x = msg.pt.y = 0;
191             queue_hardware_message( &msg, 0, MSG_HARDWARE_RAW );
192         }
193         else if ((tmpMsg.message>= WM_MOUSEFIRST) && (tmpMsg.message <= WM_MOUSELAST))
194         {
195             switch (tmpMsg.message)
196             {
197             case WM_LBUTTONDOWN:
198                 MouseButtonsStates[0]=AsyncMouseButtonsStates[0]=TRUE;break;
199             case WM_LBUTTONUP:
200                 MouseButtonsStates[0]=AsyncMouseButtonsStates[0]=FALSE;break;
201             case WM_MBUTTONDOWN:
202                 MouseButtonsStates[1]=AsyncMouseButtonsStates[1]=TRUE;break;
203             case WM_MBUTTONUP:
204                 MouseButtonsStates[1]=AsyncMouseButtonsStates[1]=FALSE;break;
205             case WM_RBUTTONDOWN:
206                 MouseButtonsStates[2]=AsyncMouseButtonsStates[2]=TRUE;break;
207             case WM_RBUTTONUP:
208                 MouseButtonsStates[2]=AsyncMouseButtonsStates[2]=FALSE;break;
209             }
210             AsyncKeyStateTable[VK_LBUTTON]= InputKeyStateTable[VK_LBUTTON] = MouseButtonsStates[0] ? 0x80 : 0;
211             AsyncKeyStateTable[VK_MBUTTON]= InputKeyStateTable[VK_MBUTTON] = MouseButtonsStates[1] ? 0x80 : 0;
212             AsyncKeyStateTable[VK_RBUTTON]= InputKeyStateTable[VK_RBUTTON] = MouseButtonsStates[2] ? 0x80 : 0;
213             SetCursorPos(tmpMsg.paramL,tmpMsg.paramH);
214             msg.lParam=MAKELONG(tmpMsg.paramL,tmpMsg.paramH);
215             msg.wParam=0;
216             if (MouseButtonsStates[0]) msg.wParam |= MK_LBUTTON;
217             if (MouseButtonsStates[1]) msg.wParam |= MK_MBUTTON;
218             if (MouseButtonsStates[2]) msg.wParam |= MK_RBUTTON;
219
220             msg.pt.x = tmpMsg.paramL;
221             msg.pt.y = tmpMsg.paramH;
222             queue_hardware_message( &msg, 0, MSG_HARDWARE_RAW );
223         }
224         HOOK_CallHooksA( WH_JOURNALPLAYBACK, HC_SKIP, 0, (LPARAM)&tmpMsg);
225     }
226     else
227     {
228         if( tmpMsg.message == WM_QUEUESYNC )
229             if (HOOK_IsHooked( WH_CBT ))
230                 HOOK_CallHooksA( WH_CBT, HCBT_QS, 0, 0L);
231     }
232 }
233
234
235 /***********************************************************************
236  *          process_raw_keyboard_message
237  *
238  * returns TRUE if the contents of 'msg' should be passed to the application
239  */
240 static BOOL process_raw_keyboard_message( MSG *msg, ULONG_PTR extra_info )
241 {
242     if (!(msg->hwnd = GetFocus()))
243     {
244         /* Send the message to the active window instead,  */
245         /* translating messages to their WM_SYS equivalent */
246         msg->hwnd = GetActiveWindow();
247         if (msg->message < WM_SYSKEYDOWN) msg->message += WM_SYSKEYDOWN - WM_KEYDOWN;
248     }
249
250     if (HOOK_IsHooked( WH_JOURNALRECORD ))
251     {
252         EVENTMSG event;
253
254         event.message = msg->message;
255         event.hwnd    = msg->hwnd;
256         event.time    = msg->time;
257         event.paramL  = (msg->wParam & 0xFF) | (HIWORD(msg->lParam) << 8);
258         event.paramH  = msg->lParam & 0x7FFF;
259         if (HIWORD(msg->lParam) & 0x0100) event.paramH |= 0x8000; /* special_key - bit */
260         HOOK_CallHooksA( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event );
261     }
262
263     return (msg->hwnd != 0);
264 }
265
266
267 /***********************************************************************
268  *          process_cooked_keyboard_message
269  *
270  * returns TRUE if the contents of 'msg' should be passed to the application
271  */
272 static BOOL process_cooked_keyboard_message( MSG *msg, BOOL remove )
273 {
274     if (remove)
275     {
276         /* Handle F1 key by sending out WM_HELP message */
277         if ((msg->message == WM_KEYUP) &&
278             (msg->wParam == VK_F1) &&
279             (msg->hwnd != GetDesktopWindow()) &&
280             !MENU_IsMenuActive())
281         {
282             HELPINFO hi;
283             hi.cbSize = sizeof(HELPINFO);
284             hi.iContextType = HELPINFO_WINDOW;
285             hi.iCtrlId = GetWindowLongA( msg->hwnd, GWL_ID );
286             hi.hItemHandle = msg->hwnd;
287             hi.dwContextId = GetWindowContextHelpId( msg->hwnd );
288             hi.MousePos = msg->pt;
289             SendMessageA(msg->hwnd, WM_HELP, 0, (LPARAM)&hi);
290         }
291     }
292
293     if (HOOK_CallHooksA( WH_KEYBOARD, remove ? HC_ACTION : HC_NOREMOVE,
294                          LOWORD(msg->wParam), msg->lParam ))
295     {
296         /* skip this message */
297         HOOK_CallHooksA( WH_CBT, HCBT_KEYSKIPPED, LOWORD(msg->wParam), msg->lParam );
298         return FALSE;
299     }
300     return TRUE;
301 }
302
303
304 /***********************************************************************
305  *          process_raw_mouse_message
306  *
307  * returns TRUE if the contents of 'msg' should be passed to the application
308  */
309 static BOOL process_raw_mouse_message( MSG *msg, ULONG_PTR extra_info )
310 {
311     static MSG clk_msg;
312
313     POINT pt;
314     INT ht, hittest;
315
316     /* find the window to dispatch this mouse message to */
317
318     hittest = HTCLIENT;
319     if (!(msg->hwnd = PERQDATA_GetCaptureWnd( &ht )))
320     {
321         /* If no capture HWND, find window which contains the mouse position.
322          * Also find the position of the cursor hot spot (hittest) */
323         HWND hWndScope = (HWND)extra_info;
324
325         if (!IsWindow(hWndScope)) hWndScope = 0;
326         if (!(msg->hwnd = WINPOS_WindowFromPoint( hWndScope, msg->pt, &hittest )))
327             msg->hwnd = GetDesktopWindow();
328         ht = hittest;
329     }
330
331     if (HOOK_IsHooked( WH_JOURNALRECORD ))
332     {
333         EVENTMSG event;
334         event.message = msg->message;
335         event.time    = msg->time;
336         event.hwnd    = msg->hwnd;
337         event.paramL  = msg->pt.x;
338         event.paramH  = msg->pt.y;
339         HOOK_CallHooksA( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event );
340     }
341
342     /* translate double clicks */
343
344     if ((msg->message == WM_LBUTTONDOWN) ||
345         (msg->message == WM_RBUTTONDOWN) ||
346         (msg->message == WM_MBUTTONDOWN))
347     {
348         BOOL update = TRUE;
349         /* translate double clicks -
350          * note that ...MOUSEMOVEs can slip in between
351          * ...BUTTONDOWN and ...BUTTONDBLCLK messages */
352
353         if (GetClassLongA( msg->hwnd, GCL_STYLE ) & CS_DBLCLKS || ht != HTCLIENT )
354         {
355            if ((msg->message == clk_msg.message) &&
356                (msg->hwnd == clk_msg.hwnd) &&
357                (msg->time - clk_msg.time < doubleClickSpeed) &&
358                (abs(msg->pt.x - clk_msg.pt.x) < GetSystemMetrics(SM_CXDOUBLECLK)/2) &&
359                (abs(msg->pt.y - clk_msg.pt.y) < GetSystemMetrics(SM_CYDOUBLECLK)/2))
360            {
361                msg->message += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
362                clk_msg.message = 0;
363                update = FALSE;
364            }
365         }
366         /* update static double click conditions */
367         if (update) clk_msg = *msg;
368     }
369
370     pt = msg->pt;
371     /* Note: windows has no concept of a non-client wheel message */
372     if (hittest != HTCLIENT && msg->message != WM_MOUSEWHEEL)
373     {
374         msg->message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
375         msg->wParam = hittest;
376     }
377     else ScreenToClient( msg->hwnd, &pt );
378     msg->lParam = MAKELONG( pt.x, pt.y );
379     return TRUE;
380 }
381
382
383 /***********************************************************************
384  *          process_cooked_mouse_message
385  *
386  * returns TRUE if the contents of 'msg' should be passed to the application
387  */
388 static BOOL process_cooked_mouse_message( MSG *msg, BOOL remove )
389 {
390     INT hittest = HTCLIENT;
391     UINT raw_message = msg->message;
392     BOOL eatMsg;
393
394     if (msg->message >= WM_NCMOUSEFIRST && msg->message <= WM_NCMOUSELAST)
395     {
396         raw_message += WM_MOUSEFIRST - WM_NCMOUSEFIRST;
397         hittest = msg->wParam;
398     }
399     if (raw_message == WM_LBUTTONDBLCLK ||
400         raw_message == WM_RBUTTONDBLCLK ||
401         raw_message == WM_MBUTTONDBLCLK)
402     {
403         raw_message += WM_LBUTTONDOWN - WM_LBUTTONDBLCLK;
404     }
405
406     if (HOOK_IsHooked( WH_MOUSE ))
407     {
408         MOUSEHOOKSTRUCT hook;
409         hook.pt           = msg->pt;
410         hook.hwnd         = msg->hwnd;
411         hook.wHitTestCode = hittest;
412         hook.dwExtraInfo  = 0;
413         if (HOOK_CallHooksA( WH_MOUSE, remove ? HC_ACTION : HC_NOREMOVE,
414                              msg->message, (LPARAM)&hook ))
415         {
416             hook.pt           = msg->pt;
417             hook.hwnd         = msg->hwnd;
418             hook.wHitTestCode = hittest;
419             hook.dwExtraInfo  = 0;
420             HOOK_CallHooksA( WH_CBT, HCBT_CLICKSKIPPED, msg->message, (LPARAM)&hook );
421             return FALSE;
422         }
423     }
424
425     if ((hittest == HTERROR) || (hittest == HTNOWHERE))
426     {
427         SendMessageA( msg->hwnd, WM_SETCURSOR, msg->hwnd, MAKELONG( hittest, raw_message ));
428         return FALSE;
429     }
430
431     if (!remove || GetCapture()) return TRUE;
432
433     eatMsg = FALSE;
434
435     if ((raw_message == WM_LBUTTONDOWN) ||
436         (raw_message == WM_RBUTTONDOWN) ||
437         (raw_message == WM_MBUTTONDOWN))
438     {
439         HWND hwndTop = WIN_GetTopParent( msg->hwnd );
440
441         /* Send the WM_PARENTNOTIFY,
442          * note that even for double/nonclient clicks
443          * notification message is still WM_L/M/RBUTTONDOWN.
444          */
445         MSG_SendParentNotify( msg->hwnd, raw_message, 0, msg->pt );
446
447         /* Activate the window if needed */
448
449         if (msg->hwnd != GetActiveWindow() && hwndTop != GetDesktopWindow())
450         {
451             LONG ret = SendMessageA( msg->hwnd, WM_MOUSEACTIVATE, hwndTop,
452                                      MAKELONG( hittest, raw_message ) );
453
454             switch(ret)
455             {
456             case MA_NOACTIVATEANDEAT:
457                 eatMsg = TRUE;
458                 /* fall through */
459             case MA_NOACTIVATE:
460                 break;
461             case MA_ACTIVATEANDEAT:
462                 eatMsg = TRUE;
463                 /* fall through */
464             case MA_ACTIVATE:
465                 if (hwndTop != GetForegroundWindow() )
466                 {
467                     if (!WINPOS_SetActiveWindow( hwndTop, TRUE , TRUE ))
468                         eatMsg = TRUE;
469                 }
470                 break;
471             default:
472                 WARN( "unknown WM_MOUSEACTIVATE code %ld\n", ret );
473                 break;
474             }
475         }
476     }
477
478     /* send the WM_SETCURSOR message */
479
480     /* Windows sends the normal mouse message as the message parameter
481        in the WM_SETCURSOR message even if it's non-client mouse message */
482     SendMessageA( msg->hwnd, WM_SETCURSOR, msg->hwnd, MAKELONG( hittest, raw_message ));
483
484     return !eatMsg;
485 }
486
487
488 /***********************************************************************
489  *          process_hardware_message
490  *
491  * returns TRUE if the contents of 'msg' should be passed to the application
492  */
493 BOOL MSG_process_raw_hardware_message( MSG *msg, ULONG_PTR extra_info, HWND hwnd_filter,
494                                        UINT first, UINT last, BOOL remove )
495 {
496     if (is_keyboard_message( msg->message ))
497     {
498         if (!process_raw_keyboard_message( msg, extra_info )) return FALSE;
499     }
500     else if (is_mouse_message( msg->message ))
501     {
502         if (!process_raw_mouse_message( msg, extra_info )) return FALSE;
503     }
504     else
505     {
506         ERR( "unknown message type %x\n", msg->message );
507         return FALSE;
508     }
509
510     /* check destination thread and filters */
511     if (!check_message_filter( msg, hwnd_filter, first, last ) ||
512         GetWindowThreadProcessId( msg->hwnd, NULL ) != GetCurrentThreadId())
513     {
514         /* queue it for later, or for another thread */
515         queue_hardware_message( msg, extra_info, MSG_HARDWARE_COOKED );
516         return FALSE;
517     }
518
519     /* save the message in the cooked queue if we didn't want to remove it */
520     if (!remove) queue_hardware_message( msg, extra_info, MSG_HARDWARE_COOKED );
521     return TRUE;
522 }
523
524
525 /***********************************************************************
526  *          MSG_process_cooked_hardware_message
527  *
528  * returns TRUE if the contents of 'msg' should be passed to the application
529  */
530 BOOL MSG_process_cooked_hardware_message( MSG *msg, BOOL remove )
531 {
532     if (is_keyboard_message( msg->message ))
533         return process_cooked_keyboard_message( msg, remove );
534
535     if (is_mouse_message( msg->message ))
536         return process_cooked_mouse_message( msg, remove );
537
538     ERR( "unknown message type %x\n", msg->message );
539     return FALSE;
540 }
541
542
543 /**********************************************************************
544  *              SetDoubleClickTime (USER32.@)
545  */
546 BOOL WINAPI SetDoubleClickTime( UINT interval )
547 {
548     doubleClickSpeed = interval ? interval : 500;
549     return TRUE;
550 }               
551
552
553 /**********************************************************************
554  *              GetDoubleClickTime (USER32.@)
555  */
556 UINT WINAPI GetDoubleClickTime(void)
557 {
558     return doubleClickSpeed;
559 }               
560
561
562 /***********************************************************************
563  *              WaitMessage (USER.112) Suspend thread pending messages
564  *              WaitMessage (USER32.@) Suspend thread pending messages
565  *
566  * WaitMessage() suspends a thread until events appear in the thread's
567  * queue.
568  */
569 BOOL WINAPI WaitMessage(void)
570 {
571     return (MsgWaitForMultipleObjectsEx( 0, NULL, INFINITE, QS_ALLINPUT, 0 ) != WAIT_FAILED);
572 }
573
574
575 /***********************************************************************
576  *              MsgWaitForMultipleObjectsEx   (USER32.@)
577  */
578 DWORD WINAPI MsgWaitForMultipleObjectsEx( DWORD count, CONST HANDLE *pHandles,
579                                           DWORD timeout, DWORD mask, DWORD flags )
580 {
581     HANDLE handles[MAXIMUM_WAIT_OBJECTS];
582     DWORD i, ret;
583     MESSAGEQUEUE *msgQueue;
584
585     if (count > MAXIMUM_WAIT_OBJECTS-1)
586     {
587         SetLastError( ERROR_INVALID_PARAMETER );
588         return WAIT_FAILED;
589     }
590
591     if (!(msgQueue = QUEUE_Current())) return WAIT_FAILED;
592
593     /* set the queue mask */
594     SERVER_START_REQ( set_queue_mask )
595     {
596         req->wake_mask    = (flags & MWMO_INPUTAVAILABLE) ? mask : 0;
597         req->changed_mask = mask;
598         req->skip_wait    = 0;
599         SERVER_CALL();
600     }
601     SERVER_END_REQ;
602
603     /* Add the thread event to the handle list */
604     for (i = 0; i < count; i++) handles[i] = pHandles[i];
605     handles[count] = msgQueue->server_queue;
606
607
608     if (USER_Driver.pMsgWaitForMultipleObjectsEx)
609     {
610         ret = USER_Driver.pMsgWaitForMultipleObjectsEx( count+1, handles, timeout, mask, flags );
611         if (ret == count+1) ret = count; /* pretend the msg queue is ready */
612     }
613     else
614         ret = WaitForMultipleObjectsEx( count+1, handles, flags & MWMO_WAITALL,
615                                         timeout, flags & MWMO_ALERTABLE );
616     return ret;
617 }
618
619
620 /***********************************************************************
621  *              MsgWaitForMultipleObjects (USER32.@)
622  */
623 DWORD WINAPI MsgWaitForMultipleObjects( DWORD count, CONST HANDLE *handles,
624                                         BOOL wait_all, DWORD timeout, DWORD mask )
625 {
626     return MsgWaitForMultipleObjectsEx( count, handles, timeout, mask,
627                                         wait_all ? MWMO_WAITALL : 0 );
628 }
629
630
631 /***********************************************************************
632  *              WaitForInputIdle (USER32.@)
633  */
634 DWORD WINAPI WaitForInputIdle( HANDLE hProcess, DWORD dwTimeOut )
635 {
636     DWORD cur_time, ret;
637     HANDLE idle_event = -1;
638
639     SERVER_START_REQ( wait_input_idle )
640     {
641         req->handle = hProcess;
642         req->timeout = dwTimeOut;
643         if (!(ret = SERVER_CALL_ERR())) idle_event = req->event;
644     }
645     SERVER_END_REQ;
646     if (ret) return 0xffffffff;  /* error */
647     if (!idle_event) return 0;  /* no event to wait on */
648
649     cur_time = GetTickCount();
650
651     TRACE("waiting for %x\n", idle_event );
652     while ( dwTimeOut > GetTickCount() - cur_time || dwTimeOut == INFINITE )
653     {
654         ret = MsgWaitForMultipleObjects ( 1, &idle_event, FALSE, dwTimeOut, QS_SENDMESSAGE );
655         if ( ret == ( WAIT_OBJECT_0 + 1 ))
656         {
657             process_sent_messages();
658             continue;
659         }
660         if ( ret == WAIT_TIMEOUT || ret == 0xFFFFFFFF )
661         {
662             TRACE("timeout or error\n");
663             return ret;
664         }
665         else
666         {
667             TRACE("finished\n");
668             return 0;
669         }
670     }
671
672     return WAIT_TIMEOUT;
673 }
674
675
676 /***********************************************************************
677  *              UserYield (USER.332)
678  *              UserYield16 (USER32.@)
679  */
680 void WINAPI UserYield16(void)
681 {
682    DWORD count;
683
684     /* Handle sent messages */
685     process_sent_messages();
686
687     /* Yield */
688     ReleaseThunkLock(&count);
689     if (count)
690     {
691         RestoreThunkLock(count);
692         /* Handle sent messages again */
693         process_sent_messages();
694     }
695 }
696
697
698 struct accent_char
699 {
700     BYTE ac_accent;
701     BYTE ac_char;
702     BYTE ac_result;
703 };
704
705 static const struct accent_char accent_chars[] =
706 {
707 /* A good idea should be to read /usr/X11/lib/X11/locale/iso8859-x/Compose */
708     {'`', 'A', '\300'},  {'`', 'a', '\340'},
709     {'\'', 'A', '\301'}, {'\'', 'a', '\341'},
710     {'^', 'A', '\302'},  {'^', 'a', '\342'},
711     {'~', 'A', '\303'},  {'~', 'a', '\343'},
712     {'"', 'A', '\304'},  {'"', 'a', '\344'},
713     {'O', 'A', '\305'},  {'o', 'a', '\345'},
714     {'0', 'A', '\305'},  {'0', 'a', '\345'},
715     {'A', 'A', '\305'},  {'a', 'a', '\345'},
716     {'A', 'E', '\306'},  {'a', 'e', '\346'},
717     {',', 'C', '\307'},  {',', 'c', '\347'},
718     {'`', 'E', '\310'},  {'`', 'e', '\350'},
719     {'\'', 'E', '\311'}, {'\'', 'e', '\351'},
720     {'^', 'E', '\312'},  {'^', 'e', '\352'},
721     {'"', 'E', '\313'},  {'"', 'e', '\353'},
722     {'`', 'I', '\314'},  {'`', 'i', '\354'},
723     {'\'', 'I', '\315'}, {'\'', 'i', '\355'},
724     {'^', 'I', '\316'},  {'^', 'i', '\356'},
725     {'"', 'I', '\317'},  {'"', 'i', '\357'},
726     {'-', 'D', '\320'},  {'-', 'd', '\360'},
727     {'~', 'N', '\321'},  {'~', 'n', '\361'},
728     {'`', 'O', '\322'},  {'`', 'o', '\362'},
729     {'\'', 'O', '\323'}, {'\'', 'o', '\363'},
730     {'^', 'O', '\324'},  {'^', 'o', '\364'},
731     {'~', 'O', '\325'},  {'~', 'o', '\365'},
732     {'"', 'O', '\326'},  {'"', 'o', '\366'},
733     {'/', 'O', '\330'},  {'/', 'o', '\370'},
734     {'`', 'U', '\331'},  {'`', 'u', '\371'},
735     {'\'', 'U', '\332'}, {'\'', 'u', '\372'},
736     {'^', 'U', '\333'},  {'^', 'u', '\373'},
737     {'"', 'U', '\334'},  {'"', 'u', '\374'},
738     {'\'', 'Y', '\335'}, {'\'', 'y', '\375'},
739     {'T', 'H', '\336'},  {'t', 'h', '\376'},
740     {'s', 's', '\337'},  {'"', 'y', '\377'},
741     {'s', 'z', '\337'},  {'i', 'j', '\377'},
742         /* iso-8859-2 uses this */
743     {'<', 'L', '\245'},  {'<', 'l', '\265'},    /* caron */
744     {'<', 'S', '\251'},  {'<', 's', '\271'},
745     {'<', 'T', '\253'},  {'<', 't', '\273'},
746     {'<', 'Z', '\256'},  {'<', 'z', '\276'},
747     {'<', 'C', '\310'},  {'<', 'c', '\350'},
748     {'<', 'E', '\314'},  {'<', 'e', '\354'},
749     {'<', 'D', '\317'},  {'<', 'd', '\357'},
750     {'<', 'N', '\322'},  {'<', 'n', '\362'},
751     {'<', 'R', '\330'},  {'<', 'r', '\370'},
752     {';', 'A', '\241'},  {';', 'a', '\261'},    /* ogonek */
753     {';', 'E', '\312'},  {';', 'e', '\332'},
754     {'\'', 'Z', '\254'}, {'\'', 'z', '\274'},   /* acute */
755     {'\'', 'R', '\300'}, {'\'', 'r', '\340'},
756     {'\'', 'L', '\305'}, {'\'', 'l', '\345'},
757     {'\'', 'C', '\306'}, {'\'', 'c', '\346'},
758     {'\'', 'N', '\321'}, {'\'', 'n', '\361'},
759 /*  collision whith S, from iso-8859-9 !!! */
760     {',', 'S', '\252'},  {',', 's', '\272'},    /* cedilla */
761     {',', 'T', '\336'},  {',', 't', '\376'},
762     {'.', 'Z', '\257'},  {'.', 'z', '\277'},    /* dot above */
763     {'/', 'L', '\243'},  {'/', 'l', '\263'},    /* slash */
764     {'/', 'D', '\320'},  {'/', 'd', '\360'},
765     {'(', 'A', '\303'},  {'(', 'a', '\343'},    /* breve */
766     {'\275', 'O', '\325'}, {'\275', 'o', '\365'},       /* double acute */
767     {'\275', 'U', '\334'}, {'\275', 'u', '\374'},
768     {'0', 'U', '\332'},  {'0', 'u', '\372'},    /* ring above */
769         /* iso-8859-3 uses this */
770     {'/', 'H', '\241'},  {'/', 'h', '\261'},    /* slash */
771     {'>', 'H', '\246'},  {'>', 'h', '\266'},    /* circumflex */
772     {'>', 'J', '\254'},  {'>', 'j', '\274'},
773     {'>', 'C', '\306'},  {'>', 'c', '\346'},
774     {'>', 'G', '\330'},  {'>', 'g', '\370'},
775     {'>', 'S', '\336'},  {'>', 's', '\376'},
776 /*  collision whith G( from iso-8859-9 !!!   */
777     {'(', 'G', '\253'},  {'(', 'g', '\273'},    /* breve */
778     {'(', 'U', '\335'},  {'(', 'u', '\375'},
779 /*  collision whith I. from iso-8859-3 !!!   */
780     {'.', 'I', '\251'},  {'.', 'i', '\271'},    /* dot above */
781     {'.', 'C', '\305'},  {'.', 'c', '\345'},
782     {'.', 'G', '\325'},  {'.', 'g', '\365'},
783         /* iso-8859-4 uses this */
784     {',', 'R', '\243'},  {',', 'r', '\263'},    /* cedilla */
785     {',', 'L', '\246'},  {',', 'l', '\266'},
786     {',', 'G', '\253'},  {',', 'g', '\273'},
787     {',', 'N', '\321'},  {',', 'n', '\361'},
788     {',', 'K', '\323'},  {',', 'k', '\363'},
789     {'~', 'I', '\245'},  {'~', 'i', '\265'},    /* tilde */
790     {'-', 'E', '\252'},  {'-', 'e', '\272'},    /* macron */
791     {'-', 'A', '\300'},  {'-', 'a', '\340'},
792     {'-', 'I', '\317'},  {'-', 'i', '\357'},
793     {'-', 'O', '\322'},  {'-', 'o', '\362'},
794     {'-', 'U', '\336'},  {'-', 'u', '\376'},
795     {'/', 'T', '\254'},  {'/', 't', '\274'},    /* slash */
796     {'.', 'E', '\314'},  {'.', 'e', '\344'},    /* dot above */
797     {';', 'I', '\307'},  {';', 'i', '\347'},    /* ogonek */
798     {';', 'U', '\331'},  {';', 'u', '\371'},
799         /* iso-8859-9 uses this */
800         /* iso-8859-9 has really bad choosen G( S, and I. as they collide
801          * whith the same letters on other iso-8859-x (that is they are on
802          * different places :-( ), if you use turkish uncomment these and
803          * comment out the lines in iso-8859-2 and iso-8859-3 sections
804          * FIXME: should be dynamic according to chosen language
805          *        if/when Wine has turkish support.  
806          */ 
807 /*  collision whith G( from iso-8859-3 !!!   */
808 /*  {'(', 'G', '\320'},  {'(', 'g', '\360'}, */ /* breve */
809 /*  collision whith S, from iso-8859-2 !!! */
810 /*  {',', 'S', '\336'},  {',', 's', '\376'}, */ /* cedilla */
811 /*  collision whith I. from iso-8859-3 !!!   */
812 /*  {'.', 'I', '\335'},  {'.', 'i', '\375'}, */ /* dot above */
813 };
814
815
816 /***********************************************************************
817  *              TranslateMessage (USER32.@)
818  *
819  * Implementation of TranslateMessage.
820  *
821  * TranslateMessage translates virtual-key messages into character-messages,
822  * as follows :
823  * WM_KEYDOWN/WM_KEYUP combinations produce a WM_CHAR or WM_DEADCHAR message.
824  * ditto replacing WM_* with WM_SYS*
825  * This produces WM_CHAR messages only for keys mapped to ASCII characters
826  * by the keyboard driver.
827  */
828 BOOL WINAPI TranslateMessage( const MSG *msg )
829 {
830     static int dead_char;
831     UINT message;
832     WCHAR wp[2];
833
834     if (msg->message >= WM_KEYFIRST && msg->message <= WM_KEYLAST)
835         TRACE_(key)("(%s, %04X, %08lX)\n",
836                     SPY_GetMsgName(msg->message), msg->wParam, msg->lParam );
837
838     if ((msg->message != WM_KEYDOWN) && (msg->message != WM_SYSKEYDOWN)) return FALSE;
839
840     TRACE_(key)("Translating key %s (%04x), scancode %02x\n",
841                  SPY_GetVKeyName(msg->wParam), msg->wParam, LOBYTE(HIWORD(msg->lParam)));
842
843     /* FIXME : should handle ToUnicode yielding 2 */
844     switch (ToUnicode(msg->wParam, HIWORD(msg->lParam), QueueKeyStateTable, wp, 2, 0))
845     {
846     case 1:
847         message = (msg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
848         /* Should dead chars handling go in ToAscii ? */
849         if (dead_char)
850         {
851             int i;
852
853             if (wp[0] == ' ') wp[0] =  dead_char;
854             if (dead_char == 0xa2) dead_char = '(';
855             else if (dead_char == 0xa8) dead_char = '"';
856             else if (dead_char == 0xb2) dead_char = ';';
857             else if (dead_char == 0xb4) dead_char = '\'';
858             else if (dead_char == 0xb7) dead_char = '<';
859             else if (dead_char == 0xb8) dead_char = ',';
860             else if (dead_char == 0xff) dead_char = '.';
861             for (i = 0; i < sizeof(accent_chars)/sizeof(accent_chars[0]); i++)
862                 if ((accent_chars[i].ac_accent == dead_char) &&
863                     (accent_chars[i].ac_char == wp[0]))
864                 {
865                     wp[0] = accent_chars[i].ac_result;
866                     break;
867                 }
868             dead_char = 0;
869         }
870         TRACE_(key)("1 -> PostMessage(%s)\n", SPY_GetMsgName(message));
871         PostMessageW( msg->hwnd, message, wp[0], msg->lParam );
872         return TRUE;
873
874     case -1:
875         message = (msg->message == WM_KEYDOWN) ? WM_DEADCHAR : WM_SYSDEADCHAR;
876         dead_char = wp[0];
877         TRACE_(key)("-1 -> PostMessage(%s)\n", SPY_GetMsgName(message));
878         PostMessageW( msg->hwnd, message, wp[0], msg->lParam );
879         return TRUE;
880     }
881     return FALSE;
882 }
883
884
885 /***********************************************************************
886  *              DispatchMessageA (USER32.@)
887  */
888 LONG WINAPI DispatchMessageA( const MSG* msg )
889 {
890     WND * wndPtr;
891     LONG retval;
892     int painting;
893     
894       /* Process timer messages */
895     if ((msg->message == WM_TIMER) || (msg->message == WM_SYSTIMER))
896     {
897         if (msg->lParam)
898         {
899 /*            HOOK_CallHooks32A( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */
900
901             /* before calling window proc, verify whether timer is still valid;
902                there's a slim chance that the application kills the timer
903                between GetMessage and DispatchMessage API calls */
904             if (!TIMER_IsTimerValid(msg->hwnd, (UINT) msg->wParam, (HWINDOWPROC) msg->lParam))
905                 return 0; /* invalid winproc */
906
907             return CallWindowProcA( (WNDPROC)msg->lParam, msg->hwnd,
908                                    msg->message, msg->wParam, GetTickCount() );
909         }
910     }
911
912     if (!msg->hwnd) return 0;
913     if (!(wndPtr = WIN_FindWndPtr( msg->hwnd ))) return 0;
914     if (!wndPtr->winproc)
915     {
916         retval = 0;
917         goto END;
918     }
919     painting = (msg->message == WM_PAINT);
920     if (painting) wndPtr->flags |= WIN_NEEDS_BEGINPAINT;
921 /*    HOOK_CallHooks32A( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */
922
923     SPY_EnterMessage( SPY_DISPATCHMESSAGE, msg->hwnd, msg->message,
924                       msg->wParam, msg->lParam );
925     retval = CallWindowProcA( (WNDPROC)wndPtr->winproc,
926                                 msg->hwnd, msg->message,
927                                 msg->wParam, msg->lParam );
928     SPY_ExitMessage( SPY_RESULT_OK, msg->hwnd, msg->message, retval,
929                      msg->wParam, msg->lParam );
930
931     WIN_ReleaseWndPtr(wndPtr);
932     wndPtr = WIN_FindWndPtr(msg->hwnd);
933
934     if (painting && wndPtr &&
935         (wndPtr->flags & WIN_NEEDS_BEGINPAINT) && wndPtr->hrgnUpdate)
936     {
937         ERR("BeginPaint not called on WM_PAINT for hwnd %04x!\n", 
938             msg->hwnd);
939         wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
940         /* Validate the update region to avoid infinite WM_PAINT loop */
941         RedrawWindow( wndPtr->hwndSelf, NULL, 0,
942                       RDW_NOFRAME | RDW_VALIDATE | RDW_NOCHILDREN | RDW_NOINTERNALPAINT );
943     }
944 END:
945     WIN_ReleaseWndPtr(wndPtr);
946     return retval;
947 }
948
949
950 /***********************************************************************
951  *              DispatchMessageW (USER32.@) Process Message
952  *
953  * Process the message specified in the structure *_msg_.
954  *
955  * If the lpMsg parameter points to a WM_TIMER message and the
956  * parameter of the WM_TIMER message is not NULL, the lParam parameter
957  * points to the function that is called instead of the window
958  * procedure.
959  *  
960  * The message must be valid.
961  *
962  * RETURNS
963  *
964  *   DispatchMessage() returns the result of the window procedure invoked.
965  *
966  * CONFORMANCE
967  *
968  *   ECMA-234, Win32 
969  *
970  */
971 LONG WINAPI DispatchMessageW( const MSG* msg )
972 {
973     WND * wndPtr;
974     LONG retval;
975     int painting;
976     
977       /* Process timer messages */
978     if ((msg->message == WM_TIMER) || (msg->message == WM_SYSTIMER))
979     {
980         if (msg->lParam)
981         {
982 /*            HOOK_CallHooks32W( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */
983
984             /* before calling window proc, verify whether timer is still valid;
985                there's a slim chance that the application kills the timer
986                between GetMessage and DispatchMessage API calls */
987             if (!TIMER_IsTimerValid(msg->hwnd, (UINT) msg->wParam, (HWINDOWPROC) msg->lParam))
988                 return 0; /* invalid winproc */
989
990             return CallWindowProcW( (WNDPROC)msg->lParam, msg->hwnd,
991                                    msg->message, msg->wParam, GetTickCount() );
992         }
993     }
994
995     if (!msg->hwnd) return 0;
996     if (!(wndPtr = WIN_FindWndPtr( msg->hwnd ))) return 0;
997     if (!wndPtr->winproc)
998     {
999         retval = 0;
1000         goto END;
1001     }
1002     painting = (msg->message == WM_PAINT);
1003     if (painting) wndPtr->flags |= WIN_NEEDS_BEGINPAINT;
1004 /*    HOOK_CallHooks32W( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */
1005
1006     SPY_EnterMessage( SPY_DISPATCHMESSAGE, msg->hwnd, msg->message,
1007                       msg->wParam, msg->lParam );
1008     retval = CallWindowProcW( (WNDPROC)wndPtr->winproc,
1009                                 msg->hwnd, msg->message,
1010                                 msg->wParam, msg->lParam );
1011     SPY_ExitMessage( SPY_RESULT_OK, msg->hwnd, msg->message, retval,
1012                      msg->wParam, msg->lParam );
1013
1014     WIN_ReleaseWndPtr(wndPtr);
1015     wndPtr = WIN_FindWndPtr(msg->hwnd);
1016
1017     if (painting && wndPtr &&
1018         (wndPtr->flags & WIN_NEEDS_BEGINPAINT) && wndPtr->hrgnUpdate)
1019     {
1020         ERR("BeginPaint not called on WM_PAINT for hwnd %04x!\n", 
1021             msg->hwnd);
1022         wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
1023         /* Validate the update region to avoid infinite WM_PAINT loop */
1024         RedrawWindow( wndPtr->hwndSelf, NULL, 0,
1025                       RDW_NOFRAME | RDW_VALIDATE | RDW_NOCHILDREN | RDW_NOINTERNALPAINT );
1026     }
1027 END:
1028     WIN_ReleaseWndPtr(wndPtr);
1029     return retval;
1030 }
1031
1032
1033 /***********************************************************************
1034  *              RegisterWindowMessage (USER.118)
1035  *              RegisterWindowMessageA (USER32.@)
1036  */
1037 WORD WINAPI RegisterWindowMessageA( LPCSTR str )
1038 {
1039     TRACE("%s\n", str );
1040     return GlobalAddAtomA( str );
1041 }
1042
1043
1044 /***********************************************************************
1045  *              RegisterWindowMessageW (USER32.@)
1046  */
1047 WORD WINAPI RegisterWindowMessageW( LPCWSTR str )
1048 {
1049     TRACE("%p\n", str );
1050     return GlobalAddAtomW( str );
1051 }
1052
1053
1054 /***********************************************************************
1055  *              BroadcastSystemMessage (USER32.@)
1056  */
1057 LONG WINAPI BroadcastSystemMessage(
1058         DWORD dwFlags,LPDWORD recipients,UINT uMessage,WPARAM wParam,
1059         LPARAM lParam
1060 ) {
1061         FIXME("(%08lx,%08lx,%08x,%08x,%08lx): stub!\n",
1062               dwFlags,*recipients,uMessage,wParam,lParam
1063         );
1064         return 0;
1065 }