diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.cpp
index 6a0aa4ecf..526b43749 100644
--- a/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.cpp
+++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.cpp
@@ -25,14 +25,14 @@ class MultiJoystick: public Inputs::Joystick {
}
}
- std::vector get_inputs() override {
- std::vector inputs;
-
- for(const auto &joystick: joysticks_) {
- std::vector 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 &get_inputs() override {
+ if(inputs.empty()) {
+ for(const auto &joystick: joysticks_) {
+ std::vector 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 inputs;
std::vector joysticks_;
};
diff --git a/Inputs/Joystick.hpp b/Inputs/Joystick.hpp
index ee15eee2f..486d4470a 100644
--- a/Inputs/Joystick.hpp
+++ b/Inputs/Joystick.hpp
@@ -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 get_inputs() = 0;
+ /// @returns The list of all inputs defined on this joystick.
+ virtual std::vector &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 &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(input.info.control.index+1);
+ if(axis_types_.size() < required_size) {
+ axis_types_.resize(required_size);
+ }
+ axis_types_[static_cast(input.info.control.index)] = is_digital_axis ? AxisType::Digital : AxisType::Analogue;
+ }
+ }
+ }
+
+ std::vector &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 inputs_;
+
+ enum class AxisType {
+ Digital,
+ Analogue
+ };
+ std::vector axis_types_;
+};
+
}
#endif /* Joystick_hpp */
diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp
index 4f84e58ac..10c170f82 100644
--- a/Machines/Atari2600/Atari2600.cpp
+++ b/Machines/Atari2600/Atari2600.cpp
@@ -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 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;
diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp
index 101e7a617..2933f7f5b 100644
--- a/Machines/ColecoVision/ColecoVision.cpp
+++ b/Machines/ColecoVision/ColecoVision.cpp
@@ -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 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;
diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp
index 0fe5cc65c..31a8f5ca2 100644
--- a/Machines/Commodore/Vic-20/Vic20.cpp
+++ b/Machines/Commodore/Vic-20/Vic20.cpp
@@ -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 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;