1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-23 21:29:11 +00:00

Merge pull request #500 from TomHarte/MacJoysticks

Implements initial joystick support for the Mac
This commit is contained in:
Thomas Harte 2018-07-22 16:56:40 -04:00 committed by GitHub
commit 6a73fe7d65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 525 additions and 0 deletions

View File

@ -128,6 +128,24 @@ class Joystick {
set_input(input, 0.5f);
}
}
/*!
Gets the number of input fire buttons.
This is cached by default, but it's virtual so overridable.
*/
virtual int get_number_of_fire_buttons() {
if(number_of_buttons_ >= 0) return number_of_buttons_;
number_of_buttons_ = 0;
for(const auto &input: get_inputs()) {
if(input.type == Input::Type::Fire) ++number_of_buttons_;
}
return number_of_buttons_;
}
private:
int number_of_buttons_ = -1;
};
/*!

View File

@ -605,6 +605,7 @@
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF990A1C8FBA6F0075DAFB /* CRTOpenGL.cpp */; };
4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF99121C8FBA6F0075DAFB /* TextureTarget.cpp */; };
4BBFBB6C1EE8401E00C01E7A /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBFBB6A1EE8401E00C01E7A /* ZX8081.cpp */; };
4BBFE83D21015D9C00BF1C40 /* CSJoystickManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BBFE83C21015D9C00BF1C40 /* CSJoystickManager.m */; };
4BBFFEE61F7B27F1005F3FEB /* TrackSerialiser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBFFEE51F7B27F1005F3FEB /* TrackSerialiser.cpp */; };
4BC39568208EE6CF0044766B /* DiskIICard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC39566208EE6CF0044766B /* DiskIICard.cpp */; };
4BC39569208EE6CF0044766B /* DiskIICard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC39566208EE6CF0044766B /* DiskIICard.cpp */; };
@ -1345,6 +1346,8 @@
4BBF99191C8FC2750075DAFB /* CRTTypes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CRTTypes.hpp; sourceTree = "<group>"; };
4BBFBB6A1EE8401E00C01E7A /* ZX8081.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ZX8081.cpp; path = Parsers/ZX8081.cpp; sourceTree = "<group>"; };
4BBFBB6B1EE8401E00C01E7A /* ZX8081.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ZX8081.hpp; path = Parsers/ZX8081.hpp; sourceTree = "<group>"; };
4BBFE83C21015D9C00BF1C40 /* CSJoystickManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSJoystickManager.m; sourceTree = "<group>"; };
4BBFE83E21015DAE00BF1C40 /* CSJoystickManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSJoystickManager.h; sourceTree = "<group>"; };
4BBFFEE51F7B27F1005F3FEB /* TrackSerialiser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TrackSerialiser.cpp; sourceTree = "<group>"; };
4BC39565208EDFCE0044766B /* Card.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Card.hpp; sourceTree = "<group>"; };
4BC39566208EE6CF0044766B /* DiskIICard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DiskIICard.cpp; sourceTree = "<group>"; };
@ -2760,6 +2763,7 @@
4BB73EA01B587A5100552FC2 /* Clock Signal */ = {
isa = PBXGroup;
children = (
4BBFE83B21015D9C00BF1C40 /* Joystick Manager */,
4BB73ECF1B587A6700552FC2 /* Clock Signal.entitlements */,
4B1414501B58848C00E04248 /* ClockSignal-Bridging-Header.h */,
4BB73EAD1B587A5100552FC2 /* Info.plist */,
@ -2916,6 +2920,15 @@
path = Internals;
sourceTree = "<group>";
};
4BBFE83B21015D9C00BF1C40 /* Joystick Manager */ = {
isa = PBXGroup;
children = (
4BBFE83C21015D9C00BF1C40 /* CSJoystickManager.m */,
4BBFE83E21015DAE00BF1C40 /* CSJoystickManager.h */,
);
path = "Joystick Manager";
sourceTree = "<group>";
};
4BC3B74C1CD194CC00F86E85 /* Shaders */ = {
isa = PBXGroup;
children = (
@ -3824,6 +3837,7 @@
4B4518A21F75FD1C00926311 /* G64.cpp in Sources */,
4B89452C201967B4007DE474 /* Tape.cpp in Sources */,
4B448E811F1C45A00009ABD6 /* TZX.cpp in Sources */,
4BBFE83D21015D9C00BF1C40 /* CSJoystickManager.m in Sources */,
4BEBFB512002DB30000708CC /* DiskROM.cpp in Sources */,
4B89451C201967B4007DE474 /* Disk.cpp in Sources */,
4B302184208A550100773308 /* DiskII.cpp in Sources */,

View File

@ -4,6 +4,10 @@
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.device.bluetooth</key>
<true/>
<key>com.apple.security.device.usb</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
</dict>

View File

@ -12,6 +12,8 @@
#import "CSOpenGLView.h"
#import "CSAudioQueue.h"
#import "CSBestEffortUpdater.h"
#import "CSJoystickManager.h"
#include "KeyCodes.h"

View File

@ -9,4 +9,5 @@
import Cocoa
class DocumentController: NSDocumentController {
let joystickManager = CSJoystickManager()
}

View File

@ -229,6 +229,13 @@ class MachineDocument:
func windowDidResignKey(_ notification: Notification) {
if let machine = self.machine {
machine.clearAllKeys()
machine.joystickManager = nil
}
}
func windowDidBecomeKey(_ notification: Notification) {
if let machine = self.machine {
machine.joystickManager = (DocumentController.shared as! DocumentController).joystickManager
}
}

View File

@ -0,0 +1,80 @@
//
// CSJoystickManager.h
// Clock Signal
//
// Created by Thomas Harte on 19/07/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
//
#import <Foundation/Foundation.h>
/*!
Models a single joystick button.
Buttons have an index and are either currently pressed, or not.
*/
@interface CSJoystickButton: NSObject
/// The button index. By convention the USB spec defines the first button as number 1.
@property(nonatomic, readonly) NSInteger index;
@property(nonatomic, readonly) bool isPressed;
@end
typedef NS_ENUM(NSInteger, CSJoystickAxisType) {
CSJoystickAxisTypeX,
CSJoystickAxisTypeY,
CSJoystickAxisTypeZ,
};
/*!
Models a joystick axis.
Axes have a nominated type and a continuous value between 0 and 1.
*/
@interface CSJoystickAxis: NSObject
@property(nonatomic, readonly) CSJoystickAxisType type;
/// The current position of this axis in the range [0, 1].
@property(nonatomic, readonly) float position;
@end
typedef NS_OPTIONS(NSInteger, CSJoystickHatDirection) {
CSJoystickHatDirectionUp = 1 << 0,
CSJoystickHatDirectionDown = 1 << 1,
CSJoystickHatDirectionLeft = 1 << 2,
CSJoystickHatDirectionRight = 1 << 3,
};
/*!
Models a joystick hat.
A hat is a digital directional input, so e.g. this is how thumbpads are represented.
*/
@interface CSJoystickHat: NSObject
@property(nonatomic, readonly) CSJoystickHatDirection direction;
@end
/*!
Models a joystick.
A joystick is a collection of buttons, axes and hats, each of which holds a current
state. The holder must use @c update to cause this joystick to read a fresh copy
of its state.
*/
@interface CSJoystick: NSObject
@property(nonatomic, readonly) NSArray<CSJoystickButton *> *buttons;
@property(nonatomic, readonly) NSArray<CSJoystickAxis *> *axes;
@property(nonatomic, readonly) NSArray<CSJoystickHat *> *hats;
- (void)update;
@end
/*!
The joystick manager watches for joystick connections and disconnections and
offers a list of joysticks currently attached.
Be warned: this means using Apple's IOKit directly to watch for Bluetooth and
USB HID devices. So to use this code, make sure you have USB and Bluetooth
enabled for the app's sandbox.
*/
@interface CSJoystickManager : NSObject
@property(nonatomic, readonly) NSArray<CSJoystick *> *joysticks;
/// Updates all joysticks.
- (void)update;
@end

View File

@ -0,0 +1,333 @@
//
// CSJoystickManager.m
// Clock Signal
//
// Created by Thomas Harte on 19/07/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
//
#import "CSJoystickManager.h"
@import IOKit;
#include <IOKit/hid/IOHIDLib.h>
#pragma mark - CSJoystickButton
@implementation CSJoystickButton {
IOHIDElementRef _element;
}
- (instancetype)initWithElement:(IOHIDElementRef)element index:(NSInteger)index {
self = [super init];
if(self) {
_index = index;
_element = (IOHIDElementRef)CFRetain(element);
}
return self;
}
- (void)dealloc {
CFRelease(_element);
}
- (NSString *)description {
return [NSString stringWithFormat:@"<CSJoystickButton: %p>; button %ld, %@", self, (long)self.index, self.isPressed ? @"pressed" : @"released"];
}
- (IOHIDElementRef)element {
return _element;
}
- (void)setIsPressed:(bool)isPressed {
_isPressed = isPressed;
}
@end
#pragma mark - CSJoystickAxis
@implementation CSJoystickAxis {
IOHIDElementRef _element;
}
- (instancetype)initWithElement:(IOHIDElementRef)element type:(CSJoystickAxisType)type {
self = [super init];
if(self) {
_element = (IOHIDElementRef)CFRetain(element);
_type = type;
_position = 0.5f;
}
return self;
}
- (void)dealloc {
CFRelease(_element);
}
- (NSString *)description {
return [NSString stringWithFormat:@"<CSJoystickAxis: %p>; type %d, value %0.2f", self, (int)self.type, self.position];
}
- (IOHIDElementRef)element {
return _element;
}
- (void)setPosition:(float)position {
_position = position;
}
@end
#pragma mark - CSJoystickHat
@implementation CSJoystickHat {
IOHIDElementRef _element;
}
- (instancetype)initWithElement:(IOHIDElementRef)element {
self = [super init];
if(self) {
_element = (IOHIDElementRef)CFRetain(element);
}
return self;
}
- (void)dealloc {
CFRelease(_element);
}
- (NSString *)description {
return [NSString stringWithFormat:@"<CSJoystickHat: %p>; direction %ld", self, (long)self.direction];
}
- (IOHIDElementRef)element {
return _element;
}
- (void)setDirection:(CSJoystickHatDirection)direction {
_direction = direction;
}
@end
#pragma mark - CSJoystick
@implementation CSJoystick {
IOHIDDeviceRef _device;
}
- (instancetype)initWithButtons:(NSArray<CSJoystickButton *> *)buttons
axes:(NSArray<CSJoystickAxis *> *)axes
hats:(NSArray<CSJoystickHat *> *)hats
device:(IOHIDDeviceRef)device {
self = [super init];
if(self) {
// Sort buttons by index.
_buttons = [buttons sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"index" ascending:YES]]];
// Sort axes by enum value.
_axes = [axes sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"type" ascending:YES]]];
// Hats have no guaranteed ordering.
_hats = hats;
// Keep hold of the device.
_device = (IOHIDDeviceRef)CFRetain(device);
}
return self;
}
- (void)dealloc {
CFRelease(_device);
}
- (NSString *)description {
return [NSString stringWithFormat:@"<CSJoystick: %p>; buttons %@, axes %@, hats %@", self, self.buttons, self.axes, self.hats];
}
- (void)update {
// Update buttons.
for(CSJoystickButton *button in _buttons) {
IOHIDValueRef value;
if(IOHIDDeviceGetValue(_device, button.element, &value) == kIOReturnSuccess) {
// Some pressure-sensitive buttons return values greater than 1 for hard presses,
// but this class rationalised everything to Boolean.
button.isPressed = !!IOHIDValueGetIntegerValue(value);
}
}
// Update hats.
for(CSJoystickHat *hat in _hats) {
IOHIDValueRef value;
if(IOHIDDeviceGetValue(_device, hat.element, &value) == kIOReturnSuccess) {
// Hats report a direction, which is either one of eight or one of four.
CFIndex integerValue = IOHIDValueGetIntegerValue(value) - IOHIDElementGetLogicalMin(hat.element);
const CFIndex range = 1 + IOHIDElementGetLogicalMax(hat.element) - IOHIDElementGetLogicalMin(hat.element);
integerValue *= 8 / range;
// Map from the HID direction to the bit field.
switch(integerValue) {
default: hat.direction = 0; break;
case 0: hat.direction = CSJoystickHatDirectionUp; break;
case 1: hat.direction = CSJoystickHatDirectionUp | CSJoystickHatDirectionRight; break;
case 2: hat.direction = CSJoystickHatDirectionRight; break;
case 3: hat.direction = CSJoystickHatDirectionRight | CSJoystickHatDirectionDown; break;
case 4: hat.direction = CSJoystickHatDirectionDown; break;
case 5: hat.direction = CSJoystickHatDirectionDown | CSJoystickHatDirectionLeft; break;
case 6: hat.direction = CSJoystickHatDirectionLeft; break;
case 7: hat.direction = CSJoystickHatDirectionLeft | CSJoystickHatDirectionUp; break;
}
}
}
// Update axes.
for(CSJoystickAxis *axis in _axes) {
IOHIDValueRef value;
if(IOHIDDeviceGetValue(_device, axis.element, &value) == kIOReturnSuccess) {
const CFIndex integerValue = IOHIDValueGetIntegerValue(value) - IOHIDElementGetLogicalMin(axis.element);
const CFIndex range = 1 + IOHIDElementGetLogicalMax(axis.element) - IOHIDElementGetLogicalMin(axis.element);
axis.position = (float)integerValue / (float)range;
}
}
}
- (IOHIDDeviceRef)device {
return _device;
}
@end
#pragma mark - CSJoystickManager
@interface CSJoystickManager ()
- (void)deviceMatched:(IOHIDDeviceRef)device result:(IOReturn)result sender:(void *)sender;
- (void)deviceRemoved:(IOHIDDeviceRef)device result:(IOReturn)result sender:(void *)sender;
@end
static void DeviceMatched(void *context, IOReturn result, void *sender, IOHIDDeviceRef device) {
[(__bridge CSJoystickManager *)context deviceMatched:device result:result sender:sender];
}
static void DeviceRemoved(void *context, IOReturn result, void *sender, IOHIDDeviceRef device) {
[(__bridge CSJoystickManager *)context deviceRemoved:device result:result sender:sender];
}
@implementation CSJoystickManager {
IOHIDManagerRef _hidManager;
NSMutableArray<CSJoystick *> *_joysticks;
}
- (instancetype)init {
self = [super init];
if(self) {
_joysticks = [[NSMutableArray alloc] init];
_hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
if(!_hidManager) return nil;
NSArray<NSDictionary<NSString *, NSNumber *> *> *const multiple = @[
@{ @kIOHIDDeviceUsagePageKey: @(kHIDPage_GenericDesktop), @kIOHIDDeviceUsageKey: @(kHIDUsage_GD_Joystick) },
@{ @kIOHIDDeviceUsagePageKey: @(kHIDPage_GenericDesktop), @kIOHIDDeviceUsageKey: @(kHIDUsage_GD_GamePad) },
@{ @kIOHIDDeviceUsagePageKey: @(kHIDPage_GenericDesktop), @kIOHIDDeviceUsageKey: @(kHIDUsage_GD_MultiAxisController) },
];
IOHIDManagerSetDeviceMatchingMultiple(_hidManager, (__bridge CFArrayRef)multiple);
IOHIDManagerRegisterDeviceMatchingCallback(_hidManager, DeviceMatched, (__bridge void *)self);
IOHIDManagerRegisterDeviceRemovalCallback(_hidManager, DeviceRemoved, (__bridge void *)self);
IOHIDManagerScheduleWithRunLoop(_hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
if(IOHIDManagerOpen(_hidManager, kIOHIDOptionsTypeNone) != kIOReturnSuccess) {
NSLog(@"Failed to open HID manager");
// something
return nil;
}
}
return self;
}
- (void)dealloc {
IOHIDManagerUnscheduleFromRunLoop(_hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
IOHIDManagerClose(_hidManager, kIOHIDOptionsTypeNone);
CFRelease(_hidManager);
}
- (void)deviceMatched:(IOHIDDeviceRef)device result:(IOReturn)result sender:(void *)sender {
// Double check this joystick isn't already known.
for(CSJoystick *joystick in _joysticks) {
if(joystick.device == device) return;
}
// Prepare to collate a list of buttons, axes and hats for the new device.
NSMutableArray<CSJoystickButton *> *buttons = [[NSMutableArray alloc] init];
NSMutableArray<CSJoystickAxis *> *axes = [[NSMutableArray alloc] init];
NSMutableArray<CSJoystickHat *> *hats = [[NSMutableArray alloc] init];
// Inspect all elements for those that are comprehensible to this code.
const CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone);
for(CFIndex index = 0; index < CFArrayGetCount(elements); ++index) {
const IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, index);
// Check that this element is either on the generic desktop page or else is a button.
const uint32_t usagePage = IOHIDElementGetUsagePage(element);
if(usagePage != kHIDPage_GenericDesktop && usagePage != kHIDPage_Button) continue;
// Then inspect the type.
switch(IOHIDElementGetType(element)) {
default: break;
case kIOHIDElementTypeInput_Button: {
// Add a button; pretty easy stuff. 'Usage' provides a button index.
const uint32_t usage = IOHIDElementGetUsage(element);
[buttons addObject:[[CSJoystickButton alloc] initWithElement:element index:usage]];
} break;
case kIOHIDElementTypeInput_Misc:
case kIOHIDElementTypeInput_Axis: {
CSJoystickAxisType axisType;
switch(IOHIDElementGetUsage(element)) {
default: continue;
// Three analogue axes are implemented here; there are another three sets
// of these that could be parsed in the future if interesting.
case kHIDUsage_GD_X: axisType = CSJoystickAxisTypeX; break;
case kHIDUsage_GD_Y: axisType = CSJoystickAxisTypeY; break;
case kHIDUsage_GD_Z: axisType = CSJoystickAxisTypeZ; break;
// A hatswitch is a multi-directional control all of its own.
case kHIDUsage_GD_Hatswitch:
[hats addObject:[[CSJoystickHat alloc] initWithElement:element]];
continue;
}
// Add the axis; if it was a hat switch or unrecognised then the code doesn't
// reach here.
[axes addObject:[[CSJoystickAxis alloc] initWithElement:element type:axisType]];
} break;
}
}
CFRelease(elements);
// Add this joystick to the list.
[_joysticks addObject:[[CSJoystick alloc] initWithButtons:buttons axes:axes hats:hats device:device]];
}
- (void)deviceRemoved:(IOHIDDeviceRef)device result:(IOReturn)result sender:(void *)sender {
// If this joystick was recorded, remove it.
for(CSJoystick *joystick in [_joysticks copy]) {
if(joystick.device == device) {
[_joysticks removeObject:joystick];
return;
}
}
}
- (void)update {
[self.joysticks makeObjectsPerformSelector:@selector(update)];
}
- (NSArray<CSJoystick *> *)joysticks {
return [_joysticks copy];
}
@end

