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