mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 23:52:26 +00:00
Merge pull request #461 from TomHarte/Joystick
Introduces Joystick support for the Apple II
This commit is contained in:
commit
8d8f244bf5
@ -25,14 +25,14 @@ class MultiJoystick: public Inputs::Joystick {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DigitalInput> get_inputs() override {
|
||||
std::vector<DigitalInput> inputs;
|
||||
|
||||
for(const auto &joystick: joysticks_) {
|
||||
std::vector<DigitalInput> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -40,11 +40,18 @@ class MultiJoystick: public Inputs::Joystick {
|
||||
return inputs;
|
||||
}
|
||||
|
||||
void set_digital_input(const DigitalInput &digital_input, bool is_active) override {
|
||||
void set_input(const Input &digital_input, bool is_active) override {
|
||||
for(const auto &joystick: joysticks_) {
|
||||
joystick->set_digital_input(digital_input, is_active);
|
||||
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_;
|
||||
};
|
||||
|
||||
|
@ -21,28 +21,73 @@ class Joystick {
|
||||
public:
|
||||
virtual ~Joystick() {}
|
||||
|
||||
struct DigitalInput {
|
||||
/*!
|
||||
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 {
|
||||
Up, Down, Left, Right, Fire,
|
||||
// Half-axis inputs.
|
||||
Up, Down, Left, Right,
|
||||
// Full-axis inputs.
|
||||
Horizontal, Vertical,
|
||||
// Fire buttons.
|
||||
Fire,
|
||||
// Other labelled keys.
|
||||
Key
|
||||
} type;
|
||||
union {
|
||||
};
|
||||
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
|
||||
};
|
||||
Precision precision() const {
|
||||
return is_analogue_axis() ? Precision::Analogue : Precision::Digital;
|
||||
}
|
||||
|
||||
/*!
|
||||
Holds extra information pertaining to the input.
|
||||
|
||||
@c Type::Key inputs declare the symbol printed on them.
|
||||
|
||||
All other types of input have an associated index, indicating whether they
|
||||
are the zeroth, first, second, third, etc of those things. E.g. a joystick
|
||||
may have two fire buttons, which will be buttons 0 and 1.
|
||||
*/
|
||||
union Info {
|
||||
struct {
|
||||
int index;
|
||||
size_t index;
|
||||
} control;
|
||||
struct {
|
||||
wchar_t symbol;
|
||||
} key;
|
||||
} info;
|
||||
};
|
||||
Info info;
|
||||
// TODO: Find a way to make the above safely const; may mean not using a union.
|
||||
|
||||
DigitalInput(Type type, int index = 0) : type(type) {
|
||||
Input(Type type, size_t index = 0) :
|
||||
type(type) {
|
||||
info.control.index = index;
|
||||
}
|
||||
DigitalInput(wchar_t symbol) : type(Key) {
|
||||
Input(wchar_t symbol) : type(Key) {
|
||||
info.key.symbol = symbol;
|
||||
}
|
||||
|
||||
bool operator == (const DigitalInput &rhs) {
|
||||
bool operator == (const Input &rhs) {
|
||||
if(rhs.type != type) return false;
|
||||
if(rhs.type == Key) {
|
||||
return rhs.info.key.symbol == info.key.symbol;
|
||||
@ -52,17 +97,123 @@ class Joystick {
|
||||
}
|
||||
};
|
||||
|
||||
virtual std::vector<DigitalInput> get_inputs() = 0;
|
||||
/// @returns The list of all inputs defined on this joystick.
|
||||
virtual std::vector<Input> &get_inputs() = 0;
|
||||
|
||||
// Host interface.
|
||||
virtual void set_digital_input(const DigitalInput &digital_input, bool is_active) = 0;
|
||||
/*!
|
||||
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;
|
||||
|
||||
/*!
|
||||
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_digital_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 stick_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(stick_types_.size() < required_size) {
|
||||
stick_types_.resize(required_size);
|
||||
}
|
||||
stick_types_[static_cast<size_t>(input.info.control.index)] = is_digital_axis ? StickType::Digital : StickType::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() || stick_types_[input.info.control.index] == StickType::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;
|
||||
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), is_active ? 0.25f : 0.5f); break;
|
||||
case Type::Right: did_set_input(Input(Type::Horizontal, input.info.control.index), is_active ? 0.75f : 0.5f); break;
|
||||
case Type::Up: did_set_input(Input(Type::Vertical, input.info.control.index), is_active ? 0.25f : 0.5f); break;
|
||||
case Type::Down: did_set_input(Input(Type::Vertical, input.info.control.index), is_active ? 0.75f : 0.5f); 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() && stick_types_[input.info.control.index] == StickType::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;
|
||||
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 StickType {
|
||||
Digital,
|
||||
Analogue
|
||||
};
|
||||
std::vector<StickType> stick_types_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* Joystick_hpp */
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "../../Activity/Source.hpp"
|
||||
#include "../ConfigurationTarget.hpp"
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../JoystickMachine.hpp"
|
||||
#include "../KeyboardMachine.hpp"
|
||||
#include "../Utility/MemoryFuzzer.hpp"
|
||||
#include "../Utility/StringSerialiser.hpp"
|
||||
@ -51,6 +52,7 @@ class ConcreteMachine:
|
||||
public Inputs::Keyboard,
|
||||
public AppleII::Machine,
|
||||
public Activity::Source,
|
||||
public JoystickMachine::Machine,
|
||||
public AppleII::Card::Delegate {
|
||||
private:
|
||||
struct VideoBusHandler : public AppleII::Video::BusHandler {
|
||||
@ -173,6 +175,56 @@ class ConcreteMachine:
|
||||
// MARK - quick loading
|
||||
bool should_load_quickly_ = false;
|
||||
|
||||
// MARK - joysticks
|
||||
class Joystick: public Inputs::ConcreteJoystick {
|
||||
public:
|
||||
Joystick() :
|
||||
ConcreteJoystick({
|
||||
Input(Input::Horizontal),
|
||||
Input(Input::Vertical),
|
||||
|
||||
// The Apple II offers three buttons between two joysticks;
|
||||
// this emulator puts three buttons on each joystick and
|
||||
// combines them.
|
||||
Input(Input::Fire, 0),
|
||||
Input(Input::Fire, 1),
|
||||
Input(Input::Fire, 2),
|
||||
}) {}
|
||||
|
||||
void did_set_input(const Input &input, float value) override {
|
||||
if(!input.info.control.index && (input.type == Input::Type::Horizontal || input.type == Input::Type::Vertical))
|
||||
axes[(input.type == Input::Type::Horizontal) ? 0 : 1] = 1.0f - value;
|
||||
}
|
||||
|
||||
void did_set_input(const Input &input, bool value) override {
|
||||
if(input.type == Input::Type::Fire && input.info.control.index < 3) {
|
||||
buttons[input.info.control.index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
bool buttons[3] = {false, false, false};
|
||||
float axes[2] = {0.5f, 0.5f};
|
||||
};
|
||||
|
||||
// On an Apple II, the programmer strobes 0xc070 and that causes each analogue input
|
||||
// to begin a charge and discharge cycle **if they are not already charging**.
|
||||
// The greater the analogue input, the faster they will charge and therefore the sooner
|
||||
// they will discharge.
|
||||
//
|
||||
// This emulator models that with analogue_charge_ being essentially the amount of time,
|
||||
// in charge threshold units, since 0xc070 was last strobed. But if any of the analogue
|
||||
// inputs were already partially charged then they gain a bias in analogue_biases_.
|
||||
//
|
||||
// It's a little indirect, but it means only having to increment the one value in the
|
||||
// main loop.
|
||||
float analogue_charge_ = 0.0f;
|
||||
float analogue_biases_[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
|
||||
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
|
||||
bool analogue_channel_is_discharged(size_t channel) {
|
||||
return static_cast<Joystick *>(joysticks_[channel >> 1].get())->axes[channel & 1] < analogue_charge_ + analogue_biases_[channel];
|
||||
}
|
||||
|
||||
public:
|
||||
ConcreteMachine():
|
||||
m6502_(*this),
|
||||
@ -198,6 +250,10 @@ class ConcreteMachine:
|
||||
|
||||
// Also, start with randomised memory contents.
|
||||
Memory::Fuzz(ram_, sizeof(ram_));
|
||||
|
||||
// Add a couple of joysticks.
|
||||
joysticks_.emplace_back(new Joystick);
|
||||
joysticks_.emplace_back(new Joystick);
|
||||
}
|
||||
|
||||
~ConcreteMachine() {
|
||||
@ -377,16 +433,49 @@ class ConcreteMachine:
|
||||
break;
|
||||
|
||||
case 0xc061: // Switch input 0.
|
||||
*value &= 0x7f;
|
||||
if(static_cast<Joystick *>(joysticks_[0].get())->buttons[0] || static_cast<Joystick *>(joysticks_[1].get())->buttons[2])
|
||||
*value |= 0x80;
|
||||
break;
|
||||
case 0xc062: // Switch input 1.
|
||||
*value &= 0x7f;
|
||||
if(static_cast<Joystick *>(joysticks_[0].get())->buttons[1] || static_cast<Joystick *>(joysticks_[1].get())->buttons[1])
|
||||
*value |= 0x80;
|
||||
break;
|
||||
case 0xc063: // Switch input 2.
|
||||
*value &= 0x7f;
|
||||
if(static_cast<Joystick *>(joysticks_[0].get())->buttons[2] || static_cast<Joystick *>(joysticks_[1].get())->buttons[0])
|
||||
*value |= 0x80;
|
||||
break;
|
||||
|
||||
case 0xc064: // Analogue input 0.
|
||||
case 0xc065: // Analogue input 1.
|
||||
case 0xc066: // Analogue input 2.
|
||||
case 0xc067: { // Analogue input 3.
|
||||
const size_t input = address - 0xc064;
|
||||
*value &= 0x7f;
|
||||
if(analogue_channel_is_discharged(input)) {
|
||||
*value |= 0x80;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
} else {
|
||||
// Write-only switches.
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xc070: { // Permit analogue inputs that are currently discharged to begin a charge cycle.
|
||||
// Ensure those that were still charging retain that state.
|
||||
for(size_t c = 0; c < 4; ++c) {
|
||||
if(analogue_channel_is_discharged(c)) {
|
||||
analogue_biases_[c] = 0.0f;
|
||||
} else {
|
||||
analogue_biases_[c] += analogue_charge_;
|
||||
}
|
||||
}
|
||||
analogue_charge_ = 0.0f;
|
||||
} break;
|
||||
|
||||
/* Read-write switches. */
|
||||
case 0xc050: update_video(); video_->set_graphics_mode(); break;
|
||||
case 0xc051: update_video(); video_->set_text_mode(); break;
|
||||
@ -494,6 +583,9 @@ class ConcreteMachine:
|
||||
}
|
||||
}
|
||||
|
||||
// Update analogue charge level.
|
||||
analogue_charge_ = std::min(analogue_charge_ + 1.0f / 2820.0f, 1.0f);
|
||||
|
||||
return Cycles(1);
|
||||
}
|
||||
|
||||
@ -619,6 +711,11 @@ class ConcreteMachine:
|
||||
Configurable::append_quick_load_tape_selection(selection_set, true);
|
||||
return selection_set;
|
||||
}
|
||||
|
||||
// MARK: JoystickMachine
|
||||
std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() override {
|
||||
return joysticks_;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@ -630,3 +727,4 @@ Machine *Machine::AppleII() {
|
||||
}
|
||||
|
||||
Machine::~Machine() {}
|
||||
|
||||
|
@ -37,30 +37,27 @@ 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) :
|
||||
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) {}
|
||||
|
||||
std::vector<DigitalInput> get_inputs() override {
|
||||
return {
|
||||
DigitalInput(DigitalInput::Up),
|
||||
DigitalInput(DigitalInput::Down),
|
||||
DigitalInput(DigitalInput::Left),
|
||||
DigitalInput(DigitalInput::Right),
|
||||
DigitalInput(DigitalInput::Fire)
|
||||
};
|
||||
}
|
||||
|
||||
void set_digital_input(const DigitalInput &digital_input, bool is_active) override {
|
||||
void did_set_input(const Input &digital_input, bool is_active) override {
|
||||
switch(digital_input.type) {
|
||||
case DigitalInput::Up: bus_->mos6532_.update_port_input(0, 0x10 >> shift_, is_active); break;
|
||||
case DigitalInput::Down: bus_->mos6532_.update_port_input(0, 0x20 >> shift_, is_active); break;
|
||||
case DigitalInput::Left: bus_->mos6532_.update_port_input(0, 0x40 >> shift_, is_active); break;
|
||||
case DigitalInput::Right: bus_->mos6532_.update_port_input(0, 0x80 >> shift_, is_active); break;
|
||||
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;
|
||||
case Input::Left: bus_->mos6532_.update_port_input(0, 0x40 >> shift_, is_active); break;
|
||||
case Input::Right: bus_->mos6532_.update_port_input(0, 0x80 >> shift_, is_active); break;
|
||||
|
||||
// TODO: latching
|
||||
case DigitalInput::Fire:
|
||||
case Input::Fire:
|
||||
if(is_active)
|
||||
bus_->tia_input_value_[fire_tia_input_] &= ~0x80;
|
||||
else
|
||||
|
@ -32,30 +32,29 @@ const int sn76489_divider = 2;
|
||||
namespace Coleco {
|
||||
namespace Vision {
|
||||
|
||||
class Joystick: public Inputs::Joystick {
|
||||
class Joystick: public Inputs::ConcreteJoystick {
|
||||
public:
|
||||
std::vector<DigitalInput> get_inputs() override {
|
||||
return {
|
||||
DigitalInput(DigitalInput::Up),
|
||||
DigitalInput(DigitalInput::Down),
|
||||
DigitalInput(DigitalInput::Left),
|
||||
DigitalInput(DigitalInput::Right),
|
||||
Joystick() :
|
||||
ConcreteJoystick({
|
||||
Input(Input::Up),
|
||||
Input(Input::Down),
|
||||
Input(Input::Left),
|
||||
Input(Input::Right),
|
||||
|
||||
DigitalInput(DigitalInput::Fire, 0),
|
||||
DigitalInput(DigitalInput::Fire, 1),
|
||||
Input(Input::Fire, 0),
|
||||
Input(Input::Fire, 1),
|
||||
|
||||
DigitalInput('0'), DigitalInput('1'), DigitalInput('2'),
|
||||
DigitalInput('3'), DigitalInput('4'), DigitalInput('5'),
|
||||
DigitalInput('6'), DigitalInput('7'), DigitalInput('8'),
|
||||
DigitalInput('9'), DigitalInput('*'), DigitalInput('#'),
|
||||
};
|
||||
}
|
||||
Input('0'), Input('1'), Input('2'),
|
||||
Input('3'), Input('4'), Input('5'),
|
||||
Input('6'), Input('7'), Input('8'),
|
||||
Input('9'), Input('*'), Input('#'),
|
||||
}) {}
|
||||
|
||||
void set_digital_input(const DigitalInput &digital_input, bool is_active) override {
|
||||
void did_set_input(const Input &digital_input, bool is_active) override {
|
||||
switch(digital_input.type) {
|
||||
default: return;
|
||||
|
||||
case DigitalInput::Key:
|
||||
case Input::Key:
|
||||
if(!is_active) keypad_ |= 0xf;
|
||||
else {
|
||||
uint8_t mask = 0xf;
|
||||
@ -78,11 +77,11 @@ class Joystick: public Inputs::Joystick {
|
||||
}
|
||||
break;
|
||||
|
||||
case DigitalInput::Up: if(is_active) direction_ &= ~0x01; else direction_ |= 0x01; break;
|
||||
case DigitalInput::Right: if(is_active) direction_ &= ~0x02; else direction_ |= 0x02; break;
|
||||
case DigitalInput::Down: if(is_active) direction_ &= ~0x04; else direction_ |= 0x04; break;
|
||||
case DigitalInput::Left: if(is_active) direction_ &= ~0x08; else direction_ |= 0x08; break;
|
||||
case DigitalInput::Fire:
|
||||
case Input::Up: if(is_active) direction_ &= ~0x01; else direction_ |= 0x01; break;
|
||||
case Input::Right: if(is_active) direction_ &= ~0x02; else direction_ |= 0x02; break;
|
||||
case Input::Down: if(is_active) direction_ &= ~0x04; else direction_ |= 0x04; break;
|
||||
case Input::Left: if(is_active) direction_ &= ~0x08; else direction_ |= 0x08; break;
|
||||
case Input::Fire:
|
||||
switch(digital_input.info.control.index) {
|
||||
default: break;
|
||||
case 0: if(is_active) direction_ &= ~0x40; else direction_ |= 0x40; break;
|
||||
|
@ -257,31 +257,28 @@ 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) :
|
||||
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) {}
|
||||
|
||||
std::vector<DigitalInput> get_inputs() override {
|
||||
return {
|
||||
DigitalInput(DigitalInput::Up),
|
||||
DigitalInput(DigitalInput::Down),
|
||||
DigitalInput(DigitalInput::Left),
|
||||
DigitalInput(DigitalInput::Right),
|
||||
DigitalInput(DigitalInput::Fire)
|
||||
};
|
||||
}
|
||||
|
||||
void set_digital_input(const DigitalInput &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;
|
||||
case DigitalInput::Up: mapped_input = Up; break;
|
||||
case DigitalInput::Down: mapped_input = Down; break;
|
||||
case DigitalInput::Left: mapped_input = Left; break;
|
||||
case DigitalInput::Right: mapped_input = Right; break;
|
||||
case DigitalInput::Fire: mapped_input = Fire; break;
|
||||
case Input::Up: mapped_input = Up; break;
|
||||
case Input::Down: mapped_input = Down; break;
|
||||
case Input::Left: mapped_input = Left; break;
|
||||
case Input::Right: mapped_input = Right; break;
|
||||
case Input::Fire: mapped_input = Fire; break;
|
||||
}
|
||||
|
||||
user_port_via_port_handler_.set_joystick_state(mapped_input, is_active);
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14113" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14109"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14113"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||
@ -129,22 +129,6 @@
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
|
||||
<items>
|
||||
<menuItem title="Undo" enabled="NO" keyEquivalent="z" id="dRJ-4n-Yzg">
|
||||
<connections>
|
||||
<action selector="undo:" target="-1" id="M6e-cu-g7V"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Redo" enabled="NO" keyEquivalent="Z" id="6dh-zS-Vam">
|
||||
<connections>
|
||||
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
|
||||
<menuItem title="Cut" enabled="NO" keyEquivalent="x" id="uRl-iY-unG">
|
||||
<connections>
|
||||
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Copy" enabled="NO" keyEquivalent="c" id="x3v-GG-iWU">
|
||||
<connections>
|
||||
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
|
||||
@ -155,192 +139,6 @@
|
||||
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Paste and Match Style" enabled="NO" keyEquivalent="V" id="WeT-3V-zwk">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Delete" enabled="NO" id="pa3-QI-u2k">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Select All" enabled="NO" keyEquivalent="a" id="Ruw-6m-B2m">
|
||||
<connections>
|
||||
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
|
||||
<menuItem title="Find" id="4EN-yA-p0u">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Find" id="1b7-l0-nxx">
|
||||
<items>
|
||||
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
|
||||
<connections>
|
||||
<action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
|
||||
<items>
|
||||
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
|
||||
<connections>
|
||||
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
|
||||
<connections>
|
||||
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
|
||||
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Substitutions" id="9ic-FL-obx">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
|
||||
<items>
|
||||
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
|
||||
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smart Links" id="cwL-P1-jid">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Data Detectors" id="tRr-pd-1PS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Transformations" id="2oI-Rn-ZJC">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
|
||||
<items>
|
||||
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Speech" id="xrE-MZ-jX0">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
|
||||
<items>
|
||||
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
@ -357,6 +155,23 @@
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Input" id="5bL-VY-cxd">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Input" id="6yv-Cf-E9r">
|
||||
<items>
|
||||
<menuItem title="Use Keyboard as Keyboard" state="on" tag="100" keyEquivalent="k" id="TfX-0B-j4U">
|
||||
<connections>
|
||||
<action selector="useKeyboardAsKeyboard:" target="-1" id="6fl-fS-Oe9"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Use Keyboard as Joystick" tag="101" enabled="NO" keyEquivalent="j" id="5mn-ch-Xv6">
|
||||
<connections>
|
||||
<action selector="useKeyboardAsJoystick:" target="-1" id="Yz7-CL-f0y"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Window" id="aUF-d1-5bR">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
|
||||
|
@ -256,4 +256,38 @@ class MachineDocument:
|
||||
@IBAction func cancelCreateMachine(_ sender: NSButton?) {
|
||||
close()
|
||||
}
|
||||
|
||||
// MARK: Joystick-via-the-keyboard selection
|
||||
@IBAction func useKeyboardAsKeyboard(_ sender: NSMenuItem?) {
|
||||
machine.inputMode = .keyboard
|
||||
}
|
||||
|
||||
@IBAction func useKeyboardAsJoystick(_ sender: NSMenuItem?) {
|
||||
machine.inputMode = .joystick
|
||||
}
|
||||
|
||||
override func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
|
||||
if let menuItem = item as? NSMenuItem {
|
||||
switch item.action {
|
||||
case #selector(self.useKeyboardAsKeyboard):
|
||||
if machine == nil || !machine.hasKeyboard {
|
||||
return false
|
||||
}
|
||||
|
||||
menuItem.state = machine.inputMode == .keyboard ? .on : .off
|
||||
return true
|
||||
|
||||
case #selector(self.useKeyboardAsJoystick):
|
||||
if machine == nil || !machine.hasJoystick {
|
||||
return false
|
||||
}
|
||||
|
||||
menuItem.state = machine.inputMode == .joystick ? .on : .off
|
||||
return true
|
||||
|
||||
default: break
|
||||
}
|
||||
}
|
||||
return super.validateUserInterfaceItem(item)
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,11 @@ typedef NS_ENUM(NSInteger, CSMachineVideoSignal) {
|
||||
CSMachineVideoSignalRGB
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
|
||||
CSMachineKeyboardInputModeKeyboard,
|
||||
CSMachineKeyboardInputModeJoystick
|
||||
};
|
||||
|
||||
// Deliberately low; to ensure CSMachine has been declared as an @class already.
|
||||
#import "CSAtari2600.h"
|
||||
#import "CSZX8081.h"
|
||||
@ -63,6 +68,11 @@ typedef NS_ENUM(NSInteger, CSMachineVideoSignal) {
|
||||
|
||||
- (bool)supportsVideoSignal:(CSMachineVideoSignal)videoSignal;
|
||||
|
||||
// Input control.
|
||||
@property (nonatomic, readonly) BOOL hasKeyboard;
|
||||
@property (nonatomic, readonly) BOOL hasJoystick;
|
||||
@property (nonatomic, assign) CSMachineKeyboardInputMode inputMode;
|
||||
|
||||
// Special-case accessors; undefined behaviour if accessed for a machine not of the corresponding type.
|
||||
@property (nonatomic, readonly) CSAtari2600 *atari2600;
|
||||
@property (nonatomic, readonly) CSZX8081 *zx8081;
|
||||
|
@ -69,6 +69,8 @@ struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate, public LockP
|
||||
_machine.reset(Machine::MachineForTargets(_analyser.targets, CSROMFetcher(), error));
|
||||
if(!_machine) return nil;
|
||||
|
||||
_inputMode = _machine->keyboard_machine() ? CSMachineKeyboardInputModeKeyboard : CSMachineKeyboardInputModeJoystick;
|
||||
|
||||
_delegateMachineAccessLock = [[NSLock alloc] init];
|
||||
|
||||
_speakerDelegate.machine = self;
|
||||
@ -171,7 +173,7 @@ struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate, public LockP
|
||||
|
||||
- (void)setKey:(uint16_t)key characters:(NSString *)characters isPressed:(BOOL)isPressed {
|
||||
auto keyboard_machine = _machine->keyboard_machine();
|
||||
if(keyboard_machine) {
|
||||
if(self.inputMode == CSMachineKeyboardInputModeKeyboard && keyboard_machine) {
|
||||
// Don't pass anything on if this is not new information.
|
||||
if(_depressedKeys[key] == !!isPressed) return;
|
||||
_depressedKeys[key] = !!isPressed;
|
||||
@ -248,23 +250,27 @@ struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate, public LockP
|
||||
}
|
||||
|
||||
auto joystick_machine = _machine->joystick_machine();
|
||||
if(joystick_machine) {
|
||||
if(self.inputMode == CSMachineKeyboardInputModeJoystick && joystick_machine) {
|
||||
@synchronized(self) {
|
||||
std::vector<std::unique_ptr<Inputs::Joystick>> &joysticks = joystick_machine->get_joysticks();
|
||||
if(!joysticks.empty()) {
|
||||
// Convert to a C++ bool so that the following calls are resolved correctly even if overloaded.
|
||||
bool is_pressed = !!isPressed;
|
||||
switch(key) {
|
||||
case VK_LeftArrow: joysticks[0]->set_digital_input(Inputs::Joystick::DigitalInput::Left, isPressed); break;
|
||||
case VK_RightArrow: joysticks[0]->set_digital_input(Inputs::Joystick::DigitalInput::Right, isPressed); break;
|
||||
case VK_UpArrow: joysticks[0]->set_digital_input(Inputs::Joystick::DigitalInput::Up, isPressed); break;
|
||||
case VK_DownArrow: joysticks[0]->set_digital_input(Inputs::Joystick::DigitalInput::Down, isPressed); break;
|
||||
case VK_Space: joysticks[0]->set_digital_input(Inputs::Joystick::DigitalInput::Fire, isPressed); break;
|
||||
case VK_ANSI_A: joysticks[0]->set_digital_input(Inputs::Joystick::DigitalInput(Inputs::Joystick::DigitalInput::Fire, 0), isPressed); break;
|
||||
case VK_ANSI_S: joysticks[0]->set_digital_input(Inputs::Joystick::DigitalInput(Inputs::Joystick::DigitalInput::Fire, 1), isPressed); break;
|
||||
case VK_LeftArrow: joysticks[0]->set_input(Inputs::Joystick::Input::Left, is_pressed); break;
|
||||
case VK_RightArrow: joysticks[0]->set_input(Inputs::Joystick::Input::Right, is_pressed); break;
|
||||
case VK_UpArrow: joysticks[0]->set_input(Inputs::Joystick::Input::Up, is_pressed); break;
|
||||
case VK_DownArrow: joysticks[0]->set_input(Inputs::Joystick::Input::Down, is_pressed); break;
|
||||
case VK_Space: joysticks[0]->set_input(Inputs::Joystick::Input::Fire, is_pressed); break;
|
||||
case VK_ANSI_A: joysticks[0]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Fire, 0), is_pressed); break;
|
||||
case VK_ANSI_S: joysticks[0]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Fire, 1), is_pressed); break;
|
||||
case VK_ANSI_D: joysticks[0]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Fire, 2), is_pressed); break;
|
||||
case VK_ANSI_F: joysticks[0]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Fire, 3), is_pressed); break;
|
||||
default:
|
||||
if(characters) {
|
||||
joysticks[0]->set_digital_input(Inputs::Joystick::DigitalInput([characters characterAtIndex:0]), isPressed);
|
||||
joysticks[0]->set_input(Inputs::Joystick::Input([characters characterAtIndex:0]), is_pressed);
|
||||
} else {
|
||||
joysticks[0]->set_digital_input(Inputs::Joystick::DigitalInput::Fire, isPressed);
|
||||
joysticks[0]->set_input(Inputs::Joystick::Input::Fire, is_pressed);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -392,4 +398,14 @@ struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate, public LockP
|
||||
return [[CSZX8081 alloc] initWithZX8081:_machine->raw_pointer() owner:self];
|
||||
}
|
||||
|
||||
#pragma mark - Input device queries
|
||||
|
||||
- (BOOL)hasJoystick {
|
||||
return !!_machine->joystick_machine();
|
||||
}
|
||||
|
||||
- (BOOL)hasKeyboard {
|
||||
return !!_machine->keyboard_machine();
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -464,16 +464,16 @@ int main(int argc, char *argv[]) {
|
||||
std::vector<std::unique_ptr<Inputs::Joystick>> &joysticks = joystick_machine->get_joysticks();
|
||||
if(!joysticks.empty()) {
|
||||
switch(event.key.keysym.scancode) {
|
||||
case SDL_SCANCODE_LEFT: joysticks[0]->set_digital_input(Inputs::Joystick::DigitalInput::Left, is_pressed); break;
|
||||
case SDL_SCANCODE_RIGHT: joysticks[0]->set_digital_input(Inputs::Joystick::DigitalInput::Right, is_pressed); break;
|
||||
case SDL_SCANCODE_UP: joysticks[0]->set_digital_input(Inputs::Joystick::DigitalInput::Up, is_pressed); break;
|
||||
case SDL_SCANCODE_DOWN: joysticks[0]->set_digital_input(Inputs::Joystick::DigitalInput::Down, is_pressed); break;
|
||||
case SDL_SCANCODE_SPACE: joysticks[0]->set_digital_input(Inputs::Joystick::DigitalInput::Fire, is_pressed); break;
|
||||
case SDL_SCANCODE_A: joysticks[0]->set_digital_input(Inputs::Joystick::DigitalInput(Inputs::Joystick::DigitalInput::Fire, 0), is_pressed); break;
|
||||
case SDL_SCANCODE_S: joysticks[0]->set_digital_input(Inputs::Joystick::DigitalInput(Inputs::Joystick::DigitalInput::Fire, 1), is_pressed); break;
|
||||
case SDL_SCANCODE_LEFT: joysticks[0]->set_input(Inputs::Joystick::Input::Left, is_pressed); break;
|
||||
case SDL_SCANCODE_RIGHT: joysticks[0]->set_input(Inputs::Joystick::Input::Right, is_pressed); break;
|
||||
case SDL_SCANCODE_UP: joysticks[0]->set_input(Inputs::Joystick::Input::Up, is_pressed); break;
|
||||
case SDL_SCANCODE_DOWN: joysticks[0]->set_input(Inputs::Joystick::Input::Down, is_pressed); break;
|
||||
case SDL_SCANCODE_SPACE: joysticks[0]->set_input(Inputs::Joystick::Input::Fire, is_pressed); break;
|
||||
case SDL_SCANCODE_A: joysticks[0]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Fire, 0), is_pressed); break;
|
||||
case SDL_SCANCODE_S: joysticks[0]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Fire, 1), is_pressed); break;
|
||||
default: {
|
||||
const char *key_name = SDL_GetKeyName(event.key.keysym.sym);
|
||||
joysticks[0]->set_digital_input(Inputs::Joystick::DigitalInput(key_name[0]), is_pressed);
|
||||
joysticks[0]->set_input(Inputs::Joystick::Input(key_name[0]), is_pressed);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user