/* * Message queues related functions * * Copyright 1993, 1994 Alexandre Julliard */ #include #include #include #include #include #include "message.h" #include "win.h" #include "gdi.h" #include "sysmetrics.h" #include "heap.h" #include "hook.h" #include "spy.h" #include "winpos.h" #include "atom.h" #include "dde.h" #include "queue.h" #include "winproc.h" #include "stddebug.h" /* #define DEBUG_MSG */ #include "debug.h" #define WM_NCMOUSEFIRST WM_NCMOUSEMOVE #define WM_NCMOUSELAST WM_NCMBUTTONDBLCLK #define HWND_BROADCAST16 ((HWND16)0xffff) #define HWND_BROADCAST32 ((HWND32)0xffffffff) #define ASCII_CHAR_HACK 0x0800 typedef enum { SYSQ_MSG_ABANDON, SYSQ_MSG_SKIP, SYSQ_MSG_ACCEPT } SYSQ_STATUS; extern WPARAM lastEventChar; /* event.c */ extern BOOL MouseButtonsStates[3]; extern BOOL AsyncMouseButtonsStates[3]; extern BYTE KeyStateTable[256]; extern BYTE AsyncKeyStateTable[256]; extern MESSAGEQUEUE *pCursorQueue; /* queue.c */ extern MESSAGEQUEUE *pActiveQueue; DWORD MSG_WineStartTicks; /* Ticks at Wine startup */ static WORD doubleClickSpeed = 452; static INT32 debugSMRL = 0; /* intertask SendMessage() recursion level */ /*********************************************************************** * MSG_TranslateMouseMsg * * Translate an mouse hardware event into a real mouse message. * Return value indicates whether the translated message must be passed * to the user. * Actions performed: * - Find the window for this message. * - Translate button-down messages in double-clicks. * - Send the WM_NCHITTEST message to find where the cursor is. * - Activate the window if needed. * - Translate the message into a non-client message, or translate * the coordinates to client coordinates. * - Send the WM_SETCURSOR message. */ static SYSQ_STATUS MSG_TranslateMouseMsg( MSG16 *msg, BOOL remove ) { WND *pWnd; BOOL eatMsg = FALSE; INT16 hittest; MOUSEHOOKSTRUCT16 *hook; BOOL32 ret; static DWORD lastClickTime = 0; static WORD lastClickMsg = 0; static POINT16 lastClickPos = { 0, 0 }; POINT16 pt = msg->pt; MESSAGEQUEUE *queue = (MESSAGEQUEUE *)GlobalLock16(GetTaskQueue(0)); BOOL mouseClick = ((msg->message == WM_LBUTTONDOWN) || (msg->message == WM_RBUTTONDOWN) || (msg->message == WM_MBUTTONDOWN)); /* Find the window */ if ((msg->hwnd = GetCapture16()) != 0) { BOOL32 ret; ScreenToClient16( msg->hwnd, &pt ); msg->lParam = MAKELONG( pt.x, pt.y ); /* No need to further process the message */ if (!HOOK_GetHook( WH_MOUSE, GetTaskQueue(0)) || !(hook = SEGPTR_NEW(MOUSEHOOKSTRUCT16))) return SYSQ_MSG_ACCEPT; hook->pt = msg->pt; hook->hwnd = msg->hwnd; hook->wHitTestCode = HTCLIENT; hook->dwExtraInfo = 0; ret = !HOOK_CallHooks( WH_MOUSE, remove ? HC_ACTION : HC_NOREMOVE, msg->message, (LPARAM)SEGPTR_GET(hook)); SEGPTR_FREE(hook); return ret ? SYSQ_MSG_ACCEPT : SYSQ_MSG_SKIP ; } hittest = WINPOS_WindowFromPoint( msg->pt, &pWnd ); if (pWnd->hmemTaskQ != GetTaskQueue(0)) { /* Not for the current task */ if (queue) QUEUE_ClearWakeBit( queue, QS_MOUSE ); /* Wake up the other task */ queue = (MESSAGEQUEUE *)GlobalLock16( pWnd->hmemTaskQ ); if (queue) QUEUE_SetWakeBit( queue, QS_MOUSE ); return SYSQ_MSG_ABANDON; } pCursorQueue = queue; msg->hwnd = pWnd->hwndSelf; if ((hittest != HTERROR) && mouseClick) { HWND hwndTop = WIN_GetTopParent( msg->hwnd ); /* Send the WM_PARENTNOTIFY message */ WIN_SendParentNotify( msg->hwnd, msg->message, 0, MAKELPARAM( msg->pt.x, msg->pt.y ) ); /* Activate the window if needed */ if (msg->hwnd != GetActiveWindow() && msg->hwnd != GetDesktopWindow16()) { LONG ret = SendMessage16( msg->hwnd, WM_MOUSEACTIVATE, hwndTop, MAKELONG( hittest, msg->message ) ); if ((ret == MA_ACTIVATEANDEAT) || (ret == MA_NOACTIVATEANDEAT)) eatMsg = TRUE; if (((ret == MA_ACTIVATE) || (ret == MA_ACTIVATEANDEAT)) && hwndTop != GetActiveWindow() ) WINPOS_SetActiveWindow( hwndTop, TRUE , TRUE ); } } /* Send the WM_SETCURSOR message */ SendMessage16( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message )); if (eatMsg) return SYSQ_MSG_SKIP; /* Check for double-click */ if (mouseClick) { BOOL dbl_click = FALSE; if ((msg->message == lastClickMsg) && (msg->time - lastClickTime < doubleClickSpeed) && (abs(msg->pt.x - lastClickPos.x) < SYSMETRICS_CXDOUBLECLK/2) && (abs(msg->pt.y - lastClickPos.y) < SYSMETRICS_CYDOUBLECLK/2)) dbl_click = TRUE; if (dbl_click && (hittest == HTCLIENT)) { /* Check whether window wants the double click message. */ dbl_click = (pWnd->class->style & CS_DBLCLKS) != 0; } if (dbl_click) switch(msg->message) { case WM_LBUTTONDOWN: msg->message = WM_LBUTTONDBLCLK; break; case WM_RBUTTONDOWN: msg->message = WM_RBUTTONDBLCLK; break; case WM_MBUTTONDOWN: msg->message = WM_MBUTTONDBLCLK; break; } if (remove) { lastClickTime = msg->time; lastClickMsg = msg->message; lastClickPos = msg->pt; } } /* Build the translated message */ if (hittest == HTCLIENT) ScreenToClient16( msg->hwnd, &pt ); else { msg->wParam = hittest; msg->message += WM_NCLBUTTONDOWN - WM_LBUTTONDOWN; } msg->lParam = MAKELONG( pt.x, pt.y ); /* Call the WH_MOUSE hook */ if (!HOOK_GetHook( WH_MOUSE, GetTaskQueue(0)) || !(hook = SEGPTR_NEW(MOUSEHOOKSTRUCT16))) return SYSQ_MSG_ACCEPT; hook->pt = msg->pt; hook->hwnd = msg->hwnd; hook->wHitTestCode = hittest; hook->dwExtraInfo = 0; ret = !HOOK_CallHooks( WH_MOUSE, remove ? HC_ACTION : HC_NOREMOVE, msg->message, (LPARAM)SEGPTR_GET(hook) ); SEGPTR_FREE(hook); return ret ? SYSQ_MSG_ACCEPT : SYSQ_MSG_SKIP; } /*********************************************************************** * MSG_TranslateKeyboardMsg * * Translate an keyboard hardware event into a real message. * Return value indicates whether the translated message must be passed * to the user. */ static SYSQ_STATUS MSG_TranslateKeyboardMsg( MSG16 *msg, BOOL remove ) { WND *pWnd; /* Should check Ctrl-Esc and PrintScreen here */ msg->hwnd = GetFocus16(); if (!msg->hwnd) { /* Send the message to the active window instead, */ /* translating messages to their WM_SYS equivalent */ msg->hwnd = GetActiveWindow(); if( msg->message < WM_SYSKEYDOWN ) msg->message += WM_SYSKEYDOWN - WM_KEYDOWN; } pWnd = WIN_FindWndPtr( msg->hwnd ); if (pWnd && (pWnd->hmemTaskQ != GetTaskQueue(0))) { /* Not for the current task */ MESSAGEQUEUE *queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) ); if (queue) QUEUE_ClearWakeBit( queue, QS_KEY ); /* Wake up the other task */ queue = (MESSAGEQUEUE *)GlobalLock16( pWnd->hmemTaskQ ); if (queue) QUEUE_SetWakeBit( queue, QS_KEY ); return SYSQ_MSG_ABANDON; } return (HOOK_CallHooks( WH_KEYBOARD, remove ? HC_ACTION : HC_NOREMOVE, msg->wParam, msg->lParam )) ? SYSQ_MSG_SKIP : SYSQ_MSG_ACCEPT; } /*********************************************************************** * MSG_JournalRecordMsg * * Build an EVENTMSG structure and call JOURNALRECORD hook */ static void MSG_JournalRecordMsg( MSG16 *msg ) { EVENTMSG16 *event = SEGPTR_NEW(EVENTMSG16); if (!event) return; event->message = msg->message; event->time = msg->time; if ((msg->message >= WM_KEYFIRST) && (msg->message <= WM_KEYLAST)) { event->paramL = (msg->wParam & 0xFF) | (HIWORD(msg->lParam) << 8); event->paramH = msg->lParam & 0x7FFF; if (HIWORD(msg->lParam) & 0x0100) event->paramH |= 0x8000; /* special_key - bit */ HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)SEGPTR_GET(event) ); } else if ((msg->message >= WM_MOUSEFIRST) && (msg->message <= WM_MOUSELAST)) { event->paramL = LOWORD(msg->lParam); /* X pos */ event->paramH = HIWORD(msg->lParam); /* Y pos */ ClientToScreen16( msg->hwnd, (LPPOINT16)&event->paramL ); HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)SEGPTR_GET(event) ); } else if ((msg->message >= WM_NCMOUSEFIRST) && (msg->message <= WM_NCMOUSELAST)) { event->paramL = LOWORD(msg->lParam); /* X pos */ event->paramH = HIWORD(msg->lParam); /* Y pos */ event->message += WM_MOUSEMOVE-WM_NCMOUSEMOVE;/* give no info about NC area */ HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)SEGPTR_GET(event) ); } SEGPTR_FREE(event); } /***************************************************************** * MSG_JournalPlayBackIsAscii */ static BOOL MSG_JournalPlayBackIsAscii(WPARAM wParam) { return ((wParam>VK_HELP && wParammessage>= WM_KEYFIRST) && (tmpMsg->message <= WM_KEYLAST)) { wParam=tmpMsg->paramL & 0xFF; lParam=MAKELONG(tmpMsg->paramH&0x7ffff,tmpMsg->paramL>>8); if (tmpMsg->message == WM_KEYDOWN || tmpMsg->message == WM_SYSKEYDOWN) { for (keyDown=i=0; i<256 && !keyDown; i++) if (KeyStateTable[i] & 0x80) keyDown++; if (!keyDown) lParam |= 0x40000000; AsyncKeyStateTable[wParam]=KeyStateTable[wParam] |= 0x80; if (MSG_JournalPlayBackIsAscii(wParam)) { lastEventChar= wParam; /* control TranslateMessage() */ lParam |= (LONG)((LONG)ASCII_CHAR_HACK*0x10000L); if (!(KeyStateTable[VK_SHIFT] & 0x80) && !(KeyStateTable[VK_CAPITAL] & 0x80)) lastEventChar= tolower(lastEventChar); if (KeyStateTable[VK_CONTROL] & 0x80) lastEventChar&=0x1f; } } else /* WM_KEYUP, WM_SYSKEYUP */ { lParam |= 0xC0000000; AsyncKeyStateTable[wParam]=KeyStateTable[wParam] &= ~0x80; } if (KeyStateTable[VK_MENU] & 0x80) lParam |= 0x20000000; if (tmpMsg->paramH & 0x8000) /*special_key bit*/ lParam |= 0x01000000; hardware_event( tmpMsg->message, wParam, lParam,0, 0, tmpMsg->time, 0 ); } else { if ((tmpMsg->message>= WM_MOUSEFIRST) && (tmpMsg->message <= WM_MOUSELAST)) { switch (tmpMsg->message) { case WM_LBUTTONDOWN:MouseButtonsStates[0]=AsyncMouseButtonsStates[0]=1;break; case WM_LBUTTONUP: MouseButtonsStates[0]=AsyncMouseButtonsStates[0]=0;break; case WM_MBUTTONDOWN:MouseButtonsStates[1]=AsyncMouseButtonsStates[1]=1;break; case WM_MBUTTONUP: MouseButtonsStates[1]=AsyncMouseButtonsStates[1]=0;break; case WM_RBUTTONDOWN:MouseButtonsStates[2]=AsyncMouseButtonsStates[2]=1;break; case WM_RBUTTONUP: MouseButtonsStates[2]=AsyncMouseButtonsStates[2]=0;break; } AsyncKeyStateTable[VK_LBUTTON]= KeyStateTable[VK_LBUTTON] = MouseButtonsStates[0] << 8; AsyncKeyStateTable[VK_MBUTTON]= KeyStateTable[VK_MBUTTON] = MouseButtonsStates[1] << 8; AsyncKeyStateTable[VK_RBUTTON]= KeyStateTable[VK_RBUTTON] = MouseButtonsStates[2] << 8; SetCursorPos(tmpMsg->paramL,tmpMsg->paramH); lParam=MAKELONG(tmpMsg->paramL,tmpMsg->paramH); wParam=0; if (MouseButtonsStates[0]) wParam |= MK_LBUTTON; if (MouseButtonsStates[1]) wParam |= MK_MBUTTON; if (MouseButtonsStates[2]) wParam |= MK_RBUTTON; hardware_event( tmpMsg->message, wParam, lParam, tmpMsg->paramL, tmpMsg->paramH, tmpMsg->time, 0 ); } } HOOK_CallHooks( WH_JOURNALPLAYBACK, HC_SKIP, 0, (LPARAM)SEGPTR_GET(tmpMsg)); } else result= QS_MOUSE | QS_KEY; SEGPTR_FREE(tmpMsg); } return result; } /*********************************************************************** * MSG_PeekHardwareMsg * * Peek for a hardware message matching the hwnd and message filters. */ static BOOL MSG_PeekHardwareMsg( MSG16 *msg, HWND hwnd, WORD first, WORD last, BOOL remove ) { SYSQ_STATUS status; MESSAGEQUEUE *sysMsgQueue = QUEUE_GetSysQueue(); int i, pos = sysMsgQueue->nextMessage; /* If the queue is empty, attempt to fill it */ if (!sysMsgQueue->msgCount && XPending(display)) EVENT_WaitXEvent( FALSE, FALSE ); for (i = 0; i < sysMsgQueue->msgCount; i++, pos++) { if (pos >= sysMsgQueue->queueSize) pos = 0; *msg = sysMsgQueue->messages[pos].msg; /* Translate message; return FALSE immediately on SYSQ_MSG_ABANDON */ if ((msg->message >= WM_MOUSEFIRST) && (msg->message <= WM_MOUSELAST)) { if ((status = MSG_TranslateMouseMsg(msg,remove)) == SYSQ_MSG_ABANDON) return FALSE; } else if ((msg->message >= WM_KEYFIRST) && (msg->message <= WM_KEYLAST)) { if ((status = MSG_TranslateKeyboardMsg(msg,remove)) == SYSQ_MSG_ABANDON) return FALSE; } else /* Non-standard hardware event */ { HARDWAREHOOKSTRUCT16 *hook; if ((hook = SEGPTR_NEW(HARDWAREHOOKSTRUCT16))) { BOOL32 ret; hook->hWnd = msg->hwnd; hook->wMessage = msg->message; hook->wParam = msg->wParam; hook->lParam = msg->lParam; ret = HOOK_CallHooks( WH_HARDWARE, remove ? HC_ACTION : HC_NOREMOVE, 0, (LPARAM)SEGPTR_GET(hook) ); SEGPTR_FREE(hook); status = ret ? SYSQ_MSG_SKIP : SYSQ_MSG_ACCEPT; } } if (status == SYSQ_MSG_SKIP) { if (remove) QUEUE_RemoveMsg( sysMsgQueue, pos ); /* FIXME: call CBT_CLICKSKIPPED from here */ continue; } /* Check message against filters */ if (hwnd && (msg->hwnd != hwnd)) continue; if ((first || last) && ((msg->message < first) || (msg->message > last))) continue; if (remove) { if (HOOK_GetHook( WH_JOURNALRECORD, GetTaskQueue(0) )) MSG_JournalRecordMsg( msg ); QUEUE_RemoveMsg( sysMsgQueue, pos ); } return TRUE; } return FALSE; } /********************************************************************** * SetDoubleClickTime (USER.20) */ void SetDoubleClickTime( WORD interval ) { doubleClickSpeed = interval ? interval : 500; } /********************************************************************** * GetDoubleClickTime (USER.21) */ WORD GetDoubleClickTime() { return doubleClickSpeed; } /*********************************************************************** * MSG_SendMessage * * Implementation of an inter-task SendMessage. */ static LRESULT MSG_SendMessage( HQUEUE16 hDestQueue, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam ) { INT32 prevSMRL = debugSMRL; QSMCTRL qCtrl = { 0, 1}; MESSAGEQUEUE *queue, *destQ; if (!(queue = (MESSAGEQUEUE*)GlobalLock16( GetTaskQueue(0) ))) return 0; if (!(destQ = (MESSAGEQUEUE*)GlobalLock16( hDestQueue ))) return 0; if (IsTaskLocked() || !IsWindow(hwnd)) return 0; debugSMRL+=4; dprintf_sendmsg(stddeb,"%*sSM: %s [%04x] (%04x -> %04x)\n", prevSMRL, "", SPY_GetMsgName(msg), msg, queue->self, hDestQueue ); if( !(queue->wakeBits & QS_SMPARAMSFREE) ) { dprintf_sendmsg(stddeb,"\tIntertask SendMessage: sleeping since unreplied SendMessage pending\n"); queue->changeBits &= ~QS_SMPARAMSFREE; QUEUE_WaitBits( QS_SMPARAMSFREE ); } /* resume sending */ queue->hWnd = hwnd; queue->msg = msg; queue->wParam = wParam; queue->lParam = lParam; queue->hPrevSendingTask = destQ->hSendingTask; destQ->hSendingTask = GetTaskQueue(0); queue->wakeBits &= ~QS_SMPARAMSFREE; dprintf_sendmsg(stddeb,"%*ssm: smResultInit = %08x\n", prevSMRL, "", (unsigned)&qCtrl); queue->smResultInit = &qCtrl; QUEUE_SetWakeBit( destQ, QS_SENDMESSAGE ); /* perform task switch and wait for the result */ while( qCtrl.bPending ) { if (!(queue->wakeBits & QS_SMRESULT)) { queue->changeBits &= ~QS_SMRESULT; DirectedYield( destQ->hTask ); QUEUE_WaitBits( QS_SMRESULT ); dprintf_sendmsg(stddeb,"\tsm: have result!\n"); } /* got something */ dprintf_sendmsg(stddeb,"%*ssm: smResult = %08x\n", prevSMRL, "", (unsigned)queue->smResult ); queue->smResult->lResult = queue->SendMessageReturn; queue->smResult->bPending = FALSE; queue->wakeBits &= ~QS_SMRESULT; if( queue->smResult != &qCtrl ) dprintf_msg(stddeb,"%*ssm: weird scenes inside the goldmine!\n", prevSMRL, ""); } queue->smResultInit = NULL; dprintf_sendmsg(stddeb,"%*sSM: [%04x] returning %08lx\n", prevSMRL, "", msg, qCtrl.lResult); debugSMRL-=4; return qCtrl.lResult; } /*********************************************************************** * ReplyMessage (USER.115) */ void ReplyMessage( LRESULT result ) { MESSAGEQUEUE *senderQ; MESSAGEQUEUE *queue; if (!(queue = (MESSAGEQUEUE*)GlobalLock16( GetTaskQueue(0) ))) return; dprintf_msg(stddeb,"ReplyMessage, queue %04x\n", queue->self); while( (senderQ = (MESSAGEQUEUE*)GlobalLock16( queue->InSendMessageHandle))) { dprintf_msg(stddeb,"\trpm: replying to %04x (%04x -> %04x)\n", queue->msg, queue->self, senderQ->self); if( queue->wakeBits & QS_SENDMESSAGE ) { QUEUE_ReceiveMessage( queue ); continue; /* ReceiveMessage() already called us */ } if(!(senderQ->wakeBits & QS_SMRESULT) ) break; OldYield(); } if( !senderQ ) { dprintf_msg(stddeb,"\trpm: done\n"); return; } senderQ->SendMessageReturn = result; dprintf_msg(stddeb,"\trpm: smResult = %08x, result = %08lx\n", (unsigned)queue->smResultCurrent, result ); senderQ->smResult = queue->smResultCurrent; queue->InSendMessageHandle = 0; QUEUE_SetWakeBit( senderQ, QS_SMRESULT ); DirectedYield( queue->hSendingTask ); } /*********************************************************************** * MSG_PeekMessage */ static BOOL MSG_PeekMessage( LPMSG16 msg, HWND hwnd, WORD first, WORD last, WORD flags, BOOL peek ) { int pos, mask; MESSAGEQUEUE *msgQueue; HQUEUE16 hQueue; #ifdef CONFIG_IPC DDE_TestDDE(hwnd); /* do we have dde handling in the window ?*/ DDE_GetRemoteMessage(); #endif /* CONFIG_IPC */ mask = QS_POSTMESSAGE | QS_SENDMESSAGE; /* Always selected */ if (first || last) { /* MSWord gets stuck if we do not check for nonclient mouse messages */ if ((first <= WM_KEYLAST) && (last >= WM_KEYFIRST)) mask |= QS_KEY; if ( ((first <= WM_MOUSELAST) && (last >= WM_MOUSEFIRST)) || ((first <= WM_NCMOUSELAST) && (last >= WM_NCMOUSEFIRST)) ) mask |= QS_MOUSE; if ((first <= WM_TIMER) && (last >= WM_TIMER)) mask |= QS_TIMER; if ((first <= WM_SYSTIMER) && (last >= WM_SYSTIMER)) mask |= QS_TIMER; if ((first <= WM_PAINT) && (last >= WM_PAINT)) mask |= QS_PAINT; } else mask |= QS_MOUSE | QS_KEY | QS_TIMER | QS_PAINT; if (IsTaskLocked()) flags |= PM_NOYIELD; while(1) { hQueue = GetTaskQueue(0); msgQueue = (MESSAGEQUEUE *)GlobalLock16( hQueue ); if (!msgQueue) return FALSE; msgQueue->changeBits = 0; /* First handle a message put by SendMessage() */ if (msgQueue->wakeBits & QS_SENDMESSAGE) QUEUE_ReceiveMessage( msgQueue ); /* Now handle a WM_QUIT message * * FIXME: PostQuitMessage() should post WM_QUIT and * set QS_POSTMESSAGE wakebit instead of this. */ if (msgQueue->wPostQMsg && (!first || WM_QUIT >= first) && (!last || WM_QUIT <= last) ) { msg->hwnd = hwnd; msg->message = WM_QUIT; msg->wParam = msgQueue->wExitCode; msg->lParam = 0; break; } /* Now find a normal message */ if (((msgQueue->wakeBits & mask) & QS_POSTMESSAGE) && ((pos = QUEUE_FindMsg( msgQueue, hwnd, first, last )) != -1)) { QMSG *qmsg = &msgQueue->messages[pos]; *msg = qmsg->msg; msgQueue->GetMessageTimeVal = msg->time; msgQueue->GetMessagePosVal = *(DWORD *)&msg->pt; msgQueue->GetMessageExtraInfoVal = qmsg->extraInfo; if (flags & PM_REMOVE) QUEUE_RemoveMsg( msgQueue, pos ); break; } msgQueue->changeBits |= MSG_JournalPlayBackMsg(); /* Now find a hardware event */ if (((msgQueue->wakeBits & mask) & (QS_MOUSE | QS_KEY)) && MSG_PeekHardwareMsg( msg, hwnd, first, last, flags & PM_REMOVE )) { /* Got one */ msgQueue->GetMessageTimeVal = msg->time; msgQueue->GetMessagePosVal = *(DWORD *)&msg->pt; msgQueue->GetMessageExtraInfoVal = 0; /* Always 0 for now */ break; } /* Check again for SendMessage */ if (msgQueue->wakeBits & QS_SENDMESSAGE) QUEUE_ReceiveMessage( msgQueue ); /* Now find a WM_PAINT message */ if ((msgQueue->wakeBits & mask) & QS_PAINT) { WND* wndPtr; msg->hwnd = WIN_FindWinToRepaint( hwnd , hQueue ); msg->message = WM_PAINT; msg->wParam = 0; msg->lParam = 0; if ((wndPtr = WIN_FindWndPtr(msg->hwnd))) { if( wndPtr->dwStyle & WS_MINIMIZE && wndPtr->class->hIcon ) { msg->message = WM_PAINTICON; msg->wParam = 1; } if( !hwnd || msg->hwnd == hwnd || IsChild(hwnd,msg->hwnd) ) { if( wndPtr->flags & WIN_INTERNAL_PAINT && !wndPtr->hrgnUpdate) { wndPtr->flags &= ~WIN_INTERNAL_PAINT; QUEUE_DecPaintCount( hQueue ); } break; } } } /* Check for timer messages, but yield first */ if (!(flags & PM_NOYIELD)) { UserYield(); if (msgQueue->wakeBits & QS_SENDMESSAGE) QUEUE_ReceiveMessage( msgQueue ); } if ((msgQueue->wakeBits & mask) & QS_TIMER) { if (TIMER_GetTimerMsg(msg, hwnd, hQueue, flags & PM_REMOVE)) break; } if (peek) { if (!(flags & PM_NOYIELD)) UserYield(); return FALSE; } msgQueue->wakeMask = mask; QUEUE_WaitBits( mask ); } /* We got a message */ if (peek) return TRUE; else return (msg->message != WM_QUIT); } /*********************************************************************** * MSG_InternalGetMessage * * GetMessage() function for internal use. Behave like GetMessage(), * but also call message filters and optionally send WM_ENTERIDLE messages. * 'hwnd' must be the handle of the dialog or menu window. * 'code' is the message filter value (MSGF_??? codes). */ BOOL32 MSG_InternalGetMessage( MSG16 *msg, HWND32 hwnd, HWND32 hwndOwner, WPARAM32 code, WORD flags, BOOL32 sendIdle ) { for (;;) { if (sendIdle) { if (!MSG_PeekMessage( msg, 0, 0, 0, flags, TRUE )) { /* No message present -> send ENTERIDLE and wait */ if (IsWindow(hwndOwner)) SendMessage16( hwndOwner, WM_ENTERIDLE, code, (LPARAM)hwnd ); MSG_PeekMessage( msg, 0, 0, 0, flags, FALSE ); } } else /* Always wait for a message */ MSG_PeekMessage( msg, 0, 0, 0, flags, FALSE ); /* Call message filters */ if (HOOK_GetHook( WH_SYSMSGFILTER, GetTaskQueue(0) ) || HOOK_GetHook( WH_MSGFILTER, GetTaskQueue(0) )) { MSG16 *pmsg = SEGPTR_NEW(MSG16); if (pmsg) { BOOL32 ret; *pmsg = *msg; ret = ((BOOL16)HOOK_CallHooks( WH_SYSMSGFILTER, code, 0, (LPARAM)SEGPTR_GET(pmsg) ) || (BOOL16)HOOK_CallHooks( WH_MSGFILTER, code, 0, (LPARAM)SEGPTR_GET(pmsg) )); SEGPTR_FREE(pmsg); if (ret) { /* Message filtered -> remove it from the queue */ /* if it's still there. */ if (!(flags & PM_REMOVE)) MSG_PeekMessage( msg, 0, 0, 0, PM_REMOVE, TRUE ); continue; } } } return (msg->message != WM_QUIT); } } /*********************************************************************** * PeekMessage16 (USER.109) */ BOOL16 PeekMessage16( LPMSG16 msg, HWND16 hwnd, UINT16 first, UINT16 last, UINT16 flags ) { return MSG_PeekMessage( msg, hwnd, first, last, flags, TRUE ); } /*********************************************************************** * GetMessage (USER.108) */ BOOL GetMessage( SEGPTR msg, HWND hwnd, UINT first, UINT last ) { MSG16 *lpmsg = (MSG16 *)PTR_SEG_TO_LIN(msg); MSG_PeekMessage( lpmsg, hwnd, first, last, PM_REMOVE, FALSE ); dprintf_msg(stddeb,"message %04x, hwnd %04x, filter(%04x - %04x)\n", lpmsg->message, hwnd, first, last ); HOOK_CallHooks( WH_GETMESSAGE, HC_ACTION, 0, (LPARAM)msg ); return (lpmsg->message != WM_QUIT); } /*********************************************************************** * PostMessage (USER.110) */ BOOL PostMessage( HWND hwnd, WORD message, WORD wParam, LONG lParam ) { MSG16 msg; WND *wndPtr; msg.hwnd = hwnd; msg.message = message; msg.wParam = wParam; msg.lParam = lParam; msg.time = GetTickCount(); msg.pt.x = 0; msg.pt.y = 0; #ifdef CONFIG_IPC if (DDE_PostMessage(&msg)) return TRUE; #endif /* CONFIG_IPC */ if (hwnd == HWND_BROADCAST16) { dprintf_msg(stddeb,"PostMessage // HWND_BROADCAST !\n"); for (wndPtr = WIN_GetDesktop()->child; wndPtr; wndPtr = wndPtr->next) { if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION) { dprintf_msg(stddeb,"BROADCAST Message to hWnd=%04x m=%04X w=%04X l=%08lX !\n", wndPtr->hwndSelf, message, wParam, lParam); PostMessage( wndPtr->hwndSelf, message, wParam, lParam ); } } dprintf_msg(stddeb,"PostMessage // End of HWND_BROADCAST !\n"); return TRUE; } wndPtr = WIN_FindWndPtr( hwnd ); if (!wndPtr || !wndPtr->hmemTaskQ) return FALSE; return QUEUE_AddMsg( wndPtr->hmemTaskQ, &msg, 0 ); } /*********************************************************************** * PostAppMessage (USER.116) */ BOOL PostAppMessage( HTASK hTask, WORD message, WORD wParam, LONG lParam ) { MSG16 msg; if (GetTaskQueue(hTask) == 0) return FALSE; msg.hwnd = 0; msg.message = message; msg.wParam = wParam; msg.lParam = lParam; msg.time = GetTickCount(); msg.pt.x = 0; msg.pt.y = 0; return QUEUE_AddMsg( GetTaskQueue(hTask), &msg, 0 ); } /*********************************************************************** * SendMessage16 (USER.111) */ LRESULT SendMessage16( HWND16 hwnd, UINT16 msg, WPARAM16 wParam, LPARAM lParam) { WND * wndPtr; LRESULT ret; #ifdef CONFIG_IPC MSG16 DDE_msg = { hwnd, msg, wParam, lParam }; if (DDE_SendMessage(&DDE_msg)) return TRUE; #endif /* CONFIG_IPC */ if (hwnd == HWND_BROADCAST16) { dprintf_msg(stddeb,"SendMessage // HWND_BROADCAST !\n"); for (wndPtr = WIN_GetDesktop()->child; wndPtr; wndPtr = wndPtr->next) { if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION) { dprintf_msg(stddeb,"BROADCAST Message to hWnd=%04x m=%04X w=%04lX l=%08lX !\n", wndPtr->hwndSelf, msg, (DWORD)wParam, lParam); SendMessage16( wndPtr->hwndSelf, msg, wParam, lParam ); } } dprintf_msg(stddeb,"SendMessage // End of HWND_BROADCAST !\n"); return TRUE; } if (HOOK_GetHook( WH_CALLWNDPROC, GetTaskQueue(0) )) { struct msgstruct { LPARAM lParam; WPARAM16 wParam; UINT16 wMsg; HWND16 hWnd; } *pmsg; if ((pmsg = SEGPTR_NEW(struct msgstruct))) { pmsg->hWnd = hwnd; pmsg->wMsg = msg; pmsg->wParam = wParam; pmsg->lParam = lParam; HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, 1, (LPARAM)SEGPTR_GET(pmsg) ); hwnd = pmsg->hWnd; msg = pmsg->wMsg; wParam = pmsg->wParam; lParam = pmsg->lParam; } } if (!(wndPtr = WIN_FindWndPtr( hwnd ))) { fprintf( stderr, "SendMessage16: invalid hwnd %04x\n", hwnd ); return 0; } if (QUEUE_IsDoomedQueue(wndPtr->hmemTaskQ)) return 0; /* Don't send anything if the task is dying */ if (wndPtr->hmemTaskQ != GetTaskQueue(0)) return MSG_SendMessage( wndPtr->hmemTaskQ, hwnd, msg, wParam, lParam ); SPY_EnterMessage( SPY_SENDMESSAGE16, hwnd, msg, wParam, lParam ); ret = CallWindowProc16( (WNDPROC16)wndPtr->winproc, hwnd, msg, wParam, lParam ); SPY_ExitMessage( SPY_RESULT_OK16, hwnd, msg, ret ); return ret; } /*********************************************************************** * SendMessage32A (USER32.453) */ LRESULT SendMessage32A(HWND32 hwnd, UINT32 msg, WPARAM32 wParam, LPARAM lParam) { WND * wndPtr; LRESULT ret; if (hwnd == HWND_BROADCAST32) { for (wndPtr = WIN_GetDesktop()->child; wndPtr; wndPtr = wndPtr->next) { /* FIXME: should use something like EnumWindows here */ if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION) SendMessage32A( wndPtr->hwndSelf, msg, wParam, lParam ); } return TRUE; } /* FIXME: call hooks */ if (!(wndPtr = WIN_FindWndPtr( hwnd ))) { fprintf( stderr, "SendMessage32A: invalid hwnd %08x\n", hwnd ); return 0; } if (WINPROC_GetProcType( wndPtr->winproc ) == WIN_PROC_16) { /* Use SendMessage16 for now to get hooks right */ UINT16 msg16; WPARAM16 wParam16; if (WINPROC_MapMsg32ATo16( msg, wParam, &msg16, &wParam16, &lParam ) == -1) return 0; ret = SendMessage16( hwnd, msg16, wParam16, lParam ); WINPROC_UnmapMsg32ATo16( msg16, wParam16, lParam ); return ret; } if (QUEUE_IsDoomedQueue(wndPtr->hmemTaskQ)) return 0; /* Don't send anything if the task is dying */ if (wndPtr->hmemTaskQ != GetTaskQueue(0)) { fprintf( stderr, "SendMessage32A: intertask message not supported\n" ); return 0; } SPY_EnterMessage( SPY_SENDMESSAGE32, hwnd, msg, wParam, lParam ); ret = CallWindowProc32A( (WNDPROC32)wndPtr->winproc, hwnd, msg, wParam, lParam ); SPY_ExitMessage( SPY_RESULT_OK32, hwnd, msg, ret ); return ret; } /*********************************************************************** * SendMessage32W (USER32.458) */ LRESULT SendMessage32W(HWND32 hwnd, UINT32 msg, WPARAM32 wParam, LPARAM lParam) { WND * wndPtr; LRESULT ret; if (hwnd == HWND_BROADCAST32) { for (wndPtr = WIN_GetDesktop()->child; wndPtr; wndPtr = wndPtr->next) { /* FIXME: should use something like EnumWindows here */ if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION) SendMessage32W( wndPtr->hwndSelf, msg, wParam, lParam ); } return TRUE; } /* FIXME: call hooks */ if (!(wndPtr = WIN_FindWndPtr( hwnd ))) { fprintf( stderr, "SendMessage32W: invalid hwnd %08x\n", hwnd ); return 0; } if (QUEUE_IsDoomedQueue(wndPtr->hmemTaskQ)) return 0; /* Don't send anything if the task is dying */ if (wndPtr->hmemTaskQ != GetTaskQueue(0)) { fprintf( stderr, "SendMessage32W: intertask message not supported\n" ); return 0; } SPY_EnterMessage( SPY_SENDMESSAGE32, hwnd, msg, wParam, lParam ); ret = CallWindowProc32W( (WNDPROC32)wndPtr->winproc, hwnd, msg, wParam, lParam ); SPY_ExitMessage( SPY_RESULT_OK32, hwnd, msg, ret ); return ret; } /*********************************************************************** * WaitMessage (USER.112) */ void WaitMessage( void ) { QUEUE_WaitBits( QS_ALLINPUT ); } /*********************************************************************** * TranslateMessage (USER.113) * * This should call ToAscii but it is currently broken */ BOOL TranslateMessage( LPMSG16 msg ) { UINT message = msg->message; /* BYTE wparam[2]; */ if ((message == WM_KEYDOWN) || (message == WM_KEYUP) || (message == WM_SYSKEYDOWN) || (message == WM_SYSKEYUP)) { dprintf_msg(stddeb, "Translating key %04x, scancode %04x\n", msg->wParam, HIWORD(msg->lParam) ); if( HIWORD(msg->lParam) & ASCII_CHAR_HACK ) /* if( ToAscii( msg->wParam, HIWORD(msg->lParam), (LPSTR)&KeyStateTable, wparam, 0 ) ) */ { message += 2 - (message & 0x0001); PostMessage( msg->hwnd, message, lastEventChar, msg->lParam ); return TRUE; } } return FALSE; } /*********************************************************************** * DispatchMessage (USER.114) */ LONG DispatchMessage( const MSG16* msg ) { WND * wndPtr; LONG retval; int painting; /* Process timer messages */ if ((msg->message == WM_TIMER) || (msg->message == WM_SYSTIMER)) { if (msg->lParam) { /* HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */ return CallWindowProc16( (WNDPROC16)msg->lParam, msg->hwnd, msg->message, msg->wParam, GetTickCount() ); } } if (!msg->hwnd) return 0; if (!(wndPtr = WIN_FindWndPtr( msg->hwnd ))) return 0; if (!wndPtr->winproc) return 0; painting = (msg->message == WM_PAINT); if (painting) wndPtr->flags |= WIN_NEEDS_BEGINPAINT; /* HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */ SPY_EnterMessage( SPY_DISPATCHMESSAGE16, msg->hwnd, msg->message, msg->wParam, msg->lParam ); retval = CallWindowProc16( (WNDPROC16)wndPtr->winproc, msg->hwnd, msg->message, msg->wParam, msg->lParam ); SPY_ExitMessage( SPY_RESULT_OK16, msg->hwnd, msg->message, retval ); if (painting && (wndPtr = WIN_FindWndPtr( msg->hwnd )) && (wndPtr->flags & WIN_NEEDS_BEGINPAINT) && wndPtr->hrgnUpdate) { fprintf(stderr, "BeginPaint not called on WM_PAINT for hwnd %04x!\n", msg->hwnd); wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT; /* Validate the update region to avoid infinite WM_PAINT loop */ ValidateRect32( msg->hwnd, NULL ); } return retval; } /*********************************************************************** * RegisterWindowMessage16 (USER.118) */ WORD RegisterWindowMessage16( SEGPTR str ) { dprintf_msg(stddeb, "RegisterWindowMessage16: %08lx\n", (DWORD)str ); return GlobalAddAtom16( str ); } /*********************************************************************** * RegisterWindowMessage32A (USER32.436) */ WORD RegisterWindowMessage32A( LPCSTR str ) { dprintf_msg(stddeb, "RegisterWindowMessage32A: %s\n", str ); return GlobalAddAtom32A( str ); } /*********************************************************************** * RegisterWindowMessage32W (USER32.437) */ WORD RegisterWindowMessage32W( LPCWSTR str ) { dprintf_msg(stddeb, "RegisterWindowMessage32W: %p\n", str ); return GlobalAddAtom32W( str ); } /*********************************************************************** * GetTickCount (USER.13) (KERNEL32.299) */ DWORD GetTickCount(void) { struct timeval t; gettimeofday( &t, NULL ); return ((t.tv_sec * 1000) + (t.tv_usec / 1000)) - MSG_WineStartTicks; } /*********************************************************************** * GetCurrentTime (USER.15) * * (effectively identical to GetTickCount) */ DWORD GetCurrentTime(void) { return GetTickCount(); } /*********************************************************************** * InSendMessage (USER.192) */ BOOL InSendMessage() { MESSAGEQUEUE *queue; if (!(queue = (MESSAGEQUEUE *)GlobalLock16( GetTaskQueue(0) ))) return 0; return (BOOL)queue->InSendMessageHandle; }