Files
itgmania212121/stepmania/src/InputFilter.cpp
T

381 lines
11 KiB
C++
Raw Normal View History

2003-02-16 04:01:45 +00:00
#include "global.h"
2002-04-16 17:31:00 +00:00
#include "InputFilter.h"
2002-05-01 19:14:55 +00:00
#include "RageLog.h"
2002-04-16 17:31:00 +00:00
#include "RageInput.h"
2005-06-06 18:30:12 +00:00
#include "RageUtil.h"
2003-07-26 21:58:48 +00:00
#include "RageThreads.h"
#include "Preference.h"
#include "Foreach.h"
2002-06-14 22:25:22 +00:00
#include <set>
2005-12-28 20:41:24 +00:00
struct ButtonState
{
ButtonState();
bool m_BeingHeld; // actual current state
bool m_bLastReportedHeld; // last state reported by Update()
2006-01-22 01:00:06 +00:00
RString m_sComment;
2005-12-28 20:41:24 +00:00
float m_fSecsHeld;
// Timestamp of m_BeingHeld changing.
RageTimer m_BeingHeldTime;
// The time that we actually reported the last event (used for debouncing).
RageTimer m_LastReportTime;
// The timestamp of the last reported change. Unlike m_BeingHeldTime, this
// value is debounced along with the input state. (This is the same as
// m_fSecsHeld, except this isn't affected by Update scaling.)
RageTimer m_LastInputTime;
2005-12-28 20:41:24 +00:00
};
namespace
{
/* Maintain a set of all interesting buttons: buttons which are being held
* down, or which were held down and need a RELEASE event. We use this to
* optimize InputFilter::Update, so we don't have to process every button
* we know about when most of them aren't in use. This set is protected
* by queuemutex. */
2005-12-28 20:41:24 +00:00
typedef map<DeviceInput, ButtonState> ButtonStateMap;
ButtonStateMap g_ButtonStates;
ButtonState &GetButtonState( const DeviceInput &di )
{
return g_ButtonStates[di];
}
DeviceInputList g_CurrentState;
set<DeviceInput> g_DisableRepeat;
}
2005-11-06 15:24:26 +00:00
/*
* Some input devices require debouncing. Do this on both press and release. After
* reporting a change in state, don't report another for the debounce period. If a
* button is reported pressed, report it. If the button is immediately reported
* released, wait a period before reporting it; if the button is repressed during
* that time, the release is never reported.
*
* The detail is important: if a button is pressed for 1ms and released, we must
* always report it, even if the debounce period is 10ms, since it might be a coin
* counter with a very short signal. The only time we discard events is if a button
* is pressed, released and then pressed again quickly.
*
* This delay in events is ordinarily not noticable, because the we report initial
* presses and releases immediately. However, if a real press is ever delayed,
* this won't cause timing problems, because the event timestamp is preserved.
*/
static Preference<float> g_fInputDebounceTime( "InputDebounceTime", 0 );
2002-04-16 17:31:00 +00:00
2002-05-19 01:59:48 +00:00
InputFilter* INPUTFILTER = NULL; // global and accessable from anywhere in our program
2002-04-16 17:31:00 +00:00
2006-09-13 02:59:05 +00:00
static const float TIME_BEFORE_REPEATS = 0.375f;
static const float REPEATS_PER_SEC = 8;
2006-09-13 02:49:05 +00:00
static float g_fTimeBeforeRepeats, g_fTimeBetweenRepeats;
2003-07-26 21:58:48 +00:00
InputFilter::InputFilter()
2003-06-22 04:38:05 +00:00
{
queuemutex = new RageMutex("InputFilter");
2003-09-03 21:49:11 +00:00
2003-07-26 22:29:34 +00:00
Reset();
ResetRepeatRate();
2003-07-26 21:58:48 +00:00
}
InputFilter::~InputFilter()
{
delete queuemutex;
2005-12-28 20:41:24 +00:00
g_ButtonStates.clear();
2003-06-22 04:38:05 +00:00
}
void InputFilter::Reset()
{
2006-09-26 21:04:43 +00:00
FOREACH_InputDevice( i )
2006-09-08 18:59:57 +00:00
ResetDevice( InputDevice(i) );
}
2006-09-13 03:01:51 +00:00
void InputFilter::SetRepeatRate( float fDelay, float fRepeatRate )
{
2006-09-13 02:49:05 +00:00
g_fTimeBeforeRepeats = fDelay;
g_fTimeBetweenRepeats = 1/fRepeatRate;
}
void InputFilter::ResetRepeatRate()
{
2006-09-13 03:01:51 +00:00
SetRepeatRate( TIME_BEFORE_REPEATS, REPEATS_PER_SEC );
}
2005-12-28 20:41:24 +00:00
ButtonState::ButtonState():
m_BeingHeldTime(RageZeroTimer),
m_LastReportTime(RageZeroTimer)
{
m_BeingHeld = false;
m_bLastReportedHeld = false;
m_fSecsHeld = 0;
}
2005-10-15 16:47:44 +00:00
void InputFilter::ButtonPressed( const DeviceInput &di, bool Down )
{
2003-09-03 21:49:11 +00:00
LockMut(*queuemutex);
2005-05-17 20:50:39 +00:00
if( di.ts.IsZero() )
LOG->Warn( "InputFilter::ButtonPressed: zero timestamp is invalid" );
2006-09-26 21:08:50 +00:00
ASSERT_M( di.device < NUM_InputDevice, ssprintf("%i", di.device) );
2005-12-28 09:53:03 +00:00
ASSERT_M( di.button < NUM_DeviceButton, ssprintf("%i", di.button) );
2005-06-08 06:29:48 +00:00
ButtonState &bs = GetButtonState( di );
2004-09-09 22:14:33 +00:00
if( bs.m_BeingHeld != Down )
{
/* Flush any delayed input, like Update() (in case Update() isn't being called). */
RageTimer now;
CheckButtonChange( bs, di, now );
bs.m_BeingHeld = Down;
bs.m_BeingHeldTime = di.ts;
/* Try to report presses immediately. */
CheckButtonChange( bs, di, now );
}
2003-09-03 21:49:11 +00:00
}
2006-01-22 01:00:06 +00:00
void InputFilter::SetButtonComment( const DeviceInput &di, const RString &sComment )
2005-05-08 09:19:35 +00:00
{
LockMut(*queuemutex);
ButtonState &bs = GetButtonState( di );
2005-05-08 09:19:35 +00:00
bs.m_sComment = sComment;
}
2006-09-08 18:59:57 +00:00
/* Release all buttons on the given device. */
void InputFilter::ResetDevice( InputDevice device )
{
LockMut(*queuemutex);
RageTimer now;
const ButtonStateMap ButtonStates( g_ButtonStates );
FOREACHM_CONST( DeviceInput, ButtonState, ButtonStates, b )
2006-09-08 18:59:57 +00:00
{
const DeviceInput &di = b->first;
if( di.device == device )
2006-09-15 22:52:05 +00:00
ButtonPressed( DeviceInput(device, di.button, 0, now), false );
2006-09-08 18:59:57 +00:00
}
}
/* Check for reportable presses. */
void InputFilter::CheckButtonChange( ButtonState &bs, DeviceInput di, const RageTimer &now )
{
if( bs.m_BeingHeld == bs.m_bLastReportedHeld )
return;
/* If the last IET_FIRST_PRESS or IET_RELEASE event was sent too recently,
2005-11-06 15:24:26 +00:00
* wait a while before sending it. */
if( now - bs.m_LastReportTime < g_fInputDebounceTime )
return;
bs.m_LastReportTime = now;
bs.m_bLastReportedHeld = bs.m_BeingHeld;
bs.m_fSecsHeld = 0;
bs.m_LastInputTime = bs.m_BeingHeldTime;
di.ts = bs.m_BeingHeldTime;
2006-09-08 01:11:57 +00:00
ReportButtonChange( di, bs.m_bLastReportedHeld? IET_FIRST_PRESS:IET_RELEASE );
if( !bs.m_bLastReportedHeld )
g_DisableRepeat.erase( di );
2006-09-08 01:11:57 +00:00
}
void InputFilter::ReportButtonChange( const DeviceInput &di, InputEventType t )
{
queue.push_back( InputEvent() );
InputEvent &ie = queue.back();
ie.type = t;
ie.di = di;
/*
* Include a list of all buttons that were pressed at the time of this event. We
* can create this efficiently using g_ButtonStates. Use a vector and not a
* map, for efficiency; most code will not use this information. Iterating over
* g_ButtonStates will be in DeviceInput order, so users can binary search this
* list (eg. std::lower_bound).
*/
MakeButtonStateList( ie.m_ButtonState );
g_CurrentState = ie.m_ButtonState;
}
void InputFilter::MakeButtonStateList( vector<DeviceInput> &aInputOut ) const
{
aInputOut.reserve( g_ButtonStates.size() );
for( ButtonStateMap::const_iterator it = g_ButtonStates.begin(); it != g_ButtonStates.end(); ++it )
{
const DeviceInput &di = it->first;
const ButtonState &bs = it->second;
if( !bs.m_bLastReportedHeld )
continue;
aInputOut.push_back( di );
aInputOut.back().ts = bs.m_LastInputTime;
}
}
2005-11-06 13:00:34 +00:00
void InputFilter::Update( float fDeltaTime )
2002-04-16 17:31:00 +00:00
{
2003-09-03 21:49:11 +00:00
RageTimer now;
2005-12-18 22:53:47 +00:00
INPUTMAN->Update();
2002-04-16 17:31:00 +00:00
2003-07-14 06:21:12 +00:00
/* Make sure that nothing gets inserted while we do this, to prevent
* things like "key pressed, key release, key repeat". */
2003-07-26 21:58:48 +00:00
LockMut(*queuemutex);
2003-07-14 06:21:12 +00:00
2006-09-15 23:58:23 +00:00
DeviceInput di( DEVICE_NONE, DeviceButton_Invalid, 1.0f, now );
2003-10-07 05:59:58 +00:00
2005-12-28 20:41:24 +00:00
vector<ButtonStateMap::iterator> ButtonsToErase;
FOREACHM( DeviceInput, ButtonState, g_ButtonStates, b )
2002-04-16 17:31:00 +00:00
{
2005-12-28 20:41:24 +00:00
di.device = b->first.device;
di.button = b->first.button;
ButtonState &bs = b->second;
2003-10-07 05:59:58 +00:00
/* Generate IET_FIRST_PRESS and IET_RELEASE events that were delayed. */
CheckButtonChange( bs, di, now );
2006-09-13 02:59:05 +00:00
/* Generate IET_REPEAT events. */
if( !bs.m_bLastReportedHeld )
{
2005-12-28 20:41:24 +00:00
// If the key isn't pressed, and hasn't been pressed for a while (so debouncing
// isn't interested in it), purge the entry.
if( now - bs.m_LastReportTime > g_fInputDebounceTime )
ButtonsToErase.push_back( b );
continue;
}
2003-09-03 21:49:11 +00:00
/* If repeats are disabled for this button, skip. */
if( g_DisableRepeat.find(di) != g_DisableRepeat.end() )
continue;
const float fOldHoldTime = bs.m_fSecsHeld;
bs.m_fSecsHeld += fDeltaTime;
const float fNewHoldTime = bs.m_fSecsHeld;
2002-04-16 17:31:00 +00:00
2006-09-13 03:04:04 +00:00
if( fNewHoldTime <= g_fTimeBeforeRepeats )
continue;
float fRepeatTime;
if( fOldHoldTime < g_fTimeBeforeRepeats )
{
fRepeatTime = g_fTimeBeforeRepeats;
}
else
{
2006-09-13 03:04:04 +00:00
float fAdjustedOldHoldTime = fOldHoldTime - g_fTimeBeforeRepeats;
float fAdjustedNewHoldTime = fNewHoldTime - g_fTimeBeforeRepeats;
if( int(fAdjustedOldHoldTime/g_fTimeBetweenRepeats) == int(fAdjustedNewHoldTime/g_fTimeBetweenRepeats) )
continue;
fRepeatTime = ftruncf( fNewHoldTime, g_fTimeBetweenRepeats );
2002-04-16 17:31:00 +00:00
}
2006-09-13 03:04:04 +00:00
/* Set the timestamp to the exact time of the repeat. This way,
* as long as tab/` aren't being used, the timestamp will always
* increase steadily during repeats. */
di.ts = bs.m_LastInputTime + fRepeatTime;
ReportButtonChange( di, IET_REPEAT );
2002-04-16 17:31:00 +00:00
}
2005-12-28 20:41:24 +00:00
FOREACH( ButtonStateMap::iterator, ButtonsToErase, it )
g_ButtonStates.erase( *it );
}
template<typename T, typename IT>
const T *FindItemBinarySearch( IT begin, IT end, const T &i )
{
IT it = lower_bound( begin, end, i );
if( it == end || *it != i )
return NULL;
return &*it;
}
2006-09-08 04:26:23 +00:00
bool InputFilter::IsBeingPressed( const DeviceInput &di, const DeviceInputList *pButtonState ) const
{
2005-12-28 20:41:24 +00:00
LockMut(*queuemutex);
if( pButtonState == NULL )
pButtonState = &g_CurrentState;
const DeviceInput *pDI = FindItemBinarySearch( pButtonState->begin(), pButtonState->end(), di );
return pDI != NULL;
}
2006-09-08 04:26:23 +00:00
float InputFilter::GetSecsHeld( const DeviceInput &di, const DeviceInputList *pButtonState ) const
{
2005-12-28 20:41:24 +00:00
LockMut(*queuemutex);
if( pButtonState == NULL )
pButtonState = &g_CurrentState;
const DeviceInput *pDI = FindItemBinarySearch( pButtonState->begin(), pButtonState->end(), di );
if( pDI == NULL )
return 0;
return pDI->ts.Ago();
}
2006-01-22 01:00:06 +00:00
RString InputFilter::GetButtonComment( const DeviceInput &di ) const
2005-05-08 09:19:35 +00:00
{
LockMut(*queuemutex);
return GetButtonState( di ).m_sComment;
2005-05-08 09:19:35 +00:00
}
2005-10-15 16:47:44 +00:00
void InputFilter::ResetKeyRepeat( const DeviceInput &di )
{
2005-12-28 20:41:24 +00:00
LockMut(*queuemutex);
GetButtonState( di ).m_fSecsHeld = 0;
}
/* Stop repeating the specified key until released. */
void InputFilter::RepeatStopKey( const DeviceInput &di )
{
LockMut(*queuemutex);
/* If the button is up, do nothing. */
ButtonState &bs = GetButtonState( di );
if( !bs.m_bLastReportedHeld )
return;
g_DisableRepeat.insert( di );
}
2006-09-07 23:58:46 +00:00
void InputFilter::GetInputEvents( vector<InputEvent> &array )
{
array.clear();
2003-07-26 21:58:48 +00:00
LockMut(*queuemutex);
array.swap( queue );
}
2004-06-08 01:24:17 +00:00
2006-09-08 18:54:01 +00:00
void InputFilter::GetPressedButtons( vector<DeviceInput> &array ) const
2005-12-28 20:41:24 +00:00
{
LockMut(*queuemutex);
2006-09-08 04:26:23 +00:00
array = g_CurrentState;
2005-12-28 20:41:24 +00:00
}
2004-06-08 01:24:17 +00:00
/*
* (c) 2001-2004 Chris Danford
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/