diff --git a/Configurable/Configurable.hpp b/Configurable/Configurable.hpp new file mode 100644 index 000000000..b5ccf7406 --- /dev/null +++ b/Configurable/Configurable.hpp @@ -0,0 +1,76 @@ +// +// Configurable.h +// Clock Signal +// +// Created by Thomas Harte on 17/11/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#ifndef Configurable_h +#define Configurable_h + +#include +#include +#include + +namespace Configurable { + +/*! + The Option class hierarchy provides a way for components, machines, etc, to provide a named + list of typed options to which they can respond. +*/ +struct Option { + std::string long_name; + std::string short_name; + virtual ~Option() {} + + Option(const std::string &long_name, const std::string &short_name) : long_name(long_name), short_name(short_name) {} +}; + +struct BooleanOption: public Option { + BooleanOption(const std::string &long_name, const std::string &short_name) : Option(long_name, short_name) {} +}; + +struct ListOption: public Option { + std::vector options; + ListOption(const std::string &long_name, const std::string &short_name, const std::vector &options) : Option(long_name, short_name), options(options) {} +}; + +/*! + Selections are responses to Options. +*/ +struct Selection { + virtual ~Selection() {} +}; + +struct BooleanSelection: public Selection { + bool value; + BooleanSelection(bool value) : value(value) {} +}; + +struct ListSelection: public Selection { + std::string value; + ListSelection(const std::string value) : value(value) {} +}; + +using SelectionSet = std::map>; + +/*! + A Configuratble provides the options that it responds to and allows selections to be set. +*/ +struct Device { + virtual std::vector> get_options() = 0; + virtual void set_selections(const SelectionSet &selection_by_option) = 0; + virtual SelectionSet get_accurate_selections() = 0; + virtual SelectionSet get_user_friendly_selections() = 0; +}; + +template T *selection(const Configurable::SelectionSet &selections_by_option, const std::string &name) { + auto selection = selections_by_option.find(name); + if(selection == selections_by_option.end()) return nullptr; + return dynamic_cast(selection->second.get()); +} + +} + +#endif /* Configurable_h */ diff --git a/Machines/ConfigurationTarget.hpp b/Machines/ConfigurationTarget.hpp index 1a658db78..1dd5f54ec 100644 --- a/Machines/ConfigurationTarget.hpp +++ b/Machines/ConfigurationTarget.hpp @@ -10,6 +10,9 @@ #define ConfigurationTarget_hpp #include "../StaticAnalyser/StaticAnalyser.hpp" +#include "../Configurable/Configurable.hpp" + +#include namespace ConfigurationTarget { diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 20a9661c3..9300b71dc 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -416,6 +416,35 @@ class ConcreteMachine: return keyboard_mapper_; } + std::vector> get_options() override { + std::vector> options; + options.emplace_back(new Configurable::BooleanOption("Load Tapes Quickly", "quickload")); + options.emplace_back(new Configurable::ListOption("Display", "display", {"composite", "rgb"})); + return options; + } + + void set_selections(const Configurable::SelectionSet &selections_by_option) override { + auto quickload = Configurable::selection(selections_by_option, "quickload"); + if(quickload) set_use_fast_tape_hack(quickload->value); + + auto display = Configurable::selection(selections_by_option, "display"); + if(display) get_crt()->set_output_device((display->value == "rgb") ? Outputs::CRT::OutputDevice::Monitor : Outputs::CRT::OutputDevice::Television); + } + + Configurable::SelectionSet get_accurate_selections() override { + Configurable::SelectionSet selection_set; + selection_set["quickload"] = std::unique_ptr(new Configurable::BooleanSelection(false)); + selection_set["display"] = std::unique_ptr(new Configurable::ListSelection("composite")); + return selection_set; + } + + Configurable::SelectionSet get_user_friendly_selections() override { + Configurable::SelectionSet selection_set; + selection_set["quickload"] = std::unique_ptr(new Configurable::BooleanSelection(true)); + selection_set["display"] = std::unique_ptr(new Configurable::ListSelection("rgb")); + return selection_set; + } + private: inline void update_display() { if(cycles_since_display_update_ > 0) { diff --git a/Machines/Electron/Electron.hpp b/Machines/Electron/Electron.hpp index 5d39b9386..53f05bd15 100644 --- a/Machines/Electron/Electron.hpp +++ b/Machines/Electron/Electron.hpp @@ -9,6 +9,7 @@ #ifndef Electron_hpp #define Electron_hpp +#include "../../Configurable/Configurable.hpp" #include "../ConfigurationTarget.hpp" #include "../CRTMachine.hpp" #include "../KeyboardMachine.hpp" @@ -41,7 +42,8 @@ enum ROMSlot: uint8_t { class Machine: public CRTMachine::Machine, public ConfigurationTarget::Machine, - public KeyboardMachine::Machine { + public KeyboardMachine::Machine, + public Configurable::Device { public: virtual ~Machine(); diff --git a/Machines/Utility/MachineForTarget.cpp b/Machines/Utility/MachineForTarget.cpp index 869a203a4..c03431fb7 100644 --- a/Machines/Utility/MachineForTarget.cpp +++ b/Machines/Utility/MachineForTarget.cpp @@ -21,23 +21,30 @@ template class TypedDynamicMachine: public ::Machine::DynamicMachine public: TypedDynamicMachine(T *machine) : machine_(machine) {} - ConfigurationTarget::Machine *configuration_target() { - return dynamic_cast(machine_.get()); + ConfigurationTarget::Machine *configuration_target() override { + return get(); } - CRTMachine::Machine *crt_machine() { - return dynamic_cast(machine_.get()); + CRTMachine::Machine *crt_machine() override { + return get(); } - JoystickMachine::Machine *joystick_machine() { - return dynamic_cast(machine_.get()); + JoystickMachine::Machine *joystick_machine() override { + return get(); } - KeyboardMachine::Machine *keyboard_machine() { - return dynamic_cast(machine_.get()); + KeyboardMachine::Machine *keyboard_machine() override { + return get(); + } + + Configurable::Device *configurable_device() override { + return get(); } private: + template Class *get() { + return dynamic_cast(machine_.get()); + } std::unique_ptr machine_; }; diff --git a/Machines/Utility/MachineForTarget.hpp b/Machines/Utility/MachineForTarget.hpp index 14adcecf7..fbfb05466 100644 --- a/Machines/Utility/MachineForTarget.hpp +++ b/Machines/Utility/MachineForTarget.hpp @@ -11,6 +11,7 @@ #include "../../StaticAnalyser/StaticAnalyser.hpp" +#include "../../Configurable/Configurable.hpp" #include "../ConfigurationTarget.hpp" #include "../CRTMachine.hpp" #include "../JoystickMachine.hpp" @@ -29,6 +30,7 @@ struct DynamicMachine { virtual CRTMachine::Machine *crt_machine() = 0; virtual JoystickMachine::Machine *joystick_machine() = 0; virtual KeyboardMachine::Machine *keyboard_machine() = 0; + virtual Configurable::Device *configurable_device() = 0; }; /*! diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 3a31a6a60..7cca3f355 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -680,6 +680,7 @@ 4B30512C1D989E2200B4FED8 /* Drive.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Drive.hpp; sourceTree = ""; }; 4B30512E1D98ACC600B4FED8 /* Plus3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Plus3.cpp; path = Electron/Plus3.cpp; sourceTree = ""; }; 4B30512F1D98ACC600B4FED8 /* Plus3.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Plus3.hpp; path = Electron/Plus3.hpp; sourceTree = ""; }; + 4B31B88F1FBFBCD800C140D5 /* Configurable.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Configurable.hpp; sourceTree = ""; }; 4B322DF31F5A26BF004EB04C /* 6502Implementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = 6502Implementation.hpp; sourceTree = ""; }; 4B322DF41F5A2714004EB04C /* 6502Storage.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = 6502Storage.hpp; sourceTree = ""; }; 4B322DFD1F5A2981004EB04C /* Z80AllRAM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Z80AllRAM.cpp; sourceTree = ""; }; @@ -1509,6 +1510,15 @@ name = Electron; sourceTree = ""; }; + 4B31B88E1FBFBCD800C140D5 /* Configurable */ = { + isa = PBXGroup; + children = ( + 4B31B88F1FBFBCD800C140D5 /* Configurable.hpp */, + ); + name = Configurable; + path = ../../Configurable; + sourceTree = ""; + }; 4B322DFC1F5A2981004EB04C /* AllRAM */ = { isa = PBXGroup; children = ( @@ -2259,6 +2269,7 @@ 4BF660691F281573002CB053 /* ClockReceiver */, 4BC9DF4A1D04691600F44158 /* Components */, 4B3940E81DA83C8700427841 /* Concurrency */, + 4B31B88E1FBFBCD800C140D5 /* Configurable */, 4B055A761FAE78210060FFFF /* Frameworks */, 4B86E2581F8C628F006FAA45 /* Inputs */, 4BB73EDC1B587CA500552FC2 /* Machines */, diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index 217025698..3d025f2a2 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -252,6 +252,12 @@ int main(int argc, char *argv[]) { SDL_GetWindowSize(window, &window_width, &window_height); std::cout << "Window size is " << window_width << ", " << window_height << std::endl; + // Establish user-friendly options by default. + Configurable::Device *configurable_device = machine->configurable_device(); + if(configurable_device) { + configurable_device->set_selections(configurable_device->get_user_friendly_selections()); + } + // Run the main event loop until the OS tells us to quit. bool should_quit = false; while(!should_quit) {