123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657(*****************************************************************************)(* *)(* SPDX-License-Identifier: MIT *)(* Copyright (c) 2025 Nomadic Labs <contact@nomadic-labs.com> *)(* *)(*****************************************************************************)(** Generic debounce timer for deferring operations during rapid input.
Usage pattern:
1. Call [mark] when an event occurs that should trigger a deferred action
2. Call [is_ready] periodically (e.g., in service_cycle) to check if the
debounce period has elapsed
3. When [is_ready] returns true, perform the deferred action and call [clear]
Thread-safe: uses Atomic operations for concurrent access. *)typet={last_event_time:floatAtomic.t;pending:boolAtomic.t;debounce_ms:int;}letcreate?(debounce_ms=250)()={last_event_time=Atomic.make0.0;pending=Atomic.makefalse;debounce_ms}(** Mark that an event occurred. Resets the debounce timer. *)letmarkt=Atomic.sett.last_event_time(Unix.gettimeofday());Atomic.sett.pendingtrue(** Check if the debounce period has elapsed since the last event.
Returns true if there's a pending event and enough time has passed. *)letis_readyt=ifnot(Atomic.gett.pending)thenfalseelseletnow=Unix.gettimeofday()inletlast=Atomic.gett.last_event_timeinletelapsed_ms=(now-.last)*.1000.0inelapsed_ms>=float_of_intt.debounce_ms(** Clear the pending state after handling the debounced event. *)letcleart=Atomic.sett.pendingfalse(** Check if there's a pending event (regardless of whether debounce elapsed). *)lethas_pendingt=Atomic.gett.pending(** Get the configured debounce period in milliseconds. *)letdebounce_mst=t.debounce_ms(** Convenience: check if ready and automatically clear if so.
Returns true if the action should be performed now. *)letcheck_and_cleart=ifis_readytthen(cleart;true)elsefalse