1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 08:49:37 +00:00

Reintroduces joystick support; eliminates CSBestEffortUpdater.

This commit is contained in:
Thomas Harte 2020-02-08 21:27:04 -05:00
parent b76a5870b3
commit c26c8992ae
6 changed files with 61 additions and 142 deletions

View File

@ -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 = "<group>"; };
4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PCMTrackTests.mm; sourceTree = "<group>"; };
4BD5D2672199148100DDF17D /* ScanTargetGLSLFragments.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScanTargetGLSLFragments.cpp; sourceTree = "<group>"; };
4BD5F1931D13528900631CD1 /* CSBestEffortUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSBestEffortUpdater.h; path = Updater/CSBestEffortUpdater.h; sourceTree = "<group>"; };
4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSBestEffortUpdater.mm; path = Updater/CSBestEffortUpdater.mm; sourceTree = "<group>"; };
4BD601A920D89F2A00CBCE57 /* Log.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Log.hpp; path = ../../Outputs/Log.hpp; sourceTree = "<group>"; };
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 = "<group>"; };
@ -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 = "<group>";
};
4BD5F1961D1352A000631CD1 /* Updater */ = {
isa = PBXGroup;
children = (
4BD5F1931D13528900631CD1 /* CSBestEffortUpdater.h */,
4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.mm */,
);
name = Updater;
sourceTree = "<group>";
};
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 */,

View File

@ -13,7 +13,6 @@
#import "CSOpenGLView.h"
#import "CSROMReceiverView.h"
#import "CSBestEffortUpdater.h"
#import "CSJoystickManager.h"
#import "NSData+CRC32.h"

View File

@ -57,8 +57,6 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
*/
- (nullable instancetype)initWithAnalyser:(nonnull CSStaticAnalyser *)result missingROMs:(nullable inout NSMutableArray<CSMissingROM *> *)missingROMs NS_DESIGNATED_INITIALIZER;
- (NSTimeInterval)runForInterval:(NSTimeInterval)interval untilEvent:(int)events;
- (float)idealSamplingRateFromRange:(NSRange)range;
- (void)setAudioSamplingRate:(float)samplingRate bufferSize:(NSUInteger)bufferSize;

View File

@ -159,6 +159,8 @@ struct ActivityObserver: public Activity::Observer {
double _refreshPeriod;
BOOL _isSyncLocking;
NSTimer *_joystickTimer;
std::unique_ptr<Outputs::Display::OpenGL::ScanTarget> _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<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]);
}
// 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]);
}
}
}
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 {

View File

@ -1,27 +0,0 @@
//
// CSBestEffortUpdater.h
// Clock Signal
//
// Created by Thomas Harte on 16/06/2016.
// Copyright 2016 Thomas Harte. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreVideo/CoreVideo.h>
#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

View File

@ -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