diff --git a/src/Makefile.am b/src/Makefile.am index c3b4ff25..1caa0d15 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,4 +11,6 @@ rtorrent_LDADD = \ $(top_srcdir)/src/input/libsub_input.a rtorrent_SOURCES = \ - main.cc + main.cc \ + signal_handler.cc \ + signal_handler.h diff --git a/src/display/Makefile.am b/src/display/Makefile.am index 8c881367..6f2484d8 100644 --- a/src/display/Makefile.am +++ b/src/display/Makefile.am @@ -8,6 +8,8 @@ libsub_display_a_SOURCES = \ window.cc \ window.h \ window_download_list.cc \ - window_download_list.h + window_download_list.h \ + window_title.cc \ + window_title.h INCLUDES = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) diff --git a/src/display/canvas.h b/src/display/canvas.h index 331876f8..fa745dec 100644 --- a/src/display/canvas.h +++ b/src/display/canvas.h @@ -27,6 +27,20 @@ public: void erase() { werase(m_window); } + void print_border(chtype ls, chtype rs, + chtype ts, chtype bs, + chtype tl, chtype tr, + chtype bl, chtype br) { wborder(m_window, ls, rs, ts, bs, tl, tr, bl, br); } + + // Initialize stdscr. + static void init(); + static void cleanup(); + + static int get_screen_width() { int x, y; getmaxyx(stdscr, y, x); return x; } + static int get_screen_height() { int x, y; getmaxyx(stdscr, y, x); return y; } + + static void do_update() { doupdate(); } + void print(int x, int y, const char* str) { mvwprintw(m_window, y, x, str); } template @@ -43,19 +57,13 @@ public: void print(int x, int y, const char* str, A1 a1, A2 a2, A3 a3, A4 a4) { mvwprintw(m_window, y, x, str, a1, a2, a3, a4); } - void print_border(chtype ls, chtype rs, - chtype ts, chtype bs, - chtype tl, chtype tr, - chtype bl, chtype br) { wborder(m_window, ls, rs, ts, bs, tl, tr, bl, br); } + template + void print(int x, int y, const char* str, + A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { mvwprintw(m_window, y, x, str, a1, a2, a3, a4, a5); } - // Initialize stdscr. - static void init(); - static void cleanup(); - - static int get_screen_width() { int x, y; getmaxyx(stdscr, y, x); return x; } - static int get_screen_height() { int x, y; getmaxyx(stdscr, y, x); return y; } - - static void do_update() { doupdate(); } + template + void print(int x, int y, const char* str, + A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) { mvwprintw(m_window, y, x, str, a1, a2, a3, a4, a5, a6); } private: WINDOW* m_window; diff --git a/src/display/manager.cc b/src/display/manager.cc index 2a9c0b2a..72c0040d 100644 --- a/src/display/manager.cc +++ b/src/display/manager.cc @@ -38,10 +38,13 @@ Manager::adjust_layout() { int height = 0, h; for (iterator itr = begin(); itr != end(); ++itr, height += h) { - h = (*itr)->is_dynamic() ? ((dynamic + countDynamic - 1) / countDynamic) : 0; - countDynamic--; - dynamic -= h; + if ((*itr)->is_dynamic()) { + dynamic -= h = (dynamic + countDynamic - 1) / countDynamic; + countDynamic--; + } else { + h = 0; + } h += (*itr)->get_min_height(); diff --git a/src/display/window_download_list.cc b/src/display/window_download_list.cc index 6e6a081b..a992fb8f 100644 --- a/src/display/window_download_list.cc +++ b/src/display/window_download_list.cc @@ -19,12 +19,36 @@ WindowDownloadList::redraw() { int pos = 1; - for (engine::DownloadList::iterator itr = m_downloads->begin(); itr != m_downloads->end(); ++itr, ++pos) - m_canvas->print(1, pos, "Download: %s Open: %c Tracker: %c Active: %c", - itr->get_download().get_name().c_str(), - itr->get_download().is_open() ? 'Y' : 'N', - itr->get_download().is_tracker_busy() ? 'Y' : 'N', - itr->get_download().is_active() ? 'Y' : 'N'); + // Remember to check for end of screen too. + + for (engine::DownloadList::iterator itr = m_downloads->begin(); itr != m_downloads->end(); ++itr) { + m_canvas->print(0, pos++, "%c %s", + false ? '*' : ' ', + itr->get_download().get_name().c_str()); + + if (itr->get_download().get_chunks_done() != itr->get_download().get_chunks_total() || !itr->get_download().is_open()) + m_canvas->print(0, pos++, "%c Torrent: %.1f / %.1f MiB Rate:%5.1f /%5.1f KiB Uploaded: %.1f MiB", + false ? '*' : ' ', + (double)itr->get_download().get_bytes_done() / (double)(1 << 20), + (double)itr->get_download().get_bytes_total() / (double)(1 << 20), + (double)itr->get_download().get_rate_up() / 1024.0, + (double)itr->get_download().get_rate_down() / 1024.0, + (double)itr->get_download().get_bytes_up() / (double)(1 << 20)); + + else + m_canvas->print(0, pos++, "%c Torrent: Done %.1f MiB Rate:%5.1f /%5.1f KiB Uploaded: %.1f MiB", + false ? '*' : ' ', + (double)itr->get_download().get_bytes_total() / (double)(1 << 20), + (double)itr->get_download().get_rate_up() / 1024.0, + (double)itr->get_download().get_rate_down() / 1024.0, + (double)itr->get_download().get_bytes_up() / (double)(1 << 20)); + + m_canvas->print(0, pos++, "%c Tracker: [%c:%i] %s", + false ? '*' : ' ', + itr->get_download().is_tracker_busy() ? 'C' : ' ', + (int)(itr->get_download().get_tracker_timeout() / 1000000), + ""); + } } } diff --git a/src/display/window_download_list.h b/src/display/window_download_list.h index e879246d..8bba82ef 100644 --- a/src/display/window_download_list.h +++ b/src/display/window_download_list.h @@ -13,7 +13,7 @@ class WindowDownloadList : public Window { public: WindowDownloadList(engine::DownloadList* d); - virtual void redraw(); + virtual void redraw(); private: engine::DownloadList* m_downloads; diff --git a/src/display/window_title.cc b/src/display/window_title.cc new file mode 100644 index 00000000..ff491b37 --- /dev/null +++ b/src/display/window_title.cc @@ -0,0 +1,25 @@ +#include "config.h" + +#include + +#include "canvas.h" +#include "window_title.h" + +namespace display { + +WindowTitle::WindowTitle() : + Window(new Canvas, false, 1) { + + m_title += "rtorrent " VERSION " - "; + m_title += torrent::get(torrent::LIBRARY_NAME); +} + +void +WindowTitle::redraw() { + m_canvas->erase(); + + m_canvas->print(std::max(0, (m_canvas->get_width() - (int)m_title.size()) / 2 - 4), 0, + "*** %s ***", m_title.c_str()); +} + +} diff --git a/src/display/window_title.h b/src/display/window_title.h new file mode 100644 index 00000000..cad88a61 --- /dev/null +++ b/src/display/window_title.h @@ -0,0 +1,20 @@ +#ifndef RTORRENT_DISPLAY_WINDOW_TITLE_H +#define RTORRENT_DISPLAY_WINDOW_TITLE_H + +#include "window.h" + +namespace display { + +class WindowTitle : public Window { +public: + WindowTitle(); + + virtual void redraw(); + +private: + std::string m_title; +}; + +} + +#endif diff --git a/src/engine/download.h b/src/engine/download.h index 9c88ac6f..4a0d6221 100644 --- a/src/engine/download.h +++ b/src/engine/download.h @@ -16,6 +16,8 @@ public: void open() { m_download.open(); } void close() { m_download.close(); } + void stop() { m_download.stop(); } + void hash_check(bool resume = false) { m_download.hash_check(resume); } bool operator == (const std::string& str) { return str == m_download.get_hash(); } diff --git a/src/engine/poll.cc b/src/engine/poll.cc index 6eb4140d..f5ec033d 100644 --- a/src/engine/poll.cc +++ b/src/engine/poll.cc @@ -20,8 +20,7 @@ Poll::poll() { m_maxFd = std::max(m_maxFd, 1); - if (m_readStdin) - FD_SET(0, &m_readSet); + FD_SET(0, &m_readSet); if (m_curlStack.is_busy()) m_curlStack.fdset(&m_readSet, &m_writeSet, &m_exceptSet, &m_maxFd); @@ -35,17 +34,19 @@ Poll::poll() { m_maxFd = select(m_maxFd, &m_readSet, &m_writeSet, &m_exceptSet, &timeout); - if (m_maxFd < 0) + if (m_maxFd == EINTR) + m_slotSelectInterrupted(); + else if (m_maxFd < 0) throw std::runtime_error("Poll::work(): select error"); } void Poll::work() { - if (m_readStdin && FD_ISSET(0, &m_readSet)) { + if (FD_ISSET(0, &m_readSet)) { int key; while ((key = getch()) >= 0) - m_readStdin(key); + m_slotReadStdin(key); } if (m_curlStack.is_busy()) diff --git a/src/engine/poll.h b/src/engine/poll.h index 22a5626e..8cc7579a 100644 --- a/src/engine/poll.h +++ b/src/engine/poll.h @@ -10,22 +10,20 @@ namespace engine { class Poll { public: + typedef sigc::slot0 Slot; typedef sigc::slot1 SlotInt; - Poll() : m_running(true) {} - - bool is_running() { return m_running; } - void poll(); void work(); void register_http(); - void slot_read_stdin(SlotInt s) { m_readStdin = s; } + void slot_read_stdin(SlotInt s) { m_slotReadStdin = s; } + void slot_select_interrupted(Slot s) { m_slotSelectInterrupted = s; } private: - bool m_running; - SlotInt m_readStdin; + SlotInt m_slotReadStdin; + Slot m_slotSelectInterrupted; int m_maxFd; fd_set m_readSet; diff --git a/src/main.cc b/src/main.cc index 29cb60c4..a44b8efa 100644 --- a/src/main.cc +++ b/src/main.cc @@ -7,6 +7,7 @@ #include "display/canvas.h" #include "display/manager.h" +#include "display/window_title.h" #include "display/window_download_list.h" #include "engine/poll.h" @@ -15,24 +16,48 @@ #include "input/bindings.h" #include "input/manager.h" -int main(int argc, char** argv) { +#include "signal_handler.h" + +bool start_shutdown = false; +bool is_shutting_down = false; + +engine::Poll poll; +engine::DownloadList downloads; + +display::Manager displayManager; +input::Manager inputManager; + +void +set_shutdown() { + start_shutdown = true; +} + +void +do_shutdown() { + if (is_shutting_down) + // Be quick about it... + return; + + is_shutting_down = true; + + torrent::listen_close(); + + std::for_each(downloads.begin(), downloads.end(), std::mem_fun_ref(&engine::Download::stop)); +} + +int +main(int argc, char** argv) { try { display::Canvas::init(); - engine::Poll poll; - engine::DownloadList downloads; - - display::Manager display; - - input::Manager inputManager; - inputManager.push_back(new input::Bindings); poll.slot_read_stdin(sigc::mem_fun(inputManager, &input::Manager::pressed)); poll.register_http(); - display.push_back(new display::WindowDownloadList(&downloads)); + displayManager.push_back(new display::WindowTitle); + displayManager.push_back(new display::WindowDownloadList(&downloads)); torrent::initialize(); torrent::listen_open(6880, 6999); @@ -46,9 +71,14 @@ int main(int argc, char** argv) { itr->hash_check(); } - while (poll.is_running()) { - display.adjust_layout(); - display.do_update(); + SignalHandler::set_handler(SIGINT, sigc::ptr_fun(&set_shutdown)); + + while (!is_shutting_down || !torrent::get(torrent::SHUTDOWN_DONE)) { + if (start_shutdown && !is_shutting_down) + do_shutdown(); + + displayManager.adjust_layout(); + displayManager.do_update(); poll.poll(); poll.work(); @@ -62,5 +92,7 @@ int main(int argc, char** argv) { std::cout << "Caught exception: \"" << e.what() << '"' << std::endl; } + torrent::cleanup(); + return 0; } diff --git a/src/signal_handler.cc b/src/signal_handler.cc new file mode 100644 index 00000000..ddffcb4b --- /dev/null +++ b/src/signal_handler.cc @@ -0,0 +1,47 @@ +#include "config.h" + +#include +#include "signal_handler.h" + +SignalHandler::Slot SignalHandler::m_handlers[HIGHEST_SIGNAL]; + +void +SignalHandler::set_default(unsigned int signum) { + if (signum >= HIGHEST_SIGNAL) + throw std::logic_error("SignalHandler::clear(...) received invalid signal value"); + + signal(signum, SIG_DFL); + m_handlers[signum].disconnect(); +} + +void +SignalHandler::set_ignore(unsigned int signum) { + if (signum >= HIGHEST_SIGNAL) + throw std::logic_error("SignalHandler::clear(...) received invalid signal value"); + + signal(signum, SIG_IGN); + m_handlers[signum].disconnect(); +} + +void +SignalHandler::set_handler(unsigned int signum, Slot slot) { + if (signum >= HIGHEST_SIGNAL) + throw std::logic_error("SignalHandler::handle(...) received invalid signal value"); + + if (slot.empty()) + throw std::logic_error("SignalHandler::handle(...) received an empty slot"); + + signal(signum, &SignalHandler::caught); + m_handlers[signum] = slot; +} + +void +SignalHandler::caught(int signum) { + if ((unsigned)signum >= HIGHEST_SIGNAL) + throw std::logic_error("SignalHandler::caught(...) received invalid signal from the kernel, bork bork bork"); + + if (m_handlers[signum].empty()) + throw std::logic_error("SignalHandler::caught(...) received a signal we don't have a handler for"); + + m_handlers[signum](); +} diff --git a/src/signal_handler.h b/src/signal_handler.h new file mode 100644 index 00000000..0b0b3daf --- /dev/null +++ b/src/signal_handler.h @@ -0,0 +1,23 @@ +#ifndef RTORRENT_SIGNAL_HANDLER_H +#define RTORRENT_SIGNAL_HANDLER_H + +#include +#include + +class SignalHandler { +public: + typedef sigc::slot0 Slot; + + static const unsigned int HIGHEST_SIGNAL = 32; + + static void set_default(unsigned int signum); + static void set_ignore(unsigned int signum); + static void set_handler(unsigned int signum, Slot slot); + +private: + static void caught(int signum); + + static Slot m_handlers[HIGHEST_SIGNAL]; +}; + +#endif