From 51da21b844a387bdf57d3596adf1f50dc0d959dd Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 13 Jun 2018 19:22:34 -0400 Subject: [PATCH] Formally introduces keyboard-as-joystick for the Mac, and fixes discovered joystick issues. Specifically: * digital to analogue conversion not returning to centre; * Apple II axes being the wrong way around; and * Apple II buttons using improper selection logic. --- Inputs/Joystick.hpp | 10 +- Machines/AppleII/AppleII.cpp | 22 +- .../Mac/Clock Signal/Base.lproj/MainMenu.xib | 223 ++---------------- .../Documents/MachineDocument.swift | 34 +++ .../Mac/Clock Signal/Machine/CSMachine.h | 10 + .../Mac/Clock Signal/Machine/CSMachine.mm | 18 +- 6 files changed, 98 insertions(+), 219 deletions(-) diff --git a/Inputs/Joystick.hpp b/Inputs/Joystick.hpp index 6d9c0ed80..ca0c39085 100644 --- a/Inputs/Joystick.hpp +++ b/Inputs/Joystick.hpp @@ -168,11 +168,11 @@ class ConcreteJoystick: public Joystick { 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; + 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; } } diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index 3ff718594..21305ad4a 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -192,11 +192,14 @@ class ConcreteMachine: }) {} void did_set_input(const Input &input, float value) override { - axes[(input.type == Input::Type::Horizontal) ? 1 : 0] = value; + 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 { - buttons[input.info.control.index] = value; + if(input.type == Input::Type::Fire && input.info.control.index < 3) { + buttons[input.info.control.index] = value; + } } bool buttons[3] = {false, false, false}; @@ -430,16 +433,19 @@ class ConcreteMachine: break; case 0xc061: // Switch input 0. - if(!static_cast(joysticks_[0].get())->buttons[0] || !static_cast(joysticks_[1].get())->buttons[2]) - *value &= 0x7f; + *value &= 0x7f; + if(static_cast(joysticks_[0].get())->buttons[0] || static_cast(joysticks_[1].get())->buttons[2]) + *value |= 0x80; break; case 0xc062: // Switch input 1. - if(!static_cast(joysticks_[0].get())->buttons[1] || !static_cast(joysticks_[1].get())->buttons[1]) - *value &= 0x7f; + *value &= 0x7f; + if(static_cast(joysticks_[0].get())->buttons[1] || static_cast(joysticks_[1].get())->buttons[1]) + *value |= 0x80; break; case 0xc063: // Switch input 2. - if(!static_cast(joysticks_[0].get())->buttons[2] || !static_cast(joysticks_[1].get())->buttons[0]) - *value &= 0x7f; + *value &= 0x7f; + if(static_cast(joysticks_[0].get())->buttons[2] || static_cast(joysticks_[1].get())->buttons[0]) + *value |= 0x80; break; case 0xc064: // Analogue input 0. diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib b/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib index 9a56125ec..ccd7fca32 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib @@ -1,8 +1,8 @@ - + - + @@ -129,22 +129,6 @@ - - - - - - - - - - - - - - - - @@ -155,192 +139,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -357,6 +155,23 @@ + + + + + + + + + + + + + + + + + diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 8d5453830..61b403bd0 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -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) + } } diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h index 9d07af255..4b0ceddb8 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h @@ -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; diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index a91c3e246..a0ca43fb1 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -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,7 +250,7 @@ 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> &joysticks = joystick_machine->get_joysticks(); if(!joysticks.empty()) { @@ -262,6 +264,8 @@ struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate, public LockP 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_input(Inputs::Joystick::Input([characters characterAtIndex:0]), is_pressed); @@ -394,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