From c26c8992ae37f89a80891a44a91fc55d1615a441 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 8 Feb 2020 21:27:04 -0500 Subject: [PATCH] Reintroduces joystick support; eliminates CSBestEffortUpdater. --- .../Clock Signal.xcodeproj/project.pbxproj | 14 --- .../ClockSignal-Bridging-Header.h | 1 - .../Mac/Clock Signal/Machine/CSMachine.h | 2 - .../Mac/Clock Signal/Machine/CSMachine.mm | 108 ++++++++++-------- .../Updater/CSBestEffortUpdater.h | 27 ----- .../Updater/CSBestEffortUpdater.mm | 51 --------- 6 files changed, 61 insertions(+), 142 deletions(-) delete mode 100644 OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.h delete mode 100644 OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.mm diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index f4165bef8..751840b21 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -812,7 +812,6 @@ 4BD4A8D01E077FD20020D856 /* PCMTrackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */; }; 4BD5D2682199148100DDF17D /* ScanTargetGLSLFragments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5D2672199148100DDF17D /* ScanTargetGLSLFragments.cpp */; }; 4BD5D2692199148100DDF17D /* ScanTargetGLSLFragments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5D2672199148100DDF17D /* ScanTargetGLSLFragments.cpp */; }; - 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.mm */; }; 4BD61664206B2AC800236112 /* QuickLoadOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4BD61662206B2AC700236112 /* QuickLoadOptions.xib */; }; 4BD67DCB209BE4D700AB2146 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD67DCA209BE4D600AB2146 /* StaticAnalyser.cpp */; }; 4BD67DCC209BE4D700AB2146 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD67DCA209BE4D600AB2146 /* StaticAnalyser.cpp */; }; @@ -1689,8 +1688,6 @@ 4BD468F61D8DF41D0084958B /* 1770.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = 1770.hpp; path = 1770/1770.hpp; sourceTree = ""; }; 4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PCMTrackTests.mm; sourceTree = ""; }; 4BD5D2672199148100DDF17D /* ScanTargetGLSLFragments.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTargetGLSLFragments.cpp; sourceTree = ""; }; - 4BD5F1931D13528900631CD1 /* CSBestEffortUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSBestEffortUpdater.h; path = Updater/CSBestEffortUpdater.h; sourceTree = ""; }; - 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSBestEffortUpdater.mm; path = Updater/CSBestEffortUpdater.mm; sourceTree = ""; }; 4BD601A920D89F2A00CBCE57 /* Log.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Log.hpp; path = ../../Outputs/Log.hpp; sourceTree = ""; }; 4BD61663206B2AC700236112 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/QuickLoadOptions.xib"; sourceTree = SOURCE_ROOT; }; 4BD67DC9209BE4D600AB2146 /* StaticAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StaticAnalyser.hpp; sourceTree = ""; }; @@ -3276,7 +3273,6 @@ 4BB73EAA1B587A5100552FC2 /* MainMenu.xib */, 4BE5F85A1C3E1C2500C43F01 /* Resources */, 4BDA00DB22E60EE900AC3CD0 /* ROMRequester */, - 4BD5F1961D1352A000631CD1 /* Updater */, 4B55CE5A1C3B7D6F0093A61B /* Views */, ); path = "Clock Signal"; @@ -3624,15 +3620,6 @@ name = 1770; sourceTree = ""; }; - 4BD5F1961D1352A000631CD1 /* Updater */ = { - isa = PBXGroup; - children = ( - 4BD5F1931D13528900631CD1 /* CSBestEffortUpdater.h */, - 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.mm */, - ); - name = Updater; - sourceTree = ""; - }; 4BD67DC8209BE4D600AB2146 /* DiskII */ = { isa = PBXGroup; children = ( @@ -4493,7 +4480,6 @@ 4B4518A41F75FD1C00926311 /* OricMFMDSK.cpp in Sources */, 4B4B1A3C200198CA00A0F866 /* KonamiSCC.cpp in Sources */, 4BB0A65B2044FD3000FB3688 /* SN76489.cpp in Sources */, - 4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.mm in Sources */, 4B894532201967B4007DE474 /* 6502.cpp in Sources */, 4BDB61EC203285AE0048AF91 /* Atari2600OptionsPanel.swift in Sources */, 4BBB70A8202014E2002FE009 /* MultiCRTMachine.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal/ClockSignal-Bridging-Header.h b/OSBindings/Mac/Clock Signal/ClockSignal-Bridging-Header.h index c39778ffd..ebbf066d9 100644 --- a/OSBindings/Mac/Clock Signal/ClockSignal-Bridging-Header.h +++ b/OSBindings/Mac/Clock Signal/ClockSignal-Bridging-Header.h @@ -13,7 +13,6 @@ #import "CSOpenGLView.h" #import "CSROMReceiverView.h" -#import "CSBestEffortUpdater.h" #import "CSJoystickManager.h" #import "NSData+CRC32.h" diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h index 18bda6979..844f20243 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h @@ -57,8 +57,6 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) { */ - (nullable instancetype)initWithAnalyser:(nonnull CSStaticAnalyser *)result missingROMs:(nullable inout NSMutableArray *)missingROMs NS_DESIGNATED_INITIALIZER; -- (NSTimeInterval)runForInterval:(NSTimeInterval)interval untilEvent:(int)events; - - (float)idealSamplingRateFromRange:(NSRange)range; - (void)setAudioSamplingRate:(float)samplingRate bufferSize:(NSUInteger)bufferSize; diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 19f8d1292..dad64bee8 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -159,6 +159,8 @@ struct ActivityObserver: public Activity::Observer { double _refreshPeriod; BOOL _isSyncLocking; + NSTimer *_joystickTimer; + std::unique_ptr _scanTarget; } @@ -211,6 +213,7 @@ struct ActivityObserver: public Activity::Observer { _speakerDelegate.machineAccessLock = _delegateMachineAccessLock; _joystickMachine = _machine->joystick_machine(); + [self updateJoystickTimer]; _isUpdating.clear(); } return self; @@ -225,6 +228,8 @@ struct ActivityObserver: public Activity::Observer { } - (void)dealloc { + [_joystickTimer invalidate]; + // The two delegate's references to this machine are nilled out here because close_output may result // in a data flush, which might cause an audio callback, which could cause the audio queue to decide // that it's out of data, resulting in an attempt further to run the machine while it is dealloc'ing. @@ -270,57 +275,64 @@ struct ActivityObserver: public Activity::Observer { } } -- (NSTimeInterval)runForInterval:(NSTimeInterval)interval untilEvent:(int)events { +- (void)updateJoystickTimer { + // Joysticks updates are scheduled for a nominal 200 polls/second, using a plain old NSTimer. + if(_joystickMachine && _joystickManager) { + _joystickTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 / 200.0 target:self selector:@selector(updateJoysticks) userInfo:nil repeats:YES]; + } else { + [_joystickTimer invalidate]; + _joystickTimer = nil; + } +} + +- (void)updateJoysticks { + [_joystickManager update]; + + // TODO: configurable mapping from physical joypad inputs to machine inputs. + // Until then, apply a default mapping. + @synchronized(self) { - if(_joystickMachine && _joystickManager) { - [_joystickManager update]; + size_t c = 0; + auto &machine_joysticks = _joystickMachine->get_joysticks(); + for(CSJoystick *joystick in _joystickManager.joysticks) { + size_t target = c % machine_joysticks.size(); + ++c; - // TODO: configurable mapping from physical joypad inputs to machine inputs. - // Until then, apply a default mapping. - - size_t c = 0; - auto &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); - } - } else { - // 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)); - } + // 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); + } + } else { + // 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 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]); - } + // Forward all fire buttons, mapping as a function of index. + if(machine_joysticks[target]->get_number_of_fire_buttons()) { + std::vector 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]); } } } - return _machine->crt_machine()->run_until(interval, events); } } @@ -387,15 +399,17 @@ struct ActivityObserver: public Activity::Observer { } - (void)setJoystickManager:(CSJoystickManager *)joystickManager { - @synchronized(self) { - _joystickManager = joystickManager; - if(_joystickMachine) { + _joystickManager = joystickManager; + if(_joystickMachine) { + @synchronized(self) { auto &machine_joysticks = _joystickMachine->get_joysticks(); for(const auto &joystick: machine_joysticks) { joystick->reset_all_inputs(); } } } + + [self updateJoystickTimer]; } - (void)setKey:(uint16_t)key characters:(NSString *)characters isPressed:(BOOL)isPressed { diff --git a/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.h b/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.h deleted file mode 100644 index 970104107..000000000 --- a/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// CSBestEffortUpdater.h -// Clock Signal -// -// Created by Thomas Harte on 16/06/2016. -// Copyright 2016 Thomas Harte. All rights reserved. -// - -#import -#import - -#import "CSMachine.h" - -// The following is coupled to the definitions in CRTMachine.hpp, but exposed here -// for the benefit of Swift. -typedef NS_ENUM(NSInteger, CSBestEffortUpdaterEvent) { - CSBestEffortUpdaterEventAudioNeeded = 1 << 0 -}; - -@interface CSBestEffortUpdater : NSObject - -- (void)update; -- (void)updateWithEvent:(CSBestEffortUpdaterEvent)event; -- (void)flush; -- (void)setMachine:(CSMachine *)machine; - -@end diff --git a/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.mm b/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.mm deleted file mode 100644 index a0ba3e21f..000000000 --- a/OSBindings/Mac/Clock Signal/Updater/CSBestEffortUpdater.mm +++ /dev/null @@ -1,51 +0,0 @@ -// -// CSBestEffortUpdater.m -// Clock Signal -// -// Created by Thomas Harte on 16/06/2016. -// Copyright 2016 Thomas Harte. All rights reserved. -// - -#import "CSBestEffortUpdater.h" - -#include "BestEffortUpdater.hpp" - -struct UpdaterDelegate: public Concurrency::BestEffortUpdater::Delegate { - __weak CSMachine *machine; - - Time::Seconds update(Concurrency::BestEffortUpdater *updater, Time::Seconds seconds, bool did_skip_previous_update, int flags) final { - return [machine runForInterval:seconds untilEvent:flags]; - } -}; - -@implementation CSBestEffortUpdater { - Concurrency::BestEffortUpdater _updater; - UpdaterDelegate _updaterDelegate; -} - -- (instancetype)init { - self = [super init]; - if(self) { - _updater.set_delegate(&_updaterDelegate); - } - return self; -} - -- (void)update { - _updater.update(); -} - -- (void)updateWithEvent:(CSBestEffortUpdaterEvent)event { - _updater.update((int)event); -} - -- (void)flush { - _updater.flush(); -} - -- (void)setMachine:(CSMachine *)machine { - _updater.flush(); - _updaterDelegate.machine = machine; -} - -@end