Release 961023
[wine/multimedia.git] / windows / message.c
blob3e5f253cee9e432a32b04f7023061b1678eb7358
1 /*
2 * Message queues related functions
4 * Copyright 1993, 1994 Alexandre Julliard
5 */
7 #include <stdlib.h>
8 #include <string.h>
9 #include <ctype.h>
10 #include <sys/time.h>
11 #include <sys/types.h>
13 #include "message.h"
14 #include "win.h"
15 #include "gdi.h"
16 #include "sysmetrics.h"
17 #include "heap.h"
18 #include "hook.h"
19 #include "spy.h"
20 #include "winpos.h"
21 #include "atom.h"
22 #include "dde.h"
23 #include "queue.h"
24 #include "winproc.h"
25 #include "stddebug.h"
26 /* #define DEBUG_MSG */
27 #include "debug.h"
29 #define WM_NCMOUSEFIRST WM_NCMOUSEMOVE
30 #define WM_NCMOUSELAST WM_NCMBUTTONDBLCLK
32 #define HWND_BROADCAST16 ((HWND16)0xffff)
33 #define HWND_BROADCAST32 ((HWND32)0xffffffff)
35 #define ASCII_CHAR_HACK 0x0800
37 typedef enum { SYSQ_MSG_ABANDON, SYSQ_MSG_SKIP, SYSQ_MSG_ACCEPT } SYSQ_STATUS;
39 extern WPARAM16 lastEventChar; /* event.c */
40 extern BOOL MouseButtonsStates[3];
41 extern BOOL AsyncMouseButtonsStates[3];
42 extern BYTE KeyStateTable[256];
43 extern BYTE AsyncKeyStateTable[256];
45 extern MESSAGEQUEUE *pCursorQueue; /* queue.c */
46 extern MESSAGEQUEUE *pActiveQueue;
48 DWORD MSG_WineStartTicks; /* Ticks at Wine startup */
50 static WORD doubleClickSpeed = 452;
51 static INT32 debugSMRL = 0; /* intertask SendMessage() recursion level */
53 /***********************************************************************
54 * MSG_TranslateMouseMsg
56 * Translate an mouse hardware event into a real mouse message.
57 * Return value indicates whether the translated message must be passed
58 * to the user.
59 * Actions performed:
60 * - Find the window for this message.
61 * - Translate button-down messages in double-clicks.
62 * - Send the WM_NCHITTEST message to find where the cursor is.
63 * - Activate the window if needed.
64 * - Translate the message into a non-client message, or translate
65 * the coordinates to client coordinates.
66 * - Send the WM_SETCURSOR message.
68 static SYSQ_STATUS MSG_TranslateMouseMsg( MSG16 *msg, BOOL remove )
70 WND *pWnd;
71 BOOL eatMsg = FALSE;
72 INT16 hittest;
73 MOUSEHOOKSTRUCT16 *hook;
74 BOOL32 ret;
75 static DWORD lastClickTime = 0;
76 static WORD lastClickMsg = 0;
77 static POINT16 lastClickPos = { 0, 0 };
78 POINT16 pt = msg->pt;
79 MESSAGEQUEUE *queue = (MESSAGEQUEUE *)GlobalLock16(GetTaskQueue(0));
81 BOOL mouseClick = ((msg->message == WM_LBUTTONDOWN) ||
82 (msg->message == WM_RBUTTONDOWN) ||
83 (msg->message == WM_MBUTTONDOWN));
85 /* Find the window */
87 if ((msg->hwnd = GetCapture16()) != 0)
89 BOOL32 ret;
91 ScreenToClient16( msg->hwnd, &pt );
92 msg->lParam = MAKELONG( pt.x, pt.y );
93 /* No need to further process the message */
95 if (!HOOK_GetHook( WH_MOUSE, GetTaskQueue(0)) ||
96 !(hook = SEGPTR_NEW(MOUSEHOOKSTRUCT16)))
97 return SYSQ_MSG_ACCEPT;
98 hook->pt = msg->pt;
99 hook->hwnd = msg->hwnd;
100 hook->wHitTestCode = HTCLIENT;
101 hook->dwExtraInfo = 0;
102 ret = !HOOK_CallHooks( WH_MOUSE, remove ? HC_ACTION : HC_NOREMOVE,
103 msg->message, (LPARAM)SEGPTR_GET(hook));
104 SEGPTR_FREE(hook);
105 return ret ? SYSQ_MSG_ACCEPT : SYSQ_MSG_SKIP ;
108 hittest = WINPOS_WindowFromPoint( msg->pt, &pWnd );
109 if (pWnd->hmemTaskQ != GetTaskQueue(0))
111 /* Not for the current task */
112 if (queue) QUEUE_ClearWakeBit( queue, QS_MOUSE );
113 /* Wake up the other task */
114 queue = (MESSAGEQUEUE *)GlobalLock16( pWnd->hmemTaskQ );
115 if (queue) QUEUE_SetWakeBit( queue, QS_MOUSE );
116 return SYSQ_MSG_ABANDON;
118 pCursorQueue = queue;
119 msg->hwnd = pWnd->hwndSelf;
121 if ((hittest != HTERROR) && mouseClick)
123 HWND hwndTop = WIN_GetTopParent( msg->hwnd );
125 /* Send the WM_PARENTNOTIFY message */
127 WIN_SendParentNotify( msg->hwnd, msg->message, 0,
128 MAKELPARAM( msg->pt.x, msg->pt.y ) );
130 /* Activate the window if needed */
132 if (msg->hwnd != GetActiveWindow() &&
133 msg->hwnd != GetDesktopWindow16())
135 LONG ret = SendMessage16( msg->hwnd, WM_MOUSEACTIVATE, hwndTop,
136 MAKELONG( hittest, msg->message ) );
138 if ((ret == MA_ACTIVATEANDEAT) || (ret == MA_NOACTIVATEANDEAT))
139 eatMsg = TRUE;
141 if (((ret == MA_ACTIVATE) || (ret == MA_ACTIVATEANDEAT))
142 && hwndTop != GetActiveWindow() )
143 WINPOS_SetActiveWindow( hwndTop, TRUE , TRUE );
147 /* Send the WM_SETCURSOR message */
149 SendMessage16( msg->hwnd, WM_SETCURSOR, (WPARAM16)msg->hwnd,
150 MAKELONG( hittest, msg->message ));
151 if (eatMsg) return SYSQ_MSG_SKIP;
153 /* Check for double-click */
155 if (mouseClick)
157 BOOL dbl_click = FALSE;
159 if ((msg->message == lastClickMsg) &&
160 (msg->time - lastClickTime < doubleClickSpeed) &&
161 (abs(msg->pt.x - lastClickPos.x) < SYSMETRICS_CXDOUBLECLK/2) &&
162 (abs(msg->pt.y - lastClickPos.y) < SYSMETRICS_CYDOUBLECLK/2))
163 dbl_click = TRUE;
165 if (dbl_click && (hittest == HTCLIENT))
167 /* Check whether window wants the double click message. */
168 dbl_click = (pWnd->class->style & CS_DBLCLKS) != 0;
171 if (dbl_click) switch(msg->message)
173 case WM_LBUTTONDOWN: msg->message = WM_LBUTTONDBLCLK; break;
174 case WM_RBUTTONDOWN: msg->message = WM_RBUTTONDBLCLK; break;
175 case WM_MBUTTONDOWN: msg->message = WM_MBUTTONDBLCLK; break;
178 if (remove)
180 lastClickTime = msg->time;
181 lastClickMsg = msg->message;
182 lastClickPos = msg->pt;
186 /* Build the translated message */
188 if (hittest == HTCLIENT)
189 ScreenToClient16( msg->hwnd, &pt );
190 else
192 msg->wParam = hittest;
193 msg->message += WM_NCLBUTTONDOWN - WM_LBUTTONDOWN;
195 msg->lParam = MAKELONG( pt.x, pt.y );
197 /* Call the WH_MOUSE hook */
199 if (!HOOK_GetHook( WH_MOUSE, GetTaskQueue(0)) ||
200 !(hook = SEGPTR_NEW(MOUSEHOOKSTRUCT16)))
201 return SYSQ_MSG_ACCEPT;
203 hook->pt = msg->pt;
204 hook->hwnd = msg->hwnd;
205 hook->wHitTestCode = hittest;
206 hook->dwExtraInfo = 0;
207 ret = !HOOK_CallHooks( WH_MOUSE, remove ? HC_ACTION : HC_NOREMOVE,
208 msg->message, (LPARAM)SEGPTR_GET(hook) );
209 SEGPTR_FREE(hook);
210 return ret ? SYSQ_MSG_ACCEPT : SYSQ_MSG_SKIP;
214 /***********************************************************************
215 * MSG_TranslateKeyboardMsg
217 * Translate an keyboard hardware event into a real message.
218 * Return value indicates whether the translated message must be passed
219 * to the user.
221 static SYSQ_STATUS MSG_TranslateKeyboardMsg( MSG16 *msg, BOOL remove )
223 WND *pWnd;
225 /* Should check Ctrl-Esc and PrintScreen here */
227 msg->hwnd = GetFocus16();
228 if (!msg->hwnd)
230 /* Send the message to the active window instead, */
231 /* translating messages to their WM_SYS equivalent */
233 msg->hwnd = GetActiveWindow();
235 if( msg->message < WM_SYSKEYDOWN )
236 msg->message += WM_SYSKEYDOWN - WM_KEYDOWN;
238 pWnd = WIN_FindWndPtr( msg->hwnd );
239 if (pWnd && (pWnd->hmemTaskQ != GetTaskQueue(0)))
241 /* Not for the current task */
242 MESSAGEQUEUE *queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) );
243 if (queue) QUEUE_ClearWakeBit( queue, QS_KEY );
244 /* Wake up the other task */
245 queue = (MESSAGEQUEUE *)GlobalLock16( pWnd->hmemTaskQ );
246 if (queue) QUEUE_SetWakeBit( queue, QS_KEY );
247 return SYSQ_MSG_ABANDON;
249 return (HOOK_CallHooks( WH_KEYBOARD, remove ? HC_ACTION : HC_NOREMOVE,
250 msg->wParam, msg->lParam ))
251 ? SYSQ_MSG_SKIP : SYSQ_MSG_ACCEPT;
255 /***********************************************************************
256 * MSG_JournalRecordMsg
258 * Build an EVENTMSG structure and call JOURNALRECORD hook
260 static void MSG_JournalRecordMsg( MSG16 *msg )
262 EVENTMSG16 *event = SEGPTR_NEW(EVENTMSG16);
263 if (!event) return;
264 event->message = msg->message;
265 event->time = msg->time;
266 if ((msg->message >= WM_KEYFIRST) && (msg->message <= WM_KEYLAST))
268 event->paramL = (msg->wParam & 0xFF) | (HIWORD(msg->lParam) << 8);
269 event->paramH = msg->lParam & 0x7FFF;
270 if (HIWORD(msg->lParam) & 0x0100)
271 event->paramH |= 0x8000; /* special_key - bit */
272 HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0,
273 (LPARAM)SEGPTR_GET(event) );
275 else if ((msg->message >= WM_MOUSEFIRST) && (msg->message <= WM_MOUSELAST))
277 event->paramL = LOWORD(msg->lParam); /* X pos */
278 event->paramH = HIWORD(msg->lParam); /* Y pos */
279 ClientToScreen16( msg->hwnd, (LPPOINT16)&event->paramL );
280 HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0,
281 (LPARAM)SEGPTR_GET(event) );
283 else if ((msg->message >= WM_NCMOUSEFIRST) &&
284 (msg->message <= WM_NCMOUSELAST))
286 event->paramL = LOWORD(msg->lParam); /* X pos */
287 event->paramH = HIWORD(msg->lParam); /* Y pos */
288 event->message += WM_MOUSEMOVE-WM_NCMOUSEMOVE;/* give no info about NC area */
289 HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0,
290 (LPARAM)SEGPTR_GET(event) );
292 SEGPTR_FREE(event);
295 /*****************************************************************
296 * MSG_JournalPlayBackIsAscii
298 static BOOL MSG_JournalPlayBackIsAscii(WPARAM16 wParam)
300 return ((wParam>VK_HELP && wParam<VK_F1) ||
301 wParam == VK_SPACE ||
302 wParam == VK_ESCAPE ||
303 wParam == VK_RETURN ||
304 wParam == VK_TAB ||
305 wParam == VK_BACK);
309 /***********************************************************************
310 * MSG_JournalPlayBackMsg
312 * Get an EVENTMSG struct via call JOURNALPAYBACK hook function
314 static int MSG_JournalPlayBackMsg(void)
316 EVENTMSG16 *tmpMsg;
317 long wtime,lParam;
318 WORD keyDown,i,wParam,result=0;
320 if ( HOOK_GetHook(WH_JOURNALPLAYBACK, 0) )
322 tmpMsg = SEGPTR_NEW(EVENTMSG16);
323 wtime=HOOK_CallHooks( WH_JOURNALPLAYBACK, HC_GETNEXT, 0, (LPARAM)SEGPTR_GET(tmpMsg));
324 /* dprintf_msg(stddeb,"Playback wait time =%ld\n",wtime); */
325 if (wtime<=0)
327 wtime=0;
328 if ((tmpMsg->message>= WM_KEYFIRST) && (tmpMsg->message <= WM_KEYLAST))
330 wParam=tmpMsg->paramL & 0xFF;
331 lParam=MAKELONG(tmpMsg->paramH&0x7ffff,tmpMsg->paramL>>8);
332 if (tmpMsg->message == WM_KEYDOWN || tmpMsg->message == WM_SYSKEYDOWN)
334 for (keyDown=i=0; i<256 && !keyDown; i++)
335 if (KeyStateTable[i] & 0x80)
336 keyDown++;
337 if (!keyDown)
338 lParam |= 0x40000000;
339 AsyncKeyStateTable[wParam]=KeyStateTable[wParam] |= 0x80;
340 if (MSG_JournalPlayBackIsAscii(wParam))
342 lastEventChar= wParam; /* control TranslateMessage() */
343 lParam |= (LONG)((LONG)ASCII_CHAR_HACK*0x10000L);
345 if (!(KeyStateTable[VK_SHIFT] & 0x80) &&
346 !(KeyStateTable[VK_CAPITAL] & 0x80))
347 lastEventChar= tolower(lastEventChar);
348 if (KeyStateTable[VK_CONTROL] & 0x80)
349 lastEventChar&=0x1f;
352 else /* WM_KEYUP, WM_SYSKEYUP */
354 lParam |= 0xC0000000;
355 AsyncKeyStateTable[wParam]=KeyStateTable[wParam] &= ~0x80;
357 if (KeyStateTable[VK_MENU] & 0x80)
358 lParam |= 0x20000000;
359 if (tmpMsg->paramH & 0x8000) /*special_key bit*/
360 lParam |= 0x01000000;
361 hardware_event( tmpMsg->message, wParam, lParam,0, 0, tmpMsg->time, 0 );
363 else
365 if ((tmpMsg->message>= WM_MOUSEFIRST) && (tmpMsg->message <= WM_MOUSELAST))
367 switch (tmpMsg->message)
369 case WM_LBUTTONDOWN:MouseButtonsStates[0]=AsyncMouseButtonsStates[0]=1;break;
370 case WM_LBUTTONUP: MouseButtonsStates[0]=AsyncMouseButtonsStates[0]=0;break;
371 case WM_MBUTTONDOWN:MouseButtonsStates[1]=AsyncMouseButtonsStates[1]=1;break;
372 case WM_MBUTTONUP: MouseButtonsStates[1]=AsyncMouseButtonsStates[1]=0;break;
373 case WM_RBUTTONDOWN:MouseButtonsStates[2]=AsyncMouseButtonsStates[2]=1;break;
374 case WM_RBUTTONUP: MouseButtonsStates[2]=AsyncMouseButtonsStates[2]=0;break;
376 AsyncKeyStateTable[VK_LBUTTON]= KeyStateTable[VK_LBUTTON] = MouseButtonsStates[0] << 8;
377 AsyncKeyStateTable[VK_MBUTTON]= KeyStateTable[VK_MBUTTON] = MouseButtonsStates[1] << 8;
378 AsyncKeyStateTable[VK_RBUTTON]= KeyStateTable[VK_RBUTTON] = MouseButtonsStates[2] << 8;
379 SetCursorPos(tmpMsg->paramL,tmpMsg->paramH);
380 lParam=MAKELONG(tmpMsg->paramL,tmpMsg->paramH);
381 wParam=0;
382 if (MouseButtonsStates[0]) wParam |= MK_LBUTTON;
383 if (MouseButtonsStates[1]) wParam |= MK_MBUTTON;
384 if (MouseButtonsStates[2]) wParam |= MK_RBUTTON;
385 hardware_event( tmpMsg->message, wParam, lParam,
386 tmpMsg->paramL, tmpMsg->paramH, tmpMsg->time, 0 );
389 HOOK_CallHooks( WH_JOURNALPLAYBACK, HC_SKIP, 0, (LPARAM)SEGPTR_GET(tmpMsg));
391 else
392 result= QS_MOUSE | QS_KEY;
393 SEGPTR_FREE(tmpMsg);
395 return result;
398 /***********************************************************************
399 * MSG_PeekHardwareMsg
401 * Peek for a hardware message matching the hwnd and message filters.
403 static BOOL MSG_PeekHardwareMsg( MSG16 *msg, HWND hwnd, WORD first, WORD last,
404 BOOL remove )
406 SYSQ_STATUS status = SYSQ_MSG_ACCEPT;
407 MESSAGEQUEUE *sysMsgQueue = QUEUE_GetSysQueue();
408 int i, pos = sysMsgQueue->nextMessage;
410 /* If the queue is empty, attempt to fill it */
411 if (!sysMsgQueue->msgCount && XPending(display))
412 EVENT_WaitXEvent( FALSE, FALSE );
414 for (i = 0; i < sysMsgQueue->msgCount; i++, pos++)
416 if (pos >= sysMsgQueue->queueSize) pos = 0;
417 *msg = sysMsgQueue->messages[pos].msg;
419 /* Translate message; return FALSE immediately on SYSQ_MSG_ABANDON */
421 if ((msg->message >= WM_MOUSEFIRST) && (msg->message <= WM_MOUSELAST))
423 if ((status = MSG_TranslateMouseMsg(msg,remove)) == SYSQ_MSG_ABANDON)
424 return FALSE;
426 else if ((msg->message >= WM_KEYFIRST) && (msg->message <= WM_KEYLAST))
428 if ((status = MSG_TranslateKeyboardMsg(msg,remove)) == SYSQ_MSG_ABANDON)
429 return FALSE;
431 else /* Non-standard hardware event */
433 HARDWAREHOOKSTRUCT16 *hook;
434 if ((hook = SEGPTR_NEW(HARDWAREHOOKSTRUCT16)))
436 BOOL32 ret;
437 hook->hWnd = msg->hwnd;
438 hook->wMessage = msg->message;
439 hook->wParam = msg->wParam;
440 hook->lParam = msg->lParam;
441 ret = HOOK_CallHooks( WH_HARDWARE,
442 remove ? HC_ACTION : HC_NOREMOVE,
443 0, (LPARAM)SEGPTR_GET(hook) );
444 SEGPTR_FREE(hook);
445 status = ret ? SYSQ_MSG_SKIP : SYSQ_MSG_ACCEPT;
449 if (status == SYSQ_MSG_SKIP)
451 if (remove) QUEUE_RemoveMsg( sysMsgQueue, pos );
452 /* FIXME: call CBT_CLICKSKIPPED from here */
453 continue;
456 /* Check message against filters */
458 if (hwnd && (msg->hwnd != hwnd)) continue;
459 if ((first || last) &&
460 ((msg->message < first) || (msg->message > last))) continue;
461 if (remove)
463 if (HOOK_GetHook( WH_JOURNALRECORD, GetTaskQueue(0) ))
464 MSG_JournalRecordMsg( msg );
465 QUEUE_RemoveMsg( sysMsgQueue, pos );
467 return TRUE;
469 return FALSE;
473 /**********************************************************************
474 * SetDoubleClickTime (USER.20)
476 void SetDoubleClickTime( WORD interval )
478 doubleClickSpeed = interval ? interval : 500;
482 /**********************************************************************
483 * GetDoubleClickTime (USER.21)
485 WORD GetDoubleClickTime()
487 return doubleClickSpeed;
491 /***********************************************************************
492 * MSG_SendMessage
494 * Implementation of an inter-task SendMessage.
496 static LRESULT MSG_SendMessage( HQUEUE16 hDestQueue, HWND hwnd, UINT msg,
497 WPARAM16 wParam, LPARAM lParam )
499 INT32 prevSMRL = debugSMRL;
500 QSMCTRL qCtrl = { 0, 1};
501 MESSAGEQUEUE *queue, *destQ;
503 if (!(queue = (MESSAGEQUEUE*)GlobalLock16( GetTaskQueue(0) ))) return 0;
504 if (!(destQ = (MESSAGEQUEUE*)GlobalLock16( hDestQueue ))) return 0;
506 if (IsTaskLocked() || !IsWindow(hwnd)) return 0;
508 debugSMRL+=4;
509 dprintf_sendmsg(stddeb,"%*sSM: %s [%04x] (%04x -> %04x)\n",
510 prevSMRL, "", SPY_GetMsgName(msg), msg, queue->self, hDestQueue );
512 if( !(queue->wakeBits & QS_SMPARAMSFREE) )
514 dprintf_sendmsg(stddeb,"\tIntertask SendMessage: sleeping since unreplied SendMessage pending\n");
515 queue->changeBits &= ~QS_SMPARAMSFREE;
516 QUEUE_WaitBits( QS_SMPARAMSFREE );
519 /* resume sending */
521 queue->hWnd = hwnd;
522 queue->msg = msg;
523 queue->wParam = wParam;
524 queue->lParam = lParam;
525 queue->hPrevSendingTask = destQ->hSendingTask;
526 destQ->hSendingTask = GetTaskQueue(0);
528 queue->wakeBits &= ~QS_SMPARAMSFREE;
530 dprintf_sendmsg(stddeb,"%*ssm: smResultInit = %08x\n", prevSMRL, "", (unsigned)&qCtrl);
532 queue->smResultInit = &qCtrl;
534 QUEUE_SetWakeBit( destQ, QS_SENDMESSAGE );
536 /* perform task switch and wait for the result */
538 while( qCtrl.bPending )
540 if (!(queue->wakeBits & QS_SMRESULT))
542 queue->changeBits &= ~QS_SMRESULT;
543 DirectedYield( destQ->hTask );
544 QUEUE_WaitBits( QS_SMRESULT );
545 dprintf_sendmsg(stddeb,"\tsm: have result!\n");
547 /* got something */
549 dprintf_sendmsg(stddeb,"%*ssm: smResult = %08x\n", prevSMRL, "", (unsigned)queue->smResult );
551 queue->smResult->lResult = queue->SendMessageReturn;
552 queue->smResult->bPending = FALSE;
553 queue->wakeBits &= ~QS_SMRESULT;
555 if( queue->smResult != &qCtrl )
556 dprintf_msg(stddeb,"%*ssm: weird scenes inside the goldmine!\n", prevSMRL, "");
558 queue->smResultInit = NULL;
560 dprintf_sendmsg(stddeb,"%*sSM: [%04x] returning %08lx\n", prevSMRL, "", msg, qCtrl.lResult);
561 debugSMRL-=4;
563 return qCtrl.lResult;
567 /***********************************************************************
568 * ReplyMessage (USER.115)
570 void ReplyMessage( LRESULT result )
572 MESSAGEQUEUE *senderQ;
573 MESSAGEQUEUE *queue;
575 if (!(queue = (MESSAGEQUEUE*)GlobalLock16( GetTaskQueue(0) ))) return;
577 dprintf_msg(stddeb,"ReplyMessage, queue %04x\n", queue->self);
579 while( (senderQ = (MESSAGEQUEUE*)GlobalLock16( queue->InSendMessageHandle)))
581 dprintf_msg(stddeb,"\trpm: replying to %04x (%04x -> %04x)\n",
582 queue->msg, queue->self, senderQ->self);
584 if( queue->wakeBits & QS_SENDMESSAGE )
586 QUEUE_ReceiveMessage( queue );
587 continue; /* ReceiveMessage() already called us */
590 if(!(senderQ->wakeBits & QS_SMRESULT) ) break;
591 OldYield();
593 if( !senderQ ) { dprintf_msg(stddeb,"\trpm: done\n"); return; }
595 senderQ->SendMessageReturn = result;
596 dprintf_msg(stddeb,"\trpm: smResult = %08x, result = %08lx\n",
597 (unsigned)queue->smResultCurrent, result );
599 senderQ->smResult = queue->smResultCurrent;
600 queue->InSendMessageHandle = 0;
602 QUEUE_SetWakeBit( senderQ, QS_SMRESULT );
603 DirectedYield( queue->hSendingTask );
607 /***********************************************************************
608 * MSG_PeekMessage
610 static BOOL MSG_PeekMessage( LPMSG16 msg, HWND hwnd, WORD first, WORD last,
611 WORD flags, BOOL peek )
613 int pos, mask;
614 MESSAGEQUEUE *msgQueue;
615 HQUEUE16 hQueue;
617 #ifdef CONFIG_IPC
618 DDE_TestDDE(hwnd); /* do we have dde handling in the window ?*/
619 DDE_GetRemoteMessage();
620 #endif /* CONFIG_IPC */
622 mask = QS_POSTMESSAGE | QS_SENDMESSAGE; /* Always selected */
623 if (first || last)
625 /* MSWord gets stuck if we do not check for nonclient mouse messages */
627 if ((first <= WM_KEYLAST) && (last >= WM_KEYFIRST)) mask |= QS_KEY;
628 if ( ((first <= WM_MOUSELAST) && (last >= WM_MOUSEFIRST)) ||
629 ((first <= WM_NCMOUSELAST) && (last >= WM_NCMOUSEFIRST)) ) mask |= QS_MOUSE;
630 if ((first <= WM_TIMER) && (last >= WM_TIMER)) mask |= QS_TIMER;
631 if ((first <= WM_SYSTIMER) && (last >= WM_SYSTIMER)) mask |= QS_TIMER;
632 if ((first <= WM_PAINT) && (last >= WM_PAINT)) mask |= QS_PAINT;
634 else mask |= QS_MOUSE | QS_KEY | QS_TIMER | QS_PAINT;
636 if (IsTaskLocked()) flags |= PM_NOYIELD;
638 while(1)
640 hQueue = GetTaskQueue(0);
641 msgQueue = (MESSAGEQUEUE *)GlobalLock16( hQueue );
642 if (!msgQueue) return FALSE;
643 msgQueue->changeBits = 0;
645 /* First handle a message put by SendMessage() */
647 if (msgQueue->wakeBits & QS_SENDMESSAGE)
648 QUEUE_ReceiveMessage( msgQueue );
650 /* Now handle a WM_QUIT message
652 * FIXME: PostQuitMessage() should post WM_QUIT and
653 * set QS_POSTMESSAGE wakebit instead of this.
656 if (msgQueue->wPostQMsg &&
657 (!first || WM_QUIT >= first) &&
658 (!last || WM_QUIT <= last) )
660 msg->hwnd = hwnd;
661 msg->message = WM_QUIT;
662 msg->wParam = msgQueue->wExitCode;
663 msg->lParam = 0;
664 break;
667 /* Now find a normal message */
669 if (((msgQueue->wakeBits & mask) & QS_POSTMESSAGE) &&
670 ((pos = QUEUE_FindMsg( msgQueue, hwnd, first, last )) != -1))
672 QMSG *qmsg = &msgQueue->messages[pos];
673 *msg = qmsg->msg;
674 msgQueue->GetMessageTimeVal = msg->time;
675 msgQueue->GetMessagePosVal = *(DWORD *)&msg->pt;
676 msgQueue->GetMessageExtraInfoVal = qmsg->extraInfo;
678 if (flags & PM_REMOVE) QUEUE_RemoveMsg( msgQueue, pos );
679 break;
682 msgQueue->changeBits |= MSG_JournalPlayBackMsg();
684 /* Now find a hardware event */
686 if (((msgQueue->wakeBits & mask) & (QS_MOUSE | QS_KEY)) &&
687 MSG_PeekHardwareMsg( msg, hwnd, first, last, flags & PM_REMOVE ))
689 /* Got one */
690 msgQueue->GetMessageTimeVal = msg->time;
691 msgQueue->GetMessagePosVal = *(DWORD *)&msg->pt;
692 msgQueue->GetMessageExtraInfoVal = 0; /* Always 0 for now */
693 break;
696 /* Check again for SendMessage */
698 if (msgQueue->wakeBits & QS_SENDMESSAGE)
699 QUEUE_ReceiveMessage( msgQueue );
701 /* Now find a WM_PAINT message */
703 if ((msgQueue->wakeBits & mask) & QS_PAINT)
705 WND* wndPtr;
706 msg->hwnd = WIN_FindWinToRepaint( hwnd , hQueue );
707 msg->message = WM_PAINT;
708 msg->wParam = 0;
709 msg->lParam = 0;
711 if ((wndPtr = WIN_FindWndPtr(msg->hwnd)))
713 if( wndPtr->dwStyle & WS_MINIMIZE &&
714 wndPtr->class->hIcon )
716 msg->message = WM_PAINTICON;
717 msg->wParam = 1;
720 if( !hwnd || msg->hwnd == hwnd || IsChild(hwnd,msg->hwnd) )
722 if( wndPtr->flags & WIN_INTERNAL_PAINT && !wndPtr->hrgnUpdate)
724 wndPtr->flags &= ~WIN_INTERNAL_PAINT;
725 QUEUE_DecPaintCount( hQueue );
727 break;
732 /* Check for timer messages, but yield first */
734 if (!(flags & PM_NOYIELD))
736 UserYield();
737 if (msgQueue->wakeBits & QS_SENDMESSAGE)
738 QUEUE_ReceiveMessage( msgQueue );
740 if ((msgQueue->wakeBits & mask) & QS_TIMER)
742 if (TIMER_GetTimerMsg(msg, hwnd, hQueue, flags & PM_REMOVE)) break;
745 if (peek)
747 if (!(flags & PM_NOYIELD)) UserYield();
748 return FALSE;
750 msgQueue->wakeMask = mask;
751 QUEUE_WaitBits( mask );
754 /* We got a message */
755 if (peek) return TRUE;
756 else return (msg->message != WM_QUIT);
760 /***********************************************************************
761 * MSG_InternalGetMessage
763 * GetMessage() function for internal use. Behave like GetMessage(),
764 * but also call message filters and optionally send WM_ENTERIDLE messages.
765 * 'hwnd' must be the handle of the dialog or menu window.
766 * 'code' is the message filter value (MSGF_??? codes).
768 BOOL32 MSG_InternalGetMessage( MSG16 *msg, HWND32 hwnd, HWND32 hwndOwner,
769 WPARAM32 code, WORD flags, BOOL32 sendIdle )
771 for (;;)
773 if (sendIdle)
775 if (!MSG_PeekMessage( msg, 0, 0, 0, flags, TRUE ))
777 /* No message present -> send ENTERIDLE and wait */
778 if (IsWindow(hwndOwner))
779 SendMessage16( hwndOwner, WM_ENTERIDLE,
780 code, (LPARAM)hwnd );
781 MSG_PeekMessage( msg, 0, 0, 0, flags, FALSE );
784 else /* Always wait for a message */
785 MSG_PeekMessage( msg, 0, 0, 0, flags, FALSE );
787 /* Call message filters */
789 if (HOOK_GetHook( WH_SYSMSGFILTER, GetTaskQueue(0) ) ||
790 HOOK_GetHook( WH_MSGFILTER, GetTaskQueue(0) ))
792 MSG16 *pmsg = SEGPTR_NEW(MSG16);
793 if (pmsg)
795 BOOL32 ret;
796 *pmsg = *msg;
797 ret = ((BOOL16)HOOK_CallHooks( WH_SYSMSGFILTER, code, 0,
798 (LPARAM)SEGPTR_GET(pmsg) ) ||
799 (BOOL16)HOOK_CallHooks( WH_MSGFILTER, code, 0,
800 (LPARAM)SEGPTR_GET(pmsg) ));
801 SEGPTR_FREE(pmsg);
802 if (ret)
804 /* Message filtered -> remove it from the queue */
805 /* if it's still there. */
806 if (!(flags & PM_REMOVE))
807 MSG_PeekMessage( msg, 0, 0, 0, PM_REMOVE, TRUE );
808 continue;
813 return (msg->message != WM_QUIT);
818 /***********************************************************************
819 * PeekMessage16 (USER.109)
821 BOOL16 PeekMessage16( LPMSG16 msg, HWND16 hwnd, UINT16 first,
822 UINT16 last, UINT16 flags )
824 return MSG_PeekMessage( msg, hwnd, first, last, flags, TRUE );
828 /***********************************************************************
829 * GetMessage (USER.108)
831 BOOL GetMessage( SEGPTR msg, HWND hwnd, UINT first, UINT last )
833 MSG16 *lpmsg = (MSG16 *)PTR_SEG_TO_LIN(msg);
834 MSG_PeekMessage( lpmsg,
835 hwnd, first, last, PM_REMOVE, FALSE );
837 dprintf_msg(stddeb,"message %04x, hwnd %04x, filter(%04x - %04x)\n", lpmsg->message,
838 hwnd, first, last );
839 HOOK_CallHooks( WH_GETMESSAGE, HC_ACTION, 0, (LPARAM)msg );
840 return (lpmsg->message != WM_QUIT);
844 /***********************************************************************
845 * PostMessage (USER.110)
847 BOOL PostMessage( HWND hwnd, WORD message, WORD wParam, LONG lParam )
849 MSG16 msg;
850 WND *wndPtr;
852 msg.hwnd = hwnd;
853 msg.message = message;
854 msg.wParam = wParam;
855 msg.lParam = lParam;
856 msg.time = GetTickCount();
857 msg.pt.x = 0;
858 msg.pt.y = 0;
860 #ifdef CONFIG_IPC
861 if (DDE_PostMessage(&msg))
862 return TRUE;
863 #endif /* CONFIG_IPC */
865 if (hwnd == HWND_BROADCAST16)
867 dprintf_msg(stddeb,"PostMessage // HWND_BROADCAST !\n");
868 for (wndPtr = WIN_GetDesktop()->child; wndPtr; wndPtr = wndPtr->next)
870 if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION)
872 dprintf_msg(stddeb,"BROADCAST Message to hWnd=%04x m=%04X w=%04X l=%08lX !\n",
873 wndPtr->hwndSelf, message, wParam, lParam);
874 PostMessage( wndPtr->hwndSelf, message, wParam, lParam );
877 dprintf_msg(stddeb,"PostMessage // End of HWND_BROADCAST !\n");
878 return TRUE;
881 wndPtr = WIN_FindWndPtr( hwnd );
882 if (!wndPtr || !wndPtr->hmemTaskQ) return FALSE;
884 return QUEUE_AddMsg( wndPtr->hmemTaskQ, &msg, 0 );
887 /***********************************************************************
888 * PostAppMessage16 (USER.116)
890 BOOL16 PostAppMessage16( HTASK16 hTask, UINT16 message, WPARAM16 wParam,
891 LPARAM lParam )
893 MSG16 msg;
895 if (GetTaskQueue(hTask) == 0) return FALSE;
896 msg.hwnd = 0;
897 msg.message = message;
898 msg.wParam = wParam;
899 msg.lParam = lParam;
900 msg.time = GetTickCount();
901 msg.pt.x = 0;
902 msg.pt.y = 0;
904 return QUEUE_AddMsg( GetTaskQueue(hTask), &msg, 0 );
908 /***********************************************************************
909 * SendMessage16 (USER.111)
911 LRESULT SendMessage16( HWND16 hwnd, UINT16 msg, WPARAM16 wParam, LPARAM lParam)
913 WND * wndPtr;
914 LRESULT ret;
916 #ifdef CONFIG_IPC
917 MSG16 DDE_msg = { hwnd, msg, wParam, lParam };
918 if (DDE_SendMessage(&DDE_msg)) return TRUE;
919 #endif /* CONFIG_IPC */
921 if (hwnd == HWND_BROADCAST16)
923 dprintf_msg(stddeb,"SendMessage // HWND_BROADCAST !\n");
924 for (wndPtr = WIN_GetDesktop()->child; wndPtr; wndPtr = wndPtr->next)
926 if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION)
928 dprintf_msg(stddeb,"BROADCAST Message to hWnd=%04x m=%04X w=%04lX l=%08lX !\n",
929 wndPtr->hwndSelf, msg, (DWORD)wParam, lParam);
930 SendMessage16( wndPtr->hwndSelf, msg, wParam, lParam );
933 dprintf_msg(stddeb,"SendMessage // End of HWND_BROADCAST !\n");
934 return TRUE;
937 if (HOOK_GetHook( WH_CALLWNDPROC, GetTaskQueue(0) ))
939 struct msgstruct
941 LPARAM lParam;
942 WPARAM16 wParam;
943 UINT16 wMsg;
944 HWND16 hWnd;
945 } *pmsg;
947 if ((pmsg = SEGPTR_NEW(struct msgstruct)))
949 pmsg->hWnd = hwnd;
950 pmsg->wMsg = msg;
951 pmsg->wParam = wParam;
952 pmsg->lParam = lParam;
953 HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, 1,
954 (LPARAM)SEGPTR_GET(pmsg) );
955 hwnd = pmsg->hWnd;
956 msg = pmsg->wMsg;
957 wParam = pmsg->wParam;
958 lParam = pmsg->lParam;
959 SEGPTR_FREE( pmsg );
963 if (!(wndPtr = WIN_FindWndPtr( hwnd )))
965 fprintf( stderr, "SendMessage16: invalid hwnd %04x\n", hwnd );
966 return 0;
968 if (QUEUE_IsDoomedQueue(wndPtr->hmemTaskQ))
969 return 0; /* Don't send anything if the task is dying */
970 if (wndPtr->hmemTaskQ != GetTaskQueue(0))
971 return MSG_SendMessage( wndPtr->hmemTaskQ, hwnd, msg, wParam, lParam );
973 SPY_EnterMessage( SPY_SENDMESSAGE16, hwnd, msg, wParam, lParam );
974 ret = CallWindowProc16( (WNDPROC16)wndPtr->winproc,
975 hwnd, msg, wParam, lParam );
976 SPY_ExitMessage( SPY_RESULT_OK16, hwnd, msg, ret );
977 return ret;
981 /***********************************************************************
982 * SendMessage32A (USER32.453)
984 LRESULT SendMessage32A(HWND32 hwnd, UINT32 msg, WPARAM32 wParam, LPARAM lParam)
986 WND * wndPtr;
987 LRESULT ret;
989 if (hwnd == HWND_BROADCAST32)
991 for (wndPtr = WIN_GetDesktop()->child; wndPtr; wndPtr = wndPtr->next)
993 /* FIXME: should use something like EnumWindows here */
994 if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION)
995 SendMessage32A( wndPtr->hwndSelf, msg, wParam, lParam );
997 return TRUE;
1000 /* FIXME: call hooks */
1002 if (!(wndPtr = WIN_FindWndPtr( hwnd )))
1004 fprintf( stderr, "SendMessage32A: invalid hwnd %08x\n", hwnd );
1005 return 0;
1008 if (WINPROC_GetProcType( wndPtr->winproc ) == WIN_PROC_16)
1010 /* Use SendMessage16 for now to get hooks right */
1011 UINT16 msg16;
1012 WPARAM16 wParam16;
1013 if (WINPROC_MapMsg32ATo16( msg, wParam, &msg16, &wParam16, &lParam ) == -1)
1014 return 0;
1015 ret = SendMessage16( hwnd, msg16, wParam16, lParam );
1016 WINPROC_UnmapMsg32ATo16( msg16, wParam16, lParam );
1017 return ret;
1020 if (QUEUE_IsDoomedQueue(wndPtr->hmemTaskQ))
1021 return 0; /* Don't send anything if the task is dying */
1023 if (wndPtr->hmemTaskQ != GetTaskQueue(0))
1025 fprintf( stderr, "SendMessage32A: intertask message not supported\n" );
1026 return 0;
1029 SPY_EnterMessage( SPY_SENDMESSAGE32, hwnd, msg, wParam, lParam );
1030 ret = CallWindowProc32A( (WNDPROC32)wndPtr->winproc,
1031 hwnd, msg, wParam, lParam );
1032 SPY_ExitMessage( SPY_RESULT_OK32, hwnd, msg, ret );
1033 return ret;
1037 /***********************************************************************
1038 * SendMessage32W (USER32.458)
1040 LRESULT SendMessage32W(HWND32 hwnd, UINT32 msg, WPARAM32 wParam, LPARAM lParam)
1042 WND * wndPtr;
1043 LRESULT ret;
1045 if (hwnd == HWND_BROADCAST32)
1047 for (wndPtr = WIN_GetDesktop()->child; wndPtr; wndPtr = wndPtr->next)
1049 /* FIXME: should use something like EnumWindows here */
1050 if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION)
1051 SendMessage32W( wndPtr->hwndSelf, msg, wParam, lParam );
1053 return TRUE;
1056 /* FIXME: call hooks */
1058 if (!(wndPtr = WIN_FindWndPtr( hwnd )))
1060 fprintf( stderr, "SendMessage32W: invalid hwnd %08x\n", hwnd );
1061 return 0;
1063 if (QUEUE_IsDoomedQueue(wndPtr->hmemTaskQ))
1064 return 0; /* Don't send anything if the task is dying */
1065 if (wndPtr->hmemTaskQ != GetTaskQueue(0))
1067 fprintf( stderr, "SendMessage32W: intertask message not supported\n" );
1068 return 0;
1071 SPY_EnterMessage( SPY_SENDMESSAGE32, hwnd, msg, wParam, lParam );
1072 ret = CallWindowProc32W( (WNDPROC32)wndPtr->winproc,
1073 hwnd, msg, wParam, lParam );
1074 SPY_ExitMessage( SPY_RESULT_OK32, hwnd, msg, ret );
1075 return ret;
1079 /***********************************************************************
1080 * WaitMessage (USER.112)
1082 void WaitMessage( void )
1084 QUEUE_WaitBits( QS_ALLINPUT );
1088 /***********************************************************************
1089 * TranslateMessage (USER.113)
1091 * This should call ToAscii but it is currently broken
1095 BOOL TranslateMessage( LPMSG16 msg )
1097 UINT message = msg->message;
1098 /* BYTE wparam[2]; */
1100 if ((message == WM_KEYDOWN) || (message == WM_KEYUP) ||
1101 (message == WM_SYSKEYDOWN) || (message == WM_SYSKEYUP))
1103 dprintf_msg(stddeb, "Translating key %04x, scancode %04x\n", msg->wParam,
1104 HIWORD(msg->lParam) );
1106 if( HIWORD(msg->lParam) & ASCII_CHAR_HACK )
1108 /* if( ToAscii( msg->wParam, HIWORD(msg->lParam), (LPSTR)&KeyStateTable,
1109 wparam, 0 ) )
1112 message += 2 - (message & 0x0001);
1114 PostMessage( msg->hwnd, message, lastEventChar, msg->lParam );
1116 return TRUE;
1119 return FALSE;
1123 /***********************************************************************
1124 * DispatchMessage (USER.114)
1126 LONG DispatchMessage( const MSG16* msg )
1128 WND * wndPtr;
1129 LONG retval;
1130 int painting;
1132 /* Process timer messages */
1133 if ((msg->message == WM_TIMER) || (msg->message == WM_SYSTIMER))
1135 if (msg->lParam)
1137 /* HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */
1138 return CallWindowProc16( (WNDPROC16)msg->lParam, msg->hwnd,
1139 msg->message, msg->wParam, GetTickCount() );
1143 if (!msg->hwnd) return 0;
1144 if (!(wndPtr = WIN_FindWndPtr( msg->hwnd ))) return 0;
1145 if (!wndPtr->winproc) return 0;
1146 painting = (msg->message == WM_PAINT);
1147 if (painting) wndPtr->flags |= WIN_NEEDS_BEGINPAINT;
1148 /* HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */
1150 SPY_EnterMessage( SPY_DISPATCHMESSAGE16, msg->hwnd, msg->message,
1151 msg->wParam, msg->lParam );
1152 retval = CallWindowProc16( (WNDPROC16)wndPtr->winproc,
1153 msg->hwnd, msg->message,
1154 msg->wParam, msg->lParam );
1155 SPY_ExitMessage( SPY_RESULT_OK16, msg->hwnd, msg->message, retval );
1157 if (painting && (wndPtr = WIN_FindWndPtr( msg->hwnd )) &&
1158 (wndPtr->flags & WIN_NEEDS_BEGINPAINT) && wndPtr->hrgnUpdate)
1160 fprintf(stderr, "BeginPaint not called on WM_PAINT for hwnd %04x!\n",
1161 msg->hwnd);
1162 wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
1163 /* Validate the update region to avoid infinite WM_PAINT loop */
1164 ValidateRect32( msg->hwnd, NULL );
1166 return retval;
1170 /***********************************************************************
1171 * RegisterWindowMessage16 (USER.118)
1173 WORD RegisterWindowMessage16( SEGPTR str )
1175 dprintf_msg(stddeb, "RegisterWindowMessage16: %08lx\n", (DWORD)str );
1176 return GlobalAddAtom16( str );
1180 /***********************************************************************
1181 * RegisterWindowMessage32A (USER32.436)
1183 WORD RegisterWindowMessage32A( LPCSTR str )
1185 dprintf_msg(stddeb, "RegisterWindowMessage32A: %s\n", str );
1186 return GlobalAddAtom32A( str );
1190 /***********************************************************************
1191 * RegisterWindowMessage32W (USER32.437)
1193 WORD RegisterWindowMessage32W( LPCWSTR str )
1195 dprintf_msg(stddeb, "RegisterWindowMessage32W: %p\n", str );
1196 return GlobalAddAtom32W( str );
1200 /***********************************************************************
1201 * GetTickCount (USER.13) (KERNEL32.299)
1203 DWORD GetTickCount(void)
1205 struct timeval t;
1206 gettimeofday( &t, NULL );
1207 return ((t.tv_sec * 1000) + (t.tv_usec / 1000)) - MSG_WineStartTicks;
1211 /***********************************************************************
1212 * GetCurrentTime (USER.15)
1214 * (effectively identical to GetTickCount)
1216 DWORD GetCurrentTime(void)
1218 return GetTickCount();
1222 /***********************************************************************
1223 * InSendMessage (USER.192)
1225 BOOL InSendMessage()
1227 MESSAGEQUEUE *queue;
1229 if (!(queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) )))
1230 return 0;
1231 return (BOOL)queue->InSendMessageHandle;