From 8c7e5bd0d1f966af2b22293a3a0780c419fb9c95 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Wed, 14 Oct 2015 14:45:22 +0300 Subject: [PATCH] Add a simple networked command interface --- Build | 1 + source/client.cpp | 85 +++++++++++++++++++++++++++++++++++++ source/client.h | 31 ++++++++++++++ source/networkinterface.cpp | 71 +++++++++++++++++++++++++++++++ source/networkinterface.h | 45 ++++++++++++++++++++ source/xinema.cpp | 40 ++++++++++++----- source/xinema.h | 11 ++++- 7 files changed, 271 insertions(+), 13 deletions(-) create mode 100644 source/client.cpp create mode 100644 source/client.h create mode 100644 source/networkinterface.cpp create mode 100644 source/networkinterface.h diff --git a/Build b/Build index a3f1888..f4bec33 100644 --- a/Build +++ b/Build @@ -7,6 +7,7 @@ package "xinema" require "xlib"; require "libxine"; require "sigc++-2.0"; + require "mspnet"; source "source"; build_info { diff --git a/source/client.cpp b/source/client.cpp new file mode 100644 index 0000000..dbe6091 --- /dev/null +++ b/source/client.cpp @@ -0,0 +1,85 @@ +#include +#include +#include "client.h" +#include "xinema.h" + +using namespace std; +using namespace Msp; + +Client::Client(Xinema &x, Net::StreamSocket *s): + xinema(x), + socket(s), + stale(false) +{ + socket->signal_data_available.connect(sigc::mem_fun(this, &Client::data_available)); + socket->signal_end_of_file.connect(sigc::mem_fun(this, &Client::end_of_stream)); +} + +void Client::data_available() +{ + char rbuf[1024]; + unsigned len = socket->read(rbuf, sizeof(rbuf)); + buffer.append(rbuf, len); + + string::size_type start = 0; + while(1) + { + string::size_type newline = buffer.find('\n', start); + if(newline==string::npos) + break; + + try + { + process_command(buffer.substr(start, newline-start)); + } + catch(const exception &e) + { + send_reply(string("error ")+e.what()); + return; + } + + start = newline+1; + } + + buffer.erase(0, start); +} + +void Client::end_of_stream() +{ + stale = true; +} + +void Client::process_command(const string &cmd) +{ + string::size_type space = cmd.find(' '); + string keyword = cmd.substr(0, space); + string args; + if(space!=string::npos) + args = cmd.substr(space+1); + if(keyword=="list_directory") + list_directory(args); + else if(keyword=="play_file") + xinema.play_file(args); + else + send_reply("error Invalid command"); +} + +void Client::send_reply(const string &reply) +{ + socket->write(reply); + socket->put('\n'); +} + +void Client::list_directory(const FS::Path &dn) +{ + list files = FS::list_files(dn); + + send_reply("directory "+dn.str()); + for(list::const_iterator i=files.begin(); i!=files.end(); ++i) + { + if(FS::is_dir(dn / *i)) + send_reply("subdir "+*i); + else + send_reply("file "+*i); + } +} diff --git a/source/client.h b/source/client.h new file mode 100644 index 0000000..1f543fc --- /dev/null +++ b/source/client.h @@ -0,0 +1,31 @@ +#ifndef CLIENT_H_ +#define CLIENT_H_ + +#include +#include + +class Xinema; + +class Client +{ +private: + Xinema &xinema; + Msp::Net::StreamSocket *socket; + std::string buffer; + bool stale; + +public: + Client(Xinema &, Msp::Net::StreamSocket *); + + bool is_stale() const { return stale; } + +private: + void data_available(); + void end_of_stream(); + + void process_command(const std::string &); + void send_reply(const std::string &); + void list_directory(const Msp::FS::Path &); +}; + +#endif diff --git a/source/networkinterface.cpp b/source/networkinterface.cpp new file mode 100644 index 0000000..74c80dc --- /dev/null +++ b/source/networkinterface.cpp @@ -0,0 +1,71 @@ +#include +#include +#include "client.h" +#include "networkinterface.h" + +using namespace std; +using namespace Msp; + +NetworkInterface::NetworkInterface(Xinema &x): + xinema(x) +{ + Net::SockAddr *addr = Net::resolve("::", "34588", Net::INET6); + server_sock = new Net::StreamServerSocket(addr->get_family()); + server_sock->listen(*addr); + delete addr; + + server_sock->signal_data_available.connect(sigc::mem_fun(this, &NetworkInterface::connection_available)); + + event_disp.add(*server_sock); + + thread = new NetworkThread(*this); +} + +NetworkInterface::~NetworkInterface() +{ + thread->terminate(); + delete thread; + delete server_sock; +} + +void NetworkInterface::connection_available() +{ + Net::StreamSocket *sock = server_sock->accept(); + event_disp.add(*sock); + clients.push_back(new Client(xinema, sock)); +} + + +NetworkInterface::NetworkThread::NetworkThread(NetworkInterface &n): + network(n), + done(false) +{ + network.event_disp.add(wakeup_pipe); + launch(); +} + +void NetworkInterface::NetworkThread::terminate() +{ + done = true; + wakeup_pipe.put('w'); + join(); +} + +void NetworkInterface::NetworkThread::main() +{ + while(!done) + { + network.event_disp.tick(); + + for(list::iterator i=network.clients.begin(); i!=network.clients.end(); ) + { + if((*i)->is_stale()) + { + delete *i; + network.clients.erase(i++); + } + else + ++i; + } + } +} diff --git a/source/networkinterface.h b/source/networkinterface.h new file mode 100644 index 0000000..fbb66b9 --- /dev/null +++ b/source/networkinterface.h @@ -0,0 +1,45 @@ +#ifndef NETWORKINTERFACE_H_ +#define NETWORKINTERFACE_H_ + +#include +#include +#include +#include + +class Client; +class Xinema; + +class NetworkInterface +{ +private: + class NetworkThread: public Msp::Thread + { + private: + NetworkInterface &network; + Msp::IO::Pipe wakeup_pipe; + bool done; + + public: + NetworkThread(NetworkInterface &); + + void terminate(); + + private: + virtual void main(); + }; + + Xinema &xinema; + Msp::Net::StreamServerSocket *server_sock; + Msp::IO::EventDispatcher event_disp; + NetworkThread *thread; + std::list clients; + +public: + NetworkInterface(Xinema &); + ~NetworkInterface(); + +private: + void connection_available(); +}; + +#endif diff --git a/source/xinema.cpp b/source/xinema.cpp index e4051cd..fc042e1 100644 --- a/source/xinema.cpp +++ b/source/xinema.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include "xineengine.h" #include "xinema.h" #include "xinestream.h" @@ -7,13 +7,12 @@ using namespace std; using namespace Msp; -Xinema::Xinema(int argc, char **argv): - window(display, 1920, 1080) +Xinema::Xinema(int, char **): + window(display, 1920, 1080), + network(*this), + engine(0), + stream(0) { - GetOpt getopt; - getopt.add_argument("filename", filename, GetOpt::REQUIRED_ARG); - getopt(argc, argv); - window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &Xinema::exit), 0)); } @@ -23,12 +22,11 @@ int Xinema::main() display.tick(); engine = new XineEngine(window, &display_mutex); - stream = new XineStream(*engine, filename); - stream->play(); Application::main(); - delete stream; + if(stream) + delete stream; delete engine; return exit_code; @@ -36,10 +34,30 @@ int Xinema::main() void Xinema::tick() { + { + MutexLock lock(command_mutex); + if(!pending_mrl.empty()) + { + delete stream; + stream = new XineStream(*engine, pending_mrl); + stream->play(); + pending_mrl.clear(); + } + } + { MutexLock lock(display_mutex); display.tick(); } - stream->tick(); + if(stream) + stream->tick(); + + Time::sleep(10*Time::msec); +} + +void Xinema::play_file(const FS::Path &fn) +{ + MutexLock lock(command_mutex); + pending_mrl = "file://"+fn.str(); } diff --git a/source/xinema.h b/source/xinema.h index 3c3f75a..c54732d 100644 --- a/source/xinema.h +++ b/source/xinema.h @@ -3,8 +3,10 @@ #include #include +#include #include #include +#include "networkinterface.h" class XineEngine; class XineStream; @@ -12,20 +14,25 @@ class XineStream; class Xinema: public Msp::RegisteredApplication { private: - std::string filename; Msp::Graphics::Display display; Msp::Mutex display_mutex; Msp::Graphics::Window window; + NetworkInterface network; XineEngine *engine; XineStream *stream; + Msp::Mutex command_mutex; + std::string pending_mrl; + public: Xinema(int, char **); virtual int main(); - private: virtual void tick(); + +public: + void play_file(const Msp::FS::Path &); }; #endif -- 2.45.2