You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
343 lines
9.2 KiB
343 lines
9.2 KiB
#include "stdafx.h"
|
|
#include "Motion.h"
|
|
#include "Action.h"
|
|
|
|
|
|
/***************************************************************************\
|
|
*****************************************************************************
|
|
*
|
|
* class Action
|
|
*
|
|
*****************************************************************************
|
|
\***************************************************************************/
|
|
|
|
//---------------------------------------------------------------------------
|
|
Action::Action()
|
|
{
|
|
m_cEventsInPeriod = 0;
|
|
m_cPeriods = 0;
|
|
m_fPresent = FALSE;
|
|
m_fDestroy = FALSE;
|
|
m_flLastProgress = 0.0f;
|
|
m_pThread = ::GetThread();
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
Action::~Action()
|
|
{
|
|
AssertMsg(!m_DEBUG_fInFire, "Can't destroy if should be locked for Schedule::xwFireNL()\n");
|
|
|
|
//
|
|
// We need to notify the application that it needs to cleanup, so we
|
|
// can't take the Scheduler lock.
|
|
//
|
|
// We also need to directly setup the "Last" members so that xwFireNL()
|
|
// will signal the application that the Action is being destroyed.
|
|
//
|
|
|
|
xwFireFinalNL();
|
|
|
|
if (m_plstParent != NULL) {
|
|
m_plstParent->Unlink(this);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* Action::xwDeleteHandle
|
|
*
|
|
* xwDeleteHandle() is called when the application calls ::DeleteHandle() on
|
|
* an object.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
BOOL
|
|
Action::xwDeleteHandle()
|
|
{
|
|
if (m_fDestroy) {
|
|
PromptInvalid("Can not call DeleteHandle() on an Action after the final callback");
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// When the user calls DeleteHandle() on an Action, we need to remove it
|
|
// from the Scheduler's lists. It may also already be in a callback list
|
|
// currently be processed, but that is okay. The important thing is to
|
|
// Unlock() the Scheduler's reference so that we can properly be destroyed.
|
|
//
|
|
|
|
if (m_plstParent) {
|
|
//
|
|
// Still in a Scheduler list, so the Scheduler still has a valid lock.
|
|
//
|
|
|
|
m_plstParent->Unlink(this);
|
|
m_plstParent = NULL;
|
|
VerifyMsg(xwUnlock(), "Should still be valid after the Scheduler Unlock()");
|
|
}
|
|
|
|
|
|
//
|
|
// If the object isn't destroyed, we need to clear out the callback
|
|
// right now since the object that is being called is no longer valid.
|
|
//
|
|
// Unlike Gadgets, we actually clear out the callback here since Actions
|
|
// are usually "simple" objects without complicated callbacks. They do
|
|
// guarantee that to receive all callbacks before being destroyed. They
|
|
// only guarentee to receive a "destroy" message when the Action is
|
|
// actually destroyed.
|
|
//
|
|
|
|
BOOL fValid = BaseObject::xwDeleteHandle();
|
|
if (fValid) {
|
|
//
|
|
// The Action may still be valid if it is in a Scheduler callback list.
|
|
//
|
|
|
|
xwFireFinalNL();
|
|
}
|
|
|
|
return fValid;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
Action *
|
|
Action::Build(
|
|
IN GList<Action> * plstParent, // List containing Action
|
|
IN const GMA_ACTION * pma, // Timing information
|
|
IN DWORD dwCurTick, // Current time
|
|
IN BOOL fPresent) // Action is already present
|
|
{
|
|
AssertMsg(plstParent != NULL, "Must specify a parent");
|
|
|
|
Action * pact = ClientNew(Action);
|
|
if (pact == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Copy the Action information over and determine the amount of time between
|
|
// timeslices.
|
|
//
|
|
// For the default time (0), use 10 ms.
|
|
// For no time (-1), use 0 ms.
|
|
//
|
|
|
|
pact->m_ma = *pma;
|
|
pact->m_dwLastProcessTick = dwCurTick;
|
|
|
|
if (pact->m_ma.dwPause == 0) {
|
|
pact->m_ma.dwPause = 10;
|
|
} else if (pact->m_ma.dwPause == (DWORD) -1) {
|
|
pact->m_ma.dwPause = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// When creating the new Action, it needs to be in the future or the
|
|
// beginning part of the Action may be clipped. However, if it is actually
|
|
// in the present, set the starting time to right now so that it doesn't
|
|
// get delayed.
|
|
//
|
|
|
|
pact->m_fSingleShot = IsPresentTime(pma->flDuration);
|
|
pact->m_plstParent = plstParent;
|
|
|
|
if (fPresent) {
|
|
pact->ResetPresent(dwCurTick);
|
|
} else {
|
|
pact->ResetFuture(dwCurTick, TRUE);
|
|
}
|
|
pact->SetPresent(fPresent);
|
|
|
|
return pact;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
void
|
|
Action::Process(DWORD dwCurTime, BOOL * pfFinishedPeriod, BOOL * pfFire)
|
|
{
|
|
AssertWritePtr(pfFinishedPeriod);
|
|
AssertWritePtr(pfFire);
|
|
|
|
#if DBG
|
|
m_DEBUG_fFireValid = FALSE;
|
|
#endif // DBG
|
|
|
|
*pfFire = FALSE;
|
|
*pfFinishedPeriod = FALSE;
|
|
|
|
if (IsPresent()) {
|
|
//
|
|
// Processing a present action, so determine our progress through the
|
|
// action and callback.
|
|
//
|
|
|
|
if (m_fSingleShot) {
|
|
//
|
|
// Single-shot Action
|
|
//
|
|
|
|
m_dwLastProcessTick = dwCurTime;
|
|
m_flLastProgress = 1.0f;
|
|
*pfFinishedPeriod = TRUE;
|
|
*pfFire = TRUE;
|
|
} else {
|
|
//
|
|
// Continuous Action
|
|
//
|
|
|
|
int nElapsed = ComputePastTickDelta(dwCurTime, m_dwStartTick);
|
|
float flProgress = (nElapsed / m_ma.flDuration) / 1000.0f;
|
|
if (flProgress > 1.0f) {
|
|
flProgress = 1.0f;
|
|
}
|
|
|
|
int nDelta = ComputeTickDelta(dwCurTime, m_dwLastProcessTick + GetPauseTimeOut());
|
|
*pfFire = nDelta > 0; // Full pause has elapsed
|
|
if (*pfFire) {
|
|
m_dwLastProcessTick = dwCurTime;
|
|
}
|
|
|
|
*pfFinishedPeriod = (flProgress >= 1.0f);
|
|
m_flLastProgress = flProgress;
|
|
}
|
|
|
|
#if DBG
|
|
m_DEBUG_fFireValid = *pfFire;
|
|
#endif // DBG
|
|
AssertMsg(!m_fDestroy, "Should not be marked as being destroyed yet");
|
|
} else {
|
|
//
|
|
// Processing a future action, so advance counters
|
|
//
|
|
|
|
int nElapsed = ComputeTickDelta(dwCurTime, m_dwStartTick);
|
|
if (nElapsed >= 0) {
|
|
//
|
|
// The action is now ready to be executed.
|
|
//
|
|
|
|
*pfFinishedPeriod = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
void
|
|
Action::xwFireNL()
|
|
{
|
|
//
|
|
// NOTE: xwFireNL() expects that m_flLastProgress and m_fDestroy were
|
|
// properly filled in from the last call to Process().
|
|
//
|
|
|
|
AssertMsg(m_DEBUG_fFireValid, "Only valid if last call to Process() returned fFire");
|
|
|
|
GMA_ACTIONINFO mai;
|
|
mai.hact = (HACTION) GetHandle();
|
|
mai.pvData = m_ma.pvData;
|
|
mai.flDuration = m_ma.flDuration;
|
|
mai.flProgress = m_flLastProgress;
|
|
mai.cEvent = m_cEventsInPeriod++;
|
|
mai.fFinished = m_fDestroy;
|
|
|
|
#if DBG_CHECK_CALLBACKS
|
|
BEGIN_CALLBACK()
|
|
#endif
|
|
|
|
__try
|
|
{
|
|
(m_ma.pfnProc)(&mai);
|
|
}
|
|
__except(StdExceptionFilter(GetExceptionInformation()))
|
|
{
|
|
ExitProcess(GetExceptionCode());
|
|
}
|
|
|
|
#if DBG_CHECK_CALLBACKS
|
|
END_CALLBACK()
|
|
#endif
|
|
|
|
|
|
//
|
|
// If the Action is complete and has not been manually destroyed, destroy
|
|
// it now. The Action will still exist until the Scheduler actually
|
|
// Unlock()'s it.
|
|
//
|
|
|
|
if ((!m_fDestroy) && m_fDeleteInFire) {
|
|
AssertMsg(IsComplete(), "Must be complete to destroy");
|
|
VerifyMsg(xwDeleteHandle(), "Should still be valid.");
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* Action::xwFireFinalNL
|
|
*
|
|
* xwFireFinalNL() fires the final notification to the Action. Any
|
|
* notifications that the Action fires after this point will be sent to
|
|
* EmptyActionProc(). This function can be called both by the destructor as
|
|
* well as xwDeleteHandle() when the object doesn't finally go away.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
void
|
|
Action::xwFireFinalNL()
|
|
{
|
|
if (m_fDestroy) {
|
|
return;
|
|
}
|
|
|
|
#if DBG
|
|
m_DEBUG_fFireValid = TRUE;
|
|
#endif // DBG
|
|
|
|
m_flLastProgress = 1.0f;
|
|
m_fDestroy = TRUE;
|
|
|
|
xwFireNL();
|
|
|
|
m_ma.pfnProc = EmptyActionProc;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
void
|
|
Action::EndPeriod()
|
|
{
|
|
if ((m_ma.cRepeat != 0) && (m_ma.cRepeat != (UINT) -1)) {
|
|
m_ma.cRepeat--;
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
void
|
|
Action::EmptyActionProc(GMA_ACTIONINFO * pmai)
|
|
{
|
|
UNREFERENCED_PARAMETER(pmai);
|
|
}
|
|
|
|
|
|
#if DBG
|
|
|
|
//---------------------------------------------------------------------------
|
|
void
|
|
Action::DEBUG_MarkInFire(BOOL fInFire)
|
|
{
|
|
AssertMsg(!fInFire != !m_DEBUG_fInFire, "Must be switching states");
|
|
|
|
m_DEBUG_fInFire = fInFire;
|
|
}
|
|
|
|
#endif // DBG
|