mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2025-12-16 12:08:36 +00:00
481 lines
12 KiB
C++
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
|