Files
product-vamp/src/vamp_zero/touch_input.cpp
T
khodowany 2a1aa2c67a UI
2026-09-30 18:23:25 -05:00

125 lines
3.8 KiB
C++

#include <vamp_zero/touch_input.hpp>
#include <chrono>
#include <mutex>
#include <optional>
#include <utility>
#include <boost/asio/post.hpp>
#include <boost/asio/steady_timer.hpp>
#include <vamp_protocol/gesture.hpp>
namespace {
auto milliseconds(std::chrono::steady_clock::time_point time) -> std::uint64_t {
return static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::milliseconds>(time.time_since_epoch()).count());
}
}
struct vamp_zero::touch_input::state : std::enable_shared_from_this<state> {
state(boost::asio::any_io_executor executor, std::function<void(vamp_protocol::page_command)> handler)
: timer{std::move(executor)}, handler{std::move(handler)} {}
auto send(std::optional<vamp_protocol::page_command> 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<void(vamp_protocol::page_command)> handler;
std::mutex mutex{};
vamp_protocol::gesture_state gesture{};
std::optional<std::int32_t> 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<void(vamp_protocol::page_command)> handler)
: state_{std::make_shared<state>(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();
}