52 using std::make_shared;
53 using std::make_unique;
55 using std::string_view;
68 string help(
const vector<string>& tokens)
const override;
78 string help(
const vector<string>& tokens)
const override;
89 string help(
const vector<string>& tokens)
const override;
100 string help(
const vector<string>& tokens)
const override;
110 string help(
const vector<string>& tokens)
const override;
121 string help(
const vector<string>& tokens)
const override;
131 string help(
const vector<string>& tokens)
const override;
142 string help(
const vector<string>& tokens)
const override;
153 string help(
const vector<string>& tokens)
const override;
164 string help(
const vector<string>& tokens)
const override;
172 string help(
const vector<string>& tokens)
const override;
181 string help(
const vector<string>& tokens)
const override;
184 const string configName;
193 string help(
const vector<string>& tokens)
const override;
195 const uint64_t reference;
204 std::string
help(
const std::vector<std::string>& tokens)
const override;
214 rtScheduler = make_unique<RTScheduler>();
215 eventDistributor = make_unique<EventDistributor>(*this);
216 globalCliComm = make_unique<GlobalCliComm>();
217 globalCommandController = make_unique<GlobalCommandController>(
218 *eventDistributor, *globalCliComm, *
this);
219 globalSettings = make_unique<GlobalSettings>(
220 *globalCommandController);
221 inputEventGenerator = make_unique<InputEventGenerator>(
222 *globalCommandController, *eventDistributor, *globalSettings);
223 mixer = make_unique<Mixer>(
224 *
this, *globalCommandController);
225 diskFactory = make_unique<DiskFactory>(
227 diskManipulator = make_unique<DiskManipulator>(
228 *globalCommandController, *
this);
229 virtualDrive = make_unique<DiskChanger>(
230 *
this,
"virtual_drive");
231 filePool = make_unique<FilePool>(*globalCommandController, *
this);
232 userSettings = make_unique<UserSettings>(
233 *globalCommandController);
234 afterCommand = make_unique<AfterCommand>(
235 *
this, *eventDistributor, *globalCommandController);
236 exitCommand = make_unique<ExitCommand>(
237 *globalCommandController, *eventDistributor);
238 messageCommand = make_unique<MessageCommand>(
239 *globalCommandController);
240 machineCommand = make_unique<MachineCommand>(
241 *globalCommandController, *
this);
242 testMachineCommand = make_unique<TestMachineCommand>(
243 *globalCommandController, *
this);
244 createMachineCommand = make_unique<CreateMachineCommand>(
245 *globalCommandController, *
this);
246 deleteMachineCommand = make_unique<DeleteMachineCommand>(
247 *globalCommandController, *
this);
248 listMachinesCommand = make_unique<ListMachinesCommand>(
249 *globalCommandController, *
this);
250 activateMachineCommand = make_unique<ActivateMachineCommand>(
251 *globalCommandController, *
this);
252 storeMachineCommand = make_unique<StoreMachineCommand>(
253 *globalCommandController, *
this);
254 restoreMachineCommand = make_unique<RestoreMachineCommand>(
255 *globalCommandController, *
this);
256 getClipboardCommand = make_unique<GetClipboardCommand>(
257 *globalCommandController);
258 setClipboardCommand = make_unique<SetClipboardCommand>(
259 *globalCommandController);
260 aviRecordCommand = make_unique<AviRecorder>(*this);
261 extensionInfo = make_unique<ConfigInfo>(
262 getOpenMSXInfoCommand(),
"extensions");
263 machineInfo = make_unique<ConfigInfo>(
264 getOpenMSXInfoCommand(),
"machines");
265 realTimeInfo = make_unique<RealTimeInfo>(
266 getOpenMSXInfoCommand());
267 softwareInfoTopic = make_unique<SoftwareInfoTopic>(
268 getOpenMSXInfoCommand(), *
this);
269 tclCallbackMessages = make_unique<TclCallbackMessages>(
270 *globalCliComm, *globalCommandController);
272 createMachineSetting();
274 getGlobalSettings().getPauseSetting().attach(*
this);
287 deleteBoard(activeBoard);
295 getGlobalSettings().getPauseSetting().detach(*
this);
300 if (!softwareDatabase) {
301 softwareDatabase = make_unique<RomDatabase>(*globalCliComm);
303 return *softwareDatabase;
308 return *globalCliComm;
318 return *globalCommandController;
323 return globalCommandController->getOpenMSXInfoCommand();
328 vector<string> result;
332 while (
auto* entry = configsDir.
getEntry()) {
333 string_view name = entry->d_name;
337 name.remove_suffix(4);
338 result.emplace_back(name);
341 fullname,
"hardwareconfig.xml");
343 result.emplace_back(name);
354 void Reactor::createMachineSetting()
359 [&](
auto& name) {
return std::pair(name, count++); }));
360 machines.emplace_back(
"C-BIOS_MSX2+", 0);
362 machineSetting = make_unique<EnumSetting<int>>(
363 *globalCommandController,
"default_machine",
364 "default machine (takes effect next time openMSX is started)",
365 0, std::move(machines));
376 return activeBoard ? activeBoard->getMachineID() : string_view{};
379 vector<string_view> Reactor::getMachineIDs()
const 382 boards, [](
auto& b) {
return b->getMachineID(); }));
387 for (
auto& b : boards) {
388 if (b->getMachineID() == machineID) {
397 return make_unique<MSXMotherBoard>(*this);
405 auto* newBoard = newBoard_.get();
406 boards.push_back(move(newBoard_));
410 [&](
auto& b) {
return b.get() == &oldBoard_; });
413 if (it->get() == activeBoard) {
414 switchBoard(newBoard);
427 display = make_unique<Display>(*this);
431 display->createVideoSystem();
441 auto newBoard_ = createEmptyMotherBoard();
442 auto* newBoard = newBoard_.get();
443 newBoard->loadMachine(machine);
444 boards.push_back(move(newBoard_));
446 auto* oldBoard = activeBoard;
447 switchBoard(newBoard);
448 deleteBoard(oldBoard);
455 (
ranges::any_of(boards, [&](
auto& b) {
return b.get() == newBoard; })));
456 assert(!activeBoard ||
457 (
ranges::any_of(boards, [&](
auto& b) {
return b.get() == activeBoard; })));
459 activeBoard->activate(
false);
465 std::lock_guard<std::mutex> lock(mbMutex);
466 activeBoard = newBoard;
468 eventDistributor->distributeEvent(
486 if (board == activeBoard) {
488 switchBoard(
nullptr);
491 [&](
auto& b) {
return b.get() == board; });
492 auto board_ = move(*it);
498 garbageBoards.push_back(move(board_));
499 eventDistributor->distributeEvent(
509 activeBoard->exitCPULoopSync();
512 std::lock_guard<std::mutex> lock(mbMutex);
514 activeBoard->exitCPULoopAsync();
521 auto& commandController = *globalCommandController;
525 commandController.source(
543 getGlobalCliComm().setAllowExternalCommands();
554 activeBoard->powerUp();
559 eventDistributor->deliverEvents();
560 assert(garbageBoards.empty());
561 bool blocked = (blockedCounter > 0) || !activeBoard;
562 if (!blocked) blocked = !activeBoard->execute();
570 eventDistributor->sleep(20 * 1000);
575 void Reactor::unpause()
584 void Reactor::pause()
603 assert(blockedCounter >= 0);
609 void Reactor::update(
const Setting& setting)
611 auto& pauseSetting = getGlobalSettings().getPauseSetting();
612 if (&setting == &pauseSetting) {
613 if (pauseSetting.getBoolean()) {
622 int Reactor::signalEvent(
const std::shared_ptr<const Event>& event)
624 auto type =
event->getType();
639 auto& focusEvent = checked_cast<
const FocusEvent&>(*event);
640 if (focusEvent.getGain()) {
649 assert(!garbageBoards.empty());
650 garbageBoards.pop_back();
662 :
Command(commandController_,
"exit")
663 , distributor(distributor_)
670 switch (tokens.size()) {
683 return "Use this command to stop the emulator.\n" 684 "Optionally you can pass an exit-code.\n";
692 :
Command(commandController_,
"machine")
700 switch (tokens.size()) {
719 return "Switch to a different MSX machine.";
732 :
Command(commandController_,
"test_machine")
751 return "Test the configuration for the given machine. " 752 "Returns an error message explaining why the configuration is " 753 "invalid or an empty string in case of success.";
766 :
Command(commandController_,
"create_machine")
775 result = newBoard->getMachineID();
776 reactor.boards.push_back(move(newBoard));
781 return "Creates a new (empty) MSX machine. Returns the ID for the new " 783 "Use 'load_machine' to actually load a machine configuration " 784 "into this new machine.\n" 785 "The main reason create_machine and load_machine are two " 786 "separate commands is that sometimes you already want to know " 787 "the ID of the machine before load_machine starts emitting " 788 "events for this machine.";
796 :
Command(commandController_,
"delete_machine")
805 reactor.deleteBoard(&reactor.getMachine(tokens[1].getString()));
810 return "Deletes the given MSX machine.";
823 :
Command(commandController_,
"list_machines")
836 return "Returns a list of all machine IDs.";
844 :
Command(commandController_,
"activate_machine")
853 switch (tokens.size()) {
857 reactor.switchBoard(&reactor.getMachine(tokens[1].getString()));
865 return "Make another machine the active msx machine.\n" 866 "Or when invoked without arguments, query the ID of the " 867 "active msx machine.";
880 :
Command(commandController_,
"store_machine")
889 string_view machineID;
890 switch (tokens.size()) {
896 machineID = tokens[1].getString();
900 machineID = tokens[1].getString();
901 filename = tokens[2].getString();
905 auto& board = reactor.getMachine(machineID);
916 "store_machine Save state of current machine to file \"openmsxNNNN.xml.gz\"\n" 917 "store_machine machineID Save state of machine \"machineID\" to file \"openmsxNNNN.xml.gz\"\n" 918 "store_machine machineID <filename> Save state of machine \"machineID\" to indicated file\n" 920 "This is a low-level command, the 'savestate' script is easier to use.";
933 :
Command(commandController_,
"restore_machine")
945 switch (tokens.size()) {
953 while (dirent* d = dir.
getEntry()) {
954 int res = stat(
strCat(dirName, d->d_name).c_str(), &st);
955 if ((res == 0) && S_ISREG(st.st_mode)) {
956 time_t modTime = st.st_mtime;
957 if (modTime > lastTime) {
958 lastEntry = string(d->d_name);
963 if (lastEntry.empty()) {
966 filename = dirName + lastEntry;
970 filename = tokens[1].getString();
988 newBoard->getStateChangeDistributor().stopReplay(newBoard->getCurrentTime());
990 result = newBoard->getMachineID();
991 reactor.boards.push_back(move(newBoard));
996 return "restore_machine Load state from last saved state in default directory\n" 997 "restore_machine <filename> Load state from indicated file\n" 999 "This is a low-level command, the 'loadstate' script is easier to use.";
1012 :
Command(commandController_,
"get_clipboard_text")
1019 if (
char* text = SDL_GetClipboardText()) {
1027 return "Returns the (text) content of the clipboard as a string.";
1034 :
Command(commandController_,
"set_clipboard_text")
1041 string text(tokens[1].getString());
1042 if (SDL_SetClipboardText(text.c_str()) != 0) {
1043 const char* err = SDL_GetError();
1051 return "Send the given string to the clipboard.";
1058 const string& configName_)
1059 :
InfoTopic(openMSXInfoCommand, configName_)
1060 , configName(configName_)
1067 switch (tokens.size()) {
1075 configName, tokens[2].getString());
1076 if (
auto* info = config.findChild(
"info")) {
1077 for (
auto& i : info->getChildren()) {
1083 "Couldn't get config info: ", e.
getMessage());
1094 return strCat(
"Shows a list of available ", configName,
", " 1095 "or get meta information about the selected item.\n");
1107 :
InfoTopic(openMSXInfoCommand,
"realtime")
1108 , reference(Timer::getTime())
1116 result = delta / 1000000.0;
1121 return "Returns the time in seconds since openMSX was started.";
1128 :
InfoTopic(openMSXInfoCommand,
"software")
1136 if (tokens.size() != 3) {
1142 const RomInfo* romInfo = romDatabase.fetchRomInfo(sha1sum);
1146 "Software with sha1sum ", sha1sum.
toString(),
" not found");
1149 const char* bufStart = romDatabase.getBufferStart();
1151 "year", romInfo->
getYear(bufStart),
1163 return "Returns information about the software " 1164 "given its sha1sum, in a paired list.";
ActivateMachineCommand(CommandController &commandController, Reactor &reactor)
void execute(span< const TclObject > tokens, TclObject &result) const override
Show info on this topic.
string help(const vector< string > &tokens) const override
Print help for this topic.
auto transform(Range &&range, UnaryOp op)
Contains the main loop of openMSX.
GlobalCommandController & getGlobalCommandController() const
bool isRegularFile(const Stat &st)
string help(const vector< string > &tokens) const override
Print help for this command.
std::string_view getTitle(const char *buf) const
const std::string & getMessage() const &
string getNextNumberedFileName(string_view directory, string_view prefix, string_view extension)
Send when a (new) machine configuration is loaded.
string help(const vector< string > &tokens) const override
Print help for this command.
string help(const vector< string > &tokens) const override
Print help for this command.
void execute(span< const TclObject > tokens, TclObject &result) override
Execute this command.
FileContext preferSystemFileContext()
ParseStatus getParseStatus() const
void replaceBoard(MSXMotherBoard &oldBoard, Board newBoard)
auto unique(ForwardRange &&range)
FileContext systemFileContext()
ALWAYS_INLINE void serialize(const char *tag, const T &t, Args &&...args)
std::unique_ptr< MSXMotherBoard > Board
string help(const vector< string > &tokens) const override
Print help for this command.
void checkNumArgs(span< const TclObject > tokens, unsigned exactly, const char *errMessage) const
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this command.
MachineCommand(CommandController &commandController, Reactor &reactor)
static XMLElement loadConfig(std::string_view type, std::string_view name)
std::string_view getCompany(const char *buf) const
void execute(span< const TclObject > tokens, TclObject &result) override
Execute this command.
void distributeEvent(const EventPtr &event)
Schedule the given event for delivery.
void execute(span< const TclObject > tokens, TclObject &result) override
Execute this command.
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this command.
void execute(span< const TclObject > tokens, TclObject &result) override
Execute this command.
static std::vector< std::string > getHwConfigs(std::string_view type)
RestoreMachineCommand(CommandController &commandController, Reactor &reactor)
string help(const vector< string > &tokens) const override
Print help for this command.
void move_pop_back(VECTOR &v, typename VECTOR::iterator it)
Erase the pointed to element from the given vector.
CommandController & getCommandController()
MSXMotherBoard * getMotherBoard() const
std::string_view getOrigType(const char *buf) const
static void completeFileName(std::vector< std::string > &tokens, const FileContext &context, const RANGE &extra)
std::string_view getRemark(const char *buf) const
static std::string_view romTypeToName(RomType type)
RomDatabase & getSoftwareDatabase()
DeleteMachineCommand(CommandController &commandController, Reactor &reactor)
string help(const vector< string > &tokens) const override
Print help for this command.
string help(const vector< string > &tokens) const override
Print help for this command.
void run(CommandLineParser &parser)
Main loop.
FileContext userFileContext(string_view savePath)
string help(const vector< string > &tokens) const override
Print help for this topic.
void execute(span< const TclObject > tokens, TclObject &result) override
Execute this command.
This class represents the result of a sha1 calculation (a 160-bit value).
void execute(span< const TclObject > tokens, TclObject &result) override
Execute this command.
std::string loadMachine(const std::string &machine)
const Scripts & getStartupScripts() const
constexpr const char *const filename
ALWAYS_INLINE unsigned count(const uint8_t *pIn, const uint8_t *pMatch, const uint8_t *pInLimit)
RomType getRomType() const
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this command.
void sort(RandomAccessRange &&range)
Thanks to enen for testing this on a real cartridge:
std::string_view getCountry(const char *buf) const
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this command.
string help(const vector< string > &tokens) const override
Print help for this command.
InfoCommand & getOpenMSXInfoCommand()
void execute(span< const TclObject > tokens, TclObject &result) override
Execute this command.
bool any_of(InputRange &&range, UnaryPredicate pred)
ConfigInfo(InfoCommand &openMSXInfoCommand, const string &configName)
static void completeString(std::vector< std::string > &tokens, ITER begin, ITER end, bool caseSensitive=true)
Interpreter & getInterpreter() override
std::string toString() const
ExitCommand(CommandController &commandController, EventDistributor &distributor)
const string & getUserOpenMSXDir()
Get the openMSX dir in the user's home directory.
Delete old MSXMotherboards.
bool isDirectory(const Stat &st)
SoftwareInfoTopic(InfoCommand &openMSXInfoCommand, Reactor &reactor)
string help(const vector< string > &tokens) const override
Print help for this command.
void execute(span< const TclObject > tokens, TclObject &result) override
Execute this command.
RealTimeInfo(InfoCommand &openMSXInfoCommand)
Interpreter & getInterpreter() const final
void activate(bool active)
TestMachineCommand(CommandController &commandController, Reactor &reactor)
void execute(span< const TclObject > tokens, TclObject &result) const override
Show info on this topic.
void addDictKeyValues(Args &&... args)
std::string help(const std::vector< std::string > &tokens) const override
Print help for this topic.
string help(const vector< string > &tokens) const override
Print help for this command.
GetClipboardCommand(CommandController &commandController)
Simple wrapper around openmdir() / readdir() / closedir() functions.
SetClipboardCommand(CommandController &commandController)
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this topic.
Board createEmptyMotherBoard()
ITER find_if_unguarded(ITER first, ITER last, PRED pred)
Faster alternative to 'find_if' when it's guaranteed that the predicate will be true for at least one...
void execute(span< const TclObject > tokens, TclObject &result) override
Execute this command.
std::string strCat(Ts &&...ts)
string join(string_view part1, string_view part2)
void switchMachine(const std::string &machine)
uint64_t getTime()
Get current (real) time in us.
void addDictKeyValue(const Key &key, const Value &value)
void tabCompletion(std::vector< std::string > &tokens) const override
Attempt tab completion for this command.
void execute(span< const TclObject > tokens, TclObject &result) override
Execute this command.
void addListElements(ITER first, ITER last)
struct dirent * getEntry()
Get directory entry for next file.
ListMachinesCommand(CommandController &commandController, Reactor &reactor)
bool endsWith(string_view total, string_view part)
StoreMachineCommand(CommandController &commandController, Reactor &reactor)
auto rfind_if_unguarded(RANGE &range, PRED pred)
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this command.
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))>>
std::string_view getMachineID() const
void tabCompletion(vector< string > &tokens) const override
Attempt tab completion for this command.
std::string_view getYear(const char *buf) const
string help(const vector< string > &tokens) const override
Print help for this command.
bool isMainThread()
Returns true when called from the main thread.
CreateMachineCommand(CommandController &commandController, Reactor &reactor)
void execute(span< const TclObject > tokens, TclObject &result) const override
Show info on this topic.
void execute(span< const TclObject > tokens, TclObject &result) override
Execute this command.
Interpreter & getInterpreter()