// $Id: AfterCommand.cc 5993 2007-01-12 22:12:12Z m9710797 $ #include "AfterCommand.hh" #include "CommandController.hh" #include "CliComm.hh" #include "Scheduler.hh" #include "Schedulable.hh" #include "EventDistributor.hh" #include "Reactor.hh" #include "MSXMotherBoard.hh" #include "CommandException.hh" #include #include using std::ostringstream; using std::string; using std::vector; using std::set; namespace openmsx { class AfterCmd { public: virtual ~AfterCmd(); const std::string& getCommand() const; const std::string& getId() const; virtual const std::string& getType() const = 0; void execute(); protected: AfterCmd(AfterCommand& afterCommand, const std::string& command); private: AfterCommand& afterCommand; std::string command; std::string id; static unsigned lastAfterId; }; class AfterTimedCmd : public AfterCmd, private Schedulable { public: double getTime() const; void reschedule(); protected: AfterTimedCmd(Scheduler& scheduler, AfterCommand& afterCommand, const std::string& command, double time); private: virtual void executeUntil(const EmuTime& time, int userData); virtual void schedulerDeleted(); virtual const std::string& schedName() const; double time; }; class AfterTimeCmd : public AfterTimedCmd { public: AfterTimeCmd(Scheduler& scheduler, AfterCommand& afterCommand, const std::string& command, double time); virtual const std::string& getType() const; }; class AfterIdleCmd : public AfterTimedCmd { public: AfterIdleCmd(Scheduler& scheduler, AfterCommand& afterCommand, const std::string& command, double time); virtual const std::string& getType() const; }; template class AfterEventCmd : public AfterCmd { public: AfterEventCmd(AfterCommand& afterCommand, const std::string& type, const std::string& command); virtual const std::string& getType() const; private: const std::string type; }; AfterCommand::AfterCommand(Reactor& reactor_, EventDistributor& eventDistributor_, CommandController& commandController) : SimpleCommand(commandController, "after") , reactor(reactor_) , eventDistributor(eventDistributor_) { // TODO DETACHED <-> EMU types should be cleaned up // (moved to event iso listener?) eventDistributor.registerEventListener( OPENMSX_KEY_UP_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_KEY_DOWN_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_MOUSE_MOTION_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_MOUSE_BUTTON_UP_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_MOUSE_BUTTON_DOWN_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_JOY_AXIS_MOTION_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_JOY_BUTTON_UP_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_JOY_BUTTON_DOWN_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_FINISH_FRAME_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_BREAK_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_BOOT_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_MACHINE_LOADED_EVENT, *this); } AfterCommand::~AfterCommand() { while (!afterCmds.empty()) { delete afterCmds.begin()->second; // removes itself from map } eventDistributor.unregisterEventListener( OPENMSX_MACHINE_LOADED_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_BOOT_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_BREAK_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_FINISH_FRAME_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_JOY_BUTTON_DOWN_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_JOY_BUTTON_UP_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_JOY_AXIS_MOTION_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_MOUSE_BUTTON_DOWN_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_MOUSE_BUTTON_UP_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_MOUSE_MOTION_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_KEY_DOWN_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_KEY_UP_EVENT, *this); } string AfterCommand::execute(const vector& tokens) { if (tokens.size() < 2) { throw CommandException("Missing argument"); } if (tokens[1] == "time") { return afterTime(tokens); } else if (tokens[1] == "idle") { return afterIdle(tokens); } else if (tokens[1] == "frame") { return afterEvent(tokens); } else if (tokens[1] == "break") { return afterEvent(tokens); } else if (tokens[1] == "boot") { return afterEvent(tokens); } else if (tokens[1] == "machine_switch") { return afterEvent(tokens); } else if (tokens[1] == "info") { return afterInfo(tokens); } else if (tokens[1] == "cancel") { return afterCancel(tokens); } throw SyntaxError(); } static double getTime(const string& str) { // Use "strtod" instead of "strtof" because the latter doesn't exist in // the libc of FreeBSD. It's not equivalent, but we don't need maximum // precision or range checks, so it's close enough. double time = strtod(str.c_str(), NULL); if (time <= 0) { throw CommandException("Not a valid time specification"); } return time; } string AfterCommand::afterTime(const vector& tokens) { if (tokens.size() != 4) { throw SyntaxError(); } MSXMotherBoard* motherBoard = reactor.getMotherBoard(); if (!motherBoard) { return ""; } double time = getTime(tokens[2]); AfterTimeCmd* cmd = new AfterTimeCmd( motherBoard->getScheduler(), *this, tokens[3], time); return cmd->getId(); } string AfterCommand::afterIdle(const vector& tokens) { if (tokens.size() != 4) { throw SyntaxError(); } MSXMotherBoard* motherBoard = reactor.getMotherBoard(); if (!motherBoard) { return ""; } double time = getTime(tokens[2]); AfterIdleCmd* cmd = new AfterIdleCmd( motherBoard->getScheduler(), *this, tokens[3], time); return cmd->getId(); } template string AfterCommand::afterEvent(const vector& tokens) { if (tokens.size() != 3) { throw SyntaxError(); } AfterEventCmd* cmd = new AfterEventCmd(*this, tokens[1], tokens[2]); return cmd->getId(); } string AfterCommand::afterInfo(const vector& /*tokens*/) { string result; for (AfterCmdMap::const_iterator it = afterCmds.begin(); it != afterCmds.end(); ++it) { const AfterCmd* cmd = it->second; ostringstream str; str << cmd->getId() << ": "; str << cmd->getType() << ' '; if (dynamic_cast(cmd)) { const AfterTimedCmd* cmd2 = static_cast(cmd); str.precision(3); str << std::fixed << std::showpoint << cmd2->getTime() << ' '; } str << cmd->getCommand(); result += str.str() + '\n'; } return result; } string AfterCommand::afterCancel(const vector& tokens) { if (tokens.size() != 3) { throw SyntaxError(); } AfterCmdMap::iterator it = afterCmds.find(tokens[2]); if (it == afterCmds.end()) { throw CommandException("No delayed command with this id"); } delete it->second; return ""; } string AfterCommand::help(const vector& /*tokens*/) const { return "after time execute a command after some time\n" "after idle execute a command after some time being idle\n" "after frame execute a command after a new frame is drawn\n" "after break execute a command after a breakpoint is reached\n" "after info list all postponed commands\n" "after cancel cancel the postponed command with given id\n"; } void AfterCommand::tabCompletion(vector& tokens) const { if (tokens.size()==2) { set cmds; cmds.insert("time"); cmds.insert("idle"); cmds.insert("frame"); cmds.insert("break"); cmds.insert("boot"); cmds.insert("machine_switch"); cmds.insert("info"); cmds.insert("cancel"); completeString(tokens, cmds); } // TODO : make more complete } bool AfterCommand::signalEvent(shared_ptr event) { if (event->getType() == OPENMSX_FINISH_FRAME_EVENT) { executeEvents(); } else if (event->getType() == OPENMSX_BREAK_EVENT) { executeEvents(); } else if (event->getType() == OPENMSX_BOOT_EVENT) { executeEvents(); } else if (event->getType() == OPENMSX_MACHINE_LOADED_EVENT) { executeEvents(); } else { for (AfterCmdMap::const_iterator it = afterCmds.begin(); it != afterCmds.end(); ++it) { if (dynamic_cast(it->second)) { static_cast(it->second)->reschedule(); } } } return true; } template void AfterCommand::executeEvents() { vector tmp; // make copy because map will change for (AfterCmdMap::const_iterator it = afterCmds.begin(); it != afterCmds.end(); ++it) { if (dynamic_cast*>(it->second)) { tmp.push_back(it->second); } } for (vector::iterator it = tmp.begin(); it != tmp.end(); ++it) { (*it)->execute(); } } // class AfterCmd unsigned AfterCmd::lastAfterId = 0; AfterCmd::AfterCmd(AfterCommand& afterCommand_, const string& command_) : afterCommand(afterCommand_), command(command_) { ostringstream str; str << "after#" << ++lastAfterId; id = str.str(); afterCommand.afterCmds[id] = this; } AfterCmd::~AfterCmd() { afterCommand.afterCmds.erase(id); } const string& AfterCmd::getCommand() const { return command; } const string& AfterCmd::getId() const { return id; } void AfterCmd::execute() { try { afterCommand.getCommandController().executeCommand(command); } catch (CommandException& e) { afterCommand.getCommandController().getCliComm().printWarning( "Error executig delayed command: " + e.getMessage()); } delete this; } // class AfterTimedCmd AfterTimedCmd::AfterTimedCmd( Scheduler& scheduler, AfterCommand& afterCommand, const string& command, double time_) : AfterCmd(afterCommand, command) , Schedulable(scheduler) , time(time_) { reschedule(); } double AfterTimedCmd::getTime() const { return time; } void AfterTimedCmd::reschedule() { removeSyncPoint(); EmuTime t = getScheduler().getCurrentTime() + EmuDuration(time); setSyncPoint(t); } void AfterTimedCmd::executeUntil(const EmuTime& /*time*/, int /*userData*/) { execute(); } void AfterTimedCmd::schedulerDeleted() { delete this; } const string& AfterTimedCmd::schedName() const { static const string sched_name("AfterCmd"); return sched_name; } // class AfterTimeCmd AfterTimeCmd::AfterTimeCmd( Scheduler& scheduler, AfterCommand& afterCommand, const string& command, double time) : AfterTimedCmd(scheduler, afterCommand, command, time) { } const string& AfterTimeCmd::getType() const { static const string type("time"); return type; } // class AfterIdleCmd AfterIdleCmd::AfterIdleCmd( Scheduler& scheduler, AfterCommand& afterCommand, const string& command, double time) : AfterTimedCmd(scheduler, afterCommand, command, time) { } const string& AfterIdleCmd::getType() const { static const string type("idle"); return type; } // class AfterEventCmd template AfterEventCmd::AfterEventCmd( AfterCommand& afterCommand, const string& type_, const string& command) : AfterCmd(afterCommand, command), type(type_) { } template const string& AfterEventCmd::getType() const { return type; } } // namespace openmsx