diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 61b403bd0..6ae182abc 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -102,7 +102,7 @@ class MachineDocument: } } - func machineSpeakerDidChangeInputClock(_ machine: CSMachine!) { + func machineSpeakerDidChangeInputClock(_ machine: CSMachine) { setupAudioQueueClockRate() } @@ -147,6 +147,7 @@ class MachineDocument: self.machine = machine self.optionsPanelNibName = analysis.optionsPanelNibName setupMachineOutput() + setupActivityDisplay() } } @@ -290,4 +291,43 @@ class MachineDocument: } return super.validateUserInterfaceItem(item) } + + // MARK: Activity display. + func setupActivityDisplay() { + return + + if machine.leds.count > 0 { + let panel = NSPanel() + panel.title = "Activity" + panel.styleMask = .hudWindow + panel.setIsVisible(true) + + for name in machine.leds { + let button = NSButton() + button.title = name + button.setButtonType(.radio) + button.translatesAutoresizingMaskIntoConstraints = false + button.isEnabled = false +// button.color + panel.contentView?.addSubview(button) + + let views = ["button": button] + let horizontalConstraints = + NSLayoutConstraint.constraints( + withVisualFormat: "H:|-[button]-|", + options: NSLayoutConstraint.FormatOptions(rawValue: 0), + metrics: nil, + views: views) + let verticalConstraints = + NSLayoutConstraint.constraints( + withVisualFormat: "V:|-[button]-|", + options: NSLayoutConstraint.FormatOptions(rawValue: 0), + metrics: nil, + views: views) + + panel.contentView?.addConstraints(horizontalConstraints) + panel.contentView?.addConstraints(verticalConstraints) + } + } + } } diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h index 4b0ceddb8..670a446ce 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h @@ -15,7 +15,7 @@ @class CSMachine; @protocol CSMachineDelegate -- (void)machineSpeakerDidChangeInputClock:(CSMachine *)machine; +- (void)machineSpeakerDidChangeInputClock:(nonnull CSMachine *)machine; @end typedef NS_ENUM(NSInteger, CSMachineVideoSignal) { @@ -35,32 +35,32 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) { @interface CSMachine : NSObject -- (instancetype)init NS_UNAVAILABLE; +- (nonnull instancetype)init NS_UNAVAILABLE; /*! Initialises an instance of CSMachine. @param result The CSStaticAnalyser result that describes the machine needed. */ -- (instancetype)initWithAnalyser:(CSStaticAnalyser *)result NS_DESIGNATED_INITIALIZER; +- (nullable instancetype)initWithAnalyser:(nonnull CSStaticAnalyser *)result NS_DESIGNATED_INITIALIZER; - (void)runForInterval:(NSTimeInterval)interval; - (float)idealSamplingRateFromRange:(NSRange)range; - (void)setAudioSamplingRate:(float)samplingRate bufferSize:(NSUInteger)bufferSize; -- (void)setView:(CSOpenGLView *)view aspectRatio:(float)aspectRatio; +- (void)setView:(nullable CSOpenGLView *)view aspectRatio:(float)aspectRatio; - (void)drawViewForPixelSize:(CGSize)pixelSize onlyIfDirty:(BOOL)onlyIfDirty; -- (void)setKey:(uint16_t)key characters:(NSString *)characters isPressed:(BOOL)isPressed; +- (void)setKey:(uint16_t)key characters:(nullable NSString *)characters isPressed:(BOOL)isPressed; - (void)clearAllKeys; -@property (nonatomic, strong) CSAudioQueue *audioQueue; -@property (nonatomic, readonly) CSOpenGLView *view; -@property (nonatomic, weak) id delegate; +@property (nonatomic, strong, nullable) CSAudioQueue *audioQueue; +@property (nonatomic, readonly, nonnull) CSOpenGLView *view; +@property (nonatomic, weak, nullable) id delegate; -@property (nonatomic, readonly) NSString *userDefaultsPrefix; +@property (nonatomic, readonly, nonnull) NSString *userDefaultsPrefix; -- (void)paste:(NSString *)string; +- (void)paste:(nonnull NSString *)string; @property (nonatomic, assign) BOOL useFastLoadingHack; @property (nonatomic, assign) CSMachineVideoSignal videoSignal; @@ -73,8 +73,11 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) { @property (nonatomic, readonly) BOOL hasJoystick; @property (nonatomic, assign) CSMachineKeyboardInputMode inputMode; +// LED list. +@property (nonatomic, readonly, nonnull) NSArray *leds; + // 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; +@property (nonatomic, readonly, nullable) CSAtari2600 *atari2600; +@property (nonatomic, readonly, nullable) CSZX8081 *zx8081; @end diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index a0ca43fb1..910773d5a 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -18,6 +18,7 @@ #include "MachineForTarget.hpp" #include "StandardOptions.hpp" #include "Typer.hpp" +#include "../../../../Activity/Observer.hpp" #import "CSStaticAnalyser+TargetVector.h" #import "NSBundle+DataResource.h" @@ -28,6 +29,7 @@ @interface CSMachine() - (void)speaker:(Outputs::Speaker::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length; - (void)speakerDidChangeInputClock:(Outputs::Speaker::Speaker *)speaker; +- (void)addLED:(NSString *)led; @end struct LockProtectedDelegate { @@ -50,14 +52,36 @@ struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate, public LockP } }; +struct ActivityObserver: public Activity::Observer { + void register_led(const std::string &name) override { + [machine addLED:[NSString stringWithUTF8String:name.c_str()]]; + } + + void register_drive(const std::string &name) override { + } + + void set_led_status(const std::string &name, bool lit) override { + } + + void announce_drive_event(const std::string &name, DriveEvent event) override { + } + + void set_drive_motor_status(const std::string &name, bool is_on) override { + } + + __unsafe_unretained CSMachine *machine; +}; + @implementation CSMachine { SpeakerDelegate _speakerDelegate; + ActivityObserver _activityObserver; NSLock *_delegateMachineAccessLock; CSStaticAnalyser *_analyser; std::unique_ptr _machine; std::bitset<65536> _depressedKeys; + NSMutableArray *_leds; } - (instancetype)initWithAnalyser:(CSStaticAnalyser *)result { @@ -71,6 +95,13 @@ struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate, public LockP _inputMode = _machine->keyboard_machine() ? CSMachineKeyboardInputModeKeyboard : CSMachineKeyboardInputModeJoystick; + _leds = [[NSMutableArray alloc] init]; + Activity::Source *const activity_source = _machine->activity_source(); + if(activity_source) { + _activityObserver.machine = self; + activity_source->set_activity_observer(&_activityObserver); + } + _delegateMachineAccessLock = [[NSLock alloc] init]; _speakerDelegate.machine = self; @@ -408,4 +439,14 @@ struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate, public LockP return !!_machine->keyboard_machine(); } +#pragma mark - Activity observation + +- (void)addLED:(NSString *)led { + [_leds addObject:led]; +} + +- (NSArray *)leds { + return _leds; +} + @end