File ButtonMonitor.h¶
File List > external-docs > libDaisy > src > ui > ButtonMonitor.h
Go to the documentation of this file
Source Code¶
#pragma once
#include <stdint.h>
#include "UiEventQueue.h"
#include "../sys/system.h"
namespace daisy
{
template <typename BackendType, uint32_t numButtons>
class ButtonMonitor
{
public:
ButtonMonitor()
: queue_(nullptr),
backend_(nullptr),
timeout_(0),
doubleClickTimeout_(0),
retriggerTimeoutMs_(0),
retriggerPeriodMs_(0)
{
}
void Init(UiEventQueue& queueToAddEventsTo,
BackendType& backend,
uint16_t debounceTimeoutMs = 50,
uint32_t doubleClickTimeoutMs = 500,
uint32_t retriggerTimeoutMs = 2000,
uint32_t retriggerPeriodMs = 50)
{
queue_ = &queueToAddEventsTo;
backend_ = &backend;
timeout_ = debounceTimeoutMs;
doubleClickTimeout_ = doubleClickTimeoutMs;
retriggerTimeoutMs_ = retriggerTimeoutMs;
retriggerPeriodMs_ = retriggerPeriodMs;
for(uint32_t i = 0; i < numButtons; i++)
{
buttonStates_[i] = -timeout_; // starting in "released" state
lastClickTimes_[i] = 0;
lastRetriggerTimes_[i] = 0;
numSuccessiveClicks_[i] = 0;
}
lastCallSysTime_ = System::GetNow();
}
void Process()
{
const auto now = System::GetNow();
const auto timeDiff = now - lastCallSysTime_;
lastCallSysTime_ = now;
for(uint32_t i = 0; i < numButtons; i++)
ProcessButton(i, backend_->IsButtonPressed(i), timeDiff, now);
}
bool IsButtonPressed(uint16_t buttonId) const
{
if(buttonId >= numButtons)
return false;
else
return buttonStates_[buttonId] >= timeout_;
}
BackendType& GetBackend() { return backend_; }
uint16_t GetNumButtonsMonitored() const { return numButtons; }
private:
void ProcessButton(uint16_t id,
bool isPressed,
uint32_t timeInMsSinceLastCall,
uint32_t currentSystemTime)
{
// released or transitioning there...
if(buttonStates_[id] < 0)
{
if(!isPressed)
{
// transitioning?
if(buttonStates_[id] + 1 > -timeout_)
{
buttonStates_[id] -= timeInMsSinceLastCall;
if(buttonStates_[id] + 1 <= -timeout_)
queue_->AddButtonReleased(id);
}
}
// start transitioning towards "pressed"
else
{
buttonStates_[id] = 1;
// timeout could be set to "0" - no debouncing, send immediately.
if(buttonStates_[id] - 1 >= timeout_)
PostPhysicalButtonDownEvent(id, currentSystemTime);
}
}
else
{
if(isPressed)
{
// transitioning?
if(buttonStates_[id] - 1 < timeout_)
{
buttonStates_[id] += timeInMsSinceLastCall;
if(buttonStates_[id] - 1 >= timeout_)
PostPhysicalButtonDownEvent(id, currentSystemTime);
}
// already pressed - check retriggering if enabled
else if(retriggerTimeoutMs_ > 0)
{
const auto timeSincePress
= currentSystemTime - lastClickTimes_[id];
if(timeSincePress >= retriggerTimeoutMs_)
{
const auto timeSinceLastRetrigger
= currentSystemTime - lastRetriggerTimes_[id];
if(timeSinceLastRetrigger > retriggerPeriodMs_)
{
lastRetriggerTimes_[id] = currentSystemTime;
queue_->AddButtonPressed(
id, numSuccessiveClicks_[id], true);
}
}
}
}
// start transitioning towards "released"
else
{
buttonStates_[id] = -1;
// timeout could be set to "0" - no debouncing, send immediately.
if(buttonStates_[id] + 1 <= -timeout_)
queue_->AddButtonReleased(id);
}
}
}
void PostPhysicalButtonDownEvent(uint16_t id, uint32_t currentSystemTime)
{
const auto timeDiff = currentSystemTime - lastClickTimes_[id];
if(timeDiff <= doubleClickTimeout_)
numSuccessiveClicks_[id]++;
else
numSuccessiveClicks_[id] = 1;
lastClickTimes_[id] = currentSystemTime;
queue_->AddButtonPressed(id, numSuccessiveClicks_[id], false);
}
ButtonMonitor(const ButtonMonitor&) = delete;
ButtonMonitor& operator=(const ButtonMonitor&) = delete;
UiEventQueue* queue_;
BackendType* backend_;
uint16_t timeout_;
uint32_t doubleClickTimeout_;
uint32_t retriggerTimeoutMs_;
uint32_t retriggerPeriodMs_;
int16_t buttonStates_[numButtons]; // <= -timeout --> not pressed,
// >= timeout_ --> pressed
uint32_t lastClickTimes_[numButtons];
uint32_t lastRetriggerTimes_[numButtons];
uint8_t numSuccessiveClicks_[numButtons];
uint32_t lastCallSysTime_;
};
} // namespace daisy