diff --git a/src/Makefile.am b/src/Makefile.am index bf295730..53724948 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,4 +9,8 @@ rtorrent_LDADD = \ $(top_srcdir)/src/input/libsub_input.a rtorrent_SOURCES = \ - main.cc + downloads.cc \ + downloads.h \ + main.cc \ + poll.cc \ + poll.h diff --git a/src/curl_get.cc b/src/curl_get.cc new file mode 100644 index 00000000..e3ae11b5 --- /dev/null +++ b/src/curl_get.cc @@ -0,0 +1,119 @@ +#include "config.h" + +#include "curl_get.h" +#include "curl_stack.h" +#include + +#include +#include +#include + +CurlGet::~CurlGet() { + close(); +} + +CurlGet::CurlGet(CurlStack* s) : + m_useragent("rtorrent_unknown"), + m_out(NULL), + m_handle(NULL), + m_stack(s) { + + if (m_stack == NULL) + throw torrent::client_error("Tried to create CurlGet without a valid CurlStack"); +} + +CurlGet* +CurlGet::new_object(CurlStack* s) { + return new CurlGet(s); +} + +void CurlGet::set_url(const std::string& url) { + if (is_busy()) + throw torrent::local_error("Tried to call CurlGet::set_url on a busy object"); + + m_url = url; +} + +const std::string& +CurlGet::get_url() const { + return m_url; +} + +void CurlGet::set_out(std::ostream* out) { + if (is_busy()) + throw torrent::local_error("Tried to call CurlGet::set_url on a busy object"); + + m_out = out; +} + +std::ostream* +CurlGet::get_out() { + return m_out; +} + +void +CurlGet::set_user_agent(const std::string& s) { + curl_easy_setopt(m_handle, CURLOPT_USERAGENT, s.c_str()); + + m_useragent = s; +} + +const std::string& +CurlGet::get_user_agent() { + return m_useragent; +} + +void CurlGet::start() { + if (is_busy()) + throw torrent::local_error("Tried to call CurlGet::start on a busy object"); + + if (m_out == NULL) + throw torrent::local_error("Tried to call CurlGet::start without a valid output stream"); + + m_handle = curl_easy_init(); + + curl_easy_setopt(m_handle, CURLOPT_URL, m_url.c_str()); + curl_easy_setopt(m_handle, CURLOPT_WRITEFUNCTION, &curl_get_receive_write); + curl_easy_setopt(m_handle, CURLOPT_WRITEDATA, this); + curl_easy_setopt(m_handle, CURLOPT_FORBID_REUSE, 1); + + m_stack->add_get(this); +} + +void CurlGet::close() { + if (!is_busy()) + return; + + m_stack->remove_get(this); + + curl_easy_cleanup(m_handle); + + m_handle = NULL; +} + +void CurlGet::perform(CURLMsg* msg) { + if (msg->msg != CURLMSG_DONE) + throw torrent::client_error("CurlGet::process got CURLMSG that isn't done"); + + if (msg->data.result == CURLE_OK) { + m_done.emit(); + + } else { + m_failed.emit(curl_easy_strerror(msg->data.result)); + } +} + +size_t curl_get_receive_write(void* data, size_t size, size_t nmemb, void* handle) { + return ((CurlGet*)handle)->m_out->write((char*)data, size * nmemb).fail() ? 0 : size * nmemb; +} + +CurlGet::SignalDone& +CurlGet::signal_done() { + return m_done; +} + +CurlGet::SignalFailed& +CurlGet::signal_failed() { + return m_failed; +} + diff --git a/src/curl_get.h b/src/curl_get.h new file mode 100644 index 00000000..98dfbdcc --- /dev/null +++ b/src/curl_get.h @@ -0,0 +1,57 @@ +#ifndef LIBTORRENT_CURL_GET_H +#define LIBTORRENT_CURL_GET_H + +#include +#include +#include +#include +#include + +struct CURLMsg; + +class CurlGet : public torrent::Http { + public: + friend class CurlStack; + + CurlGet(CurlStack* s); + virtual ~CurlGet(); + + static CurlGet* new_object(CurlStack* s); + + void start(); + void close(); + + void set_url(const std::string& url); + const std::string& get_url() const; + + void set_out(std::ostream* out); + std::ostream* get_out(); + + void set_user_agent(const std::string& s); + const std::string& get_user_agent(); + + bool is_busy() { return m_handle; } + + SignalDone& signal_done(); + SignalFailed& signal_failed(); + + protected: + CURL* handle() { return m_handle; } + + void perform(CURLMsg* msg); + + private: + friend size_t curl_get_receive_write(void* data, size_t size, size_t nmemb, void* handle); + + std::string m_url; + std::string m_useragent; + std::ostream* m_out; + CURL* m_handle; + + CurlStack* m_stack; + + sigc::signal0 m_done; + sigc::signal1 m_failed; +}; + +#endif diff --git a/src/curl_stack.cc b/src/curl_stack.cc new file mode 100644 index 00000000..a16f0aaf --- /dev/null +++ b/src/curl_stack.cc @@ -0,0 +1,97 @@ +#include "curl_get.h" +#include "curl_stack.h" +#include + +#include +#include + +using namespace algo; + +namespace torrent { + +CurlStack::CurlStack() : + m_handle((void*)curl_multi_init()), + m_size(0) { +} + +CurlStack::~CurlStack() { + while (!m_getList.empty()) + m_getList.front()->close(); + + curl_multi_cleanup((CURLM*)m_handle); +} + +void CurlStack::perform() { + int s; + CURLMcode code; + + do { + code = curl_multi_perform((CURLM*)m_handle, &s); + + if (code > 0) + throw torrent::local_error("Error calling curl_multi_perform"); + + if (s != m_size) { + // Done with some handles. + int t; + + do { + CURLMsg* msg = curl_multi_info_read((CURLM*)m_handle, &t); + + CurlGetList::iterator itr = std::find_if(m_getList.begin(), m_getList.end(), + eq(call_member(&CurlGet::handle), + value(msg->easy_handle))); + + if (itr == m_getList.end()) + throw torrent::client_error("Could not find CurlGet with the right easy_handle"); + + (*itr)->perform(msg); + } while (t); + } + + } while (code == CURLM_CALL_MULTI_PERFORM); +} + +void CurlStack::fdset(fd_set* readfds, fd_set* writefds, fd_set* exceptfds, int& maxFd) { + int f; + + if (curl_multi_fdset((CURLM*)m_handle, readfds, writefds, exceptfds, &f) > 0) + throw torrent::local_error("Error calling curl_multi_fdset"); + + maxFd = std::max(f, maxFd); +} + +void CurlStack::add_get(CurlGet* get) { + CURLMcode code; + + if ((code = curl_multi_add_handle((CURLM*)m_handle, get->handle())) > 0) + throw torrent::local_error("curl_multi_add_handle \"" + std::string(curl_multi_strerror(code))); + + m_size++; + + m_getList.push_back(get); +} + +void CurlStack::remove_get(CurlGet* get) { + if (curl_multi_remove_handle((CURLM*)m_handle, get->handle()) > 0) + throw torrent::local_error("Error calling curl_multi_remove_handle"); + + CurlGetList::iterator itr = std::find(m_getList.begin(), m_getList.end(), get); + + if (itr == m_getList.end()) + throw torrent::client_error("Could not find CurlGet when calling CurlStack::remove"); + + m_getList.erase(itr); + + m_size--; +} + +void CurlStack::global_init() { + curl_global_init(CURL_GLOBAL_ALL); +} + +void CurlStack::global_cleanup() { + curl_global_cleanup(); +} + +} diff --git a/src/curl_stack.h b/src/curl_stack.h new file mode 100644 index 00000000..ed08dc31 --- /dev/null +++ b/src/curl_stack.h @@ -0,0 +1,37 @@ +#ifndef LIBTORRENT_CURL_STACK_H +#define LIBTORRENT_CURL_STACK_H + +#include + +class CurlStack { + friend class CurlGet; + + public: + typedef std::list CurlGetList; + + CurlStack(); + ~CurlStack(); + + int get_size() const { return m_size; } + bool is_busy() const { return !m_getList.empty(); } + + void perform(); + + void fdset(fd_set* readfds, fd_set* writefds, fd_set* exceptfds, int& maxFd); + + static void global_init(); + static void global_cleanup(); + + protected: + void add_get(CurlGet* get); + void remove_get(CurlGet* get); + + private: + void* m_handle; + + int m_size; + CurlGetList m_getList; +}; + +#endif + diff --git a/src/display/Makefile.am b/src/display/Makefile.am index 6a811299..2fe1d621 100644 --- a/src/display/Makefile.am +++ b/src/display/Makefile.am @@ -5,8 +5,9 @@ libsub_display_a_SOURCES = \ canvas.h \ manager.cc \ manager.h \ - manager_element.h \ + window.cc \ window.h \ - window.cc + window_downloads.cc \ + window_downloads.h INCLUDES = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) diff --git a/src/display/window_downloads.cc b/src/display/window_downloads.cc new file mode 100644 index 00000000..4e2a6c85 --- /dev/null +++ b/src/display/window_downloads.cc @@ -0,0 +1,24 @@ +#include "config.h" + +#include "window_downloads.h" +#include "canvas.h" + +namespace display { + +WindowDownloads::WindowDownloads(Downloads* d) : + Window(new Canvas, true), + m_downloads(d) { +} + +void +WindowDownloads::redraw() { + m_canvas->erase(); + m_canvas->print_border(' ', ' ', '-', '-', ' ', ' ', ' ', ' '); + + int pos = 1; + + for (Downloads::iterator itr = m_downloads->begin(); itr != m_downloads->end(); ++itr, ++pos) + m_canvas->print(1, pos, "Download: %s", itr->get_name().c_str()); +} + +} diff --git a/src/display/window_downloads.h b/src/display/window_downloads.h new file mode 100644 index 00000000..ea88dddc --- /dev/null +++ b/src/display/window_downloads.h @@ -0,0 +1,21 @@ +#ifndef RTORRENT_DISPLAY_WINDOW_DOWNLOADS_H +#define RTORRENT_DISPLAY_WINDOW_DOWNLOADS_H + +#include "downloads.h" +#include "window.h" + +namespace display { + +class WindowDownloads : public Window { +public: + WindowDownloads(Downloads* d); + + virtual void redraw(); + +private: + Downloads* m_downloads; +}; + +} + +#endif diff --git a/src/downloads.cc b/src/downloads.cc new file mode 100644 index 00000000..13d9708b --- /dev/null +++ b/src/downloads.cc @@ -0,0 +1,19 @@ +#include "config.h" + +#include + +#include "downloads.h" + +void +Downloads::create(std::istream& str) { + torrent::Download d = torrent::download_create(str); + + Base::push_back(d); +} + +void +Downloads::erase(iterator itr) { + torrent::download_remove(itr->get_hash()); + + Base::erase(itr); +} diff --git a/src/downloads.h b/src/downloads.h new file mode 100644 index 00000000..e6253360 --- /dev/null +++ b/src/downloads.h @@ -0,0 +1,25 @@ +#ifndef RTORRENT_DOWNLOADS_H +#define RTORRENT_DOWNLOADS_H + +#include +#include + +class Downloads : private std::list { +public: + typedef std::list Base; + + using Base::iterator; + using Base::const_iterator; + using Base::reverse_iterator; + using Base::const_reverse_iterator; + + using Base::begin; + using Base::end; + using Base::rbegin; + using Base::rend; + + void create(std::istream& str); + void erase(iterator itr); +}; + +#endif diff --git a/src/input/manager.cc b/src/input/manager.cc index b55d4898..16762290 100644 --- a/src/input/manager.cc +++ b/src/input/manager.cc @@ -8,9 +8,9 @@ namespace input { -bool +void Manager::pressed(int key) { - return std::find_if(begin(), end(), std::bind2nd(std::mem_fun(&Bindings::pressed), key)) != end(); + std::find_if(begin(), end(), std::bind2nd(std::mem_fun(&Bindings::pressed), key)); } } diff --git a/src/input/manager.h b/src/input/manager.h index 35ea75c3..9aeffe02 100644 --- a/src/input/manager.h +++ b/src/input/manager.h @@ -24,7 +24,9 @@ public: using Base::push_back; using Base::push_front; - bool pressed(int key); + void pressed(int key); + + // Slot for unreacted keys. }; } diff --git a/src/main.cc b/src/main.cc index 0d70f898..ada8d499 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,86 +1,49 @@ #include #include #include +#include +#include -#include "display/window.h" #include "display/canvas.h" #include "display/manager.h" +#include "display/window_downloads.h" #include "input/bindings.h" #include "input/manager.h" -class WindowTest : public display::Window { -public: - WindowTest(const std::string& str, bool reverse = false) : - Window(new display::Canvas, true, 3), m_str(str), m_reverse(reverse) {} - - virtual void redraw() { - if (m_canvas == NULL) - return; - - if (m_reverse) - m_canvas->set_background(A_REVERSE); - else - m_canvas->set_background(0); - - m_canvas->erase(); - m_canvas->print_border('|', '|', '-', '-', '+', '+', '+', '+'); - m_canvas->print(1, 1, "%s minHeight %i", m_str.c_str(), m_minHeight); - } - - void reverse() { - m_reverse = !m_reverse; - } - - void inc_min() { - m_minHeight++; - } - - void dec_min() { - if (m_minHeight) - m_minHeight--; - } - -private: - std::string m_str; - - bool m_reverse; -}; +#include "poll.h" +#include "downloads.h" int main(int argc, char** argv) { try { + Poll poll; + Downloads downloads; + display::Canvas::init(); display::Manager display; + input::Manager inputManager; + inputManager.push_back(new input::Bindings); - WindowTest window1("This is window 1"); - WindowTest window2("This is window 2"); + poll.slot_read_stdin(sigc::mem_fun(inputManager, &input::Manager::pressed)); - input::Bindings bindings1; - input::Bindings bindings2; + display.push_back(new display::WindowDownloads(&downloads)); - inputManager.push_back(&bindings1); - inputManager.push_back(&bindings2); + torrent::initialize(); + torrent::listen_open(6880, 6999); - bindings1[KEY_LEFT] = sigc::mem_fun(window1, &WindowTest::reverse); - bindings1[KEY_UP] = sigc::mem_fun(window1, &WindowTest::inc_min); - bindings1[KEY_DOWN] = sigc::mem_fun(window1, &WindowTest::dec_min); - bindings1[KEY_LEFT] = sigc::mem_fun(window1, &WindowTest::reverse); - bindings2[KEY_LEFT] = sigc::mem_fun(window2, &WindowTest::reverse); - bindings2[KEY_RIGHT] = sigc::mem_fun(window2, &WindowTest::reverse); + for (int i = 1; i < argc; ++i) { + std::fstream f(argv[i], std::ios::in); - display.push_back(&window1); - display.push_back(&window2); + downloads.create(f); + } - display.adjust_layout(); - - while (true) { + while (poll.is_running()) { display.adjust_layout(); display.do_update(); - - inputManager.pressed(getch()); - sleep(0); + poll.poll(); + poll.work(); } display::Canvas::cleanup(); diff --git a/src/poll.cc b/src/poll.cc new file mode 100644 index 00000000..4f5dd5cc --- /dev/null +++ b/src/poll.cc @@ -0,0 +1,35 @@ +#include "config.h" + +#include +#include + +#include "poll.h" + +void +Poll::poll() { + FD_ZERO(&m_readSet); + FD_ZERO(&m_writeSet); + FD_ZERO(&m_exceptSet); + + m_maxFd = 1; + + if (m_readStdin) + FD_SET(0, &m_readSet); + + timeval timeout = {60, 0}; + + m_maxFd = select(m_maxFd, &m_readSet, &m_writeSet, &m_exceptSet, &timeout); + + if (m_maxFd < 0) + throw std::runtime_error("Poll::work(): select error"); +} + +void +Poll::work() { + if (m_readStdin && FD_ISSET(0, &m_readSet)) { + int key; + + while ((key = getch()) >= 0) + m_readStdin(key); + } +} diff --git a/src/poll.h b/src/poll.h new file mode 100644 index 00000000..870577fc --- /dev/null +++ b/src/poll.h @@ -0,0 +1,30 @@ +#ifndef RTORRENT_POLL_H +#define RTORRENT_POLL_H + +#include +#include + +class Poll { +public: + typedef sigc::slot1 SlotInt; + + Poll() : m_running(true) {} + + bool is_running() { return m_running; } + + void poll(); + void work(); + + void slot_read_stdin(SlotInt s) { m_readStdin = s; } + +private: + bool m_running; + SlotInt m_readStdin; + + int m_maxFd; + fd_set m_readSet; + fd_set m_writeSet; + fd_set m_exceptSet; +}; + +#endif