125 lines
3.8 KiB
C++
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();
|
|
}
|