/**************************** Module Header ********************************\ * Module Name: mnloop.c * * Copyright 1985-90, Microsoft Corporation * * Menu Modal Loop Routines * * History: * 10-10-90 JimA Cleanup. * 03-18-91 IanJa Window revalidation added \***************************************************************************/ #include "precomp.h" #pragma hdrstop /***************************************************************************\ * xxxHandleMenuMessages * * History: \***************************************************************************/ void xxxHandleMenuMessages( LPMSG lpmsg, PMENUSTATE pMenuState, PPOPUPMENU ppopupmenu) { DWORD ch; MSG msg; UINT cmdHitArea; UINT cmdItem; LONG lParam; BOOL fThreadLock = FALSE; TL tlpwndHitArea; TL tlpwndT; UINT msgRightButton; /* * Get things out of the structure so that we can access them quicker. */ ch = lpmsg->wParam; lParam = lpmsg->lParam; /* * In this switch statement, we only look at messages we want to handle and * swallow. Messages we don't understand will get translated and * dispatched. */ switch (lpmsg->message) { case WM_RBUTTONDOWN: case WM_NCRBUTTONDOWN: if (ppopupmenu->fRightButton) goto HandleButtonDown; if (pMenuState->fButtonDown) { msgRightButton = WM_RBUTTONDOWN; EatRightButtons: if (xxxPeekMessage(&msg, NULL, 0, 0, PM_NOYIELD) && (msg.message == msgRightButton)) { xxxPeekMessage(&msg, NULL, msgRightButton, msgRightButton, PM_REMOVE); } return; } if (xxxPeekMessage(&msg, NULL, 0, 0, PM_NOYIELD) && (msg.message == lpmsg->message)) xxxPeekMessage(&msg, NULL, lpmsg->message, lpmsg->message, PM_REMOVE); else return; break; case WM_LBUTTONDOWN: case WM_NCLBUTTONDOWN: // Commented out due to TandyT whinings... // if ((ppopupmenu->trackPopupMenuFlags & TPM_RIGHTBUTTON)) // break; HandleButtonDown: /* * Find out where this mouse down occurred. */ pMenuState->mnFocus = MOUSEHOLD; pMenuState->ptMouseLast.x = LOWORD(lParam); pMenuState->ptMouseLast.y = HIWORD(lParam); cmdHitArea = xxxMNFindWindowFromPoint(ppopupmenu, &cmdItem, MAKEPOINTS(lParam)); /* * Thread lock this if it is a pwnd. This certainly isn't the way * you'd implement this if you had locking to begin with. */ switch(cmdHitArea) { case MFMWFP_OFFMENU: case MFMWFP_MAINMENU: case MFMWFP_NOITEM: case MFMWFP_ALTMENU: fThreadLock = FALSE; break; default: ThreadLock((PWND)cmdHitArea, &tlpwndHitArea); fThreadLock = TRUE; break; } if ((cmdHitArea == MFMWFP_OFFMENU) && (cmdItem == 0)) { // // Clicked in middle of nowhere, so terminate menus, and // let button pass through. CancelOut: xxxMNCancel(ppopupmenu, 0, FALSE, 0); goto Unlock; } else if (ppopupmenu->fHasMenuBar && (cmdHitArea == MFMWFP_ALTMENU)) { // // Switching between menu bar & popup // xxxMNSwitchToAlternateMenu(ppopupmenu); cmdHitArea = MFMWFP_NOITEM; } if (cmdHitArea == MFMWFP_NOITEM) { // // On menu bar (system or main) // xxxMNButtonDown(ppopupmenu, pMenuState, cmdItem, TRUE); } else { // On popup window menu UserAssert(cmdHitArea); xxxSendMessage((PWND)cmdHitArea, MN_BUTTONDOWN, cmdItem, 0L); } /* * Swallow the message since we handled it. */ /* * The excel guys change a wm_rbuttondown to a wm_lbuttondown message * in their message filter hook. Remove the message here or we'll * get in a nasty loop. * * We need to swallow msg32.message ONLY. It is possible for * the LBUTTONDOWN to not be at the head of the input queue. * If not, we will swallow a WM_MOUSEMOVE or something else like * that. The reason Peek() doesn't need to check the range * is because we've already Peek(PM_NOYIELD'ed) before, which * locked the sys queue. */ if (xxxPeekMessage(&msg, NULL, 0, 0, PM_NOYIELD) && ((msg.message == lpmsg->message) || (msg.message == WM_RBUTTONDOWN))) { xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE); } goto Unlock; case WM_MOUSEMOVE: case WM_NCMOUSEMOVE: xxxMNMouseMove(ppopupmenu, pMenuState, MAKEPOINTS(lParam)); return; case WM_RBUTTONUP: case WM_NCRBUTTONUP: if (ppopupmenu->fRightButton) goto HandleButtonUp; if (pMenuState->fButtonDown) { msgRightButton = WM_RBUTTONUP; goto EatRightButtons; } #ifdef MEMPHIS_MENUS // New feature for shell start menu -- notify when a right click // occurs on a menu item, and open a window of opportunity for // menus to recurse, allowing them to popup a context-sensitive // menu for that item. (jeffbog 9/28/95) // // BUGBUG: need to add check for Nashville+ app if ((lpmsg->message == WM_RBUTTONUP) && !ppopupmenu->fNoNotify) { PPOPUPMENU ppopupActive; if ( ppopupmenu->spwndActivePopup && ( ppopupActive = ((PMENUWND)(ppopupmenu->spwndActivePopup))->ppopupmenu) && MNISITEMSELECTED(ppopupActive)) { TL tlpwndNotify; ThreadLock( ppopupActive->spwndNotify, &tlpwndNotify ); xxxSendMessage(ppopupActive->spwndNotify, WM_MENURBUTTONUP, MNSELECTEDITEM(ppopupActive)->wID, lParam); ThreadUnlock( &tlpwndNotify ); } } #endif // MEMPHIS_MENUS break; case WM_LBUTTONUP: case WM_NCLBUTTONUP: // Commented out due to TandyT whinings... // if ((ppopupmenu->trackPopupMenuFlags & TPM_RIGHTBUTTON)) // break; HandleButtonUp: if (!pMenuState->fButtonDown) { /* * Don't care about this mouse up since we never saw the button * down for some reason. */ return; } /* * Find out where this mouse up occurred. */ pMenuState->ptMouseLast.x = LOWORD(lParam); pMenuState->ptMouseLast.y = HIWORD(lParam); cmdHitArea = xxxMNFindWindowFromPoint(ppopupmenu, &cmdItem, MAKEPOINTS(lParam)); /* * Thread lock this if it is a pwnd. This certainly isn't the way * you'd implement this if you had locking to begin with. */ switch(cmdHitArea) { case MFMWFP_OFFMENU: case MFMWFP_MAINMENU: case MFMWFP_NOITEM: case MFMWFP_ALTMENU: fThreadLock = FALSE; break; default: ThreadLock((PWND)cmdHitArea, &tlpwndHitArea); fThreadLock = TRUE; break; } if (ppopupmenu->fHasMenuBar) { if (((cmdHitArea == MFMWFP_OFFMENU) && (cmdItem == 0)) || ((cmdHitArea == MFMWFP_NOITEM) && ppopupmenu->fIsSysMenu && ppopupmenu->fToggle)) // Button up occurred in some random spot. Terminate // menus and swallow the message. goto CancelOut; } else { if ((cmdHitArea == MFMWFP_OFFMENU) && (cmdItem == 0)) { if (!ppopupmenu->fFirstClick) { // // User upclicked in some random spot. Terminate // menus and don't swallow the message. // // // Don't do anything with HWND here cuz the window is // destroyed after this SendMessage(). // // DONTREVALIDATE(); ThreadLock(ppopupmenu->spwndPopupMenu, &tlpwndT); xxxSendMessage(ppopupmenu->spwndPopupMenu, MN_CANCELMENUS, 0, 0); ThreadUnlock(&tlpwndT); goto Unlock; } } ppopupmenu->fFirstClick = FALSE; } if (cmdHitArea == MFMWFP_NOITEM) { // // This is a system menu or a menu bar and the button up // occurred on the system menu or on a menu bar item. // xxxMNButtonUp(ppopupmenu, pMenuState, cmdItem, 0); } else if ((cmdHitArea != MFMWFP_OFFMENU) && (cmdHitArea != MFMWFP_ALTMENU)) { // // Warning: It's common for the popup to go away during the // processing of this message, so don't add any code that // messes with hwnd after this call! // // DONTREVALIDATE(); // // We send lParam (that has the mouse co-ords ) for the app // to get it in its SC_RESTORE/SC_MINIMIZE messages 3.0 // compat // xxxSendMessage((PWND)cmdHitArea, MN_BUTTONUP, (DWORD)cmdItem, lParam); } else pMenuState->fButtonDown = FALSE; Unlock: if (fThreadLock) ThreadUnlock(&tlpwndHitArea); return; case WM_RBUTTONDBLCLK: case WM_NCRBUTTONDBLCLK: if (!(ppopupmenu->fRightButton)) { if (pMenuState->fButtonDown) { msgRightButton = lpmsg->message; goto EatRightButtons; } break; } else { pMenuState->mnFocus = MOUSEHOLD; cmdHitArea = xxxMNFindWindowFromPoint( ppopupmenu, &cmdItem, MAKEPOINTS(lParam)); if (cmdHitArea == MFMWFP_OFFMENU) { /* *Double click on no menu, cancel out and don't swallow so * that double clicks get us out. */ xxxMNCancel(ppopupmenu, 0, 0, 0); return; } } /* * Swallow the message since we handled it. */ if (xxxPeekMessage(&msg, NULL, 0, 0, PM_NOYIELD) && ((msg.message == WM_RBUTTONDBLCLK) || (msg.message == WM_NCRBUTTONDBLCLK))) { xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE); } return; case WM_LBUTTONDBLCLK: case WM_NCLBUTTONDBLCLK: // Commented out due to TandyT whinings... // if (ppopup->fRightButton) // break; pMenuState->mnFocus = MOUSEHOLD; cmdHitArea = xxxMNFindWindowFromPoint( ppopupmenu, &cmdItem, MAKEPOINTS(lParam)); if ((cmdHitArea == MFMWFP_OFFMENU) && (cmdItem == 0)) { // Dbl-clicked in middle of nowhere, so terminate menus, and // let button pass through. xxxMNCancel(ppopupmenu, 0, 0, 0L); return; } else if (ppopupmenu->fHasMenuBar && (cmdHitArea == MFMWFP_ALTMENU)) { // // BOGUS // TREAT LIKE BUTTON DOWN since we didn't dblclk on same item. // xxxMNSwitchToAlternateMenu(ppopupmenu); cmdHitArea = MFMWFP_NOITEM; } if (cmdHitArea == MFMWFP_NOITEM) xxxMNDoubleClick(ppopupmenu, cmdItem); else { UserAssert(cmdHitArea); ThreadLock((PWND)cmdHitArea, &tlpwndHitArea); xxxSendMessage((PWND)cmdHitArea, MN_DBLCLK, (DWORD)cmdItem, 0L); ThreadUnlock(&tlpwndHitArea); } return; case WM_KEYDOWN: case WM_SYSKEYDOWN: /* * If mouse button is down, ignore keyboard input (fix #3899, IanJa) */ if (pMenuState->fButtonDown && (ch != VK_F1)) { return; } pMenuState->mnFocus = KEYBDHOLD; switch (ch) { case VK_UP: case VK_DOWN: case VK_LEFT: case VK_RIGHT: case VK_RETURN: case VK_CANCEL: case VK_ESCAPE: case VK_MENU: case VK_F10: case VK_F1: if (ppopupmenu->spwndActivePopup) { ThreadLockAlways(ppopupmenu->spwndActivePopup, &tlpwndT); xxxSendMessage(ppopupmenu->spwndActivePopup, lpmsg->message, ch, 0L); ThreadUnlock(&tlpwndT); } else { xxxMNKeyDown(ppopupmenu, pMenuState, (UINT)ch); } break; default: TranslateKey: _TranslateMessage(lpmsg, 0); break; } return; case WM_CHAR: case WM_SYSCHAR: if (ppopupmenu->spwndActivePopup) { ThreadLockAlways(ppopupmenu->spwndActivePopup, &tlpwndT); xxxSendMessage(ppopupmenu->spwndActivePopup, lpmsg->message, ch, 0L); ThreadUnlock(&tlpwndT); } else { xxxMNChar(ppopupmenu, pMenuState, (UINT)ch); } return; case WM_SYSKEYUP: /* * Ignore ALT and F10 keyup messages since they are handled on * the KEYDOWN message. */ if (ch == VK_MENU || ch == VK_F10) { if (winOldAppHackoMaticFlags & WOAHACK_CHECKALTKEYSTATE) { if (winOldAppHackoMaticFlags & WOAHACK_IGNOREALTKEYDOWN) { winOldAppHackoMaticFlags &= ~WOAHACK_IGNOREALTKEYDOWN; winOldAppHackoMaticFlags &= ~WOAHACK_CHECKALTKEYSTATE; } else winOldAppHackoMaticFlags |= WOAHACK_IGNOREALTKEYDOWN; } return; } /* ** fall thru ** */ case WM_KEYUP: /* * Do RETURNs on the up transition only */ goto TranslateKey; case WM_SYSTIMER: /* * Prevent the caret from flashing by eating all WM_SYSTIMER messages. */ return; default: break; } if (PtiCurrent()->pq->codeCapture == NO_CAP_CLIENT) xxxCapture(PtiCurrent(), ppopupmenu->spwndNotify, SCREEN_CAPTURE); _TranslateMessage(lpmsg, 0); xxxDispatchMessage(lpmsg); } /***************************************************************************\ * xxxMenuLoop * * The menu processing entry point. * assumes: pMenuState->spwndMenu is the window which is the owner of the menu * we are processing. * * History: \***************************************************************************/ int xxxMNLoop( PPOPUPMENU ppopupmenu, PMENUSTATE pMenuState, LONG lParam, BOOL fDblClk) { int hit; MSG msg; BOOL fSendIdle = TRUE; BOOL fInQueue = FALSE; DWORD menuState; PTHREADINFO pti; TL tlpwndT; UserAssert(IsRootPopupMenu(ppopupmenu)); pMenuState->fInsideMenuLoop = TRUE; pMenuState->cmdLast = 0; pti = PtiCurrent(); pMenuState->ptMouseLast.x = pti->ptLast.x; pMenuState->ptMouseLast.y = pti->ptLast.y; /* * Set flag to false, so that we can track if windows have * been activated since entering this loop. */ pti->pq->QF_flags &= ~QF_ACTIVATIONCHANGE; /* * Were we called from xxxMenuKeyFilter? If not, simulate a LBUTTONDOWN * message to bring up the popup. */ if (lParam != 0x7FFFFFFFL) { if (_GetKeyState(((ppopupmenu->fRightButton) ? VK_RBUTTON : VK_LBUTTON)) >= 0) { /* * We think the mouse button should be down but the call to get key * state says different so we need to get outta menu mode. This * happens if clicking on the menu causes a sys modal message box to * come up before we can enter this stuff. For example, run * winfile, click on drive a: to see its tree. Activate some other * app, then open drive a: and activate winfile by clicking on the * menu. This causes a sys modal msg box to come up just before * entering menu mode. The user may have the mouse button up but * menu mode code thinks it is down... */ /* * Need to notify the app we are exiting menu mode because we told * it we were entering menu mode just before entering this function * in xxxSysCommand()... */ if (!ppopupmenu->fNoNotify) { ThreadLock(ppopupmenu->spwndNotify, &tlpwndT); xxxSendNotifyMessage(ppopupmenu->spwndNotify, WM_EXITMENULOOP, ((ppopupmenu->fIsTrackPopup && !ppopupmenu->fIsSysMenu) ? TRUE : FALSE), 0); ThreadUnlock(&tlpwndT); } goto ExitMenuLoop; } /* * Simulate a WM_LBUTTONDOWN message. */ if (!ppopupmenu->fIsTrackPopup) { /* * For TrackPopupMenus, we do it in the TrackPopupMenu function * itself so we don't want to do it again. */ if (!xxxMNStartState(ppopupmenu, MOUSEHOLD)) goto ExitMenuLoop; } if ((ppopupmenu->fRightButton)) { msg.message = (fDblClk ? WM_RBUTTONDBLCLK : WM_RBUTTONDOWN); } else { msg.message = (fDblClk ? WM_LBUTTONDBLCLK : WM_LBUTTONDOWN); } msg.lParam = lParam; msg.hwnd = HW(ppopupmenu->spwndPopupMenu); goto DoMsg; } while (pMenuState->fInsideMenuLoop) { /* * Is a message waiting for us? */ BOOL fPeek = xxxPeekMessage(&msg, NULL, 0, 0, PM_NOYIELD | PM_NOREMOVE); #ifdef DEBUG Validateppopupmenu(ppopupmenu); #endif if (fPeek) { /* * Bail if we have been forced out of menu loop */ if (ExitMenuLoop (pMenuState, ppopupmenu)) { goto ExitMenuLoop; } /* * Since we could have blocked in xxxWaitMessage (see last line * of loop) or xxxPeekMessage, reset the cached copy of * ptiCurrent()->pq: It could have changed if someone did a * DetachThreadInput() while we were away. */ if ((!ppopupmenu->fIsTrackPopup && pti->pq->spwndActive != ppopupmenu->spwndNotify && ((pti->pq->spwndActive == NULL) || !_IsChild(pti->pq->spwndActive, ppopupmenu->spwndNotify)))) { /* * End menu processing if we are no longer the active window. * This is needed in case a system modal dialog box pops up * while we are tracking the menu code for example. It also * helps out Tracer if a macro is executed while a menu is down. */ /* * Also, end menu processing if we think the mouse button is * down but it really isn't. (Happens if a sys modal dialog int * time dlg box comes up while we are in menu mode.) */ goto ExitMenuLoop; } if (ppopupmenu->fIsMenuBar && msg.message == WM_LBUTTONDBLCLK) { /* * Was the double click on the system menu or caption? */ hit = FindNCHit(ppopupmenu->spwndNotify, msg.lParam); if (hit == HTCAPTION) { PWND pwnd; PMENU pmenu; /* * Get the message out of the queue since we're gonna * process it. */ xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE); if (ExitMenuLoop (pMenuState, ppopupmenu)) { goto ExitMenuLoop; } else { pmenu = GetSysMenuHandle(ppopupmenu->spwndNotify); pwnd = ppopupmenu->spwndNotify; menuState = _GetMenuState(pmenu, SC_RESTORE & 0x0000FFF0, MF_BYCOMMAND); /* * Only send the sys command if the item is valid. If * the item doesn't exist or is disabled, then don't * post the syscommand. Note that for win2 apps, we * always send the sys command if it is a child window. * This is so hosebag apps can change the sys menu. */ if (!(menuState & MFS_GRAYED)) { _PostMessage(pwnd, WM_SYSCOMMAND, SC_RESTORE, 0); } /* * Get out of menu mode. */ goto ExitMenuLoop; } } else if (hit == HTMENU) { #ifdef NOJWAPPHACK if (!TestWF(ppopupmenu->spwndNotify, WFWIN31COMPAT) && GetAppCompatFlags(NULL) & GACF_SENDMENUDBLCLK) { /* * Hack for JustWrite. If double click on menu bar, get out * of menu mode, and don't swallow the double click * message. This way the message goes to the app and it can * process it however it pleases. */ goto ExitMenuLoop; } #endif } } fInQueue = (msg.message == WM_LBUTTONDOWN || msg.message == WM_RBUTTONDOWN || msg.message == WM_NCLBUTTONDOWN || msg.message == WM_NCRBUTTONDOWN); if (!fInQueue) { /* * Note that we call xxxPeekMessage() with the filter * set to the message we got from xxxPeekMessage() rather * than simply 0, 0. This prevents problems when * xxxPeekMessage() returns something like a WM_TIMER, * and after we get here to remove it a WM_LBUTTONDOWN, * or some higher-priority input message, gets in the * queue and gets removed accidently. Basically we want * to be sure we remove the right message in this case. * NT bug 3852 was caused by this problem. * Set the TIF_IGNOREPLAYBACKDELAY bit in case journal playback * is happening: this allows us to proceed even if the hookproc * incorrectly returns a delay now. The bit will be cleared if * this happens, so we can see why the Peek-Remove below fails. * Lotus' Freelance Graphics tutorial does such bad journalling */ pti->TIF_flags |= TIF_IGNOREPLAYBACKDELAY; if (!xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE)) { if (pti->TIF_flags & TIF_IGNOREPLAYBACKDELAY) { pti->TIF_flags &= ~TIF_IGNOREPLAYBACKDELAY; /* * It wasn't a bad journal playback: something else * made the previously peeked message disappear before * we could peek it again to remove it. */ RIPMSG1(RIP_WARNING, "Disappearing msg 0x%08lx", msg.message); goto ShowPopup; } } pti->TIF_flags &= ~TIF_IGNOREPLAYBACKDELAY; } if (!_CallMsgFilter(&msg, MSGF_MENU)) { DoMsg: xxxHandleMenuMessages(&msg, pMenuState, ppopupmenu); #ifdef DEBUG Validateppopupmenu(ppopupmenu); #endif if (ExitMenuLoop (pMenuState, ppopupmenu)) { goto ExitMenuLoop; } if ((pti->pq->QF_flags & QF_ACTIVATIONCHANGE) && pMenuState->fInsideMenuLoop) { /* * Run away and exit menu mode if another window has become * active while a menu was up. */ RIPMSG0(RIP_WARNING, "Exiting menu mode: another window activated"); goto ExitMenuLoop; } if (pti->pq->spwndCapture != ppopupmenu->spwndNotify) { TL tlpwndT3; /* * We dispatched a message to the app while in menu mode, * but the app released the mouse capture when it never * owned it and now, we will cause menu mode to screw up * unless we fix ourselves up. Set the capture back to * what we set it to in StartMenuState. (WinWorks does this) * * Lotus Freelance demo programs depend on GetCapture * returning their hwnd when in menumode. */ xxxCapture(PtiCurrent(), ppopupmenu->spwndNotify, SCREEN_CAPTURE); ThreadLock(ppopupmenu->spwndNotify, &tlpwndT3); xxxSendMessage(ppopupmenu->spwndNotify, WM_SETCURSOR, (DWORD)HW(ppopupmenu->spwndNotify), MAKELONG(MSGF_MENU, 0)); ThreadUnlock(&tlpwndT3); } if (msg.message == WM_SYSTIMER || msg.message == WM_TIMER || msg.message == WM_PAINT) goto ShowPopup; } else { if (fInQueue) xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE); } /* * Reenable WM_ENTERIDLE messages. */ fSendIdle = TRUE; } else { ShowPopup: /* * Bail if we have been forced out of menu loop */ if (ExitMenuLoop (pMenuState, ppopupmenu)) { goto ExitMenuLoop; } /* * Is a non visible popup menu around waiting to be shown? */ if (ppopupmenu->spwndActivePopup && !TestWF(ppopupmenu->spwndActivePopup, WFVISIBLE)) { TL tlpwndT2; PWND spwndActivePopup = ppopupmenu->spwndActivePopup; /* * Lock this so it doesn't go away during either of these * calls. Don't rely on ppopupmenu->spwndActivePopup * remaining the same. */ ThreadLock(spwndActivePopup, &tlpwndT2); /* * Paint the owner window before the popup menu comes up so that * the proper bits are saved. */ if (ppopupmenu->spwndNotify) { ThreadLockAlways(ppopupmenu->spwndNotify, &tlpwndT); xxxUpdateWindow(ppopupmenu->spwndNotify); ThreadUnlock(&tlpwndT); } xxxSendMessage(spwndActivePopup, MN_SHOWPOPUPWINDOW, 0, 0); /* * This is needed so that popup menus are properly drawn on sys * modal dialog boxes. */ xxxUpdateWindow(spwndActivePopup); ThreadUnlock(&tlpwndT2); continue; } if (msg.message == WM_PAINT || msg.message == WM_TIMER) { /* * We don't want to send enter idle messages if we came here via * a goto ShowPopup on paint message because there may be other * paint messages for us to handle. Zero out the msg.message * field so that if PeekMessage returns null next time around, * this outdated WM_PAINT won't be left over in the message * struct. */ msg.message = 0; continue; } #ifdef MEMPHIS_MENU_ANIMATION if (MNAnimate(TRUE)) continue; #endif // MEMPHIS_MENU_ANIMATION /* * If a hierarchical popup has been destroyed, this is a * good time to flush ppmDelayedFree */ if (ppopupmenu->fFlushDelayedFree) { MNFlushDestroyedPopups (ppopupmenu, FALSE); ppopupmenu->fFlushDelayedFree = FALSE; } /* * We need to send the WM_ENTERIDLE message only the first time * there are no messages for us to process. Subsequent times we * need to yield via WaitMessage(). This will allow other tasks to * get some time while we have a menu down. */ if (fSendIdle) { if (ppopupmenu->spwndNotify != NULL) { ThreadLockAlways(ppopupmenu->spwndNotify, &tlpwndT); xxxSendMessage(ppopupmenu->spwndNotify, WM_ENTERIDLE, MSGF_MENU, (DWORD)HW(ppopupmenu->spwndActivePopup)); ThreadUnlock(&tlpwndT); } fSendIdle = FALSE; } else xxxWaitMessage(); } /* if (PeekMessage(&msg, NULL, 0, 0, PM_NOYIELD)) else */ } /* end while (fInsideMenuLoop) */ ExitMenuLoop: pMenuState->fInsideMenuLoop = FALSE; /* * Make sure that the menu has been ended/canceled */ if (ppopupmenu->fIsTrackPopup) { if (!ppopupmenu->fInCancel) { xxxMNCancel(ppopupmenu, 0, 0, 0); } } else { if (!pMenuState->fInEndMenu) { xxxEndMenu(pMenuState); } } xxxReleaseCapture(); // Throw in an extra peek here when we exit the menu loop to ensure that the input queue // for this thread gets unlocked if there is no more input left for him. xxxPeekMessage(&msg, NULL, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_NOYIELD | PM_NOREMOVE); return(pMenuState->cmdLast); } /* xxxMenuLoop() */