]> git.tdb.fi Git - xinema.git/commitdiff
Add a simple networked command interface
authorMikko Rasa <tdb@tdb.fi>
Wed, 14 Oct 2015 11:45:22 +0000 (14:45 +0300)
committerMikko Rasa <tdb@tdb.fi>
Wed, 14 Oct 2015 11:45:22 +0000 (14:45 +0300)
Build
source/client.cpp [new file with mode: 0644]
source/client.h [new file with mode: 0644]
source/networkinterface.cpp [new file with mode: 0644]
source/networkinterface.h [new file with mode: 0644]
source/xinema.cpp
source/xinema.h

diff --git a/Build b/Build
index a3f1888247b7a0c435ab3b6d4a16355c08b58e33..f4bec33a5bc1fe83787420e2a18d0e1c79ae8a10 100644 (file)
--- 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 (file)
index 0000000..dbe6091
--- /dev/null
@@ -0,0 +1,85 @@
+#include <msp/fs/dir.h>
+#include <msp/fs/stat.h>
+#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<string> files = FS::list_files(dn);
+
+       send_reply("directory "+dn.str());
+       for(list<string>::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 (file)
index 0000000..1f543fc
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef CLIENT_H_
+#define CLIENT_H_
+
+#include <msp/fs/path.h>
+#include <msp/net/streamsocket.h>
+
+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 (file)
index 0000000..74c80dc
--- /dev/null
@@ -0,0 +1,71 @@
+#include <msp/net/inet6.h>
+#include <msp/net/resolve.h>
+#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<Client *>::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 (file)
index 0000000..fbb66b9
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef NETWORKINTERFACE_H_
+#define NETWORKINTERFACE_H_
+
+#include <msp/core/thread.h>
+#include <msp/io/eventdispatcher.h>
+#include <msp/io/pipe.h>
+#include <msp/net/streamserversocket.h>
+
+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<Client *> clients;
+
+public:
+       NetworkInterface(Xinema &);
+       ~NetworkInterface();
+
+private:
+       void connection_available();
+};
+
+#endif
index e4051cda3f15d5bb35bee8f474667d03d59f7190..fc042e1ebd78e6487de3c516caa91db8bee65230 100644 (file)
@@ -1,5 +1,5 @@
 #include <sigc++/bind.h>
-#include <msp/core/getopt.h>
+#include <msp/time/utils.h>
 #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();
 }
index 3c3f75a0eb610746433343778157b39998686bcc..c54732d464f9dc2be94af01a688ea96c55cc590c 100644 (file)
@@ -3,8 +3,10 @@
 
 #include <msp/core/application.h>
 #include <msp/core/mutex.h>
+#include <msp/fs/path.h>
 #include <msp/graphics/display.h>
 #include <msp/graphics/window.h>
+#include "networkinterface.h"
 
 class XineEngine;
 class XineStream;
@@ -12,20 +14,25 @@ class XineStream;
 class Xinema: public Msp::RegisteredApplication<Xinema>
 {
 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