From be6d3d4043064ec7e27ba9a348d7f12dcd593562 Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Fri, 8 Sep 2006 04:03:14 +0000 Subject: [PATCH] Include the complete input state with each input report. This only deals with buttons being held, so won't cause performance issues. This deals with a general problem of the input layer: you can query the current state of the inputs, but inputs are received in a queue, causing inconsistencies. This is why pressing enter during a load, then holding Alt after pressing it, causes the game to switch fullscreen after the load finishes; checking for "alt-enter" should be checking if Alt was pressed when Enter was pressed, not whether it's pressed right now. Similarly, CodeDetector has difficulty handling a "Right-Left" and a "Left-Right" code simultaneously. If both buttons are pressed quickly, it needs to check whether Right was pressed at the time Left was reported, not at the time EnteredCode was called. --- stepmania/src/InputEventPlus.h | 1 + stepmania/src/InputFilter.cpp | 54 ++++++++++++++++++++++++++++++---- stepmania/src/InputFilter.h | 9 ++++-- stepmania/src/StepMania.cpp | 1 + 4 files changed, 58 insertions(+), 7 deletions(-) diff --git a/stepmania/src/InputEventPlus.h b/stepmania/src/InputEventPlus.h index e1960d0f25..31134ff57d 100644 --- a/stepmania/src/InputEventPlus.h +++ b/stepmania/src/InputEventPlus.h @@ -17,6 +17,7 @@ public: MenuInput MenuI; StyleInput StyleI; MultiPlayer mp; + DeviceInputList InputList; }; #endif diff --git a/stepmania/src/InputFilter.cpp b/stepmania/src/InputFilter.cpp index ebf3fc96a8..de166066d3 100644 --- a/stepmania/src/InputFilter.cpp +++ b/stepmania/src/InputFilter.cpp @@ -42,6 +42,8 @@ namespace { return g_ButtonStates[di]; } + + DeviceInputList g_CurrentState; } /* @@ -190,6 +192,30 @@ void InputFilter::ReportButtonChange( const DeviceInput &di, InputEventType t ) 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 &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; + } } void InputFilter::Update( float fDeltaTime ) @@ -279,16 +305,34 @@ void InputFilter::Update( float fDeltaTime ) g_ButtonStates.erase( *it ); } -bool InputFilter::IsBeingPressed( const DeviceInput &di ) +template +const T *FindItemBinarySearch( IT begin, IT end, const T &i ) { - LockMut(*queuemutex); - return GetButtonState( di ).m_bLastReportedHeld; + IT it = lower_bound( begin, end, i ); + if( it == end || *it != i ) + return NULL; + + return &*it; } -float InputFilter::GetSecsHeld( const DeviceInput &di ) +bool InputFilter::IsBeingPressed( const DeviceInput &di, const DeviceInputList *pButtonState ) { LockMut(*queuemutex); - return GetButtonState( di ).m_LastInputTime.Ago(); + if( pButtonState == NULL ) + pButtonState = &g_CurrentState; + const DeviceInput *pDI = FindItemBinarySearch( pButtonState->begin(), pButtonState->end(), di ); + return pDI != NULL; +} + +float InputFilter::GetSecsHeld( const DeviceInput &di, const DeviceInputList *pButtonState ) +{ + 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(); } RString InputFilter::GetButtonComment( const DeviceInput &di ) const diff --git a/stepmania/src/InputFilter.h b/stepmania/src/InputFilter.h index 41c97fc3b4..bb18a2514c 100644 --- a/stepmania/src/InputFilter.h +++ b/stepmania/src/InputFilter.h @@ -32,6 +32,9 @@ struct InputEvent DeviceInput di; InputEventType type; + + /* A list of all buttons that were pressed at the time of this event: */ + DeviceInputList m_ButtonState; }; class RageMutex; @@ -52,8 +55,9 @@ public: void ResetRepeatRate(); void ResetKeyRepeat( const DeviceInput &di ); - bool IsBeingPressed( const DeviceInput &di ); - float GetSecsHeld( const DeviceInput &di ); + // If aButtonState is NULL, use the last reported state. + bool IsBeingPressed( const DeviceInput &di, const DeviceInputList *pButtonState = NULL ); + float GetSecsHeld( const DeviceInput &di, const DeviceInputList *pButtonState = NULL ); RString GetButtonComment( const DeviceInput &di ) const; void GetInputEvents( vector &aEventOut ); @@ -62,6 +66,7 @@ public: private: void CheckButtonChange( ButtonState &bs, DeviceInput di, const RageTimer &now ); void ReportButtonChange( const DeviceInput &di, InputEventType t ); + void MakeButtonStateList( vector &aInputOut ) const; vector queue; RageMutex *queuemutex; diff --git a/stepmania/src/StepMania.cpp b/stepmania/src/StepMania.cpp index 073fc501fa..8fdc63c320 100644 --- a/stepmania/src/StepMania.cpp +++ b/stepmania/src/StepMania.cpp @@ -1383,6 +1383,7 @@ void HandleInputEvents(float fDeltaTime) InputEventPlus input; input.DeviceI = ieArray[i].di; input.type = ieArray[i].type; + swap( input.InputList, ieArray[i].m_ButtonState ); // hack for testing with only one joytick if( input.DeviceI.IsJoystick() )