From e02974571a59610e84e30de0382b4e432d6a8c95 Mon Sep 17 00:00:00 2001 From: rakshasa Date: Fri, 4 Feb 2005 19:50:59 +0000 Subject: [PATCH] Alot of UI work, resize fixed. git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@253 e378c898-3ddf-0310-93e7-cc216c733640 --- src/Makefile.am | 3 +- src/core/poll.cc | 6 +- src/display/manager.cc | 2 + src/display/window.h | 6 ++ src/display/window_download_list.cc | 10 +++- src/display/window_download_list.h | 2 +- src/display/window_peer_list.cc | 4 ++ src/display/window_peer_list.h | 2 +- src/main.cc | 28 ++++++++- src/timer.h | 55 +++++++++++++++++ src/ui/Makefile.am | 10 ++++ src/ui/download.cc | 90 ++++++++++++++++++++++++++++ src/ui/download.h | 58 ++++++++++++++++++ src/ui/download_list.cc | 93 +++++++++++++++++++++++++++++ src/ui/download_list.h | 47 +++++++++++++++ 15 files changed, 406 insertions(+), 10 deletions(-) create mode 100644 src/timer.h create mode 100644 src/ui/Makefile.am create mode 100644 src/ui/download.cc create mode 100644 src/ui/download.h create mode 100644 src/ui/download_list.cc create mode 100644 src/ui/download_list.h diff --git a/src/Makefile.am b/src/Makefile.am index 0638baf7..4484d203 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,4 +15,5 @@ rtorrent_LDADD = \ rtorrent_SOURCES = \ main.cc \ signal_handler.cc \ - signal_handler.h + signal_handler.h \ + timer.h diff --git a/src/core/poll.cc b/src/core/poll.cc index 7b38b941..32a613d9 100644 --- a/src/core/poll.cc +++ b/src/core/poll.cc @@ -28,8 +28,8 @@ Poll::poll() { uint64_t t = torrent::get(torrent::TIME_SELECT); - if (t > 10000000) - t = 10000000; + if (t > 1000000) + t = 1000000; timeval timeout = {t / 1000000, t % 1000000}; @@ -50,7 +50,7 @@ Poll::work() { if (FD_ISSET(0, &m_readSet)) { int key; - while ((key = getch()) >= 0) + while ((key = getch()) != ERR) m_slotReadStdin(key); } diff --git a/src/display/manager.cc b/src/display/manager.cc index 5912ebee..16215fd7 100644 --- a/src/display/manager.cc +++ b/src/display/manager.cc @@ -60,6 +60,8 @@ Manager::adjust_layout() { (*itr)->resize(0, height, Canvas::get_screen_width(), h); } + + std::for_each(begin(), end(), std::mem_fun(&Window::mark_dirty)); } void diff --git a/src/display/window.h b/src/display/window.h index c4e5121c..1f45ed41 100644 --- a/src/display/window.h +++ b/src/display/window.h @@ -1,6 +1,8 @@ #ifndef RTORRENT_WINDOW_BASE_H #define RTORRENT_WINDOW_BASE_H +#include "timer.h" + namespace display { class Canvas; @@ -17,6 +19,8 @@ public: void refresh(); void resize(int x, int y, int w, int h); + void mark_dirty() { m_lastDraw = 0; } + virtual void redraw() = 0; protected: @@ -27,6 +31,8 @@ protected: bool m_dynamic; int m_minHeight; + + Timer m_lastDraw; }; } diff --git a/src/display/window_download_list.cc b/src/display/window_download_list.cc index ed89cecc..e8979006 100644 --- a/src/display/window_download_list.cc +++ b/src/display/window_download_list.cc @@ -15,10 +15,14 @@ WindowDownloadList::WindowDownloadList(DList* d) : void WindowDownloadList::redraw() { - m_canvas->erase(); - m_canvas->print_border(' ', ' ', '-', '-', ' ', ' ', ' ', ' '); + if (Timer::cache() - m_lastDraw < 1000000) + return; - int pos = 1; + m_lastDraw = Timer::cache(); + + m_canvas->erase(); + + int pos = 0; // Remember to check for end of screen too. diff --git a/src/display/window_download_list.h b/src/display/window_download_list.h index f65f8a06..557a6e4d 100644 --- a/src/display/window_download_list.h +++ b/src/display/window_download_list.h @@ -15,7 +15,7 @@ public: DList& get_list() { return *m_downloads; } DList::iterator get_focus() { return m_focus; } - void set_focus(DList::iterator itr) { m_focus = itr; } + void set_focus(DList::iterator itr) { m_focus = itr; mark_dirty(); } virtual void redraw(); diff --git a/src/display/window_peer_list.cc b/src/display/window_peer_list.cc index 093c623e..d1e6e717 100644 --- a/src/display/window_peer_list.cc +++ b/src/display/window_peer_list.cc @@ -15,6 +15,10 @@ WindowPeerList::WindowPeerList(PList* l) : void WindowPeerList::redraw() { + if (Timer::cache() - m_lastDraw < 1000000) + return; + + m_lastDraw = Timer::cache(); m_canvas->erase(); int x = 2; diff --git a/src/display/window_peer_list.h b/src/display/window_peer_list.h index e33fcfa7..ff3b0c5e 100644 --- a/src/display/window_peer_list.h +++ b/src/display/window_peer_list.h @@ -19,7 +19,7 @@ public: PList& get_list() { return *m_list; } PList::iterator get_focus() { return m_focus; } - void set_focus(PList::iterator itr) { m_focus = itr; } + void set_focus(PList::iterator itr) { m_focus = itr; mark_dirty(); } void slot_chunks_total(SlotChunksTotal s) { m_slotChunksTotal = s; } diff --git a/src/main.cc b/src/main.cc index 30ad366a..b399f7d5 100644 --- a/src/main.cc +++ b/src/main.cc @@ -15,14 +15,32 @@ #include "ui/control.h" #include "ui/download_list.h" +#include "input/bindings.h" + +#include "timer.h" #include "signal_handler.h" +int64_t Timer::m_cache; + bool start_shutdown = false; bool is_shutting_down = false; core::Poll poll; core::DownloadList downloads; +bool +is_resized() { + static int x = 0; + static int y = 0; + + bool r = display::Canvas::get_screen_width() != x || display::Canvas::get_screen_height() != y; + + x = display::Canvas::get_screen_width(); + y = display::Canvas::get_screen_height(); + + return r; +} + void set_shutdown() { start_shutdown = true; @@ -55,6 +73,10 @@ main(int argc, char** argv) { uiDownloadList.activate(); + // Register main key events. + input::Bindings inputMain; + uiControl.get_input().push_back(&inputMain); + poll.slot_read_stdin(sigc::mem_fun(uiControl.get_input(), &input::Manager::pressed)); poll.register_http(); @@ -76,7 +98,11 @@ main(int argc, char** argv) { if (start_shutdown && !is_shutting_down) do_shutdown(); - uiControl.get_display().adjust_layout(); + Timer::update(); + + if (is_resized()) + uiControl.get_display().adjust_layout(); + uiControl.get_display().do_update(); poll.poll(); diff --git a/src/timer.h b/src/timer.h new file mode 100644 index 00000000..85c14adb --- /dev/null +++ b/src/timer.h @@ -0,0 +1,55 @@ +#ifndef LIBTORRENT_TIMER_H +#define LIBTORRENT_TIMER_H + +#include +#include + +// Don't convert negative Timer to timeval. +class Timer { + public: + Timer() : m_time(0) {} + Timer(int64_t usec) : m_time(usec) {} + Timer(timeval tv) : m_time((int64_t)(uint32_t)tv.tv_sec * 1000000 + (int64_t)(uint32_t)tv.tv_usec % 1000000) {} + + int64_t usec() const { return m_time; } + + timeval tval() const { return (timeval) { m_time / 1000000, m_time % 1000000}; } + + Timer operator - (const Timer& t) const { return Timer(m_time - t.m_time); } + Timer operator + (const Timer& t) const { return Timer(m_time + t.m_time); } + + Timer operator -= (int64_t t) { m_time -= t; return *this; } + Timer operator -= (const Timer& t) { m_time -= t.m_time; return *this; } + + Timer operator += (int64_t t) { m_time += t; return *this; } + Timer operator += (const Timer& t) { m_time += t.m_time; return *this; } + + static Timer current() { + timeval t; + gettimeofday(&t, 0); + + return Timer(t); + } + + // Cached time, updated in the beginning of torrent::work call. + // Don't use outside socket_base read/write/except or Service::service. + + // TODO: Find out if it's worth it. The kernel is supposed to cache the + // time. Though system calls would be more expensive than we can afford. + static Timer cache() { return Timer(m_cache); } + + static void update() { m_cache = Timer::current().usec(); } + + bool operator < (const Timer& t) const { return m_time < t.m_time; } + bool operator > (const Timer& t) const { return m_time > t.m_time; } + bool operator <= (const Timer& t) const { return m_time <= t.m_time; } + bool operator >= (const Timer& t) const { return m_time >= t.m_time; } + + private: + int64_t m_time; + + // Instantiated in torrent.cc + static int64_t m_cache; +}; + +#endif // LIBTORRENT_TIMER_H diff --git a/src/ui/Makefile.am b/src/ui/Makefile.am new file mode 100644 index 00000000..21834afd --- /dev/null +++ b/src/ui/Makefile.am @@ -0,0 +1,10 @@ +noinst_LIBRARIES = libsub_ui.a + +libsub_ui_a_SOURCES = \ + control.h \ + download.cc \ + download.h \ + download_list.cc \ + download_list.h + +INCLUDES = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) diff --git a/src/ui/download.cc b/src/ui/download.cc new file mode 100644 index 00000000..1d8be774 --- /dev/null +++ b/src/ui/download.cc @@ -0,0 +1,90 @@ +#include "config.h" + +#include +#include + +#include "input/bindings.h" +#include "display/window_title.h" +#include "display/window_peer_list.h" + +#include "control.h" +#include "download.h" + +namespace ui { + +Download::Download(DPtr d, Control* c) : + m_download(d), + m_title(new WTitle(d->get_download().get_name())), + m_control(c), + m_bindings(new input::Bindings) { + + m_window = new WPeerList(&m_peers); + m_window->slot_chunks_total(sigc::mem_fun(m_download->get_download(), &torrent::Download::get_chunks_total)); + + (*m_bindings)[KEY_UP] = sigc::mem_fun(*this, &Download::receive_prev); + (*m_bindings)[KEY_DOWN] = sigc::mem_fun(*this, &Download::receive_next); + + m_download->get_download().peer_list(m_peers); + + m_connPeerConnected = m_download->get_download().signal_peer_connected(sigc::mem_fun(*this, &Download::receive_peer_connected)); + m_connPeerDisconnected = m_download->get_download().signal_peer_disconnected(sigc::mem_fun(*this, &Download::receive_peer_disconnected)); +} + +Download::~Download() { + m_connPeerConnected.disconnect(); + m_connPeerDisconnected.disconnect(); + + delete m_window; + delete m_title; + delete m_bindings; +} + +void +Download::activate() { + m_control->get_display().push_front(m_window); + m_control->get_display().push_front(m_title); + m_control->get_input().push_front(m_bindings); +} + +void +Download::disable() { + m_control->get_display().erase(m_window); + m_control->get_display().erase(m_title); + m_control->get_input().erase(m_bindings); +} + +void +Download::receive_next() { + if (m_window->get_focus() == m_window->get_list().end()) + m_window->set_focus(m_window->get_list().begin()); + else + m_window->set_focus(++m_window->get_focus()); +} + +void +Download::receive_prev() { + if (m_window->get_focus() == m_window->get_list().begin()) + m_window->set_focus(m_window->get_list().end()); + else + m_window->set_focus(--m_window->get_focus()); +} + +void +Download::receive_peer_connected(torrent::Peer p) { + m_peers.push_back(p); +} + +void +Download::receive_peer_disconnected(torrent::Peer p) { + PList::iterator itr = std::find(m_peers.begin(), m_peers.end(), p); + + if (itr == m_peers.end()) + throw std::logic_error("Download::receive_peer_disconnected(...) received a peer we don't have in our list"); + + if (itr == m_window->get_focus()) + m_window->set_focus(m_peers.erase(itr)); + else + m_peers.erase(itr); +} + +} diff --git a/src/ui/download.h b/src/ui/download.h new file mode 100644 index 00000000..80f8adf1 --- /dev/null +++ b/src/ui/download.h @@ -0,0 +1,58 @@ +#ifndef RTORRENT_UI_DOWNLOAD_H +#define RTORRENT_UI_DOWNLOAD_H + +#include +#include +#include + +#include "core/download_list.h" + +namespace display { + class WindowTitle; + class WindowPeerList; +} + +namespace ui { + +class Control; + +class Download { +public: + typedef display::WindowPeerList WPeerList; + typedef display::WindowTitle WTitle; + typedef core::DownloadList::iterator DPtr; + typedef std::list PList; + + // We own 'window'. + Download(DPtr d, Control* c); + ~Download(); + + WPeerList& get_window() { return *m_window; } + input::Bindings& get_bindings() { return *m_bindings; } + + void activate(); + void disable(); + +private: + void receive_next(); + void receive_prev(); + + void receive_peer_connected(torrent::Peer p); + void receive_peer_disconnected(torrent::Peer p); + + DPtr m_download; + PList m_peers; + + WPeerList* m_window; + WTitle* m_title; + + Control* m_control; + input::Bindings* m_bindings; + + sigc::connection m_connPeerConnected; + sigc::connection m_connPeerDisconnected; +}; + +} + +#endif diff --git a/src/ui/download_list.cc b/src/ui/download_list.cc new file mode 100644 index 00000000..ffe31640 --- /dev/null +++ b/src/ui/download_list.cc @@ -0,0 +1,93 @@ +#include "config.h" + +#include +#include + +#include "input/bindings.h" +#include "display/window_title.h" +#include "display/window_download_list.h" + +#include "control.h" +#include "download.h" +#include "download_list.h" + +namespace ui { + +DownloadList::DownloadList(core::DownloadList* l, Control* c) : + m_window(new WList(l)), + m_title(new WTitle("rtorrent " VERSION " - " + torrent::get(torrent::LIBRARY_NAME))), + m_download(NULL), + m_control(c), + m_bindings(new input::Bindings) { + + (*m_bindings)[KEY_UP] = sigc::mem_fun(*this, &DownloadList::receive_prev); + (*m_bindings)[KEY_DOWN] = sigc::mem_fun(*this, &DownloadList::receive_next); + (*m_bindings)[KEY_RIGHT] = sigc::mem_fun(*this, &DownloadList::receive_view_download); +} + +DownloadList::~DownloadList() { + delete m_window; + delete m_title; + delete m_bindings; +} + +void +DownloadList::activate() { + m_control->get_display().push_front(m_window); + m_control->get_display().push_front(m_title); + m_control->get_input().push_front(m_bindings); +} + +void +DownloadList::disable() { + m_control->get_display().erase(m_title); + m_control->get_display().erase(m_window); + m_control->get_input().erase(m_bindings); +} + +void +DownloadList::receive_next() { + if (m_window->get_focus() == m_window->get_list().end()) + m_window->set_focus(m_window->get_list().begin()); + else + m_window->set_focus(++m_window->get_focus()); +} + +void +DownloadList::receive_prev() { + if (m_window->get_focus() == m_window->get_list().begin()) + m_window->set_focus(m_window->get_list().end()); + else + m_window->set_focus(--m_window->get_focus()); +} + +void +DownloadList::receive_view_download() { + if (m_window->get_focus() == m_window->get_list().end()) + return; + + disable(); + + m_download = new Download(m_window->get_focus(), m_control); + + m_download->get_bindings()[KEY_LEFT] = sigc::mem_fun(*this, &DownloadList::receive_exit_download); + m_download->activate(); + + m_control->get_display().adjust_layout(); +} + +void +DownloadList::receive_exit_download() { + if (m_download == NULL) + throw std::logic_error("DownloadList::receive_exit_download() called but m_download == NULL"); + + m_download->disable(); + delete m_download; + m_download = NULL; + + activate(); + + m_control->get_display().adjust_layout(); +} + +} diff --git a/src/ui/download_list.h b/src/ui/download_list.h new file mode 100644 index 00000000..0adc4c3a --- /dev/null +++ b/src/ui/download_list.h @@ -0,0 +1,47 @@ +#ifndef RTORRENT_UI_DOWNLOAD_LIST_H +#define RTORRENT_UI_DOWNLOAD_LIST_H + +namespace display { + class WindowTitle; + class WindowDownloadList; +} + +namespace ui { + +class Control; +class Download; + +class DownloadList { +public: + typedef display::WindowDownloadList WList; + typedef display::WindowTitle WTitle; + + // We own 'window'. + DownloadList(core::DownloadList* l, Control* c); + ~DownloadList(); + + WList& get_window() { return *m_window; } + input::Bindings& get_bindings() { return *m_bindings; } + + void activate(); + void disable(); + +private: + void receive_next(); + void receive_prev(); + + void receive_view_download(); + void receive_exit_download(); + + WList* m_window; + WTitle* m_title; + + Download* m_download; + + Control* m_control; + input::Bindings* m_bindings; +}; + +} + +#endif