View File

@ -12,6 +12,7 @@
#import "CSFastLoading.h"
#import "CSOpenGLView.h"
#import "CSStaticAnalyser.h"
#import "CSJoystickManager.h"
@class CSMachine;
@protocol CSMachineDelegate
@ -74,6 +75,7 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
@property (nonatomic, readonly) BOOL hasKeyboard;
@property (nonatomic, readonly) BOOL hasJoystick;
@property (nonatomic, assign) CSMachineKeyboardInputMode inputMode;
@property (nonatomic, nullable) CSJoystickManager *joystickManager;
// LED list.
@property (nonatomic, readonly, nonnull) NSArray<NSString *> *leds;

View File

@ -75,7 +75,9 @@ struct ActivityObserver: public Activity::Observer {
CSStaticAnalyser *_analyser;
std::unique_ptr<Machine::DynamicMachine> _machine;
JoystickMachine::Machine *_joystickMachine;
CSJoystickManager *_joystickManager;
std::bitset<65536> _depressedKeys;
NSMutableArray<NSString *> *_leds;
}
@ -102,6 +104,8 @@ struct ActivityObserver: public Activity::Observer {
_speakerDelegate.machine = self;
_speakerDelegate.machineAccessLock = _delegateMachineAccessLock;
_joystickMachine = _machine->joystick_machine();
}
return self;
}
@ -162,6 +166,54 @@ struct ActivityObserver: public Activity::Observer {
- (void)runForInterval:(NSTimeInterval)interval {
@synchronized(self) {
if(_joystickMachine && _joystickManager) {
[_joystickManager update];
// TODO: configurable mapping from physical joypad inputs to machine inputs.
// Until then, apply a default mapping.
size_t c = 0;
std::vector<std::unique_ptr<Inputs::Joystick>> &machine_joysticks = _joystickMachine->get_joysticks();
for(CSJoystick *joystick in _joystickManager.joysticks) {
size_t target = c % machine_joysticks.size();
++++c;
// Post the first two analogue axes presented by the controller as horizontal and vertical inputs,
// unless the user seems to be using a hat.
// SDL will return a value in the range [-32768, 32767], so map from that to [0, 1.0]
if(!joystick.hats.count || !joystick.hats[0].direction) {
if(joystick.axes.count > 0) {
const float x_axis = joystick.axes[0].position;
machine_joysticks[target]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Type::Horizontal), x_axis);
}
if(joystick.axes.count > 1) {
const float y_axis = joystick.axes[1].position;
machine_joysticks[target]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Type::Vertical), y_axis);
}
}
// Forward hats as directions; hats always override analogue inputs.
for(CSJoystickHat *hat in joystick.hats) {
machine_joysticks[target]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Type::Up), !!(hat.direction & CSJoystickHatDirectionUp));
machine_joysticks[target]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Type::Down), !!(hat.direction & CSJoystickHatDirectionDown));
machine_joysticks[target]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Type::Left), !!(hat.direction & CSJoystickHatDirectionLeft));
machine_joysticks[target]->set_input(Inputs::Joystick::Input(Inputs::Joystick::Input::Type::Right), !!(hat.direction & CSJoystickHatDirectionRight));
}
// Forward all fire buttons, mapping as a function of index.
if(machine_joysticks[target]->get_number_of_fire_buttons()) {
std::vector<bool> button_states((size_t)machine_joysticks[target]->get_number_of_fire_buttons());
for(CSJoystickButton *button in joystick.buttons) {
if(button.isPressed) button_states[(size_t)(((int)button.index - 1) % machine_joysticks[target]->get_number_of_fire_buttons())] = true;
}
for(size_t index = 0; index < button_states.size(); ++index) {
machine_joysticks[target]->set_input(
Inputs::Joystick::Input(Inputs::Joystick::Input::Type::Fire, index),
button_states[index]);
}
}
}
}
_machine->crt_machine()->run_for(interval);
}
}
@ -198,6 +250,18 @@ struct ActivityObserver: public Activity::Observer {
}
}
- (void)setJoystickManager:(CSJoystickManager *)joystickManager {
@synchronized(self) {
_joystickManager = joystickManager;
if(_joystickMachine) {
std::vector<std::unique_ptr<Inputs::Joystick>> &machine_joysticks = _joystickMachine->get_joysticks();
for(const auto &joystick: machine_joysticks) {
joystick->reset_all_inputs();
}
}
}
}
- (void)setKey:(uint16_t)key characters:(NSString *)characters isPressed:(BOOL)isPressed {
auto keyboard_machine = _machine->keyboard_machine();
if(self.inputMode == CSMachineKeyboardInputModeKeyboard && keyboard_machine) {