// $Id: GlobalCliComm.cc 6126 2007-02-28 22:37:02Z manuelbi $ #include "GlobalCliComm.hh" #include "CommandException.hh" #include "XMLElement.hh" #include "EventDistributor.hh" #include "LedEvent.hh" #include "CliConnection.hh" #include "Socket.hh" #include "GlobalCommandController.hh" #include "Command.hh" #include "StringOp.hh" #include #include #include using std::cout; using std::cerr; using std::endl; using std::map; using std::set; using std::string; using std::vector; using std::auto_ptr; namespace openmsx { class UpdateCmd : public SimpleCommand { public: UpdateCmd(CommandController& commandController, GlobalCliComm& cliComm); virtual string execute(const vector& tokens); virtual string help(const vector& tokens) const; virtual void tabCompletion(vector& tokens) const; private: CliConnection& getConnection(); GlobalCliComm& cliComm; }; GlobalCliComm::GlobalCliComm(GlobalCommandController& commandController_, EventDistributor& eventDistributor_) : updateCmd(new UpdateCmd(commandController_, *this)) , commandController(commandController_) , eventDistributor(eventDistributor_) , xmlOutput(false) , sem(1) { commandController.setCliComm(this); eventDistributor.registerEventListener(OPENMSX_LED_EVENT, *this); } GlobalCliComm::~GlobalCliComm() { ScopedLock lock(sem); for (Connections::const_iterator it = connections.begin(); it != connections.end(); ++it) { delete *it; } eventDistributor.unregisterEventListener(OPENMSX_LED_EVENT, *this); commandController.setCliComm(0); } void GlobalCliComm::startInput(const string& option) { string type_name, arguments; StringOp::splitOnFirst(option, ":", type_name, arguments); auto_ptr connection; if (type_name == "stdio") { connection.reset(new StdioConnection( commandController, eventDistributor)); } #ifdef _WIN32 else if (type_name == "pipe") { OSVERSIONINFO info; info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionExA(&info); if (info.dwPlatformId == VER_PLATFORM_WIN32_NT) { connection.reset(new PipeConnection( commandController, eventDistributor, arguments)); } else { throw FatalError("Pipes are not supported on this " "version of Windows"); } } #endif else { throw FatalError("Unknown control type: '" + type_name + "'"); } xmlOutput = true; addConnection(connection); } void GlobalCliComm::addConnection(auto_ptr connection) { ScopedLock lock(sem); connections.push_back(connection.release()); } const char* const updateStr[CliComm::NUM_UPDATES] = { "led", "setting", "hardware", "plug", "unplug", "media", "status" }; void GlobalCliComm::log(LogLevel level, const string& message) { const char* const levelStr[2] = { "info", "warning" }; ScopedLock lock(sem); if (!connections.empty()) { string str = string("" + XMLElement::XMLEscape(message) + "\n"; for (Connections::const_iterator it = connections.begin(); it != connections.end(); ++it) { (*it)->output(str); } } if (!xmlOutput) { ((level == INFO) ? cout : cerr) << levelStr[level] << ": " << message << endl; } } void GlobalCliComm::update(UpdateType type, const string& name, const string& value) { update(type, "", name, value); } void GlobalCliComm::update(UpdateType type, const string& machine, const string& name, const string& value) { assert(type < NUM_UPDATES); map::iterator it = prevValues[type][machine].find(name); if (it != prevValues[type][machine].end()) { if (it->second == value) { return; } it->second = value; } else { prevValues[type][machine][name] = value; } ScopedLock lock(sem); if (!connections.empty()) { string str = string("\n"; for (Connections::const_iterator it = connections.begin(); it != connections.end(); ++it) { CliConnection& connection = **it; if (connection.getUpdateEnable(type)) { connection.output(str); } } } } bool GlobalCliComm::signalEvent(shared_ptr event) { static const string ON = "on"; static const string OFF = "off"; static map ledName; static bool init = false; if (!init) { init = true; ledName[LedEvent::POWER] = "power"; ledName[LedEvent::CAPS] = "caps"; ledName[LedEvent::KANA] = "kana"; ledName[LedEvent::PAUSE] = "pause"; ledName[LedEvent::TURBO] = "turbo"; ledName[LedEvent::FDD] = "FDD"; } assert(event->getType() == OPENMSX_LED_EVENT); const LedEvent& ledEvent = static_cast(*event); update(LED, ledEvent.getMachine(), ledName[ledEvent.getLed()], ledEvent.getStatus() ? ON : OFF); return true; } // class UpdateCmd UpdateCmd::UpdateCmd(CommandController& commandController, GlobalCliComm& cliComm_) : SimpleCommand(commandController, "update") , cliComm(cliComm_) { } static GlobalCliComm::UpdateType getType(const string& name) { for (unsigned i = 0; i < CliComm::NUM_UPDATES; ++i) { if (updateStr[i] == name) { return static_cast(i); } } throw CommandException("No such update type: " + name); } CliConnection& UpdateCmd::getConnection() { CliConnection* connection = getCommandController().getConnection(); if (!connection) { throw CommandException("This command only makes sense when " "it's used from an external application."); } return *connection; } string UpdateCmd::execute(const vector& tokens) { if (tokens.size() != 3) { throw SyntaxError(); } if (tokens[1] == "enable") { getConnection().setUpdateEnable(getType(tokens[2]), true); } else if (tokens[1] == "disable") { getConnection().setUpdateEnable(getType(tokens[2]), false); } else { throw SyntaxError(); } return ""; } string UpdateCmd::help(const vector& /*tokens*/) const { static const string helpText = "Enable or disable update events for external applications. See doc/openmsx-control-xml.txt."; return helpText; } void UpdateCmd::tabCompletion(vector& tokens) const { switch (tokens.size()) { case 2: { set ops; ops.insert("enable"); ops.insert("disable"); completeString(tokens, ops); break; } case 3: { set types(updateStr, updateStr + CliComm::NUM_UPDATES); completeString(tokens, types); } } } } // namespace openmsx