diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index a1b450ebd..202c24e47 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -157,7 +157,6 @@ 4B322E041F5A2E3C004EB04C /* Z80Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B322E031F5A2E3C004EB04C /* Z80Base.cpp */; }; 4B37EE821D7345A6006A09A4 /* BinaryDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B37EE801D7345A6006A09A4 /* BinaryDump.cpp */; }; 4B38F3481F2EC11D00D9235D /* AmstradCPC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B38F3461F2EC11D00D9235D /* AmstradCPC.cpp */; }; - 4B38F34F1F2EC6BA00D9235D /* AmstradCPCOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B38F34D1F2EC6BA00D9235D /* AmstradCPCOptions.xib */; }; 4B3940E71DA83C8300427841 /* AsyncTaskQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3940E51DA83C8300427841 /* AsyncTaskQueue.cpp */; }; 4B3BA0C31D318AEC005DD7A7 /* C1540Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */; }; 4B3BA0CE1D318B44005DD7A7 /* C1540Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C61D318B44005DD7A7 /* C1540Bridge.mm */; }; @@ -755,7 +754,6 @@ 4B37EE811D7345A6006A09A4 /* BinaryDump.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = BinaryDump.hpp; sourceTree = ""; }; 4B38F3461F2EC11D00D9235D /* AmstradCPC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AmstradCPC.cpp; path = AmstradCPC/AmstradCPC.cpp; sourceTree = ""; }; 4B38F3471F2EC11D00D9235D /* AmstradCPC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = AmstradCPC.hpp; path = AmstradCPC/AmstradCPC.hpp; sourceTree = ""; }; - 4B38F34E1F2EC6BA00D9235D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/AmstradCPCOptions.xib"; sourceTree = SOURCE_ROOT; }; 4B3940E51DA83C8300427841 /* AsyncTaskQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AsyncTaskQueue.cpp; path = ../../Concurrency/AsyncTaskQueue.cpp; sourceTree = ""; }; 4B3940E61DA83C8300427841 /* AsyncTaskQueue.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = AsyncTaskQueue.hpp; path = ../../Concurrency/AsyncTaskQueue.hpp; sourceTree = ""; }; 4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = C1540Tests.swift; sourceTree = ""; }; @@ -1875,7 +1873,6 @@ 4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */, 4B8FE2211DA19FB20090D3CE /* MachinePanel.swift */, 4B95FA9C1F11893B0008E395 /* ZX8081OptionsPanel.swift */, - 4B38F34D1F2EC6BA00D9235D /* AmstradCPCOptions.xib */, 4B8FE2131DA19D5F0090D3CE /* Atari2600Options.xib */, 4B8FE2151DA19D5F0090D3CE /* MachineDocument.xib */, 4B2A332B1DB86821002876E3 /* OricOptions.xib */, @@ -3126,7 +3123,6 @@ 4BB73EAC1B587A5100552FC2 /* MainMenu.xib in Resources */, 4B8FE21D1DA19D5F0090D3CE /* QuickLoadCompositeOptions.xib in Resources */, 4B79E4461E3AF38600141F11 /* floppy525.png in Resources */, - 4B38F34F1F2EC6BA00D9235D /* AmstradCPCOptions.xib in Resources */, 4BC9DF451D044FCA00F44158 /* ROMImages in Resources */, 4B1497981EE4B97F00CE2596 /* ZX8081Options.xib in Resources */, ); @@ -3809,14 +3805,6 @@ name = OricOptions.xib; sourceTree = ""; }; - 4B38F34D1F2EC6BA00D9235D /* AmstradCPCOptions.xib */ = { - isa = PBXVariantGroup; - children = ( - 4B38F34E1F2EC6BA00D9235D /* Base */, - ); - name = AmstradCPCOptions.xib; - sourceTree = ""; - }; 4B8FE2131DA19D5F0090D3CE /* Atari2600Options.xib */ = { isa = PBXVariantGroup; children = ( diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/AmstradCPCOptions.xib b/OSBindings/Mac/Clock Signal/Base.lproj/AmstradCPCOptions.xib deleted file mode 100644 index 544399699..000000000 --- a/OSBindings/Mac/Clock Signal/Base.lproj/AmstradCPCOptions.xib +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/OricOptions.xib b/OSBindings/Mac/Clock Signal/Base.lproj/OricOptions.xib index 53238624b..758b4caa9 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/OricOptions.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/OricOptions.xib @@ -1,8 +1,8 @@ - + - + @@ -17,7 +17,7 @@ - + @@ -40,7 +40,7 @@ - + diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/QuickLoadCompositeOptions.xib b/OSBindings/Mac/Clock Signal/Base.lproj/QuickLoadCompositeOptions.xib index 3f962f510..324266e2f 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/QuickLoadCompositeOptions.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/QuickLoadCompositeOptions.xib @@ -1,8 +1,8 @@ - + - + @@ -17,7 +17,7 @@ - + @@ -34,13 +34,14 @@ - + - - + + + diff --git a/OSBindings/Mac/Clock Signal/Documents/MachinePanel.swift b/OSBindings/Mac/Clock Signal/Documents/MachinePanel.swift index e27a946b2..dba240002 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachinePanel.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachinePanel.swift @@ -28,13 +28,24 @@ class MachinePanel: NSPanel { } } + fileprivate func signalForTag(tag: Int) -> CSMachineVideoSignal { + switch tag { + case 1: return .composite + case 2: return .sVideo + default: break + } + return .RGB + } + var displayTypeUserDefaultsKey: String { return prefixedUserDefaultsKey("displayType") } @IBOutlet var displayTypeButton: NSPopUpButton? @IBAction func setDisplayType(_ sender: NSPopUpButton!) { - machine.useCompositeOutput = (sender.indexOfSelectedItem == 1) - UserDefaults.standard.set(sender.indexOfSelectedItem, forKey: self.displayTypeUserDefaultsKey) + if let selectedItem = sender.selectedItem { + machine.videoSignal = signalForTag(tag: selectedItem.tag) + UserDefaults.standard.set(selectedItem.tag, forKey: self.displayTypeUserDefaultsKey) + } } func establishStoredOptions() { @@ -50,8 +61,21 @@ class MachinePanel: NSPanel { self.fastLoadingButton?.state = useFastLoadingHack ? .on : .off } - let displayType = standardUserDefaults.integer(forKey: self.displayTypeUserDefaultsKey) - machine.useCompositeOutput = (displayType == 1) - self.displayTypeButton?.selectItem(at: displayType) + if let displayTypeButton = self.displayTypeButton { + // Enable or disable options as per machine support. + var titlesToRemove: [String] = [] + for item in displayTypeButton.itemArray { + if !machine.supportsVideoSignal(signalForTag(tag: item.tag)) { + titlesToRemove.append(item.title) + } + } + for title in titlesToRemove { + displayTypeButton.removeItem(withTitle: title) + } + + let displayType = standardUserDefaults.integer(forKey: self.displayTypeUserDefaultsKey) + displayTypeButton.selectItem(withTag: displayType) + setDisplayType(displayTypeButton) + } } } diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h index 4f1ea43c9..cc1d40eae 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h @@ -18,6 +18,12 @@ - (void)machineSpeakerDidChangeInputClock:(CSMachine *)machine; @end +typedef NS_ENUM(NSInteger, CSMachineVideoSignal) { + CSMachineVideoSignalComposite, + CSMachineVideoSignalSVideo, + CSMachineVideoSignalRGB +}; + // Deliberately low; to ensure CSMachine has been declared as an @class already. #import "CSAtari2600.h" #import "CSZX8081.h" @@ -52,9 +58,11 @@ - (void)paste:(NSString *)string; @property (nonatomic, assign) BOOL useFastLoadingHack; -@property (nonatomic, assign) BOOL useCompositeOutput; +@property (nonatomic, assign) CSMachineVideoSignal videoSignal; @property (nonatomic, assign) BOOL useAutomaticTapeMotorControl; +- (bool)supportsVideoSignal:(CSMachineVideoSignal)videoSignal; + // 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 d99f138f1..fae8fece6 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -289,19 +289,63 @@ struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate, public LockP } } -- (void)setUseCompositeOutput:(BOOL)useCompositeOutput { +- (void)setVideoSignal:(CSMachineVideoSignal)videoSignal { Configurable::Device *configurable_device = _machine->configurable_device(); if(!configurable_device) return; @synchronized(self) { - _useCompositeOutput = useCompositeOutput; + _videoSignal = videoSignal; Configurable::SelectionSet selection_set; - append_display_selection(selection_set, useCompositeOutput ? Configurable::Display::Composite : Configurable::Display::RGB); + Configurable::Display display; + switch(videoSignal) { + case CSMachineVideoSignalRGB: display = Configurable::Display::RGB; break; + case CSMachineVideoSignalSVideo: display = Configurable::Display::SVideo; break; + case CSMachineVideoSignalComposite: display = Configurable::Display::Composite; break; + } + append_display_selection(selection_set, display); configurable_device->set_selections(selection_set); } } +- (bool)supportsVideoSignal:(CSMachineVideoSignal)videoSignal { + Configurable::Device *configurable_device = _machine->configurable_device(); + if(!configurable_device) return NO; + + // Get the options this machine provides. + std::vector> options; + @synchronized(self) { + options = configurable_device->get_options(); + } + + // Get the standard option for this video signal. + Configurable::StandardOptions option; + switch(videoSignal) { + case CSMachineVideoSignalRGB: option = Configurable::DisplayRGB; break; + case CSMachineVideoSignalSVideo: option = Configurable::DisplaySVideo; break; + case CSMachineVideoSignalComposite: option = Configurable::DisplayComposite; break; + } + std::unique_ptr display_option = std::move(standard_options(option).front()); + Configurable::ListOption *display_list_option = dynamic_cast(display_option.get()); + NSAssert(display_list_option, @"Expected display option to be a list"); + + // See whether the video signal is included in the machine options. + for(auto &candidate: options) { + Configurable::ListOption *list_option = dynamic_cast(candidate.get()); + + // Both should be list options + if(!list_option) continue; + + // Check for same name of option. + if(candidate->short_name != display_option->short_name) continue; + + // Check that the video signal option is included. + return std::find(list_option->options.begin(), list_option->options.end(), display_list_option->options.front()) != list_option->options.end(); + } + + return NO; +} + - (void)setUseAutomaticTapeMotorControl:(BOOL)useAutomaticTapeMotorControl { Configurable::Device *configurable_device = _machine->configurable_device(); if(!configurable_device) return; diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm index 88235235a..b1849f8f1 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm @@ -39,7 +39,7 @@ case Analyser::Machine::Electron: return @"QuickLoadCompositeOptions"; case Analyser::Machine::MSX: return @"QuickLoadCompositeOptions"; case Analyser::Machine::Oric: return @"OricOptions"; - case Analyser::Machine::Vic20: return @"QuickLoadOptions"; + case Analyser::Machine::Vic20: return @"QuickLoadCompositeOptions"; case Analyser::Machine::ZX8081: return @"ZX8081Options"; default: return nil; }