Release 961222
[wine/multimedia.git] / windows / message.c
blobdcfec8d1eadce2d31fcf26687ffef6e5a2367444
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 typedef enum { SYSQ_MSG_ABANDON, SYSQ_MSG_SKIP, SYSQ_MSG_ACCEPT } SYSQ_STATUS;
37 extern BOOL MouseButtonsStates[3];
38 extern BOOL AsyncMouseButtonsStates[3];
39 extern BYTE InputKeyStateTable[256];
40 extern BYTE AsyncKeyStateTable[256];
42 BYTE QueueKeyStateTable[256];
44 extern MESSAGEQUEUE *pCursorQueue; /* queue.c */
45 extern MESSAGEQUEUE *pActiveQueue;
47 DWORD MSG_WineStartTicks; /* Ticks at Wine startup */
49 static WORD doubleClickSpeed = 452;
50 static INT32 debugSMRL = 0; /* intertask SendMessage() recursion level */
52 /***********************************************************************
53 * MSG_TranslateMouseMsg
55 * Translate an mouse hardware event into a real mouse message.
56 * Return value indicates whether the translated message must be passed
57 * to the user.
58 * Actions performed:
59 * - Find the window for this message.
60 * - Translate button-down messages in double-clicks.
61 * - Send the WM_NCHITTEST message to find where the cursor is.
62 * - Activate the window if needed.
63 * - Translate the message into a non-client message, or translate
64 * the coordinates to client coordinates.
65 * - Send the WM_SETCURSOR message.
67 static SYSQ_STATUS MSG_TranslateMouseMsg( MSG16 *msg, BOOL remove )
69 WND *pWnd;
70 BOOL eatMsg = FALSE;
71 INT16 hittest;
72 MOUSEHOOKSTRUCT16 *hook;
73 BOOL32 ret;
74 static DWORD lastClickTime = 0;
75 static WORD lastClickMsg = 0;
76 static POINT16 lastClickPos = { 0, 0 };
77 POINT16 pt = msg->pt;
78 MESSAGEQUEUE *queue = (MESSAGEQUEUE *)GlobalLock16(GetTaskQueue(0));
80 BOOL mouseClick = ((msg->message == WM_LBUTTONDOWN) ||
81 (msg->message == WM_RBUTTONDOWN) ||
82 (msg->message == WM_MBUTTONDOWN));
84 /* Find the window */
86 if ((msg->hwnd = GetCapture16()) != 0)
88 BOOL32 ret;
90 ScreenToClient16( msg->hwnd, &pt );
91 msg->lParam = MAKELONG( pt.x, pt.y );
92 /* No need to further process the message */
94 if (!HOOK_IsHooked( WH_MOUSE ) ||
95 !(hook = SEGPTR_NEW(MOUSEHOOKSTRUCT16)))
96 return SYSQ_MSG_ACCEPT;
97 hook->pt = msg->pt;
98 hook->hwnd = msg->hwnd;
99 hook->wHitTestCode = HTCLIENT;
100 hook->dwExtraInfo = 0;
101 ret = !HOOK_CallHooks16( WH_MOUSE, remove ? HC_ACTION : HC_NOREMOVE,
102 msg->message, (LPARAM)SEGPTR_GET(hook));
103 SEGPTR_FREE(hook);
104 return ret ? SYSQ_MSG_ACCEPT : SYSQ_MSG_SKIP ;
107 hittest = WINPOS_WindowFromPoint( WIN_GetDesktop(), msg->pt, &pWnd );
108 if (pWnd->hmemTaskQ != GetTaskQueue(0))
110 /* Not for the current task */
111 if (queue) QUEUE_ClearWakeBit( queue, QS_MOUSE );
112 /* Wake up the other task */
113 queue = (MESSAGEQUEUE *)GlobalLock16( pWnd->hmemTaskQ );
114 if (queue) QUEUE_SetWakeBit( queue, QS_MOUSE );
115 return SYSQ_MSG_ABANDON;
117 pCursorQueue = queue;
118 msg->hwnd = pWnd->hwndSelf;
120 if ((hittest != HTERROR) && mouseClick)
122 HWND hwndTop = WIN_GetTopParent( msg->hwnd );
124 /* Send the WM_PARENTNOTIFY message */
126 WIN_SendParentNotify( msg->hwnd, msg->message, 0,
127 MAKELPARAM( msg->pt.x, msg->pt.y ) );
129 /* Activate the window if needed */
131 if (msg->hwnd != GetActiveWindow() &&
132 msg->hwnd != GetDesktopWindow16())
134 LONG ret = SendMessage16( msg->hwnd, WM_MOUSEACTIVATE, hwndTop,
135 MAKELONG( hittest, msg->message ) );
137 if ((ret == MA_ACTIVATEANDEAT) || (ret == MA_NOACTIVATEANDEAT))
138 eatMsg = TRUE;
140 if (((ret == MA_ACTIVATE) || (ret == MA_ACTIVATEANDEAT))
141 && hwndTop != GetActiveWindow() )
142 WINPOS_SetActiveWindow( hwndTop, TRUE , TRUE );
146 /* Send the WM_SETCURSOR message */
148 SendMessage16( msg->hwnd, WM_SETCURSOR, (WPARAM16)msg->hwnd,
149 MAKELONG( hittest, msg->message ));
150 if (eatMsg) return SYSQ_MSG_SKIP;
152 /* Check for double-click */
154 if (mouseClick)
156 BOOL dbl_click = FALSE;
158 if ((msg->message == lastClickMsg) &&
159 (msg->time - lastClickTime < doubleClickSpeed) &&
160 (abs(msg->pt.x - lastClickPos.x) < SYSMETRICS_CXDOUBLECLK/2) &&
161 (abs(msg->pt.y - lastClickPos.y) < SYSMETRICS_CYDOUBLECLK/2))
162 dbl_click = TRUE;
164 if (dbl_click && (hittest == HTCLIENT))
166 /* Check whether window wants the double click message. */
167 dbl_click = (pWnd->class->style & CS_DBLCLKS) != 0;
170 if (dbl_click) switch(msg->message)
172 case WM_LBUTTONDOWN: msg->message = WM_LBUTTONDBLCLK; break;
173 case WM_RBUTTONDOWN: msg->message = WM_RBUTTONDBLCLK; break;
174 case WM_MBUTTONDOWN: msg->message = WM_MBUTTONDBLCLK; break;
177 if (remove)
179 lastClickTime = msg->time;
180 lastClickMsg = msg->message;
181 lastClickPos = msg->pt;
185 /* Build the translated message */
187 if (hittest == HTCLIENT)
188 ScreenToClient16( msg->hwnd, &pt );
189 else
191 msg->wParam = hittest;
192 msg->message += WM_NCLBUTTONDOWN - WM_LBUTTONDOWN;
194 msg->lParam = MAKELONG( pt.x, pt.y );
196 /* Call the WH_MOUSE hook */
198 if (!HOOK_IsHooked( WH_MOUSE ) ||
199 !(hook = SEGPTR_NEW(MOUSEHOOKSTRUCT16)))
200 return SYSQ_MSG_ACCEPT;
202 hook->pt = msg->pt;
203 hook->hwnd = msg->hwnd;
204 hook->wHitTestCode = hittest;
205 hook->dwExtraInfo = 0;
206 ret = !HOOK_CallHooks16( WH_MOUSE, remove ? HC_ACTION : HC_NOREMOVE,
207 msg->message, (LPARAM)SEGPTR_GET(hook) );
208 SEGPTR_FREE(hook);
209 return ret ? SYSQ_MSG_ACCEPT : SYSQ_MSG_SKIP;
213 /***********************************************************************
214 * MSG_TranslateKeyboardMsg
216 * Translate an keyboard hardware event into a real message.
217 * Return value indicates whether the translated message must be passed
218 * to the user.
220 static SYSQ_STATUS MSG_TranslateKeyboardMsg( MSG16 *msg, BOOL remove )
222 WND *pWnd;
224 /* Should check Ctrl-Esc and PrintScreen here */
226 msg->hwnd = GetFocus16();
227 if (!msg->hwnd)
229 /* Send the message to the active window instead, */
230 /* translating messages to their WM_SYS equivalent */
232 msg->hwnd = GetActiveWindow();
234 if( msg->message < WM_SYSKEYDOWN )
235 msg->message += WM_SYSKEYDOWN - WM_KEYDOWN;
237 pWnd = WIN_FindWndPtr( msg->hwnd );
238 if (pWnd && (pWnd->hmemTaskQ != GetTaskQueue(0)))
240 /* Not for the current task */
241 MESSAGEQUEUE *queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) );
242 if (queue) QUEUE_ClearWakeBit( queue, QS_KEY );
243 /* Wake up the other task */
244 queue = (MESSAGEQUEUE *)GlobalLock16( pWnd->hmemTaskQ );
245 if (queue) QUEUE_SetWakeBit( queue, QS_KEY );
246 return SYSQ_MSG_ABANDON;
248 return (HOOK_CallHooks16( WH_KEYBOARD, remove ? HC_ACTION : HC_NOREMOVE,
249 msg->wParam, msg->lParam )
250 ? SYSQ_MSG_SKIP : SYSQ_MSG_ACCEPT);
254 /***********************************************************************
255 * MSG_JournalRecordMsg
257 * Build an EVENTMSG structure and call JOURNALRECORD hook
259 static void MSG_JournalRecordMsg( MSG16 *msg )
261 EVENTMSG16 *event = SEGPTR_NEW(EVENTMSG16);
262 if (!event) return;
263 event->message = msg->message;
264 event->time = msg->time;
265 if ((msg->message >= WM_KEYFIRST) && (msg->message <= WM_KEYLAST))
267 event->paramL = (msg->wParam & 0xFF) | (HIWORD(msg->lParam) << 8);
268 event->paramH = msg->lParam & 0x7FFF;
269 if (HIWORD(msg->lParam) & 0x0100)
270 event->paramH |= 0x8000; /* special_key - bit */
271 HOOK_CallHooks16( WH_JOURNALRECORD, HC_ACTION, 0,
272 (LPARAM)SEGPTR_GET(event) );
274 else if ((msg->message >= WM_MOUSEFIRST) && (msg->message <= WM_MOUSELAST))
276 event->paramL = LOWORD(msg->lParam); /* X pos */
277 event->paramH = HIWORD(msg->lParam); /* Y pos */
278 ClientToScreen16( msg->hwnd, (LPPOINT16)&event->paramL );
279 HOOK_CallHooks16( WH_JOURNALRECORD, HC_ACTION, 0,
280 (LPARAM)SEGPTR_GET(event) );
282 else if ((msg->message >= WM_NCMOUSEFIRST) &&
283 (msg->message <= WM_NCMOUSELAST))
285 event->paramL = LOWORD(msg->lParam); /* X pos */
286 event->paramH = HIWORD(msg->lParam); /* Y pos */
287 event->message += WM_MOUSEMOVE-WM_NCMOUSEMOVE;/* give no info about NC area */
288 HOOK_CallHooks16( WH_JOURNALRECORD, HC_ACTION, 0,
289 (LPARAM)SEGPTR_GET(event) );
291 SEGPTR_FREE(event);
294 /***********************************************************************
295 * MSG_JournalPlayBackMsg
297 * Get an EVENTMSG struct via call JOURNALPLAYBACK hook function
299 static int MSG_JournalPlayBackMsg(void)
301 EVENTMSG16 *tmpMsg;
302 long wtime,lParam;
303 WORD keyDown,i,wParam,result=0;
305 if ( HOOK_IsHooked( WH_JOURNALPLAYBACK ) )
307 tmpMsg = SEGPTR_NEW(EVENTMSG16);
308 wtime=HOOK_CallHooks16( WH_JOURNALPLAYBACK, HC_GETNEXT, 0,
309 (LPARAM)SEGPTR_GET(tmpMsg));
310 /* dprintf_msg(stddeb,"Playback wait time =%ld\n",wtime); */
311 if (wtime<=0)
313 wtime=0;
314 if ((tmpMsg->message>= WM_KEYFIRST) && (tmpMsg->message <= WM_KEYLAST))
316 wParam=tmpMsg->paramL & 0xFF;
317 lParam=MAKELONG(tmpMsg->paramH&0x7ffff,tmpMsg->paramL>>8);
318 if (tmpMsg->message == WM_KEYDOWN || tmpMsg->message == WM_SYSKEYDOWN)
320 for (keyDown=i=0; i<256 && !keyDown; i++)
321 if (InputKeyStateTable[i] & 0x80)
322 keyDown++;
323 if (!keyDown)
324 lParam |= 0x40000000;
325 AsyncKeyStateTable[wParam]=InputKeyStateTable[wParam] |= 0x80;
327 else /* WM_KEYUP, WM_SYSKEYUP */
329 lParam |= 0xC0000000;
330 AsyncKeyStateTable[wParam]=InputKeyStateTable[wParam] &= ~0x80;
332 if (InputKeyStateTable[VK_MENU] & 0x80)
333 lParam |= 0x20000000;
334 if (tmpMsg->paramH & 0x8000) /*special_key bit*/
335 lParam |= 0x01000000;
336 hardware_event( tmpMsg->message, wParam, lParam,0, 0, tmpMsg->time, 0 );
338 else
340 if ((tmpMsg->message>= WM_MOUSEFIRST) && (tmpMsg->message <= WM_MOUSELAST))
342 switch (tmpMsg->message)
344 case WM_LBUTTONDOWN:MouseButtonsStates[0]=AsyncMouseButtonsStates[0]=1;break;
345 case WM_LBUTTONUP: MouseButtonsStates[0]=AsyncMouseButtonsStates[0]=0;break;
346 case WM_MBUTTONDOWN:MouseButtonsStates[1]=AsyncMouseButtonsStates[1]=1;break;
347 case WM_MBUTTONUP: MouseButtonsStates[1]=AsyncMouseButtonsStates[1]=0;break;
348 case WM_RBUTTONDOWN:MouseButtonsStates[2]=AsyncMouseButtonsStates[2]=1;break;
349 case WM_RBUTTONUP: MouseButtonsStates[2]=AsyncMouseButtonsStates[2]=0;break;
351 AsyncKeyStateTable[VK_LBUTTON]= InputKeyStateTable[VK_LBUTTON] = MouseButtonsStates[0] << 8;
352 AsyncKeyStateTable[VK_MBUTTON]= InputKeyStateTable[VK_MBUTTON] = MouseButtonsStates[1] << 8;
353 AsyncKeyStateTable[VK_RBUTTON]= InputKeyStateTable[VK_RBUTTON] = MouseButtonsStates[2] << 8;
354 SetCursorPos(tmpMsg->paramL,tmpMsg->paramH);
355 lParam=MAKELONG(tmpMsg->paramL,tmpMsg->paramH);
356 wParam=0;
357 if (MouseButtonsStates[0]) wParam |= MK_LBUTTON;
358 if (MouseButtonsStates[1]) wParam |= MK_MBUTTON;
359 if (MouseButtonsStates[2]) wParam |= MK_RBUTTON;
360 hardware_event( tmpMsg->message, wParam, lParam,
361 tmpMsg->paramL, tmpMsg->paramH, tmpMsg->time, 0 );
364 HOOK_CallHooks16( WH_JOURNALPLAYBACK, HC_SKIP, 0,
365 (LPARAM)SEGPTR_GET(tmpMsg));
367 else
368 result= QS_MOUSE | QS_KEY;
369 SEGPTR_FREE(tmpMsg);
371 return result;
374 /***********************************************************************
375 * MSG_PeekHardwareMsg
377 * Peek for a hardware message matching the hwnd and message filters.
379 static BOOL MSG_PeekHardwareMsg( MSG16 *msg, HWND hwnd, WORD first, WORD last,
380 BOOL remove )
382 SYSQ_STATUS status = SYSQ_MSG_ACCEPT;
383 MESSAGEQUEUE *sysMsgQueue = QUEUE_GetSysQueue();
384 int i, pos = sysMsgQueue->nextMessage;
386 /* If the queue is empty, attempt to fill it */
387 if (!sysMsgQueue->msgCount && XPending(display))
388 EVENT_WaitXEvent( FALSE, FALSE );
390 for (i = 0; i < sysMsgQueue->msgCount; i++, pos++)
392 if (pos >= sysMsgQueue->queueSize) pos = 0;
393 *msg = sysMsgQueue->messages[pos].msg;
395 /* Translate message; return FALSE immediately on SYSQ_MSG_ABANDON */
397 if ((msg->message >= WM_MOUSEFIRST) && (msg->message <= WM_MOUSELAST))
399 if ((status = MSG_TranslateMouseMsg(msg,remove)) == SYSQ_MSG_ABANDON)
400 return FALSE;
402 else if ((msg->message >= WM_KEYFIRST) && (msg->message <= WM_KEYLAST))
404 if ((status = MSG_TranslateKeyboardMsg(msg,remove)) == SYSQ_MSG_ABANDON)
405 return FALSE;
407 else /* Non-standard hardware event */
409 HARDWAREHOOKSTRUCT16 *hook;
410 if ((hook = SEGPTR_NEW(HARDWAREHOOKSTRUCT16)))
412 BOOL32 ret;
413 hook->hWnd = msg->hwnd;
414 hook->wMessage = msg->message;
415 hook->wParam = msg->wParam;
416 hook->lParam = msg->lParam;
417 ret = HOOK_CallHooks16( WH_HARDWARE,
418 remove ? HC_ACTION : HC_NOREMOVE,
419 0, (LPARAM)SEGPTR_GET(hook) );
420 SEGPTR_FREE(hook);
421 status = ret ? SYSQ_MSG_SKIP : SYSQ_MSG_ACCEPT;
425 if (status == SYSQ_MSG_SKIP)
427 if (remove) QUEUE_RemoveMsg( sysMsgQueue, pos );
428 /* FIXME: call CBT_CLICKSKIPPED from here */
429 continue;
432 /* Check message against filters */
434 if (hwnd && (msg->hwnd != hwnd)) continue;
435 if ((first || last) &&
436 ((msg->message < first) || (msg->message > last))) continue;
437 if (remove)
439 if (HOOK_IsHooked( WH_JOURNALRECORD ))
440 MSG_JournalRecordMsg( msg );
441 QUEUE_RemoveMsg( sysMsgQueue, pos );
443 /* call CBT hook _after_ message removed from queue */
444 if (HOOK_IsHooked( WH_CBT ))
446 if (((msg->message >= WM_MOUSEFIRST) &&
447 (msg->message <= WM_MOUSELAST)) ||
448 ((msg->message >= WM_NCMOUSEFIRST) &&
449 (msg->message <= WM_NCMOUSELAST)))
451 MOUSEHOOKSTRUCT16 *hook = SEGPTR_NEW(MOUSEHOOKSTRUCT16);
452 if (hook)
454 hook->pt = msg->pt;
455 hook->hwnd = msg->hwnd;
456 hook->wHitTestCode = (INT16)SendMessage16( msg->hwnd,
457 WM_NCHITTEST, 0,
458 MAKELONG( msg->pt.x, msg->pt.y ) );
459 hook->dwExtraInfo = 0;
460 HOOK_CallHooks16( WH_CBT, HCBT_CLICKSKIPPED ,msg->message,
461 (LPARAM)SEGPTR_GET(hook) );
462 SEGPTR_FREE(hook);
465 else
466 if ((msg->message >= WM_KEYFIRST) &&
467 (msg->message <= WM_KEYLAST))
468 HOOK_CallHooks16( WH_CBT, HCBT_KEYSKIPPED,
469 msg->wParam, msg->lParam );
472 return TRUE;
474 return FALSE;
478 /**********************************************************************
479 * SetDoubleClickTime (USER.20)
481 void SetDoubleClickTime( WORD interval )
483 doubleClickSpeed = interval ? interval : 500;
487 /**********************************************************************
488 * GetDoubleClickTime (USER.21)
490 WORD GetDoubleClickTime()
492 return doubleClickSpeed;
496 /***********************************************************************
497 * MSG_SendMessage
499 * Implementation of an inter-task SendMessage.
501 static LRESULT MSG_SendMessage( HQUEUE16 hDestQueue, HWND hwnd, UINT msg,
502 WPARAM16 wParam, LPARAM lParam )
504 INT32 prevSMRL = debugSMRL;
505 QSMCTRL qCtrl = { 0, 1};
506 MESSAGEQUEUE *queue, *destQ;
508 if (!(queue = (MESSAGEQUEUE*)GlobalLock16( GetTaskQueue(0) ))) return 0;
509 if (!(destQ = (MESSAGEQUEUE*)GlobalLock16( hDestQueue ))) return 0;
511 if (IsTaskLocked() || !IsWindow(hwnd)) return 0;
513 debugSMRL+=4;
514 dprintf_sendmsg(stddeb,"%*sSM: %s [%04x] (%04x -> %04x)\n",
515 prevSMRL, "", SPY_GetMsgName(msg), msg, queue->self, hDestQueue );
517 if( !(queue->wakeBits & QS_SMPARAMSFREE) )
519 dprintf_sendmsg(stddeb,"\tIntertask SendMessage: sleeping since unreplied SendMessage pending\n");
520 queue->changeBits &= ~QS_SMPARAMSFREE;
521 QUEUE_WaitBits( QS_SMPARAMSFREE );
524 /* resume sending */
526 queue->hWnd = hwnd;
527 queue->msg = msg;
528 queue->wParam = wParam;
529 queue->lParam = lParam;
530 queue->hPrevSendingTask = destQ->hSendingTask;
531 destQ->hSendingTask = GetTaskQueue(0);
533 queue->wakeBits &= ~QS_SMPARAMSFREE;
535 dprintf_sendmsg(stddeb,"%*ssm: smResultInit = %08x\n", prevSMRL, "", (unsigned)&qCtrl);
537 queue->smResultInit = &qCtrl;
539 QUEUE_SetWakeBit( destQ, QS_SENDMESSAGE );
541 /* perform task switch and wait for the result */
543 while( qCtrl.bPending )
545 if (!(queue->wakeBits & QS_SMRESULT))
547 queue->changeBits &= ~QS_SMRESULT;
548 DirectedYield( destQ->hTask );
549 QUEUE_WaitBits( QS_SMRESULT );
550 dprintf_sendmsg(stddeb,"\tsm: have result!\n");
552 /* got something */
554 dprintf_sendmsg(stddeb,"%*ssm: smResult = %08x\n", prevSMRL, "", (unsigned)queue->smResult );
556 if (queue->smResult) { /* FIXME, smResult should always be set */
557 queue->smResult->lResult = queue->SendMessageReturn;
558 queue->smResult->bPending = FALSE;
560 queue->wakeBits &= ~QS_SMRESULT;
562 if( queue->smResult != &qCtrl )
563 dprintf_msg(stddeb,"%*ssm: weird scenes inside the goldmine!\n", prevSMRL, "");
565 queue->smResultInit = NULL;
567 dprintf_sendmsg(stddeb,"%*sSM: [%04x] returning %08lx\n", prevSMRL, "", msg, qCtrl.lResult);
568 debugSMRL-=4;
570 return qCtrl.lResult;
574 /***********************************************************************
575 * ReplyMessage (USER.115)
577 void ReplyMessage( LRESULT result )
579 MESSAGEQUEUE *senderQ;
580 MESSAGEQUEUE *queue;
582 if (!(queue = (MESSAGEQUEUE*)GlobalLock16( GetTaskQueue(0) ))) return;
584 dprintf_msg(stddeb,"ReplyMessage, queue %04x\n", queue->self);
586 while( (senderQ = (MESSAGEQUEUE*)GlobalLock16( queue->InSendMessageHandle)))
588 dprintf_msg(stddeb,"\trpm: replying to %04x (%04x -> %04x)\n",
589 queue->msg, queue->self, senderQ->self);
591 if( queue->wakeBits & QS_SENDMESSAGE )
593 QUEUE_ReceiveMessage( queue );
594 continue; /* ReceiveMessage() already called us */
597 if(!(senderQ->wakeBits & QS_SMRESULT) ) break;
598 OldYield();
600 if( !senderQ ) { dprintf_msg(stddeb,"\trpm: done\n"); return; }
602 senderQ->SendMessageReturn = result;
603 dprintf_msg(stddeb,"\trpm: smResult = %08x, result = %08lx\n",
604 (unsigned)queue->smResultCurrent, result );
606 senderQ->smResult = queue->smResultCurrent;
607 queue->InSendMessageHandle = 0;
609 QUEUE_SetWakeBit( senderQ, QS_SMRESULT );
610 DirectedYield( queue->hSendingTask );
614 /***********************************************************************
615 * MSG_PeekMessage
617 static BOOL MSG_PeekMessage( LPMSG16 msg, HWND hwnd, WORD first, WORD last,
618 WORD flags, BOOL peek )
620 int pos, mask;
621 MESSAGEQUEUE *msgQueue;
622 HQUEUE16 hQueue;
624 #ifdef CONFIG_IPC
625 DDE_TestDDE(hwnd); /* do we have dde handling in the window ?*/
626 DDE_GetRemoteMessage();
627 #endif /* CONFIG_IPC */
629 mask = QS_POSTMESSAGE | QS_SENDMESSAGE; /* Always selected */
630 if (first || last)
632 /* MSWord gets stuck if we do not check for nonclient mouse messages */
634 if ((first <= WM_KEYLAST) && (last >= WM_KEYFIRST)) mask |= QS_KEY;
635 if ( ((first <= WM_MOUSELAST) && (last >= WM_MOUSEFIRST)) ||
636 ((first <= WM_NCMOUSELAST) && (last >= WM_NCMOUSEFIRST)) ) mask |= QS_MOUSE;
637 if ((first <= WM_TIMER) && (last >= WM_TIMER)) mask |= QS_TIMER;
638 if ((first <= WM_SYSTIMER) && (last >= WM_SYSTIMER)) mask |= QS_TIMER;
639 if ((first <= WM_PAINT) && (last >= WM_PAINT)) mask |= QS_PAINT;
641 else mask |= QS_MOUSE | QS_KEY | QS_TIMER | QS_PAINT;
643 if (IsTaskLocked()) flags |= PM_NOYIELD;
645 while(1)
647 hQueue = GetTaskQueue(0);
648 msgQueue = (MESSAGEQUEUE *)GlobalLock16( hQueue );
649 if (!msgQueue) return FALSE;
650 msgQueue->changeBits = 0;
652 /* First handle a message put by SendMessage() */
654 while (msgQueue->wakeBits & QS_SENDMESSAGE)
655 QUEUE_ReceiveMessage( msgQueue );
657 /* Now handle a WM_QUIT message
659 * FIXME: PostQuitMessage() should post WM_QUIT and
660 * set QS_POSTMESSAGE wakebit instead of this.
663 if (msgQueue->wPostQMsg &&
664 (!first || WM_QUIT >= first) &&
665 (!last || WM_QUIT <= last) )
667 msg->hwnd = hwnd;
668 msg->message = WM_QUIT;
669 msg->wParam = msgQueue->wExitCode;
670 msg->lParam = 0;
671 if (flags & PM_REMOVE) msgQueue->wPostQMsg = 0;
672 break;
675 /* Now find a normal message */
677 if (((msgQueue->wakeBits & mask) & QS_POSTMESSAGE) &&
678 ((pos = QUEUE_FindMsg( msgQueue, hwnd, first, last )) != -1))
680 QMSG *qmsg = &msgQueue->messages[pos];
681 *msg = qmsg->msg;
682 msgQueue->GetMessageTimeVal = msg->time;
683 msgQueue->GetMessagePosVal = *(DWORD *)&msg->pt;
684 msgQueue->GetMessageExtraInfoVal = qmsg->extraInfo;
686 if (flags & PM_REMOVE) QUEUE_RemoveMsg( msgQueue, pos );
687 break;
690 msgQueue->changeBits |= MSG_JournalPlayBackMsg();
692 /* Now find a hardware event */
694 if (((msgQueue->wakeBits & mask) & (QS_MOUSE | QS_KEY)) &&
695 MSG_PeekHardwareMsg( msg, hwnd, first, last, flags & PM_REMOVE ))
697 /* Got one */
698 msgQueue->GetMessageTimeVal = msg->time;
699 msgQueue->GetMessagePosVal = *(DWORD *)&msg->pt;
700 msgQueue->GetMessageExtraInfoVal = 0; /* Always 0 for now */
701 break;
704 /* Check again for SendMessage */
706 while (msgQueue->wakeBits & QS_SENDMESSAGE)
707 QUEUE_ReceiveMessage( msgQueue );
709 /* Now find a WM_PAINT message */
711 if ((msgQueue->wakeBits & mask) & QS_PAINT)
713 WND* wndPtr;
714 msg->hwnd = WIN_FindWinToRepaint( hwnd , hQueue );
715 msg->message = WM_PAINT;
716 msg->wParam = 0;
717 msg->lParam = 0;
719 if ((wndPtr = WIN_FindWndPtr(msg->hwnd)))
721 if( wndPtr->dwStyle & WS_MINIMIZE &&
722 wndPtr->class->hIcon )
724 msg->message = WM_PAINTICON;
725 msg->wParam = 1;
728 if( !hwnd || msg->hwnd == hwnd || IsChild(hwnd,msg->hwnd) )
730 if( wndPtr->flags & WIN_INTERNAL_PAINT && !wndPtr->hrgnUpdate)
732 wndPtr->flags &= ~WIN_INTERNAL_PAINT;
733 QUEUE_DecPaintCount( hQueue );
735 break;
740 /* Check for timer messages, but yield first */
742 if (!(flags & PM_NOYIELD))
744 UserYield();
745 while (msgQueue->wakeBits & QS_SENDMESSAGE)
746 QUEUE_ReceiveMessage( msgQueue );
748 if ((msgQueue->wakeBits & mask) & QS_TIMER)
750 if (TIMER_GetTimerMsg(msg, hwnd, hQueue, flags & PM_REMOVE)) break;
753 if (peek)
755 if (!(flags & PM_NOYIELD)) UserYield();
756 return FALSE;
758 msgQueue->wakeMask = mask;
759 QUEUE_WaitBits( mask );
762 /* We got a message */
763 if (flags & PM_REMOVE)
765 WORD message = msg->message;
767 if (message == WM_KEYDOWN || message == WM_SYSKEYDOWN)
769 BYTE *p = &QueueKeyStateTable[msg->wParam & 0xff];
771 if (!(*p & 0x80))
772 *p ^= 0x01;
773 *p |= 0x80;
775 else if (message == WM_KEYUP || message == WM_SYSKEYUP)
776 QueueKeyStateTable[msg->wParam & 0xff] &= ~0x80;
778 if (peek) return TRUE;
779 else return (msg->message != WM_QUIT);
783 /***********************************************************************
784 * MSG_InternalGetMessage
786 * GetMessage() function for internal use. Behave like GetMessage(),
787 * but also call message filters and optionally send WM_ENTERIDLE messages.
788 * 'hwnd' must be the handle of the dialog or menu window.
789 * 'code' is the message filter value (MSGF_??? codes).
791 BOOL32 MSG_InternalGetMessage( MSG16 *msg, HWND32 hwnd, HWND32 hwndOwner,
792 WPARAM32 code, WORD flags, BOOL32 sendIdle )
794 for (;;)
796 if (sendIdle)
798 if (!MSG_PeekMessage( msg, 0, 0, 0, flags, TRUE ))
800 /* No message present -> send ENTERIDLE and wait */
801 if (IsWindow(hwndOwner))
802 SendMessage16( hwndOwner, WM_ENTERIDLE,
803 code, (LPARAM)hwnd );
804 MSG_PeekMessage( msg, 0, 0, 0, flags, FALSE );
807 else /* Always wait for a message */
808 MSG_PeekMessage( msg, 0, 0, 0, flags, FALSE );
810 /* Call message filters */
812 if (HOOK_IsHooked( WH_SYSMSGFILTER ) || HOOK_IsHooked( WH_MSGFILTER ))
814 MSG16 *pmsg = SEGPTR_NEW(MSG16);
815 if (pmsg)
817 BOOL32 ret;
818 *pmsg = *msg;
819 ret = ((BOOL16)HOOK_CallHooks16( WH_SYSMSGFILTER, code, 0,
820 (LPARAM)SEGPTR_GET(pmsg) ) ||
821 (BOOL16)HOOK_CallHooks16( WH_MSGFILTER, code, 0,
822 (LPARAM)SEGPTR_GET(pmsg) ));
823 SEGPTR_FREE(pmsg);
824 if (ret)
826 /* Message filtered -> remove it from the queue */
827 /* if it's still there. */
828 if (!(flags & PM_REMOVE))
829 MSG_PeekMessage( msg, 0, 0, 0, PM_REMOVE, TRUE );
830 continue;
835 return (msg->message != WM_QUIT);
840 /***********************************************************************
841 * PeekMessage16 (USER.109)
843 BOOL16 PeekMessage16( LPMSG16 msg, HWND16 hwnd, UINT16 first,
844 UINT16 last, UINT16 flags )
846 return MSG_PeekMessage( msg, hwnd, first, last, flags, TRUE );
850 /***********************************************************************
851 * GetMessage (USER.108)
853 BOOL GetMessage( SEGPTR msg, HWND hwnd, UINT first, UINT last )
855 MSG16 *lpmsg = (MSG16 *)PTR_SEG_TO_LIN(msg);
856 MSG_PeekMessage( lpmsg,
857 hwnd, first, last, PM_REMOVE, FALSE );
859 dprintf_msg(stddeb,"message %04x, hwnd %04x, filter(%04x - %04x)\n", lpmsg->message,
860 hwnd, first, last );
861 HOOK_CallHooks16( WH_GETMESSAGE, HC_ACTION, 0, (LPARAM)msg );
862 return (lpmsg->message != WM_QUIT);
866 /***********************************************************************
867 * PostMessage (USER.110)
869 BOOL PostMessage( HWND hwnd, WORD message, WORD wParam, LONG lParam )
871 MSG16 msg;
872 WND *wndPtr;
874 msg.hwnd = hwnd;
875 msg.message = message;
876 msg.wParam = wParam;
877 msg.lParam = lParam;
878 msg.time = GetTickCount();
879 msg.pt.x = 0;
880 msg.pt.y = 0;
882 #ifdef CONFIG_IPC
883 if (DDE_PostMessage(&msg))
884 return TRUE;
885 #endif /* CONFIG_IPC */
887 if (hwnd == HWND_BROADCAST16)
889 dprintf_msg(stddeb,"PostMessage // HWND_BROADCAST !\n");
890 for (wndPtr = WIN_GetDesktop()->child; wndPtr; wndPtr = wndPtr->next)
892 if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION)
894 dprintf_msg(stddeb,"BROADCAST Message to hWnd=%04x m=%04X w=%04X l=%08lX !\n",
895 wndPtr->hwndSelf, message, wParam, lParam);
896 PostMessage( wndPtr->hwndSelf, message, wParam, lParam );
899 dprintf_msg(stddeb,"PostMessage // End of HWND_BROADCAST !\n");
900 return TRUE;
903 wndPtr = WIN_FindWndPtr( hwnd );
904 if (!wndPtr || !wndPtr->hmemTaskQ) return FALSE;
906 return QUEUE_AddMsg( wndPtr->hmemTaskQ, &msg, 0 );
909 /***********************************************************************
910 * PostAppMessage16 (USER.116)
912 BOOL16 PostAppMessage16( HTASK16 hTask, UINT16 message, WPARAM16 wParam,
913 LPARAM lParam )
915 MSG16 msg;
917 if (GetTaskQueue(hTask) == 0) return FALSE;
918 msg.hwnd = 0;
919 msg.message = message;
920 msg.wParam = wParam;
921 msg.lParam = lParam;
922 msg.time = GetTickCount();
923 msg.pt.x = 0;
924 msg.pt.y = 0;
926 return QUEUE_AddMsg( GetTaskQueue(hTask), &msg, 0 );
930 /***********************************************************************
931 * SendMessage16 (USER.111)
933 LRESULT SendMessage16( HWND16 hwnd, UINT16 msg, WPARAM16 wParam, LPARAM lParam)
935 WND * wndPtr;
936 WND **list, **ppWnd;
937 LRESULT ret;
939 #ifdef CONFIG_IPC
940 MSG16 DDE_msg = { hwnd, msg, wParam, lParam };
941 if (DDE_SendMessage(&DDE_msg)) return TRUE;
942 #endif /* CONFIG_IPC */
944 if (hwnd == HWND_BROADCAST16)
946 dprintf_msg(stddeb,"SendMessage // HWND_BROADCAST !\n");
947 list = WIN_BuildWinArray( WIN_GetDesktop() );
948 for (ppWnd = list; *ppWnd; ppWnd++)
950 wndPtr = *ppWnd;
951 if (!IsWindow(wndPtr->hwndSelf)) continue;
952 if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION)
954 dprintf_msg(stddeb,"BROADCAST Message to hWnd=%04x m=%04X w=%04lX l=%08lX !\n",
955 wndPtr->hwndSelf, msg, (DWORD)wParam, lParam);
956 SendMessage16( wndPtr->hwndSelf, msg, wParam, lParam );
959 HeapFree( SystemHeap, 0, list );
960 dprintf_msg(stddeb,"SendMessage // End of HWND_BROADCAST !\n");
961 return TRUE;
964 if (HOOK_IsHooked( WH_CALLWNDPROC ))
966 struct msgstruct
968 LPARAM lParam;
969 WPARAM16 wParam;
970 UINT16 wMsg;
971 HWND16 hWnd;
972 } *pmsg;
974 if ((pmsg = SEGPTR_NEW(struct msgstruct)))
976 pmsg->hWnd = hwnd;
977 pmsg->wMsg = msg;
978 pmsg->wParam = wParam;
979 pmsg->lParam = lParam;
980 HOOK_CallHooks16( WH_CALLWNDPROC, HC_ACTION, 1,
981 (LPARAM)SEGPTR_GET(pmsg) );
982 hwnd = pmsg->hWnd;
983 msg = pmsg->wMsg;
984 wParam = pmsg->wParam;
985 lParam = pmsg->lParam;
986 SEGPTR_FREE( pmsg );
990 if (!(wndPtr = WIN_FindWndPtr( hwnd )))
992 fprintf( stderr, "SendMessage16: invalid hwnd %04x\n", hwnd );
993 return 0;
995 if (QUEUE_IsDoomedQueue(wndPtr->hmemTaskQ))
996 return 0; /* Don't send anything if the task is dying */
997 if (wndPtr->hmemTaskQ != GetTaskQueue(0))
998 return MSG_SendMessage( wndPtr->hmemTaskQ, hwnd, msg, wParam, lParam );
1000 SPY_EnterMessage( SPY_SENDMESSAGE16, hwnd, msg, wParam, lParam );
1001 ret = CallWindowProc16( (WNDPROC16)wndPtr->winproc,
1002 hwnd, msg, wParam, lParam );
1003 SPY_ExitMessage( SPY_RESULT_OK16, hwnd, msg, ret );
1004 return ret;
1008 /***********************************************************************
1009 * SendMessage32A (USER32.453)
1011 LRESULT SendMessage32A(HWND32 hwnd, UINT32 msg, WPARAM32 wParam, LPARAM lParam)
1013 WND * wndPtr;
1014 WND **list, **ppWnd;
1015 LRESULT ret;
1017 if (hwnd == HWND_BROADCAST32)
1019 list = WIN_BuildWinArray( WIN_GetDesktop() );
1020 for (ppWnd = list; *ppWnd; ppWnd++)
1022 wndPtr = *ppWnd;
1023 if (!IsWindow(wndPtr->hwndSelf)) continue;
1024 if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION)
1025 SendMessage32A( wndPtr->hwndSelf, msg, wParam, lParam );
1027 HeapFree( SystemHeap, 0, list );
1028 return TRUE;
1031 /* FIXME: call hooks */
1033 if (!(wndPtr = WIN_FindWndPtr( hwnd )))
1035 fprintf( stderr, "SendMessage32A: invalid hwnd %08x\n", hwnd );
1036 return 0;
1039 if (WINPROC_GetProcType( wndPtr->winproc ) == WIN_PROC_16)
1041 /* Use SendMessage16 for now to get hooks right */
1042 UINT16 msg16;
1043 WPARAM16 wParam16;
1044 if (WINPROC_MapMsg32ATo16( msg, wParam, &msg16, &wParam16, &lParam ) == -1)
1045 return 0;
1046 ret = SendMessage16( hwnd, msg16, wParam16, lParam );
1047 WINPROC_UnmapMsg32ATo16( msg, wParam16, lParam );
1048 return ret;
1051 if (QUEUE_IsDoomedQueue(wndPtr->hmemTaskQ))
1052 return 0; /* Don't send anything if the task is dying */
1054 if (wndPtr->hmemTaskQ != GetTaskQueue(0))
1056 fprintf( stderr, "SendMessage32A: intertask message not supported\n" );
1057 return 0;
1060 SPY_EnterMessage( SPY_SENDMESSAGE32, hwnd, msg, wParam, lParam );
1061 ret = CallWindowProc32A( (WNDPROC32)wndPtr->winproc,
1062 hwnd, msg, wParam, lParam );
1063 SPY_ExitMessage( SPY_RESULT_OK32, hwnd, msg, ret );
1064 return ret;
1068 /***********************************************************************
1069 * SendMessage32W (USER32.458)
1071 LRESULT SendMessage32W(HWND32 hwnd, UINT32 msg, WPARAM32 wParam, LPARAM lParam)
1073 WND * wndPtr;
1074 WND **list, **ppWnd;
1075 LRESULT ret;
1077 if (hwnd == HWND_BROADCAST32)
1079 list = WIN_BuildWinArray( WIN_GetDesktop() );
1080 for (ppWnd = list; *ppWnd; ppWnd++)
1082 wndPtr = *ppWnd;
1083 if (!IsWindow(wndPtr->hwndSelf)) continue;
1084 if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION)
1085 SendMessage32W( wndPtr->hwndSelf, msg, wParam, lParam );
1087 HeapFree( SystemHeap, 0, list );
1088 return TRUE;
1091 /* FIXME: call hooks */
1093 if (!(wndPtr = WIN_FindWndPtr( hwnd )))
1095 fprintf( stderr, "SendMessage32W: invalid hwnd %08x\n", hwnd );
1096 return 0;
1098 if (QUEUE_IsDoomedQueue(wndPtr->hmemTaskQ))
1099 return 0; /* Don't send anything if the task is dying */
1100 if (wndPtr->hmemTaskQ != GetTaskQueue(0))
1102 fprintf( stderr, "SendMessage32W: intertask message not supported\n" );
1103 return 0;
1106 SPY_EnterMessage( SPY_SENDMESSAGE32, hwnd, msg, wParam, lParam );
1107 ret = CallWindowProc32W( (WNDPROC32)wndPtr->winproc,
1108 hwnd, msg, wParam, lParam );
1109 SPY_ExitMessage( SPY_RESULT_OK32, hwnd, msg, ret );
1110 return ret;
1114 /***********************************************************************
1115 * WaitMessage (USER.112)
1117 void WaitMessage( void )
1119 QUEUE_WaitBits( QS_ALLINPUT );
1123 /***********************************************************************
1124 * TranslateMessage (USER.113)
1126 * TranlateMessage translate virtual-key messages into character-messages,
1127 * as follows :
1128 * WM_KEYDOWN/WM_KEYUP combinations produce a WM_CHAR or WM_DEADCHAR message.
1129 * ditto replacing WM_* with WM_SYS*
1130 * This produces WM_CHAR messages only for keys mapped to ASCII characters
1131 * by the keyboard driver.
1135 BOOL TranslateMessage( LPMSG16 msg )
1137 UINT message = msg->message;
1138 BYTE wparam[2];
1140 if ((debugging_msg
1141 && message != WM_MOUSEMOVE && message != WM_TIMER)
1142 || (debugging_key
1143 && message >= WM_KEYFIRST && message <= WM_KEYLAST))
1144 fprintf(stddeb, "TranslateMessage(%s, %04x, %08lx)\n",
1145 SPY_GetMsgName(msg->message), msg->wParam, msg->lParam);
1146 if ((message == WM_KEYDOWN) || (message == WM_SYSKEYDOWN))
1148 if (debugging_msg || debugging_key)
1149 fprintf(stddeb, "Translating key %04x, scancode %04x\n",
1150 msg->wParam, HIWORD(msg->lParam) );
1152 /* FIXME : should handle ToAscii yielding 2 */
1153 if (ToAscii(msg->wParam, HIWORD(msg->lParam), (LPSTR)&QueueKeyStateTable,
1154 wparam, 0))
1156 /* Map WM_KEY* to WM_*CHAR */
1157 message += 2 - (message & 0x0001);
1159 PostMessage( msg->hwnd, message, wparam[0], msg->lParam );
1161 return TRUE;
1164 return FALSE;
1168 /***********************************************************************
1169 * DispatchMessage (USER.114)
1171 LONG DispatchMessage( const MSG16* msg )
1173 WND * wndPtr;
1174 LONG retval;
1175 int painting;
1177 /* Process timer messages */
1178 if ((msg->message == WM_TIMER) || (msg->message == WM_SYSTIMER))
1180 if (msg->lParam)
1182 /* HOOK_CallHooks16( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */
1183 return CallWindowProc16( (WNDPROC16)msg->lParam, msg->hwnd,
1184 msg->message, msg->wParam, GetTickCount() );
1188 if (!msg->hwnd) return 0;
1189 if (!(wndPtr = WIN_FindWndPtr( msg->hwnd ))) return 0;
1190 if (!wndPtr->winproc) return 0;
1191 painting = (msg->message == WM_PAINT);
1192 if (painting) wndPtr->flags |= WIN_NEEDS_BEGINPAINT;
1193 /* HOOK_CallHooks16( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */
1195 SPY_EnterMessage( SPY_DISPATCHMESSAGE16, msg->hwnd, msg->message,
1196 msg->wParam, msg->lParam );
1197 retval = CallWindowProc16( (WNDPROC16)wndPtr->winproc,
1198 msg->hwnd, msg->message,
1199 msg->wParam, msg->lParam );
1200 SPY_ExitMessage( SPY_RESULT_OK16, msg->hwnd, msg->message, retval );
1202 if (painting && (wndPtr = WIN_FindWndPtr( msg->hwnd )) &&
1203 (wndPtr->flags & WIN_NEEDS_BEGINPAINT) && wndPtr->hrgnUpdate)
1205 fprintf(stderr, "BeginPaint not called on WM_PAINT for hwnd %04x!\n",
1206 msg->hwnd);
1207 wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
1208 /* Validate the update region to avoid infinite WM_PAINT loop */
1209 ValidateRect32( msg->hwnd, NULL );
1211 return retval;
1215 /***********************************************************************
1216 * RegisterWindowMessage16 (USER.118)
1218 WORD RegisterWindowMessage16( SEGPTR str )
1220 dprintf_msg(stddeb, "RegisterWindowMessage16: %08lx\n", (DWORD)str );
1221 return GlobalAddAtom16( str );
1225 /***********************************************************************
1226 * RegisterWindowMessage32A (USER32.436)
1228 WORD RegisterWindowMessage32A( LPCSTR str )
1230 dprintf_msg(stddeb, "RegisterWindowMessage32A: %s\n", str );
1231 return GlobalAddAtom32A( str );
1235 /***********************************************************************
1236 * RegisterWindowMessage32W (USER32.437)
1238 WORD RegisterWindowMessage32W( LPCWSTR str )
1240 dprintf_msg(stddeb, "RegisterWindowMessage32W: %p\n", str );
1241 return GlobalAddAtom32W( str );
1245 /***********************************************************************
1246 * GetTickCount (USER.13) (KERNEL32.299)
1248 DWORD GetTickCount(void)
1250 struct timeval t;
1251 gettimeofday( &t, NULL );
1252 return ((t.tv_sec * 1000) + (t.tv_usec / 1000)) - MSG_WineStartTicks;
1256 /***********************************************************************
1257 * GetCurrentTime (USER.15)
1259 * (effectively identical to GetTickCount)
1261 DWORD GetCurrentTime(void)
1263 return GetTickCount();
1267 /***********************************************************************
1268 * InSendMessage (USER.192)
1270 BOOL InSendMessage()
1272 MESSAGEQUEUE *queue;
1274 if (!(queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) )))
1275 return 0;
1276 return (BOOL)queue->InSendMessageHandle;