hostile-takeover/game/event.cpp
2016-01-03 23:19:26 -08:00

481 lines
12 KiB
C++

#include "ht.h"
namespace wi {
EventMgr gevm;
EventMgr::EventMgr()
{
Init();
}
EventMgr::~EventMgr()
{
}
bool EventMgr::PeekEvent(Event *pevt, long ctWait)
{
if (m_fPeekKeep) {
*pevt = m_evtPeek;
return true;
}
if (!GetEvent(pevt, ctWait))
return false;
m_fPeekKeep = true;
m_evtPeek = *pevt;
return true;
}
// TUNE:
const long kctPenHoldDelay = 50;
bool EventMgr::GetEvent(Event *pevt, long ctWait, bool fCheckPaints)
{
#if defined(WIN) && !defined(CE) && defined(DEBUG)
Assert(_CrtCheckMemory());
#endif
// Only needed for Tapwave bluetooth
#ifdef PIL
if (gfTapwave && gptra != NULL) {
extern void HostTrackServiceCallsPerGetEvent();
HostTrackServiceCallsPerGetEvent();
}
#endif
// Force stopping once we've started. Retain modifiers to the appStopEvent if there are any
if (m_fAppStopping) {
if ((m_fPeekKeep) && (m_evtPeek.eType == appStopEvent)) {
*pevt = m_evtPeek;
} else {
pevt->eType = appStopEvent;
}
return true;
}
// Service sound right away
HostSoundServiceProc();
long tStart = gtimm.GetTickCount();
long ctTimerNext = 0;
// Check the peek keep - if one exists, return it
if (m_fPeekKeep) {
*pevt = m_evtPeek;
m_fPeekKeep = false;
return true;
}
// GetEvent w/wait + timer scan and dispatch loop
long ctTotal = ctWait;
long ctElapsed = 0;
long tCurrent = tStart;
while (true) {
// Check for posted events
if (m_cevt != 0) {
*pevt = m_aevtQ[0];
m_cevt--;
memmove(m_aevtQ, m_aevtQ + 1, m_cevt * ELEMENTSIZE(m_aevtQ));
if (pevt->eType == appStopEvent)
m_fAppStopping = true;
return true;
}
// Which is shorter, the timer wait or the asked wait?
long ctNext;
if (ctWait == -1) {
ctNext = ctTimerNext;
} else {
ctNext = ctTotal - ctElapsed;
if (ctNext < 0)
ctNext = 0;
if (ctTimerNext != -1 && ctTimerNext < ctNext) {
ctNext = ctTimerNext;
}
}
// Don't wait any longer than it takes for a penHoldEvent to be produced
if (m_fTimingPenHold) {
int ctPenHoldRemaining = (int)(kctPenHoldDelay - (tCurrent - m_tPenDown));
if (ctPenHoldRemaining < 0)
ctPenHoldRemaining = 0;
if (ctNext == -1 || ctPenHoldRemaining < ctNext)
ctNext = ctPenHoldRemaining;
}
// This sleep might be too long for sound needs. Ask the sound manager
if (ctNext != 0)
ctNext = gsndm.FilterSleepTicks(ctNext);
// Get an event / wait this long (or don't wait at all if a redraw is
// pending)
bool fHaveEvent = false;
//#define PAINT_BEFORE_INPUT
#ifdef PAINT_BEFORE_INPUT
if ((m_wfRedraw & (kfRedrawDirty | kfRedrawBeforeInput)) != (kfRedrawDirty | kfRedrawBeforeInput)) {
#endif
#ifdef __CPU_68K
// In general we like to call HostGetEvent so it can sleep,
// do I/O processing, whatever. However, on 68K Palms SysGetEvent
// is very slow and feeds us a lot of pen events the processing of
// which also slows the game so on 68K devices we throttle our
// calls to GetEvent some.
if (abs(tCurrent - m_tLastMoveEvent) >
(long)m_ctMoveEventInterval) {
#else
{
#endif
// Get the event from the host. The host doesn't coalesce
// messages, but does mark messages has being coalesce
// candidates, based on what was in the queue when the post
// occured. On the game side, all pen events go into a history
// buffer for calculating flick vectors. Then, if the message
// was marked as a coalesced message, it is ignored.
while (true) {
fHaveEvent = HostGetEvent(pevt, (m_wfRedraw & kfRedrawDirty) ? 0 : ctNext);
if (!fHaveEvent)
break;
UpdatePenHistory(pevt);
if (!(pevt->ff & kfEvtCoalesce))
break;
// There should always be a non-coalesced pen event
// following a coalesced one, but just in case...
ctNext = 0;
}
}
#ifdef PAINT_BEFORE_INPUT
}
#endif
// Manage the production of penHoldEvents
if (fHaveEvent) {
switch (pevt->eType) {
case penDownEvent:
// Hold events are generated when the pen is pressed down and
// held for a specified amount of time before being released.
m_fTimingPenHold = true;
m_tPenDown = gtimm.GetTickCount();
m_xHold = pevt->x;
m_yHold = pevt->y;
break;
case penMoveEvent:
// If the pen moves by more than N pixels in any dimension then
// we'll assume the player isn't trying to tap-hold and cancel
// it.
if (m_fTimingPenHold) {
// TUNE: penhold distance threshold
if (abs(pevt->x - m_xHold) > 7 ||
abs(pevt->y - m_yHold) > 7) {
m_fTimingPenHold = false;
}
}
break;
case penUpEvent:
// When the pen is released the timer is halted.
m_fTimingPenHold = false;
break;
}
} else {
// penHoldEvents are only produced when no other events are queued.
// This seems OK.
if (m_fTimingPenHold && tCurrent - m_tPenDown >= kctPenHoldDelay) {
m_fTimingPenHold = false;
fHaveEvent = true;
pevt->eType = penHoldEvent;
pevt->idf = 0; // UNDONE: could have post-CookEvent/penDown below stash idf for use here
pevt->x = m_xHold;
pevt->y = m_yHold;
}
}
// Check for timers to dispatch
HostSoundServiceProc();
tCurrent = gtimm.GetTickCount();
if ((m_wfRedraw & (kfRedrawDirty | kfRedrawBeforeTimer)) !=
(kfRedrawDirty | kfRedrawBeforeTimer)) {
ctTimerNext = gtimm.ScanDispatch(tCurrent);
}
// If we have an event, cook it and return it
if (fHaveEvent) {
// Cook and return
gpmfrmm->CookEvent(pevt);
switch (pevt->eType) {
case appStopEvent:
m_fAppStopping = true;
break;
case penMoveEvent:
m_tLastMoveEvent = tCurrent;
// fall through
case penDownEvent:
// Raise redraw priority while the pen is down
m_wfRedraw |= kfRedrawBeforeInput;
break;
}
return true;
}
// If asked to check for paints, do it here
// Sometimes this is not done from here, like during game simulation
if (fCheckPaints)
gpmfrmm->CheckSetRedrawDirty();
// If redraw is needed, now is the time
if (m_wfRedraw & (kfRedrawDirty | kfRedrawMax)) {
if (m_wfRedraw & kfRedrawMax) {
m_wfRedraw &= ~(kfRedrawBeforeTimer | kfRedrawBeforeInput);
} else {
m_wfRedraw &= ~(kfRedrawDirty | kfRedrawBeforeTimer | kfRedrawBeforeInput);
}
pevt->eType = gamePaintEvent;
return true;
}
// If the user timeout expired, return timeout
tCurrent = gtimm.GetTickCount();
ctElapsed = tCurrent - tStart;
if (ctWait != -1) {
if (ctElapsed >= ctWait)
return false;
}
}
}
void EventMgr::UpdatePenHistory(Event *pevt)
{
bool fPenEvent = false;
switch (pevt->eType) {
case penDownEvent:
case penMoveEvent:
case penUpEvent:
fPenEvent = true;
if (m_fPenHistoryInitialized) {
m_aevtPen1History[m_ievtPen1Next] = *pevt;
m_ievtPen1Next = (m_ievtPen1Next + 1) & (kcevtPenHistory - 1);
}
break;
case penDownEvent2:
case penMoveEvent2:
case penUpEvent2:
fPenEvent = true;
if (m_fPenHistoryInitialized) {
m_aevtPen2History[m_ievtPen2Next] = *pevt;
m_ievtPen2Next = (m_ievtPen2Next + 1) & (kcevtPenHistory - 1);
}
break;
}
if (fPenEvent && !m_fPenHistoryInitialized) {
for (int n = 0; n < ARRAYSIZE(m_aevtPen1History); n++) {
m_aevtPen1History[n] = *pevt;
m_aevtPen2History[n] = *pevt;
}
m_ievtPen1Next = 0;
m_ievtPen2Next = 0;
m_fPenHistoryInitialized = true;
}
}
bool EventMgr::GetFlickVector(int nPen, FlickVector *pfliv)
{
memset(pfliv, 0, sizeof(*pfliv));
long msNow = HostGetMillisecondCount();
Point ptStart;
if (!QueryPenHistory(nPen, msNow - kcmsFlickQuantum, &ptStart))
return false;
Point ptEnd;
if (!QueryPenHistory(nPen, msNow, &ptEnd))
return false;
pfliv->dx = ptEnd.x - ptStart.x;
pfliv->dy = ptEnd.y - ptStart.y;
pfliv->cms = kcmsFlickQuantum;
return true;
}
bool EventMgr::QueryPenHistory(int nPen, long ms, Point *ppt)
{
if (!m_fPenHistoryInitialized) {
return false;
}
int ievtFirst, ievtLast;
Event *aevt;
if (nPen == 1) {
aevt = m_aevtPen1History;
ievtFirst = m_ievtPen1Next;
ievtLast = (m_ievtPen1Next - 1) & (kcevtPenHistory - 1);
} else if (nPen == 2) {
aevt = m_aevtPen2History;
ievtFirst = m_ievtPen2Next;
ievtLast = (m_ievtPen2Next - 1) & (kcevtPenHistory - 1);
} else {
return false;
}
// Find event at time t
Event *pevtOnOrBefore = NULL;
Event *pevtOnOrAfter = NULL;
for (int ievt = ievtFirst; true; ievt = (ievt + 1) & (kcevtPenHistory - 1)) {
Event *pevt = &aevt[ievt];
if (pevt->ms == ms) {
pevtOnOrBefore = pevt;
pevtOnOrAfter = NULL;
break;
}
if (pevt->ms < ms) {
pevtOnOrBefore = pevt;
}
if (pevt->ms > ms) {
pevtOnOrAfter = pevt;
break;
}
if (ievt == ievtLast)
break;
}
if (pevtOnOrBefore == NULL && pevtOnOrAfter != NULL) {
ppt->x = pevtOnOrAfter->x;
ppt->y = pevtOnOrAfter->y;
return true;
}
if (pevtOnOrBefore != NULL && pevtOnOrAfter == NULL) {
ppt->x = pevtOnOrBefore->x;
ppt->y = pevtOnOrBefore->y;
return true;
}
// Calculate a point between these two events
float flPercent = (float)(ms - pevtOnOrBefore->ms) / (float)(pevtOnOrAfter->ms - pevtOnOrBefore->ms);
ppt->x = pevtOnOrBefore->x + (int)(flPercent * (pevtOnOrAfter->x - pevtOnOrBefore->x) + 0.5f);
ppt->y = pevtOnOrBefore->y + (int)(flPercent * (pevtOnOrAfter->y - pevtOnOrBefore->y) + 0.5f);
return true;
}
void EventMgr::Init()
{
m_fPeekKeep = false;
m_cevt = 0;
m_fAppStopping = false;
m_wfRedraw = 0;
m_fTimingPenHold = false;
m_ctMoveEventInterval = 0;
m_nctMoveEventFraction = 0;
m_fPenHistoryInitialized = false;
}
bool EventMgr::DispatchEvent(Event *pevt)
{
Form *pfrm = gpmfrmm->GetFormPtr(pevt->idf);
// Trace("EventMgr::DispatchEvent: evt 0x%0lx, eType %d, idf %d", pevt, pevt->eType, pevt->idf);
if (pfrm != NULL)
return pfrm->EventProc(pevt);
return false;
}
void EventMgr::PostEvent(Event *pevt, bool fCoalesce)
{
if (fCoalesce) {
for (int nT = m_cevt - 1; nT >= 0; nT--) {
if (m_aevtQ[nT].eType == pevt->eType) {
m_aevtQ[nT] = *pevt;
return;
}
}
if (m_fPeekKeep && m_evtPeek.eType == pevt->eType) {
m_evtPeek = *pevt;
return;
}
}
Assert(m_cevt < kcevtPostMax);
m_aevtQ[m_cevt] = *pevt;
m_cevt++;
}
void EventMgr::PostEvent(int eType, bool fCoalesce)
{
Event evt;
memset(&evt, 0, sizeof(evt));
evt.eType = eType;
PostEvent(&evt);
}
void EventMgr::SetPenEventInterval(word ctInterval)
{
// Set the interval at which we'll accept pen events per second. This prevents the OS
// from flooding unneeded events through the system.
if (ctInterval >= 50)
return;
// Allow the interval to increase instantly but decrease more slowly. This way we get
// instant game response and the performance doesn't ping-pong back and forth as the
// interval gets adjusted.
word nctFractionNew = ctInterval << 4;
if (nctFractionNew > m_nctMoveEventFraction) {
m_nctMoveEventFraction = nctFractionNew;
} else {
m_nctMoveEventFraction -= (m_nctMoveEventFraction - nctFractionNew) / 4;
}
// Divide the final by 2 so that the mouse event rate is twice the frame rate. This way
// there will always be a new pen position available, at the cost of some event overhead.
m_ctMoveEventInterval = (m_nctMoveEventFraction + 16) >> 5;
}
void EventMgr::ClearAppStopping()
{
// Swallow the appStopEvent if it exists. Eating events may set fAppStopping again
// so clear it constantly
Event evt;
while (GetEvent(&evt, 0, false))
m_fAppStopping = false;
// Shouldn't be needed but just in case it is set and GetEvent returns false
m_fAppStopping = false;
}
} // namespace wi