From 1df0a43b3f4250b2f90f145b507e8bddf3c8b599 Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Mon, 19 Sep 2005 07:09:55 +0000 Subject: [PATCH] implement generic high-level input debouncing --- stepmania/src/InputFilter.cpp | 54 +++++++++++++++++++++++++++++------ stepmania/src/InputFilter.h | 11 +++++-- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/stepmania/src/InputFilter.cpp b/stepmania/src/InputFilter.cpp index 85b936851d..62372aecb1 100644 --- a/stepmania/src/InputFilter.cpp +++ b/stepmania/src/InputFilter.cpp @@ -4,7 +4,25 @@ #include "RageInput.h" #include "RageUtil.h" #include "RageThreads.h" +#include "Preference.h" +/* + * 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 g_fInputDebounceTime( "InputDebounceTime", 0 ); InputFilter* INPUTFILTER = NULL; // global and accessable from anywhere in our program @@ -49,6 +67,15 @@ void InputFilter::ResetRepeatRate() SetRepeatRate( TIME_BEFORE_SLOW_REPEATS, SLOW_REPEATS_PER_SEC, TIME_BEFORE_FAST_REPEATS, FAST_REPEATS_PER_SEC ); } +InputFilter::ButtonState::ButtonState(): + m_BeingHeldTime(RageZeroTimer), + m_LastReportTime(RageZeroTimer) +{ + m_BeingHeld = false; + m_bLastReportedHeld = false; + m_fSecsHeld = m_Level = m_LastLevel = 0; +} + void InputFilter::ButtonPressed( DeviceInput di, bool Down ) { LockMut(*queuemutex); @@ -71,13 +98,11 @@ void InputFilter::ButtonPressed( DeviceInput di, bool Down ) bs.m_Level = di.level; - if( bs.m_BeingHeld == Down ) - return; - - bs.m_BeingHeld = Down; - bs.m_fSecsHeld = 0; - - queue.push_back( InputEvent(di,Down? IET_FIRST_PRESS:IET_RELEASE) ); + if( bs.m_BeingHeld != Down ) + { + bs.m_BeingHeld = Down; + bs.m_BeingHeldTime = di.ts; + } } void InputFilter::SetButtonComment( DeviceInput di, const CString &sComment ) @@ -129,6 +154,17 @@ void InputFilter::Update(float fDeltaTime) di.button = b; di.level = bs.m_Level; + /* Generate IET_FIRST_PRESS and IET_RELEASE events. */ + if( now - bs.m_LastReportTime >= g_fInputDebounceTime && bs.m_BeingHeld != bs.m_bLastReportedHeld ) + { + bs.m_LastReportTime = now; + bs.m_bLastReportedHeld = bs.m_BeingHeld; + bs.m_fSecsHeld = 0; + + di.ts = bs.m_BeingHeldTime; + queue.push_back( InputEvent(di,bs.m_bLastReportedHeld? IET_FIRST_PRESS:IET_RELEASE) ); + } + /* Generate IET_LEVEL_CHANGED events. */ if( bs.m_LastLevel != bs.m_Level && bs.m_Level != -1 ) { @@ -137,7 +173,7 @@ void InputFilter::Update(float fDeltaTime) } /* Generate IET_FAST_REPEAT and IET_SLOW_REPEAT events. */ - if( !bs.m_BeingHeld ) + if( !bs.m_bLastReportedHeld ) continue; const float fOldHoldTime = bs.m_fSecsHeld; @@ -170,7 +206,7 @@ void InputFilter::Update(float fDeltaTime) bool InputFilter::IsBeingPressed( DeviceInput di ) { - return m_ButtonState[di.device][di.button].m_BeingHeld; + return m_ButtonState[di.device][di.button].m_bLastReportedHeld; } float InputFilter::GetSecsHeld( DeviceInput di ) diff --git a/stepmania/src/InputFilter.h b/stepmania/src/InputFilter.h index 4f9502ee11..4b3c7735d3 100644 --- a/stepmania/src/InputFilter.h +++ b/stepmania/src/InputFilter.h @@ -63,11 +63,18 @@ public: private: struct ButtonState { - ButtonState() { m_BeingHeld = false; m_fSecsHeld = m_Level = m_LastLevel = 0; } - bool m_BeingHeld; + ButtonState(); + bool m_BeingHeld; // actual current state + bool m_bLastReportedHeld; // last state reported by Update() CString m_sComment; float m_fSecsHeld; float m_Level, m_LastLevel; + + // Timestamp of the last real event that came in: the time m_BeingHeld changed. + RageTimer m_BeingHeldTime; + + // The time that we actually reported the last event (used for debouncing). + RageTimer m_LastReportTime; }; ButtonState m_ButtonState[NUM_INPUT_DEVICES][MAX_DEVICE_BUTTONS];