mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-12 08:30:05 +00:00
121 lines
2.9 KiB
C++
121 lines
2.9 KiB
C++
//
|
|
// QuadratureMouse.hpp
|
|
// Clock Signal
|
|
//
|
|
// Created by Thomas Harte on 11/06/2019.
|
|
// Copyright © 2019 Thomas Harte. All rights reserved.
|
|
//
|
|
|
|
#pragma once
|
|
|
|
#include "../Mouse.hpp"
|
|
#include <atomic>
|
|
|
|
namespace Inputs {
|
|
|
|
/*!
|
|
Provides a simple implementation of a Mouse, designed for simple
|
|
thread-safe feeding to a machine that accepts quadrature-encoded input.
|
|
|
|
TEMPORARY SIMPLIFICATION: it is assumed that the caller will be interested
|
|
in observing a signal that dictates velocity, sampling the other to
|
|
obtain direction only on transitions in the velocity signal.
|
|
|
|
Or, more concretely, of the two channels per axis, one is accurate only when
|
|
the other transitions. Hence the discussion of 'primary' and 'secondary'
|
|
channels below. This is intended to be fixed.
|
|
*/
|
|
class QuadratureMouse: public Mouse {
|
|
public:
|
|
QuadratureMouse(const int number_of_buttons) :
|
|
number_of_buttons_(number_of_buttons) {}
|
|
|
|
/*
|
|
Inputs, to satisfy the Mouse interface.
|
|
*/
|
|
void move(const int x, const int y) final {
|
|
// Accumulate all provided motion.
|
|
axes_[0] += x;
|
|
axes_[1] += y;
|
|
}
|
|
|
|
int get_number_of_buttons() const final {
|
|
return number_of_buttons_;
|
|
}
|
|
|
|
void set_button_pressed(const int index, const bool is_pressed) final {
|
|
if(is_pressed)
|
|
button_flags_ |= (1 << index);
|
|
else
|
|
button_flags_ &= ~(1 << index);
|
|
}
|
|
|
|
void reset_all_buttons() final {
|
|
button_flags_ = 0;
|
|
}
|
|
|
|
/*
|
|
Outputs.
|
|
*/
|
|
|
|
/*!
|
|
Applies a single step from the current accumulated mouse movement, which
|
|
might involve the mouse moving right, or left, or not at all.
|
|
*/
|
|
void prepare_step() {
|
|
for(int axis = 0; axis < 2; ++axis) {
|
|
// Do nothing if there's no motion to communicate.
|
|
const int axis_value = axes_[axis];
|
|
if(!axis_value) continue;
|
|
|
|
// Toggle the primary channel and set the secondary for
|
|
// negative motion. At present the y axis signals the
|
|
// secondary channel the opposite way around from the
|
|
// primary.
|
|
primaries_[axis] ^= 1;
|
|
secondaries_[axis] = primaries_[axis] ^ axis;
|
|
if(axis_value > 0) {
|
|
-- axes_[axis];
|
|
secondaries_[axis] ^= 1; // Switch to positive motion.
|
|
} else {
|
|
++ axes_[axis];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
@returns the two quadrature channels — bit 0 is the 'primary' channel
|
|
(i.e. the one that can be monitored to observe velocity) and
|
|
bit 1 is the 'secondary' (i.e. that which can be queried to
|
|
observe direction).
|
|
*/
|
|
int get_channel(int axis) const {
|
|
return primaries_[axis] | (secondaries_[axis] << 1);
|
|
}
|
|
|
|
/*!
|
|
@returns a bit mask of the currently pressed buttons.
|
|
*/
|
|
int get_button_mask() const {
|
|
return button_flags_;
|
|
}
|
|
|
|
/*!
|
|
@returns @c true if any mouse motion is waiting to be communicated;
|
|
@c false otherwise.
|
|
*/
|
|
bool has_steps() const {
|
|
return axes_[0] || axes_[1];
|
|
}
|
|
|
|
private:
|
|
const int number_of_buttons_ = 0;
|
|
std::atomic<int> button_flags_{0};
|
|
std::atomic<int> axes_[2]{0, 0};
|
|
|
|
int primaries_[2] = {0, 0};
|
|
int secondaries_[2] = {0, 0};
|
|
};
|
|
|
|
}
|