/* File: cloop.c (created 12/27/93, JKH) * * Copyright 1994 by Hilgraeve Inc. -- Monroe, MI * All rights reserved * * $Revision: 2 $ * $Date: 3/16/01 4:28p $ */ #include #pragma hdrstop #include // #define DEBUGSTR #include #include #include #include #include #include #include #if defined(CHARACTER_TRANSLATION) #include #endif #if defined(INCL_VTUTF8) #include #include #endif #include "cloop.h" #include "cloop.hh" #include "tchar.h" #include #if defined(INCL_VTUTF8) BOOL DoUTF8 = FALSE; #endif /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * FUNCTION: CLoop * * DESCRIPTION: * This function comprises the 'engine' of the engine thread. Its action * is controlled by a series of control bits that are set and cleared * by calls to CLoopRcvControl and CLoopControl * * ARGUMENTS: * pstCLoop -- Handle returned from CLoopCreateHandle * * RETURNS: * nothing */ DWORD WINAPI CLoop(LPVOID pvData) { ST_CLOOP * const pstCLoop = (ST_CLOOP *)pvData; HCOM hCom; int nRcvCount; TCHAR chData = (TCHAR)0; ECHAR echData = (ECHAR)0; HSESSION hSession; HEMU hEmu; KEY_T keyOut; ST_FCHAIN *pstCurrent; CHAINFUNC pfChainFunc; int fValidChar; #if defined(CHARACTER_TRANSLATION) HHTRANSLATE hTrans; #endif int nCount; int nIndx; CHAR chBuffer[32]; /* Yes, it is supposed to be a CHAR */ int mc; #if defined(INCL_VTUTF8) HHEMU hhEmu; UCHAR UTF8Buffer[32*3]; // Each UTF-8 character can be 3 characters in length. WCHAR UNICODEBuffer[32]; TCHAR DBCSBuffer[32]; memset(UTF8Buffer, 0, sizeof(UTF8Buffer)); memset(UNICODEBuffer, 0, sizeof(UNICODEBuffer)); memset(DBCSBuffer, 0, sizeof(DBCSBuffer)); #endif // INCL_VTUTF8 DBGOUT_NORMAL("In CLoop(%lX)\r\n", pstCLoop,0,0,0,0); assert(pstCLoop); EnterCriticalSection(&pstCLoop->csect); hSession = pstCLoop->hSession; hCom = pstCLoop->hCom; hEmu = pstCLoop->hEmu; #if defined(CHARACTER_TRANSLATION) hTrans = (HHTRANSLATE)sessQueryTranslateHdl(pstCLoop->hSession); #endif assert(hSession); assert(hCom); assert(hEmu); DBGOUT_NORMAL("CLoop got critical section\r\n", 0,0,0,0,0); while (!bittest(pstCLoop->afControl, CLOOP_TERMINATE)) { DWORD dwRet; // Normally, this CLoop function owns the critical section // controlling the CLoop data. We give it up at least once each // time through the loop to give other threads a change to call // control functions. LeaveCriticalSection(&pstCLoop->csect); // Block if there is nothing to do DBGOUT_YIELD("W+ ", 0,0,0,0,0); dwRet = WaitForMultipleObjects(DIM(pstCLoop->ahEvents), pstCLoop->ahEvents, FALSE, INFINITE); DBGOUT_YIELD("W(0x%x)", dwRet,0,0,0,0); EnterCriticalSection(&pstCLoop->csect); DBGOUT_YIELD(": ", 0,0,0,0,0); // See if a transfer has been initiated if (bittest(pstCLoop->afControl, CLOOP_TRANSFER_READY)) { DBGOUT_NORMAL("CLoop calling xfer\r\n", 0,0,0,0,0); LeaveCriticalSection(&pstCLoop->csect); xfrDoTransfer(sessQueryXferHdl(hSession)); DBGOUT_NORMAL("CLoop back from xfer\r\n", 0,0,0,0,0); EnterCriticalSection(&pstCLoop->csect); CLoopControl((HCLOOP)pstCLoop, CLOOP_CLEAR, CLOOP_TRANSFER_READY); } // If there is input waiting and we are not blocked from receiving if (!pstCLoop->afRcvBlocked) { // Give priority to receiving over sending since normally // we will receive more data than we send (CR becomes CR LF) // and since we have more control over the send rate // TODO: implement priority setting by adjusting rcv count for (nRcvCount = 10; nRcvCount--; ) { fValidChar = FALSE; if (mComRcvChar(hCom, &chData)) { DBGOUT_NORMAL("Recieved Char\r\n", 0,0,0,0,0); // Check for force to 7-bit ASCII if (pstCLoop->stWorkSettings.fASCII7) { chData &= 0x7F; } nCount = 0; #if defined(INCL_VTUTF8) // // Do the UTF-8 conversions here if this is a VT-UTF8 // emulator. // hhEmu = (HHEMU)hEmu; if (hhEmu != NULL && hhEmu->stUserSettings.nEmuId == EMU_VTUTF8) { int iUTF8BufferLength = sizeof(UTF8Buffer); int iUNICODEBufferLength = sizeof(UNICODEBuffer); int iDBCSBufferLength = sizeof(DBCSBuffer); DoUTF8 = TRUE; if (TranslateUTF8ToDBCS(chData, UTF8Buffer, iUTF8BufferLength, UNICODEBuffer, iUNICODEBufferLength, DBCSBuffer, iDBCSBufferLength) == TRUE) { // // Now copy the DBCS buffer back into the chBuffer // character array that is used later. // StrCharCopyN(chBuffer, DBCSBuffer, sizeof(chBuffer)); nCount = StrCharGetByteCount(chBuffer); // // NULL out the Existing buffer. // memset(UTF8Buffer, 0, sizeof(UTF8Buffer)); memset(UNICODEBuffer, 0, sizeof(UNICODEBuffer)); memset(DBCSBuffer, 0, sizeof(DBCSBuffer)); } else { nCount = -1; } } if (hhEmu != NULL && hhEmu->stUserSettings.nEmuId != EMU_VTUTF8) { DoUTF8 = FALSE; #endif //INCL_VTUTF8 if ( nCount >= 0 ) { #if defined(CHARACTER_TRANSLATION) LeaveCriticalSection(&pstCLoop->csect); hTrans->pfnIn(hTrans->pDllHandle, chData, &nCount, sizeof(chBuffer), chBuffer); EnterCriticalSection(&pstCLoop->csect); #else chBuffer[0] = chData; nCount = 1; #endif } #if defined(INCL_VTUTF8) } #endif for (nIndx = 0; nIndx < nCount; nIndx++) { if (pstCLoop->fDoMBCS #if defined(INCL_VTUTF8) && (hhEmu != NULL && hhEmu->stUserSettings.nEmuId != EMU_VTUTF8)) { #else ) { #endif if (pstCLoop->cLeadByte) { chData = 0; echData = chBuffer[nIndx] & 0xFF; // There must be a macro for this somewhere echData |= (ECHAR)(pstCLoop->cLeadByte << 8); pstCLoop->cLeadByte = 0; fValidChar = TRUE; } else { if (IsDBCSLeadByte(chBuffer[nIndx])) { pstCLoop->cLeadByte = chBuffer[nIndx]; } else { echData = chBuffer[nIndx] & 0xFF; fValidChar = TRUE; } } } // if (fDoMBCS) else { echData = ETEXT(chBuffer[nIndx]); fValidChar = TRUE; #if defined(INCL_VTUTF8) if (hhEmu != NULL && hhEmu->stUserSettings.nEmuId == EMU_VTUTF8) { memcpy(&echData, &chBuffer[nIndx], sizeof(ECHAR)); fValidChar = TRUE; mc = 0; nIndx +=1; } #endif } // else (fDoMBCS) if (fValidChar) { #if FALSE // Moved up above 11-Jan-95 DLW // Check for force to 7-bit ASCII if (pstCLoop->stWorkSettings.fASCII7) echData &= 0x7F; #endif // Character translation/stripping //*if ((chData = pstCLoop->panFltrIn[chData]) == -1) //* continue; // walk remote input function chain if it exists if (pstCLoop->fRmtChain) { pstCLoop->pstRmtChainNext = pstCLoop->pstRmtChain; while (pstCLoop->pstRmtChainNext) { pstCurrent = pstCLoop->pstRmtChainNext; pstCLoop->pstRmtChainNext = pstCurrent->pstNext; pfChainFunc = pstCurrent->pfFunc; LeaveCriticalSection(&pstCLoop->csect); mc = (*pfChainFunc)(chData, pstCurrent->pvUserData); EnterCriticalSection(&pstCLoop->csect); if ( mc == CLOOP_DISCARD ) break; } } if ( mc != CLOOP_DISCARD ) { pstCLoop->fDataReceived = TRUE; CLoopCharIn(pstCLoop, echData); } if (pstCLoop->afRcvBlocked) { /* * This is necessary because a script may be * checking input and then stopping after it * finds what it is looking for. If the loop * count is still possitive, the remaining * characters will "slip thru" before this * loop terminates. * This won't break in any obvious way, so be * careful to preserve this feature if this * loop is rewritten. * * TODO: when scripts are added, make sure this * works correctly with character translation. */ // Receiving has been blocked, // make sure any received // data gets displayed. LeaveCriticalSection(&pstCLoop->csect); emuComDone(hEmu); EnterCriticalSection(&pstCLoop->csect); break; } } } // // Clear the temporary input character buffer. // as the buffer has been copied into echData. // REV: 03/06/2001 // memset(chBuffer, 0, sizeof(chBuffer)); } else { CLoopRcvControl((HCLOOP)pstCLoop, CLOOP_SUSPEND, CLOOP_RB_NODATA); if (pstCLoop->fDataReceived) { // Notify emulator that there is a pause in the // stream of received data LeaveCriticalSection(&pstCLoop->csect); emuComDone(hEmu); EnterCriticalSection(&pstCLoop->csect); // If we had a delay timer already running, kill it if (pstCLoop->htimerRcvDelay) TimerDestroy(&pstCLoop->htimerRcvDelay); // The stream of incoming data has stopped, set a // timer so we can tell if it stops long enough to // do cursor tracking etc. if (TimerCreate(sessQueryTimerMux(pstCLoop->hSession), &pstCLoop->htimerRcvDelay, CLOOP_TRACKING_DELAY, /* pstCLoop->pfRcvDelay, */ CLoopRcvDelayProc, (void *)pstCLoop) != TIMER_OK) { pstCLoop->htimerRcvDelay = (HTIMER)0; assert(FALSE); } pstCLoop->fDataReceived = FALSE; } break; } } } // Check for outgoing data. if (!pstCLoop->afSndBlocked) { // (Taken verbatim from HA5G, by JMH 03-22-96): // This change was added to fix a deadlock problem. While sending // and receiving data simultaneously at high speed, it was // possible for us to issue a handshake stop at the same time // we would receive one from the host. Our code would wait // in the ComSendBufr call and stop processing. Because of this, // we would not be processing incoming data so we would never // clear the handshake stop we had issued to the other end. If // the other end was caught in the same state -- deadlock. // This test slows our text transmission down. We should // redesgin our transmission model to fix this. jkh, 1/19/95 // if (CLoopGetNextOutput(pstCLoop, &keyOut)) DEADWOOD:jmh 03-22-96 if (ComSndBufrBusy(hCom) == COM_BUSY) { // UNDO:REV 03/01/2001 I believe this is where the problem with slow text file transfer is occurring! // //We need to unlock cloop around this sleep so that calls to // emuKbdIn and emuDataIn, because may call the com // thread, which may be stuck waiting to access cloop. // LeaveCriticalSection(&pstCLoop->csect); Sleep(10); // jkh 04/29/1998 don't peg CPU /* * This is necessary because a script may be * checking input and then stopping after it * finds what it is looking for. If the loop * count is still possitive, the remaining * characters will "slip thru" before this * loop terminates. * This won't break in any obvious way, so be * careful to preserve this feature if this * loop is rewritten. * * TODO: when scripts are added, make sure this * works correctly with character translation. */ // Receiving has been blocked, // make sure any received // data gets displayed. // REV: 03/05/2001 Make sure the COM driver knows we're done. //emuComDone(hEmu); EnterCriticalSection(&pstCLoop->csect); } else if (CLoopGetNextOutput(pstCLoop, &keyOut)) { // Check for tab expansion //DbgOutStr("C", 0,0,0,0,0); if (keyOut == TEXT('\t') && pstCLoop->stWorkSettings.fExpandTabsOut && pstCLoop->stWorkSettings.nTabSizeOut) { //* int i; //* POINT pt; //* mEmuGetCursorPos( //* mGetEmulatorHdl(pstCLoop->hSession), //* &pt); //* i = pstCLoop->stWorkSettings.usTabSizeOut - //* ((pt.x + 1) % pstCLoop->stWorkSettings.usTabSizeOut); //* while (i-- > 0) //* (VOID)mEmuKbdIn(hEmu, (KEY_T)TEXT(' '), FALSE); } else { //jmh 03-22-96 We need to unlock cloop around calls to // emuKbdIn and emuDataIn, because they call the com // thread, which may be stuck waiting to access cloop. // LeaveCriticalSection(&pstCLoop->csect); emuKbdIn(hEmu, keyOut, FALSE); EnterCriticalSection(&pstCLoop->csect); } if (keyOut == TEXT('\r')) { if (pstCLoop->stWorkSettings.fLineWait) { CLoopSndControl((HCLOOP)pstCLoop, CLOOP_SUSPEND, CLOOP_SB_LINEWAIT); } if (pstCLoop->stWorkSettings.fSendCRLF) { //DEADWOOD:jkh 8/18/97 // Can't use CLoopSend here because the '\n' is placed // behind any other codes waiting in the output queue. // This caused all LF codes in a text file send to be // sent AFTER the whole file, rather than after each CR // CLoopSend((HCLOOP) pstCLoop, TEXT("\n"), 1, 0); LeaveCriticalSection(&pstCLoop->csect); emuKbdIn(hEmu, (KEY_T)'\n', FALSE); EnterCriticalSection(&pstCLoop->csect); #if 0 //DEADWOOD: RDE 20AUG98 MPT Fixed a local echo problem down in // CloopCharIn which then caused an extra linefeed // to be displayed on the local terminal. if (pstCLoop->stWorkSettings.fLocalEcho && !pstCLoop->fSuppressDsp) { //jmh 03-22-96 We need to unlock cloop around calls to // emuKbdIn and emuDataIn, because they call the com // thread, which may be stuck waiting to access cloop. // LeaveCriticalSection(&pstCLoop->csect); emuDataIn(hEmu, TEXT('\n')); emuComDone(hEmu); EnterCriticalSection(&pstCLoop->csect); } #endif } if (pstCLoop->stWorkSettings.nLineDelay) { if (TimerCreate( sessQueryTimerMux(pstCLoop->hSession), &pstCLoop->htimerCharDelay, pstCLoop->stWorkSettings.nLineDelay, pstCLoop->pfCharDelay, (void *)pstCLoop) == TIMER_OK) CLoopSndControl((HCLOOP)pstCLoop, CLOOP_SUSPEND, CLOOP_SB_DELAY); } } else { if (pstCLoop->stWorkSettings.nCharDelay) { if (TimerCreate( sessQueryTimerMux(pstCLoop->hSession), &pstCLoop->htimerCharDelay, pstCLoop->stWorkSettings.nCharDelay, pstCLoop->pfCharDelay, (void *)pstCLoop) == TIMER_OK) CLoopSndControl((HCLOOP)pstCLoop, CLOOP_SUSPEND, CLOOP_SB_DELAY); } } //* if (pstCLoop->lpLearn) //* { //* CLearnSendChar(pstCLoop->lpLearn, (METACHAR)keyOut); //* } } } // Make sure any buffered output waiting gets sent. //* ComSendPush(pstCLoop->hCom); } DBGOUT_NORMAL("Leaving CLoop(%lX)\r\n", pstCLoop,0,0,0,0); return 0; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * FUNCTION: CLoopCharIn * * DESCRIPTION: * Does rec eive processing for characters received from the remote system * * ARGUMENTS: * pstCLoop -- Handle returned from CLoopCreateHandle * mc -- Character to be processed. * * RETURNS: * nothing */ void CLoopCharIn(ST_CLOOP *pstCLoop, ECHAR chIn) { // Check for echoplex if (pstCLoop->stWorkSettings.fEchoplex) { //mpt:1-27-98 converting echars to tchar was converting // the character to a single byte. Consequently, // echoing of a dbcs character was only echoing // the second byte of the character. if ( (pstCLoop->fDoMBCS) && (chIn > 255) ) { //send first byte ComSendCharNow(pstCLoop->hCom, (TCHAR) (chIn >> 8)); //send second byte ComSendCharNow(pstCLoop->hCom, (TCHAR) (chIn & 0xFF)); } else { ComSendCharNow(pstCLoop->hCom, (TCHAR) chIn); } if (chIn == ETEXT('\r')) { ComSendCharNow(pstCLoop->hCom, TEXT('\n')); } } if (pstCLoop->stWorkSettings.fLineWait && chIn == pstCLoop->stWorkSettings.chWaitChar) CLoopSndControl((HCLOOP)pstCLoop, CLOOP_RESUME, CLOOP_SB_LINEWAIT); // Display character in normal or image mode if (!pstCLoop->fSuppressDsp) { // DbgOutStr("Dsp %02X (%c)\r\n", chIn, chIn, 0,0,0); emuDataIn(pstCLoop->hEmu, chIn); if (chIn == ETEXT('\r') && pstCLoop->stWorkSettings.fAddLF) emuDataIn(pstCLoop->hEmu, ETEXT('\n')); } } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * FUNCTION: * CLoopCharOut * * DESCRIPTION: * Called to send a character out the port with processing by the CLoop * routines. Note that this differs from CLoopSend. * * ARGUMENTS: * * * RETURNS: * */ void CLoopCharOut(HCLOOP hCLoop, TCHAR chOut) { ST_CLOOP * const pstCLoop = (ST_CLOOP *)hCLoop; int fCharValid = FALSE; ECHAR echOut = (ECHAR)0; #if defined(INCL_VTUTF8) int nUTF8Count; int nUTF8Index; HHEMU hhEmu = (HHEMU)pstCLoop->hSession; CHAR chUTF8Buffer[32]; /* Yes, it is supposed to be a CHAR */ #endif // INCL_VTUTF8 #if defined(CHARACTER_TRANSLATION) HHTRANSLATE hTrans; int nCount; int nIndx; CHAR chBuffer[32]; /* Yes, it is supposed to be a CHAR */ hTrans = (HHTRANSLATE)sessQueryTranslateHdl(pstCLoop->hSession); (*hTrans->pfnOut)(hTrans->pDllHandle, chOut, &nCount, sizeof(chBuffer), chBuffer); for (nIndx = 0; nIndx < nCount; nIndx += 1) { chOut = chBuffer[nIndx]; #endif //CHARACTER_TRANSLATION #if defined(INCL_VTUTF8) if (hhEmu != NULL && hhEmu->stUserSettings.nEmuId == EMU_VTUTF8) { UCHAR UTF8Buffer[32*3]; // Each UTF-8 character can be 3 characters in length. WCHAR UNICODEBuffer[32]; TCHAR DBCSBuffer[32]; int iUTF8BufferLength = sizeof(UTF8Buffer); int iUNICODEBufferLength = sizeof(UNICODEBuffer); int iDBCSBufferLength = sizeof(DBCSBuffer); memset(UTF8Buffer, 0, sizeof(UTF8Buffer)); memset(UNICODEBuffer, 0, sizeof(UNICODEBuffer)); memset(DBCSBuffer, 0, sizeof(DBCSBuffer)); #if 0 // TODO:REV 03/16/2001 Add the code to convert the character to UTF-8 format here. if (TranslateDBCSToUTF8(DBCSBuffer, iDBCSBufferLength, UNICODEBuffer, iUNICODEBufferLength, UTF8Buffer, iUTF8BufferLength) == TRUE) { // // Now copy the DBCS buffer back into the chBuffer // character array that is used later. // StrCharCopyN(chUTF8Buffer, UTF8Buffer, sizeof(chUTF8Buffer)); nUTF8Count = StrCharGetByteCount(chUTF8Buffer); // // NULL out the Existing buffer. // memset(UTF8Buffer, 0, sizeof(UTF8Buffer)); memset(UNICODEBuffer, 0, sizeof(UNICODEBuffer)); memset(DBCSBuffer, 0, sizeof(DBCSBuffer)); } else #endif { nUTF8Index = 0; nUTF8Count = 1; chUTF8Buffer[0] = chOut; } } else { nUTF8Index = 0; nUTF8Count = 1; chUTF8Buffer[0] = chOut; } for (nUTF8Index = 0; nUTF8Index < nUTF8Count; nUTF8Index++) { chOut = chUTF8Buffer[nUTF8Index]; #endif ComSendCharNow(pstCLoop->hCom, chOut); if (pstCLoop->stWorkSettings.fLocalEcho && !pstCLoop->fSuppressDsp) { if (pstCLoop->fDoMBCS) { if (pstCLoop->cLocalEchoLeadByte) { echOut = chOut & 0xFF; echOut |= (ECHAR)(pstCLoop->cLocalEchoLeadByte << 8); pstCLoop->cLocalEchoLeadByte = 0; fCharValid = TRUE; } else { if (IsDBCSLeadByte(chOut)) { pstCLoop->cLocalEchoLeadByte = chOut; } else { echOut = chOut & 0xFF; fCharValid = TRUE; } } } else { echOut = (ECHAR)chOut; fCharValid = TRUE; } if (fCharValid) { emuDataIn(pstCLoop->hEmu, echOut); emuComDone(pstCLoop->hEmu); } } #if defined(CHARACTER_TRANSLATION) } #endif #if defined(INCL_VTUTF8) } #endif } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * FUNCTION: * CLoopBufrOut * * DESCRIPTION: * Called to send a buffer of characters out the port with processing by the CLoop * routines. Note that this differs from CLoopSend. * * ARGUMENTS: * * * RETURNS: * void * */ void CLoopBufrOut(HCLOOP hCLoop, TCHAR *pchOut, int nLen) { ST_CLOOP * const pstCLoop = (ST_CLOOP *)hCLoop; int fCharValid = FALSE; while (nLen--) { ComSendChar(pstCLoop->hCom, *pchOut); // place chars in comsend buffer if (pstCLoop->stWorkSettings.fLocalEcho && !pstCLoop->fSuppressDsp) { if (pstCLoop->fDoMBCS) { if (pstCLoop->cLocalEchoLeadByte != 0) { *pchOut |= (TCHAR)(pstCLoop->cLocalEchoLeadByte << 8); pstCLoop->cLocalEchoLeadByte = 0; fCharValid = TRUE; } else { if (IsDBCSLeadByte(*pchOut)) { pstCLoop->cLocalEchoLeadByte = *pchOut; } else { fCharValid = TRUE; } } } else { fCharValid = TRUE; } if (fCharValid) { emuDataIn(pstCLoop->hEmu, *pchOut); emuComDone(pstCLoop->hEmu); } } ++pchOut; } ComSendPush(pstCLoop->hCom); } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * FUNCTION: CLoopRcvDelayProc * * DESCRIPTION: * * * ARGUMENTS: * * * RETURNS: * */ /* ARGSUSED */ void CALLBACK CLoopRcvDelayProc(void *pvData, long lSince) { // This timer was set the last time data input stopped. If no data // has been received since, notify the display routines so they can // do cursor tracking or whatever silly thing it is they do. ST_CLOOP *pstCLoop = (ST_CLOOP *)pvData; EnterCriticalSection(&pstCLoop->csect); if (!pstCLoop->fDataReceived) { emuTrackingNotify(pstCLoop->hEmu); } TimerDestroy(&pstCLoop->htimerRcvDelay); (void)&lSince; // avoid compiler warnings LeaveCriticalSection(&pstCLoop->csect); } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- * FUNCTION: CLoopCharDelayProc * * DESCRIPTION: * * * ARGUMENTS: * * * RETURNS: * */ /* ARGSUSED */ void CALLBACK CLoopCharDelayProc(void *pvData, long lSince) { ST_CLOOP *pstCLoop = (ST_CLOOP *)pvData; EnterCriticalSection(&pstCLoop->csect); TimerDestroy(&pstCLoop->htimerCharDelay); CLoopSndControl((HCLOOP)pstCLoop, CLOOP_RESUME, CLOOP_SB_DELAY); LeaveCriticalSection(&pstCLoop->csect); (void)&lSince; // avoid compiler warnings }