44 static constexpr uint8_t CLICKABLE_FLAG_STABLE_PRESSED = 0x01U;
45 static constexpr uint8_t CLICKABLE_FLAG_CANDIDATE_PRESSED = 0x02U;
46 static constexpr uint8_t CLICKABLE_FLAG_DEBOUNCING = 0x04U;
47 static constexpr uint8_t CLICKABLE_FLAG_LONG_FIRED = 0x08U;
48 static constexpr uint8_t CLICKABLE_FLAG_SUPER_LONG_FIRED = 0x10U;
50#ifndef CONFIG_USE_FAST_CLICKABLES
51 const uint8_t pinNumber;
53 const uint8_t pinMask;
54 const volatile uint8_t *
const pinPort;
69 const uint8_t oldSREG = SREG;
76 uint16_t pressAge_ms = 0U;
77 uint8_t debounceAge_ms = 0U;
78#if defined(LSH_DEBUG) || defined(LSH_STATIC_CONFIG_RUNTIME_CHECKS)
79 uint8_t index = UINT8_MAX;
83 [[nodiscard]]
auto hasClickableFlag(uint8_t flag)
const noexcept ->
bool
85 return (this->flags & flag) != 0U;
88 void setClickableFlag(uint8_t flag,
bool enabled)
noexcept
96 this->flags &=
static_cast<uint8_t
>(~flag);
100 [[nodiscard]]
auto stablePressed()
const noexcept ->
bool
102 return this->hasClickableFlag(CLICKABLE_FLAG_STABLE_PRESSED);
105 [[nodiscard]]
auto candidatePressed()
const noexcept ->
bool
107 return this->hasClickableFlag(CLICKABLE_FLAG_CANDIDATE_PRESSED);
110 [[nodiscard]]
auto isDebouncing()
const noexcept ->
bool
112 return this->hasClickableFlag(CLICKABLE_FLAG_DEBOUNCING);
115 [[nodiscard]]
auto releaseDebouncePending()
const noexcept ->
bool
117 return this->isDebouncing() && this->stablePressed() && !this->candidatePressed();
120 void clearTimedActionFlags()
noexcept
122 this->flags &=
static_cast<uint8_t
>(~(CLICKABLE_FLAG_LONG_FIRED | CLICKABLE_FLAG_SUPER_LONG_FIRED));
125 void startDebounce(
bool candidatePressed)
noexcept
127 this->setClickableFlag(CLICKABLE_FLAG_DEBOUNCING,
true);
128 this->setClickableFlag(CLICKABLE_FLAG_CANDIDATE_PRESSED, candidatePressed);
129 this->debounceAge_ms = 0U;
134 this->setClickableFlag(CLICKABLE_FLAG_DEBOUNCING,
false);
135 this->debounceAge_ms = 0U;
136 this->setClickableFlag(CLICKABLE_FLAG_STABLE_PRESSED, pressed);
137 this->setClickableFlag(CLICKABLE_FLAG_CANDIDATE_PRESSED, pressed);
141 [[nodiscard]]
auto updateDebouncedEdge(
bool rawPressed, uint16_t elapsed_ms)
noexcept ->
constants::ClickResult
144 using constants::timings::CLICKABLE_DEBOUNCE_TIME_MS;
146 if (!this->isDebouncing())
148 if (rawPressed == this->stablePressed())
150 return ClickResult::NO_CLICK;
152 if (CLICKABLE_DEBOUNCE_TIME_MS == 0U)
154 return this->confirmDebouncedEdge(rawPressed);
156 this->startDebounce(rawPressed);
157 return ClickResult::NO_CLICK;
160 if (rawPressed != this->candidatePressed())
165 this->setClickableFlag(CLICKABLE_FLAG_DEBOUNCING,
false);
166 this->setClickableFlag(CLICKABLE_FLAG_CANDIDATE_PRESSED, this->stablePressed());
167 this->debounceAge_ms = 0U;
168 return ClickResult::NO_CLICK;
171 const uint16_t nextDebounceAge = timeUtils::addElapsedTimeSaturated(this->debounceAge_ms, elapsed_ms);
172 this->debounceAge_ms = nextDebounceAge > UINT8_MAX ? UINT8_MAX :
static_cast<uint8_t
>(nextDebounceAge);
173 if (this->debounceAge_ms >= CLICKABLE_DEBOUNCE_TIME_MS)
175 return this->confirmDebouncedEdge(rawPressed);
177 return ClickResult::NO_CLICK;
188 template <
bool StaticConfigKnown, u
int8_t StaticDetectionFlags, u
int16_t StaticLongClick_ms, u
int16_t StaticSuperLongClick_ms>
189 [[nodiscard]]
auto clickDetectionImpl(uint16_t elapsed_ms, uint8_t detectionFlags, uint16_t longClick_ms, uint16_t superLongClick_ms)
193 using namespace constants::clickDetection;
195 const bool wasStablePressed = this->stablePressed();
196 static_cast<void>(this->updateDebouncedEdge(this->
getState(), elapsed_ms));
197 const bool isStablePressed = this->stablePressed();
199 if (!wasStablePressed && isStablePressed)
201 this->pressAge_ms = 0U;
202 this->clearTimedActionFlags();
203 if constexpr (StaticConfigKnown)
205 if constexpr ((StaticDetectionFlags & QUICK_SHORT) != 0U)
207 return ClickResult::SHORT_CLICK_QUICK;
210 else if (hasFlag(detectionFlags, QUICK_SHORT))
212 return ClickResult::SHORT_CLICK_QUICK;
214 return ClickResult::NO_CLICK_KEEPING_CLICKED;
217 if (wasStablePressed && !isStablePressed)
219 const bool timedActionFired =
220 this->hasClickableFlag(CLICKABLE_FLAG_LONG_FIRED) || this->hasClickableFlag(CLICKABLE_FLAG_SUPER_LONG_FIRED);
221 this->pressAge_ms = 0U;
222 this->clearTimedActionFlags();
223 if constexpr (StaticConfigKnown)
225 if constexpr ((StaticDetectionFlags & QUICK_SHORT) != 0U)
227 return ClickResult::NO_CLICK;
229 if constexpr ((StaticDetectionFlags & SHORT_ENABLED) != 0U)
231 return timedActionFired ? ClickResult::NO_CLICK : ClickResult::SHORT_CLICK;
233 return ClickResult::NO_CLICK;
235 if (hasFlag(detectionFlags, QUICK_SHORT))
237 return ClickResult::NO_CLICK;
239 if (!timedActionFired)
241 return hasFlag(detectionFlags, SHORT_ENABLED) ? ClickResult::SHORT_CLICK : ClickResult::NO_CLICK_NOT_SHORT_CLICKABLE;
243 return ClickResult::NO_CLICK;
246 if (!isStablePressed)
248 return ClickResult::NO_CLICK;
251 if (this->releaseDebouncePending())
253 return ClickResult::NO_CLICK_KEEPING_CLICKED;
256 this->pressAge_ms = timeUtils::addElapsedTimeSaturated(this->pressAge_ms, elapsed_ms);
257 if constexpr (StaticConfigKnown)
259 if constexpr ((StaticDetectionFlags & LONG_ENABLED) != 0U)
261 if (!this->hasClickableFlag(CLICKABLE_FLAG_LONG_FIRED) && this->pressAge_ms >= StaticLongClick_ms)
263 this->setClickableFlag(CLICKABLE_FLAG_LONG_FIRED,
true);
264 return ClickResult::LONG_CLICK;
268 if constexpr ((StaticDetectionFlags & SUPER_LONG_ENABLED) != 0U)
270 if (!this->hasClickableFlag(CLICKABLE_FLAG_SUPER_LONG_FIRED) && this->pressAge_ms >= StaticSuperLongClick_ms)
272 this->setClickableFlag(CLICKABLE_FLAG_SUPER_LONG_FIRED,
true);
273 return ClickResult::SUPER_LONG_CLICK;
279 if (hasFlag(detectionFlags, LONG_ENABLED) && !this->hasClickableFlag(CLICKABLE_FLAG_LONG_FIRED) &&
280 this->pressAge_ms >= longClick_ms)
282 this->setClickableFlag(CLICKABLE_FLAG_LONG_FIRED,
true);
283 return ClickResult::LONG_CLICK;
286 if (hasFlag(detectionFlags, SUPER_LONG_ENABLED) && !this->hasClickableFlag(CLICKABLE_FLAG_SUPER_LONG_FIRED) &&
287 this->pressAge_ms >= superLongClick_ms)
289 this->setClickableFlag(CLICKABLE_FLAG_SUPER_LONG_FIRED,
true);
290 return ClickResult::SUPER_LONG_CLICK;
293 return ClickResult::NO_CLICK_KEEPING_CLICKED;
297#ifndef CONFIG_USE_FAST_CLICKABLES
303 explicit LSH_OPTIONAL_CONSTEXPR_CTOR
Clickable(uint8_t pin) noexcept : pinNumber(pin)
306 digitalWrite(pin, LOW);
315 template <u
int8_t Pin>
324 explicit Clickable(uint8_t pin) noexcept :
Clickable(lsh::core::avr::makeFastInputPinBinding(pin))
333 template <u
int8_t Pin>
354#ifdef CONFIG_USE_FAST_CLICKABLES
358 return (*this->pinPort & this->pinMask) != 0U;
360 return (
static_cast<bool>(digitalRead(this->pinNumber)));
367 [[nodiscard]]
auto getIndex() const -> uint8_t;
383 [[nodiscard]] auto
clickDetection(uint16_t elapsed_ms, uint8_t detectionFlags, uint16_t longClick_ms, uint16_t superLongClick_ms)
386 return this->clickDetectionImpl<false, 0U, 0U, 0U>(elapsed_ms, detectionFlags, longClick_ms, superLongClick_ms);
392 template <u
int8_t DetectionFlags, u
int16_t LongClick_ms, u
int16_t SuperLongClick_ms>
395 return this->clickDetectionImpl<true, DetectionFlags, LongClick_ms, SuperLongClick_ms>(elapsed_ms, 0U, 0U, 0U);