1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-25 03:32:01 +00:00

Introduces an intermediary for digital <-> analogue conversion.

This commit is contained in:
Thomas Harte 2018-06-11 21:35:03 -04:00
parent 42d21ea3a9
commit 2954373115
5 changed files with 166 additions and 43 deletions

View File

@ -25,14 +25,14 @@ class MultiJoystick: public Inputs::Joystick {
}
}
std::vector<Input> get_inputs() override {
std::vector<Input> inputs;
for(const auto &joystick: joysticks_) {
std::vector<Input> joystick_inputs = joystick->get_inputs();
for(const auto &input: joystick_inputs) {
if(std::find(inputs.begin(), inputs.end(), input) != inputs.end()) {
inputs.push_back(input);
std::vector<Input> &get_inputs() override {
if(inputs.empty()) {
for(const auto &joystick: joysticks_) {
std::vector<Input> joystick_inputs = joystick->get_inputs();
for(const auto &input: joystick_inputs) {
if(std::find(inputs.begin(), inputs.end(), input) != inputs.end()) {
inputs.push_back(input);
}
}
}
}
@ -45,6 +45,13 @@ class MultiJoystick: public Inputs::Joystick {
joystick->set_input(digital_input, is_active);
}
}
void set_input(const Input &digital_input, float value) override {
for(const auto &joystick: joysticks_) {
joystick->set_input(digital_input, value);
}
}
void reset_all_inputs() override {
for(const auto &joystick: joysticks_) {
joystick->reset_all_inputs();
@ -52,6 +59,7 @@ class MultiJoystick: public Inputs::Joystick {
}
private:
std::vector<Input> inputs;
std::vector<Inputs::Joystick *> joysticks_;
};

View File

@ -21,6 +21,10 @@ class Joystick {
public:
virtual ~Joystick() {}
/*!
Defines a single input, any individually-measured thing a fire button or
other digital control, an analogue axis, or a button with a symbol on it.
*/
struct Input {
/// Defines the broad type of the input.
enum Type {
@ -35,10 +39,25 @@ class Joystick {
};
const Type type;
bool is_digital_axis() const {
return type < Type::Horizontal;
}
bool is_analogue_axis() const {
return type >= Type::Horizontal && type < Type::Fire;
}
bool is_axis() const {
return type < Type::Fire;
}
bool is_button() const {
return type >= Type::Fire;
}
enum Precision {
Analogue, Digital
};
const Precision precision;
Precision precision() const {
return is_analogue_axis() ? Precision::Analogue : Precision::Digital;
}
/*!
Holds extra information pertaining to the input.
@ -51,7 +70,7 @@ class Joystick {
*/
union Info {
struct {
int index;
size_t index;
} control;
struct {
wchar_t symbol;
@ -60,18 +79,16 @@ class Joystick {
Info info;
// TODO: Find a way to make the above safely const; may mean not using a union.
Input(Type type, int index = 0, Precision precision = Precision::Digital) :
type(type),
precision(precision) {
Input(Type type, size_t index = 0) :
type(type) {
info.control.index = index;
}
Input(wchar_t symbol) : type(Key), precision(Precision::Digital) {
Input(wchar_t symbol) : type(Key) {
info.key.symbol = symbol;
}
bool operator == (const Input &rhs) {
if(rhs.type != type) return false;
if(rhs.precision != precision) return false;
if(rhs.type == Key) {
return rhs.info.key.symbol == info.key.symbol;
} else {
@ -80,20 +97,125 @@ class Joystick {
}
};
virtual std::vector<Input> get_inputs() = 0;
/// @returns The list of all inputs defined on this joystick.
virtual std::vector<Input> &get_inputs() = 0;
// Host interface. Note that the two set_inputs have logic to map
// between analogue and digital inputs; if you override
/*!
Sets the digital value of @c input. This may have direct effect or
influence an analogue value; e.g. if the caller declares that ::Left is
active but this joystick has only an analogue horizontal axis, this will
cause a change to that analogue value.
*/
virtual void set_input(const Input &input, bool is_active) = 0;
virtual void set_input(const Input &input, float value) {}
/*!
Sets the analogue value of @c input. If the input is actually digital,
or if there is a digital input with a corresponding meaning (e.g. ::Left
versus the horizontal axis), this may cause a digital input to be set.
@c value should be in the range [0.0, 1.0].
*/
virtual void set_input(const Input &input, float value) = 0;
/*!
Sets all inputs to their resting state.
*/
virtual void reset_all_inputs() {
for(const auto &input: get_inputs()) {
set_input(input, false);
if(input.precision() == Input::Precision::Digital)
set_input(input, false);
else
set_input(input, 0.5f);
}
}
};
/*!
ConcreteJoystick is the class that it's expected most machines will actually subclass;
it accepts a set of Inputs at construction and thereby is able to provide the
promised analogue <-> digital mapping of Joystick.
*/
class ConcreteJoystick: public Joystick {
public:
ConcreteJoystick(const std::vector<Input> &inputs) : inputs_(inputs) {
// Size and populate axis_types_, which is used for digital <-> analogue conversion.
for(const auto &input: inputs_) {
const bool is_digital_axis = input.is_digital_axis();
const bool is_analogue_axis = input.is_analogue_axis();
if(is_digital_axis || is_analogue_axis) {
const size_t required_size = static_cast<size_t>(input.info.control.index+1);
if(axis_types_.size() < required_size) {
axis_types_.resize(required_size);
}
axis_types_[static_cast<size_t>(input.info.control.index)] = is_digital_axis ? AxisType::Digital : AxisType::Analogue;
}
}
}
std::vector<Input> &get_inputs() override final {
return inputs_;
}
void set_input(const Input &input, bool is_active) override final {
// If this is a digital setting to a digital property, just pass it along.
if(input.is_button() || axis_types_[input.info.control.index] == AxisType::Digital) {
did_set_input(input, is_active);
return;
}
// Otherwise this is logically to an analogue axis; for now just use some
// convenient hard-coded values. TODO: make these a function of time.
using Type = Joystick::Input::Type;
using Precision = Joystick::Input::Precision;
switch(input.type) {
default: did_set_input(input, is_active ? 1.0f : 0.0f); break;
case Type::Left: did_set_input(Input(Type::Horizontal, input.info.control.index), 0.25f); break;
case Type::Right: did_set_input(Input(Type::Horizontal, input.info.control.index), 0.75f); break;
case Type::Up: did_set_input(Input(Type::Vertical, input.info.control.index), 0.25f); break;
case Type::Down: did_set_input(Input(Type::Vertical, input.info.control.index), 0.75f); break;
}
}
void set_input(const Input &input, float value) override final {
// If this is an analogue setting to an analogue property, just pass it along.
if(!input.is_button() && axis_types_[input.info.control.index] == AxisType::Analogue) {
did_set_input(input, value);
return;
}
// Otherwise apply a threshold test to convert to digital, with remapping from axes to digital inputs.
using Type = Joystick::Input::Type;
using Precision = Joystick::Input::Precision;
switch(input.type) {
default: did_set_input(input, value > 0.5f); break;
case Type::Horizontal:
did_set_input(Input(Type::Left, input.info.control.index), value <= 0.25f);
did_set_input(Input(Type::Right, input.info.control.index), value >= 0.25f);
break;
case Type::Vertical:
did_set_input(Input(Type::Up, input.info.control.index), value <= 0.25f);
did_set_input(Input(Type::Down, input.info.control.index), value >= 0.25f);
break;
}
}
protected:
virtual void did_set_input(const Input &input, float value) {
}
virtual void did_set_input(const Input &input, bool value) {
}
private:
std::vector<Input> inputs_;
enum class AxisType {
Digital,
Analogue
};
std::vector<AxisType> axis_types_;
};
}
#endif /* Joystick_hpp */

View File

@ -37,22 +37,19 @@ namespace {
namespace Atari2600 {
class Joystick: public Inputs::Joystick {
class Joystick: public Inputs::ConcreteJoystick {
public:
Joystick(Bus *bus, std::size_t shift, std::size_t fire_tia_input) :
bus_(bus), shift_(shift), fire_tia_input_(fire_tia_input) {}
std::vector<Input> get_inputs() override {
return {
ConcreteJoystick({
Input(Input::Up),
Input(Input::Down),
Input(Input::Left),
Input(Input::Right),
Input(Input::Fire)
};
}
}),
bus_(bus), shift_(shift), fire_tia_input_(fire_tia_input) {}
void set_input(const Input &digital_input, bool is_active) override {
void did_set_input(const Input &digital_input, bool is_active) override {
switch(digital_input.type) {
case Input::Up: bus_->mos6532_.update_port_input(0, 0x10 >> shift_, is_active); break;
case Input::Down: bus_->mos6532_.update_port_input(0, 0x20 >> shift_, is_active); break;

View File

@ -32,10 +32,10 @@ const int sn76489_divider = 2;
namespace Coleco {
namespace Vision {
class Joystick: public Inputs::Joystick {
class Joystick: public Inputs::ConcreteJoystick {
public:
std::vector<Input> get_inputs() override {
return {
Joystick() :
ConcreteJoystick({
Input(Input::Up),
Input(Input::Down),
Input(Input::Left),
@ -48,10 +48,9 @@ class Joystick: public Inputs::Joystick {
Input('3'), Input('4'), Input('5'),
Input('6'), Input('7'), Input('8'),
Input('9'), Input('*'), Input('#'),
};
}
}) {}
void set_input(const Input &digital_input, bool is_active) override {
void did_set_input(const Input &digital_input, bool is_active) override {
switch(digital_input.type) {
default: return;

View File

@ -257,23 +257,20 @@ class Vic6560BusHandler {
/*!
Interfaces a joystick to the two VIAs.
*/
class Joystick: public Inputs::Joystick {
class Joystick: public Inputs::ConcreteJoystick {
public:
Joystick(UserPortVIA &user_port_via_port_handler, KeyboardVIA &keyboard_via_port_handler) :
user_port_via_port_handler_(user_port_via_port_handler),
keyboard_via_port_handler_(keyboard_via_port_handler) {}
std::vector<Input> get_inputs() override {
return {
ConcreteJoystick({
Input(Input::Up),
Input(Input::Down),
Input(Input::Left),
Input(Input::Right),
Input(Input::Fire)
};
}
}),
user_port_via_port_handler_(user_port_via_port_handler),
keyboard_via_port_handler_(keyboard_via_port_handler) {}
void set_input(const Input &digital_input, bool is_active) override {
void did_set_input(const Input &digital_input, bool is_active) override {
JoystickInput mapped_input;
switch(digital_input.type) {
default: return;