#include #include #include #include #include #include #include #include namespace { auto milliseconds(std::chrono::steady_clock::time_point time) -> std::uint64_t { return static_cast(std::chrono::duration_cast(time.time_since_epoch()).count()); } } struct vamp_zero::touch_input::state : std::enable_shared_from_this { state(boost::asio::any_io_executor executor, std::function handler) : timer{std::move(executor)}, handler{std::move(handler)} {} auto send(std::optional command) -> void { if (!command) { return; } boost::asio::post(timer.get_executor(), [weak = weak_from_this(), command = *command, generation = input_generation] { auto self = weak.lock(); if (!self) { return; } { auto lock = std::scoped_lock{self->mutex}; if (self->stopped || generation != self->input_generation) { return; } } self->handler(command); }); } auto schedule() -> void { auto generation = ++timer_generation; timer.cancel(); auto deadline = gesture.pending_deadline(); if (!deadline) { return; } timer.expires_at(std::chrono::steady_clock::time_point{std::chrono::milliseconds{*deadline}}); timer.async_wait([weak = weak_from_this(), generation](boost::system::error_code const &error) { auto self = weak.lock(); if (error || !self) { return; } auto lock = std::scoped_lock{self->mutex}; if (self->stopped || self->timer_generation != generation) { return; } self->send(self->gesture.update(false, milliseconds(std::chrono::steady_clock::now()))); self->schedule(); }); } boost::asio::steady_timer timer; std::function handler; std::mutex mutex{}; vamp_protocol::gesture_state gesture{}; std::optional active_touch{}; std::uint64_t timer_generation{}; std::uint64_t input_generation{}; std::bool_t stopped{}; }; vamp_zero::touch_input::touch_input(boost::asio::any_io_executor executor, std::function handler) : state_{std::make_shared(std::move(executor), std::move(handler))} {} vamp_zero::touch_input::~touch_input() { stop(); } auto vamp_zero::touch_input::handle(luma::touch_event const &event) -> std::bool_t { auto lock = std::scoped_lock{state_->mutex}; if (state_->stopped || !state_->handler) { return false; } auto consumed = std::bool_t{false}; for (auto const &point : event.points) { if (point.phase == luma::touch_phase::DOWN && !state_->active_touch) { state_->active_touch = point.id; } else if (!state_->active_touch || point.id != *state_->active_touch) { continue; } consumed = true; if (point.phase == luma::touch_phase::CANCEL) { state_->active_touch.reset(); state_->gesture.reset(); } else { auto touching = point.phase != luma::touch_phase::UP; if (!touching) { state_->active_touch.reset(); } state_->send(state_->gesture.update(touching, milliseconds(event.time))); } state_->schedule(); } return consumed; } auto vamp_zero::touch_input::stop() -> void { auto lock = std::scoped_lock{state_->mutex}; state_->stopped = true; ++state_->input_generation; state_->active_touch.reset(); state_->gesture.reset(); state_->schedule(); } auto vamp_zero::touch_input::reset() -> void { auto lock = std::scoped_lock{state_->mutex}; ++state_->input_generation; state_->active_touch.reset(); state_->gesture.reset(); state_->schedule(); }