@@ -0,0 +1,114 @@
|
||||
#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] {
|
||||
auto self = weak.lock();
|
||||
if (!self) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
auto lock = std::scoped_lock{self->mutex};
|
||||
if (self->stopped) {
|
||||
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::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_->active_touch.reset();
|
||||
state_->gesture.reset();
|
||||
state_->schedule();
|
||||
}
|
||||
Reference in New Issue
Block a user