/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    str.cpp

Abstract:

Author:

    Vlad Sadovsky   (vlads) 26-Jan-1997

Revision History:

    26-Jan-1997     VladS       added to Sti

--*/



#include "cplusinc.h"
#include "sticomm.h"

/* FlushInputQueue is a private routine to collect and dispatch all
 * messages in the input queue.  It returns TRUE if a WM_QUIT message
 * was detected in the queue, FALSE otherwise.
 */
BOOL FlushInputQueue(volatile DWORD *pidOtherThread)
{
    MSG msgTemp;
    while (PeekMessage(&msgTemp, NULL, 0, 0, PM_REMOVE)) {
        DispatchMessage(&msgTemp);

        // If we see a WM_QUIT in the queue, we need to do the same
        // sort of thing that a modal dialog does:  break out of our
        // waiting, and repost the WM_QUIT to the queue so that the
        // next message loop up in the app will also see it.  We also
        // post the message to the server thread's queue so that any
        // dialog stack displayed there will be destroyed as well.
        if (msgTemp.message == WM_QUIT) {
            if (pidOtherThread != NULL && *pidOtherThread != NULL) {
                PostThreadMessage(*pidOtherThread, msgTemp.message, msgTemp.wParam, msgTemp.lParam);
            }
            PostQuitMessage((int)msgTemp.wParam);
            return TRUE;
        }
    }
    return FALSE;
}


/* WaitAndYield() waits for the specified object using
 * MsgWaitForMultipleObjects.  If messages are received,
 * they are dispatched and waiting continues.  The return
 * value is the same as from MsgWaitForMultipleObjects.
 */
DWORD WaitAndYield(HANDLE hObject, DWORD dwTimeout, volatile DWORD *pidOtherThread /* = NULL */)
{
    DWORD dwTickCount, dwWakeReason, dwTemp;

    do {
        /* Flush any messages before we wait.  This is because
         * MsgWaitForMultipleObjects will only return when NEW
         * messages are put in the queue.
         */
        if (FlushInputQueue(pidOtherThread)) {
            dwWakeReason = WAIT_TIMEOUT;
            break;
        }

        // in case we handle messages, we want close to a true timeout
        if ((dwTimeout != 0) &&
            (dwTimeout != (DWORD)-1)) {
            // if we can timeout, store the current tick count
            // every time through
            dwTickCount = GetTickCount();
        }
        dwWakeReason = MsgWaitForMultipleObjects(1,
                                                 &hObject,
                                                 FALSE,
                                                 dwTimeout,
                                                 QS_ALLINPUT);
        // if we got a message, dispatch it, then try again
        if (dwWakeReason == 1) {
            // if we can timeout, see if we did before processing the message
            // that way, if we haven't timed out yet, we'll get at least one
            // more shot at the event
            if ((dwTimeout != 0) &&
                (dwTimeout != (DWORD)-1)) {
                if ((dwTemp = (GetTickCount()-dwTickCount)) >= dwTimeout) {
                    // if we timed out, make us drop through
                    dwWakeReason = WAIT_TIMEOUT;
                } else {
                    // subtract elapsed time from timeout and continue
                    // (we don't count time spent dispatching message)
                    dwTimeout -= dwTemp;
                }
            }
            if (FlushInputQueue(pidOtherThread)) {
                dwWakeReason = WAIT_TIMEOUT;
                break;
            }
        }
    } while (dwWakeReason == 1);

    return dwWakeReason;
}


/* WaitAndProcessSends is similar to WaitAndYield, but it only processes
 * SendMessage messages, not input messages.
 */
DWORD WaitAndProcessSends(HANDLE hObject, DWORD dwTimeout)
{
    DWORD dwWakeReason;

    do {
        dwWakeReason = MsgWaitForMultipleObjects(1,
                                                 &hObject,
                                                 FALSE,
                                                 dwTimeout,
                                                 QS_SENDMESSAGE);
        // if we got a message, yield, then try again
        if (dwWakeReason == 1) {
            MSG msgTemp;
            PeekMessage(&msgTemp, NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD);
        }
    } while (dwWakeReason == 1);

    return dwWakeReason;
}


