* Enable custom throttles, both per-download or per-IP. See http://libtorrent.rakshasa.no/ticket/20 for info and instructions. Patch by Josef Drexler.

git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@1091 e378c898-3ddf-0310-93e7-cc216c733640
This commit is contained in:
rakshasa
2009-05-14 09:49:18 +00:00
parent 05d7028f61
commit 855bf9a781
23 changed files with 618 additions and 51 deletions
+23
View File
@@ -677,6 +677,29 @@ setting <emphasis>0</emphasis>.
</para></listitem>
</varlistentry>
<varlistentry>
<term>throttle_up = <replaceable>name</replaceable>, <replaceable>upload_rate</replaceable></term>
<term>throttle_down = <replaceable>name</replaceable>, <replaceable>download_rate</replaceable></term>
<listitem><para>
Define secondary throttle and/or set the given upload or download rate. Attach to a download with the d.set_throttle_name=name command
or switch throttles with Ctrl-T. Download must be stopped when changing throttles. Note that secondary throttles only work if the
global upload/download is throttled. Setting a download to use the <emphasis>NULL</emphasis> throttle makes the download unthrottled
even when there is a global throttle. Note that this special case bypasses the global throttle entirely, and as such its rate and
transfer amounts are not included in the global statistics.
</para></listitem>
</varlistentry>
<varlistentry>
<term>throttle_ip = <replaceable>name</replaceable>, <replaceable>host</replaceable></term>
<term>throttle_ip = <replaceable>name</replaceable>, <replaceable>network/prefix</replaceable></term>
<term>throttle_ip = <replaceable>name</replaceable>, <replaceable>start</replaceable>, <replaceable>end</replaceable></term>
<listitem><para>
Use the given secondary throttle for a host, CIDR network or IP range. All peers with a matching IP will use this throttle instead
of the global throttle or a custom download throttle. The name may be <emphasis>NULL</emphasis> to make these peers unthrottled, with
the same caveats as explained above.
</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
+4
View File
@@ -44,6 +44,7 @@
#include <rak/socket_address.h>
#include <rak/string_manip.h>
#include <torrent/rate.h>
#include <torrent/throttle.h>
#include <torrent/tracker.h>
#include <torrent/connection_manager.h>
#include <torrent/data/file.h>
@@ -667,6 +668,9 @@ initialize_command_download() {
ADD_CD_VALUE_MEM_UNI("skip_rate", &torrent::Download::mutable_skip_rate, &torrent::Rate::rate);
ADD_CD_VALUE_MEM_UNI("skip_total", &torrent::Download::mutable_skip_rate, &torrent::Rate::total);
ADD_CD_STRING("set_throttle_name", std::mem_fun(&core::Download::set_throttle_name));
ADD_CD_SLOT_PUBLIC("d.get_throttle_name", call_unknown, rpc::get_variable_d_fn("rtorrent", "throttle_name"), "i:", "");
ADD_CD_VALUE_UNI("creation_date", rak::on(std::mem_fun(&core::Download::download), std::mem_fun(&torrent::Download::creation_date)));
ADD_CD_VALUE_UNI("bytes_done", rak::on(std::mem_fun(&core::Download::download), std::mem_fun(&torrent::Download::bytes_done)));
ADD_CD_VALUE_UNI("ratio", std::ptr_fun(&retrieve_d_ratio));
+124
View File
@@ -61,6 +61,120 @@
#include "control.h"
#include "command_helpers.h"
torrent::Object
apply_throttle(bool up, const torrent::Object& rawArgs) {
const torrent::Object::list_type& args = rawArgs.as_list();
torrent::Object::list_const_iterator argItr = args.begin();
const std::string& name = argItr->as_string();
if (name.empty() || name == "NULL")
throw torrent::input_error("Invalid throttle name.");
if ((++argItr)->as_string().empty())
return torrent::Object();
int64_t rate;
rpc::parse_whole_value_nothrow(argItr->as_string().c_str(), &rate);
if (rate < 0)
throw torrent::input_error("Throttle rate must be non-negative.");
core::ThrottleMap::iterator itr = control->core()->throttles().find(name);
if (itr == control->core()->throttles().end())
itr = control->core()->throttles().insert(std::make_pair(name, torrent::ThrottlePair(NULL, NULL))).first;
torrent::Throttle*& throttle = up ? itr->second.first : itr->second.second;
if (rate != 0 && throttle == NULL)
throttle = (up ? torrent::up_throttle_global() : torrent::down_throttle_global())->create_slave();
if (throttle != NULL)
throttle->set_max_rate(rate * 1024);
return torrent::Object();
}
static const int throttle_info_up = (1 << 0);
static const int throttle_info_down = (1 << 1);
static const int throttle_info_max = (1 << 2);
static const int throttle_info_rate = (1 << 3);
torrent::Object
retrieve_throttle_info(int flags, const torrent::Object& rawArgs) {
const std::string& name = rawArgs.as_string();
core::ThrottleMap::iterator itr = control->core()->throttles().find(name);
torrent::ThrottlePair throttles = itr == control->core()->throttles().end() ? torrent::ThrottlePair(NULL, NULL) : itr->second;
torrent::Throttle* throttle = flags & throttle_info_down ? throttles.second : throttles.first;
torrent::Throttle* global = flags & throttle_info_down ? torrent::down_throttle_global() : torrent::up_throttle_global();
if (throttle == NULL && name.empty())
throttle = global;
if (throttle == NULL)
return flags & throttle_info_rate ? (int64_t)0 : (int64_t)-1;
else if (!throttle->is_throttled() || !global->is_throttled())
return (int64_t)0;
else if (flags & throttle_info_rate)
return (int64_t)throttle->rate()->rate();
else
return (int64_t)throttle->max_rate();
}
std::pair<uint32_t, uint32_t>
parse_address_range(const torrent::Object::list_type& args, torrent::Object::list_type::const_iterator itr) {
unsigned int prefixWidth, ret;
char dummy;
char host[1024];
rak::address_info* ai;
ret = std::sscanf(itr->as_string().c_str(), "%1023[^/]/%d%c", host, &prefixWidth, &dummy);
if (ret < 1 || rak::address_info::get_address_info(host, PF_INET, SOCK_STREAM, &ai) != 0)
throw torrent::input_error("Could not resolve host.");
uint32_t begin, end;
rak::socket_address sa;
sa.copy(*ai->address(), ai->length());
begin = end = sa.sa_inet()->address_h();
rak::address_info::free_address_info(ai);
if (ret == 2) {
if (++itr != args.end())
throw torrent::input_error("Cannot specify both network and range end.");
uint32_t netmask = std::numeric_limits<uint32_t>::max() << (32 - prefixWidth);
if (prefixWidth >= 32 || sa.sa_inet()->address_h() & ~netmask)
throw torrent::input_error("Invalid address/prefix.");
end = sa.sa_inet()->address_h() | ~netmask;
} else if (++itr != args.end()) {
if (rak::address_info::get_address_info(itr->as_string().c_str(), PF_INET, SOCK_STREAM, &ai) != 0)
throw torrent::input_error("Could not resolve host.");
sa.copy(*ai->address(), ai->length());
rak::address_info::free_address_info(ai);
end = sa.sa_inet()->address_h();
}
// convert to [begin, end) making sure the end doesn't overflow
// (this precludes 255.255.255.255 from ever matching, but that's not a real IP anyway)
return std::make_pair<uint32_t, uint32_t>(begin, std::max(end, end + 1));
}
torrent::Object
apply_address_throttle(const torrent::Object& rawArgs) {
const torrent::Object::list_type& args = rawArgs.as_list();
if (args.size() < 2 || args.size() > 3)
throw torrent::input_error("Incorrect number of arguments.");
std::pair<uint32_t, uint32_t> range = parse_address_range(args, ++args.begin());
core::ThrottleMap::iterator throttleItr = control->core()->throttles().find(args.begin()->as_string().c_str());
if (throttleItr == control->core()->throttles().end())
throw torrent::input_error("Throttle not found.");
control->core()->set_address_throttle(range.first, range.second, throttleItr->second);
return torrent::Object();
}
torrent::Object
apply_encryption(const torrent::Object& rawArgs) {
const torrent::Object::list_type& args = rawArgs.as_list();
@@ -328,6 +442,15 @@ initialize_command_network() {
ADD_VARIABLE_VALUE("tracker_numwant", -1);
ADD_COMMAND_LIST("throttle_up", rak::bind_ptr_fn(&apply_throttle, true));
ADD_COMMAND_LIST("throttle_down", rak::bind_ptr_fn(&apply_throttle, false));
ADD_COMMAND_LIST("throttle_ip", rak::ptr_fn(&apply_address_throttle));
ADD_COMMAND_STRING("get_throttle_up_max", rak::bind_ptr_fn(&retrieve_throttle_info, throttle_info_up | throttle_info_max));
ADD_COMMAND_STRING("get_throttle_up_rate", rak::bind_ptr_fn(&retrieve_throttle_info, throttle_info_up | throttle_info_rate));
ADD_COMMAND_STRING("get_throttle_down_max", rak::bind_ptr_fn(&retrieve_throttle_info, throttle_info_down | throttle_info_max));
ADD_COMMAND_STRING("get_throttle_down_rate", rak::bind_ptr_fn(&retrieve_throttle_info, throttle_info_down | throttle_info_rate));
ADD_COMMAND_LIST("encryption", rak::ptr_fn(&apply_encryption));
ADD_COMMAND_STRING("tos", rak::ptr_fn(&apply_tos));
@@ -363,6 +486,7 @@ initialize_command_network() {
ADD_COMMAND_STRING_UN("dht", rak::make_mem_fun(control->dht_manager(), &core::DhtManager::set_start));
ADD_COMMAND_STRING_UN("dht_add_node", std::ptr_fun(&apply_dht_add_node));
ADD_COMMAND_VOID("dht_statistics", rak::make_mem_fun(control->dht_manager(), &core::DhtManager::dht_statistics));
ADD_COMMAND_STRING_TRI("dht_throttle", rak::make_mem_fun(control->dht_manager(), &core::DhtManager::set_throttle_name), rak::make_mem_fun(control->dht_manager(), &core::DhtManager::throttle_name));
ADD_VARIABLE_BOOL("peer_exchange", true);
+14
View File
@@ -315,6 +315,19 @@ apply_to_xb(const torrent::Object& rawArgs) {
return std::string(buffer);
}
torrent::Object
apply_to_throttle(const torrent::Object& rawArgs) {
int64_t arg = rawArgs.as_value();
if (arg < 0)
return "---";
else if (arg == 0)
return "off";
char buffer[32];
snprintf(buffer, 32, "%3d", (int)(arg / (1 << 10)));
return std::string(buffer);
}
// A series of if/else statements. Every even arguments are
// conditionals and odd arguments are branches to be executed, except
// the last one which is always a branch.
@@ -503,4 +516,5 @@ initialize_command_ui() {
ADD_COMMAND_VALUE("to_kb", rak::ptr_fn(&apply_to_kb));
ADD_COMMAND_VALUE("to_mb", rak::ptr_fn(&apply_to_mb));
ADD_COMMAND_VALUE("to_xb", rak::ptr_fn(&apply_to_xb));
ADD_COMMAND_VALUE("to_throttle", rak::ptr_fn(&apply_to_throttle));
}
+1
View File
@@ -32,6 +32,7 @@ libsub_core_a_SOURCES = \
poll_manager_kqueue.h \
poll_manager_select.cc \
poll_manager_select.h \
range_map.h \
view.cc \
view.h \
view_manager.cc \
+13
View File
@@ -95,6 +95,10 @@ DhtManager::start_dht() {
if (!torrent::dht_manager()->is_valid() || torrent::dht_manager()->is_active())
return;
torrent::ThrottlePair throttles = control->core()->get_throttle(m_throttleName);
torrent::dht_manager()->set_upload_throttle(throttles.first);
torrent::dht_manager()->set_download_throttle(throttles.second);
int port = rpc::call_command_value("get_dht_port");
if (port <= 0)
return;
@@ -283,6 +287,7 @@ DhtManager::dht_statistics() {
dhtStats.insert_key("dht", dht_settings[m_start]);
dhtStats.insert_key("active", torrent::dht_manager()->is_active());
dhtStats.insert_key("throttle", m_throttleName);
if (torrent::dht_manager()->is_active()) {
torrent::DhtManager::statistics_type stats = torrent::dht_manager()->get_statistics();
@@ -303,4 +308,12 @@ DhtManager::dht_statistics() {
return dhtStats;
}
void
DhtManager::set_throttle_name(const std::string& throttleName) {
if (torrent::dht_manager()->is_active())
throw torrent::input_error("Cannot set DHT throttle while active.");
m_throttleName = throttleName;
}
}
+4
View File
@@ -58,6 +58,9 @@ public:
void set_start(const std::string& arg);
void set_throttle_name(const std::string& throttleName);
const std::string& throttle_name() const { return m_throttleName; }
private:
static const int dht_disable = 0;
static const int dht_off = 1;
@@ -82,6 +85,7 @@ private:
bool m_warned;
int m_start;
std::string m_throttleName;
};
}
+12
View File
@@ -155,6 +155,18 @@ Download::receive_chunk_failed(__UNUSED uint32_t idx) {
m_chunksFailed++;
}
void
Download::set_throttle_name(const std::string& throttleName) {
if (m_download.is_active())
throw torrent::input_error("Cannot set throttle on active download.");
torrent::ThrottlePair throttles = control->core()->get_throttle(throttleName);
m_download.set_upload_throttle(throttles.first);
m_download.set_download_throttle(throttles.second);
m_download.bencode()->get_key("rtorrent").insert_key("throttle_name", throttleName);
}
void
Download::set_root_directory(const std::string& path) {
torrent::FileList* fileList = m_download.file_list();
+2
View File
@@ -117,6 +117,8 @@ public:
void set_root_directory(const std::string& path);
void set_throttle_name(const std::string& throttleName);
bool operator == (const std::string& str) const;
float distributed_copies() const;
+4
View File
@@ -336,6 +336,10 @@ DownloadFactory::initialize_rtorrent(Download* download, torrent::Object* rtorre
download->download()->set_chunks_done(std::min<uint32_t>(rtorrent->get_key_value("chunks_done"),
download->download()->file_list()->size_chunks()));
download->set_throttle_name(rtorrent->has_key_string("throttle_name")
? rtorrent->get_key_string("throttle_name")
: std::string());
rtorrent->insert_preserve_copy("ignore_commands", (int64_t)0);
rtorrent->insert_preserve_copy("views", torrent::Object::create_list());
+31
View File
@@ -54,6 +54,7 @@
#include <torrent/exceptions.h>
#include <torrent/resume.h>
#include <torrent/tracker_list.h>
#include <torrent/throttle.h>
#include "rpc/parse_commands.h"
#include "utils/directory.h"
@@ -166,9 +167,14 @@ Manager::Manager() :
m_fileStatusCache = new FileStatusCache();
m_httpQueue = new HttpQueue();
m_httpStack = new CurlStack();
torrent::Throttle* unthrottled = torrent::Throttle::create_throttle();
unthrottled->set_max_rate(0);
m_throttles["NULL"] = std::make_pair(unthrottled, unthrottled);
}
Manager::~Manager() {
torrent::Throttle::destroy_throttle(m_throttles["NULL"].first);
delete m_downloadList;
delete m_downloadStore;
@@ -185,6 +191,31 @@ Manager::set_hashing_view(View* v) {
v->signal_changed().connect(sigc::mem_fun(this, &Manager::receive_hashing_changed));
}
torrent::ThrottlePair
Manager::get_throttle(const std::string& name) {
ThrottleMap::const_iterator itr = m_throttles.find(name);
torrent::ThrottlePair throttles = (itr == m_throttles.end() ? torrent::ThrottlePair(NULL, NULL) : itr->second);
if (throttles.first == NULL)
throttles.first = torrent::up_throttle_global();
if (throttles.second == NULL)
throttles.second = torrent::down_throttle_global();
return throttles;
}
void
Manager::set_address_throttle(uint32_t begin, uint32_t end, torrent::ThrottlePair throttles) {
m_addressThrottles.set_merge(begin, end, throttles);
torrent::connection_manager()->set_address_throttle(sigc::mem_fun(control->core(), &core::Manager::get_address_throttle));
}
torrent::ThrottlePair
Manager::get_address_throttle(const sockaddr* addr) {
return m_addressThrottles.get(rak::socket_address::cast_from(addr)->sa_inet()->address_h(), torrent::ThrottlePair(NULL, NULL));
}
void
Manager::initialize_first() {
const char* poll = getenv("RTORRENT_POLL");
+17
View File
@@ -40,8 +40,11 @@
#include <iosfwd>
#include <vector>
#include <torrent/connection_manager.h>
#include "download_list.h"
#include "poll_manager.h"
#include "range_map.h"
#include "log.h"
namespace torrent {
@@ -57,6 +60,8 @@ namespace core {
class DownloadStore;
class HttpQueue;
typedef std::map<std::string, torrent::ThrottlePair> ThrottleMap;
class View;
class Manager {
@@ -84,6 +89,13 @@ public:
Log& get_log_important() { return m_logImportant; }
Log& get_log_complete() { return m_logComplete; }
ThrottleMap& throttles() { return m_throttles; }
torrent::ThrottlePair get_throttle(const std::string& name);
// Use custom throttle for the given range of IP addresses.
void set_address_throttle(uint32_t begin, uint32_t end, torrent::ThrottlePair throttles);
torrent::ThrottlePair get_address_throttle(const sockaddr* addr);
// Really should find a more descriptive name.
void initialize_first();
void initialize_second();
@@ -120,6 +132,8 @@ public:
void try_create_download_expand(const std::string& uri, int flags, command_list_type commands = command_list_type());
private:
typedef RangeMap<uint32_t, torrent::ThrottlePair> AddressThrottleMap;
void create_http(const std::string& uri);
void create_final(std::istream* s);
@@ -136,6 +150,9 @@ private:
View* m_hashingView;
ThrottleMap m_throttles;
AddressThrottleMap m_addressThrottles;
PollManager* m_pollManager;
Log m_logImportant;
Log m_logComplete;
+194
View File
@@ -0,0 +1,194 @@
// rTorrent - BitTorrent client
// Copyright (C) 2005-2008, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <jaris@ifi.uio.no>
//
// Skomakerveien 33
// 3185 Skoppum, NORWAY
#ifndef RTORRENT_CORE_RANGE_MAP_H
#define RTORRENT_CORE_RANGE_MAP_H
#include <map>
#include <stdexcept>
namespace core {
// Associate values with a range of keys, and retrieve for any key in the range.
// The template arguments have the same semantics as std::map.
// Exception: if set_merge is used, the value type must have a defined operator ==.
template<typename Key, typename T, typename Compare = std::less<Key>,
typename Alloc = std::allocator<std::pair<const Key, T> > >
class RangeMap : private std::map<Key, std::pair<Key, T>, Compare,
typename Alloc::template rebind<std::pair<const Key, std::pair<Key, T> > >::other> {
typedef std::map<Key, std::pair<Key, T>, Compare,
typename Alloc::template rebind<std::pair<const Key, std::pair<Key, T> > >::other> base_type;
public:
RangeMap() {}
RangeMap(const Compare& c) : base_type(c) {}
using base_type::const_iterator;
using base_type::const_reverse_iterator;
using base_type::clear;
using base_type::swap;
using base_type::size;
using base_type::empty;
using base_type::begin;
using base_type::end;
using base_type::rbegin;
using base_type::rend;
using base_type::key_comp;
using base_type::value_comp;
// Store a value for the range [begin, end). Returns iterator for the range.
typename RangeMap::const_iterator set_range(const Key& begin, const Key& end, const T& value);
// Same, but merge adjacent ranges having the same value. Returns iterator for the merged range.
typename RangeMap::const_iterator set_merge(Key begin, const Key& end, const T& value);
// Find range containing the given key, or end().
typename RangeMap::const_iterator find(const Key& key) const;
// Retrieve value for key in a range, throw std::out_of_range if range does not exist.
const T& get(const Key& key) const;
// Retrieve value for key in a range, return def if range does not exist.
T get(const Key& key, T def) const;
private:
typename RangeMap::iterator crop_overlap(const Key& begin, const Key& end);
};
// Semantics of an entry:
// .first End of range (exclusive), map key.
// .second.first Beginning of range.
// .second.second Value.
template<typename Key, typename T, typename C, typename A>
inline typename RangeMap<Key,T,C,A>::iterator
RangeMap<Key,T,C,A>::crop_overlap(const Key& _begin, const Key& _end) {
typename RangeMap::iterator itr = upper_bound(_begin);
while (itr != end() && key_comp()(itr->second.first, _end)) {
// There's a subrange before the new begin: need new entry (new range end means new key).
if (key_comp()(itr->second.first, _begin))
insert(itr, typename RangeMap::value_type(_begin, itr->second));
// Old end is within our range: erase entry.
if (!key_comp()(_end, itr->first)) {
erase(itr++);
// Otherwise simply set the new begin of the old range.
} else {
itr->second.first = _end;
++itr;
}
}
return itr;
}
template<typename Key, typename T, typename C, typename A>
inline typename RangeMap<Key,T,C,A>::const_iterator
RangeMap<Key,T,C,A>::set_merge(Key _begin, const Key& _end, const T& value) {
if (!key_comp()(_begin, _end))
return end();
// Crop overlapping ranges and return iterator to first range after the one we're inserting.
typename RangeMap::iterator itr = crop_overlap(_begin, _end);
// Check if range before new one is adjacent and has same value: if so erase it and use its beginning.
if (itr != begin()) {
typename RangeMap::iterator prev = itr;
if (!key_comp()((--prev)->first, _begin) && prev->second.second == value) {
_begin = prev->second.first;
erase(prev);
}
}
// Range after new one is adjacent and has same value: set new beginning.
if (itr != end() && !key_comp()(_end, itr->second.first) && itr->second.second == value) {
itr->second.first = _begin;
return itr;
}
// Otherwise, this range isn't mergeable, make new entry.
return insert(itr, typename RangeMap::value_type(_end, typename RangeMap::mapped_type(_begin, value)));
}
template<typename Key, typename T, typename C, typename A>
inline typename RangeMap<Key,T,C,A>::const_iterator
RangeMap<Key,T,C,A>::set_range(const Key& _begin, const Key& _end, const T& value) {
if (!key_comp()(_begin, _end))
return end();
return insert(crop_overlap(_begin, _end), typename RangeMap::value_type(_end, typename RangeMap::mapped_type(_begin, value)));
}
template<typename Key, typename T, typename C, typename A>
inline typename RangeMap<Key,T,C,A>::const_iterator
RangeMap<Key,T,C,A>::find(const Key& key) const {
typename RangeMap::const_iterator itr = upper_bound(key);
if (itr != end() && key_comp()(key, itr->second.first))
itr = end();
return itr;
}
template<typename Key, typename T, typename C, typename A>
inline const T&
RangeMap<Key,T,C,A>::get(const Key& key) const {
typename RangeMap::const_iterator itr = find(key);
if (itr == end())
throw std::out_of_range("RangeMap::get");
return itr->second.second;
}
template<typename Key, typename T, typename C, typename A>
inline T
RangeMap<Key,T,C,A>::get(const Key& key, T def) const {
typename RangeMap::const_iterator itr = find(key);
return (itr == end() ? def : itr->second.second);
}
}
#endif
+8 -5
View File
@@ -166,9 +166,12 @@ print_download_info(char* first, char* last, core::Download* d) {
(double)rpc::call_command_value("d.get_ratio", rpc::make_target(d)) / 1000.0);
if (d->priority() != 2)
first = print_buffer(first, last, " %s]", rpc::call_command_string("d.get_priority_str", rpc::make_target(d)).c_str());
else
first = print_buffer(first, last, "]");
first = print_buffer(first, last, " %s", rpc::call_command_string("d.get_priority_str", rpc::make_target(d)).c_str());
if (!d->bencode()->get_key("rtorrent").get_key_string("throttle_name").empty())
first = print_buffer(first, last , " %s", rpc::call_command_string("d.get_throttle_name", rpc::make_target(d)).c_str());
first = print_buffer(first, last , "]");
if (first > last)
throw torrent::internal_error("print_download_info(...) wrote past end of the buffer.");
@@ -251,12 +254,12 @@ print_client_version(char* first, char* last, const torrent::ClientInfo& clientI
char*
print_status_info(char* first, char* last) {
if (torrent::up_throttle_global()->max_rate() == 0)
if (!torrent::up_throttle_global()->is_throttled())
first = print_buffer(first, last, "[Throttle off");
else
first = print_buffer(first, last, "[Throttle %3i", torrent::up_throttle_global()->max_rate() / 1024);
if (torrent::down_throttle_global()->max_rate() == 0)
if (!torrent::down_throttle_global()->is_throttled())
first = print_buffer(first, last, "/off KB]");
else
first = print_buffer(first, last, "/%3i KB]", torrent::down_throttle_global()->max_rate() / 1024);
+1
View File
@@ -6,6 +6,7 @@ libsub_ui_a_SOURCES = \
download_list.cc \
download_list.h \
element_base.h \
element_base.cc \
element_chunks_seen.cc \
element_chunks_seen.h \
element_download_list.cc \
+54
View File
@@ -42,12 +42,14 @@
#include <torrent/exceptions.h>
#include <torrent/chunk_manager.h>
#include <torrent/connection_manager.h>
#include <torrent/throttle.h>
#include <torrent/torrent.h>
#include <torrent/tracker_list.h>
#include <torrent/data/file_list.h>
#include <torrent/peer/connection_list.h>
#include "core/download.h"
#include "core/manager.h"
#include "input/manager.h"
#include "display/window_title.h"
#include "display/window_download_statusbar.h"
@@ -179,6 +181,15 @@ Download::create_info() {
element->push_column("Send buffer:", te_command("cat=$to_kb=$get_send_buffer_size=,\" KB\""));
element->push_column("Receive buffer:", te_command("cat=$to_kb=$get_receive_buffer_size=,\" KB\""));
// TODO: Define a custom command for this and use $argument.0 instead of looking up the name multiple times?
element->push_column("Throttle:", te_command("branch=d.get_throttle_name=,\""
"cat=$d.get_throttle_name=,\\\" [Max \\\","
"$to_throttle=$get_throttle_up_max=$d.get_throttle_name=,\\\"/\\\","
"$to_throttle=$get_throttle_down_max=$d.get_throttle_name=,\\\" KB] [Rate \\\","
"$to_kb=$get_throttle_up_rate=$d.get_throttle_name=,\\\"/\\\","
"$to_kb=$get_throttle_down_rate=$d.get_throttle_name=,\\\" KB]\\\"\","
"cat=\"global\""));
element->push_back("");
element->push_column("Upload:", te_command("cat=$to_kb=$d.get_up_rate=,\" KB / \",$to_xb=$d.get_up_total="));
element->push_column("Download:", te_command("cat=$to_kb=$d.get_down_rate=,\" KB / \",$to_xb=$d.get_down_total="));
@@ -315,6 +326,32 @@ Download::receive_prev_priority() {
m_download->set_priority((m_download->priority() - 1) % 4);
}
void
Download::adjust_down_throttle(int throttle) {
core::ThrottleMap::iterator itr = control->core()->throttles().find(m_download->bencode()->get_key("rtorrent").get_key_string("throttle_name"));
if (itr == control->core()->throttles().end() || itr->second.second == NULL || itr->first == "NULL")
control->ui()->adjust_down_throttle(throttle);
else
itr->second.second->set_max_rate(std::max<int>((itr->second.second->is_throttled() ? itr->second.second->max_rate() : 0) + throttle * 1024, 0));
if (m_uiArray[DISPLAY_INFO]->is_active())
m_uiArray[DISPLAY_INFO]->mark_dirty();
}
void
Download::adjust_up_throttle(int throttle) {
core::ThrottleMap::iterator itr = control->core()->throttles().find(m_download->bencode()->get_key("rtorrent").get_key_string("throttle_name"));
if (itr == control->core()->throttles().end() || itr->second.first == NULL || itr->first == "NULL")
control->ui()->adjust_up_throttle(throttle);
else
itr->second.first->set_max_rate(std::max<int>((itr->second.first->is_throttled() ? itr->second.first->max_rate() : 0) + throttle * 1024, 0));
if (m_uiArray[DISPLAY_INFO]->is_active())
m_uiArray[DISPLAY_INFO]->mark_dirty();
}
void
Download::bind_keys() {
m_bindings['1'] = sigc::bind(sigc::mem_fun(this, &Download::receive_max_uploads), -1);
@@ -328,6 +365,23 @@ Download::bind_keys() {
m_bindings['t'] = sigc::bind(sigc::mem_fun(m_download->tracker_list(), &torrent::TrackerList::manual_request), false);
m_bindings['T'] = sigc::bind(sigc::mem_fun(m_download->tracker_list(), &torrent::TrackerList::manual_request), true);
const char* keys = control->ui()->get_throttle_keys();
m_bindings[keys[ 0]] = sigc::bind(sigc::mem_fun(this, &Download::adjust_up_throttle), 1);
m_bindings[keys[ 1]] = sigc::bind(sigc::mem_fun(this, &Download::adjust_up_throttle), -1);
m_bindings[keys[ 2]] = sigc::bind(sigc::mem_fun(this, &Download::adjust_down_throttle), 1);
m_bindings[keys[ 3]] = sigc::bind(sigc::mem_fun(this, &Download::adjust_down_throttle), -1);
m_bindings[keys[ 4]] = sigc::bind(sigc::mem_fun(this, &Download::adjust_up_throttle), 5);
m_bindings[keys[ 5]] = sigc::bind(sigc::mem_fun(this, &Download::adjust_up_throttle), -5);
m_bindings[keys[ 6]] = sigc::bind(sigc::mem_fun(this, &Download::adjust_down_throttle), 5);
m_bindings[keys[ 7]] = sigc::bind(sigc::mem_fun(this, &Download::adjust_down_throttle), -5);
m_bindings[keys[ 8]] = sigc::bind(sigc::mem_fun(this, &Download::adjust_up_throttle), 50);
m_bindings[keys[ 9]] = sigc::bind(sigc::mem_fun(this, &Download::adjust_up_throttle), -50);
m_bindings[keys[10]] = sigc::bind(sigc::mem_fun(this, &Download::adjust_down_throttle), 50);
m_bindings[keys[11]] = sigc::bind(sigc::mem_fun(this, &Download::adjust_down_throttle), -50);
}
}
+3
View File
@@ -87,6 +87,9 @@ public:
void receive_next_priority();
void receive_prev_priority();
void adjust_up_throttle(int throttle);
void adjust_down_throttle(int throttle);
display::Window* window() { return NULL; }
private:
+54
View File
@@ -0,0 +1,54 @@
// rTorrent - BitTorrent client
// Copyright (C) 2005-2007, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <jaris@ifi.uio.no>
//
// Skomakerveien 33
// 3185 Skoppum, NORWAY
#include "config.h"
#include <torrent/exceptions.h>
#include "display/frame.h"
#include "display/window.h"
#include "element_base.h"
namespace ui {
void
ElementBase::mark_dirty() {
if (is_active())
m_frame->window()->mark_dirty();
}
}
+2
View File
@@ -62,6 +62,8 @@ public:
void slot_exit(const slot_type& s) { m_slotExit = s; }
void mark_dirty();
protected:
display::Frame* m_frame;
bool m_focus;
+22
View File
@@ -72,6 +72,7 @@ ElementDownloadList::ElementDownloadList() :
m_bindings['+'] = sigc::mem_fun(*this, &ElementDownloadList::receive_next_priority);
m_bindings['-'] = sigc::mem_fun(*this, &ElementDownloadList::receive_prev_priority);
m_bindings['T'-'@']= sigc::mem_fun(*this, &ElementDownloadList::receive_cycle_throttle);
m_bindings['I'] = sigc::bind(sigc::mem_fun(*this, &ElementDownloadList::receive_command),
"branch=d.get_ignore_commands=,"
"{d.set_ignore_commands=0, print=\"Torrent set to heed commands.\"},"
@@ -185,6 +186,27 @@ ElementDownloadList::receive_prev_priority() {
m_window->mark_dirty();
}
void
ElementDownloadList::receive_cycle_throttle() {
if (m_view->focus() == m_view->end_visible())
return;
core::Download* download = *m_view->focus();
if (download->is_active()) {
control->core()->push_log("Cannot change throttle on active download.");
return;
}
core::ThrottleMap::const_iterator itr = control->core()->throttles().find(download->bencode()->get_key("rtorrent").get_key_string("throttle_name"));
if (itr == control->core()->throttles().end())
itr = control->core()->throttles().begin();
else
++itr;
download->set_throttle_name(itr == control->core()->throttles().end() ? std::string() : itr->first);
m_window->mark_dirty();
}
void
ElementDownloadList::receive_change_view(const std::string& name) {
core::ViewManager::iterator itr = control->view_manager()->find(name);
+2
View File
@@ -73,6 +73,8 @@ public:
void receive_next_priority();
void receive_prev_priority();
void receive_cycle_throttle();
void receive_change_view(const std::string& name);
private:
+27 -46
View File
@@ -120,59 +120,40 @@ Root::cleanup() {
m_control = NULL;
}
const char*
Root::get_throttle_keys() {
const std::string& keyLayout = rpc::call_command_string("get_key_layout");
if (strcasecmp(keyLayout.c_str(), "azerty") == 0)
return "qwQWsxSXdcDC";
else if (strcasecmp(keyLayout.c_str(), "qwertz") == 0)
return "ayAYsxSXdcDC";
else if (strcasecmp(keyLayout.c_str(), "dvorak") == 0)
return "a;A:oqOQejEJ";
else
return "azAZsxSXdcDC";
}
void
Root::setup_keys() {
m_control->input()->push_back(&m_bindings);
const std::string& keyLayout = rpc::call_command_string("get_key_layout");
const char* keys = get_throttle_keys();
if (strcasecmp(keyLayout.c_str(), "azerty") == 0) {
m_bindings['q'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), 1);
m_bindings['w'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), -1);
m_bindings['Q'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), 1);
m_bindings['W'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), -1);
m_bindings[keys[ 0]] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), 1);
m_bindings[keys[ 1]] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), -1);
m_bindings[keys[ 2]] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), 1);
m_bindings[keys[ 3]] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), -1);
} else if (strcasecmp(keyLayout.c_str(), "qwertz") == 0) {
m_bindings['a'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), 1);
m_bindings['y'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), -1);
m_bindings['A'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), 1);
m_bindings['Y'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), -1);
m_bindings[keys[ 4]] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), 5);
m_bindings[keys[ 5]] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), -5);
m_bindings[keys[ 6]] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), 5);
m_bindings[keys[ 7]] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), -5);
} else if (strcasecmp(keyLayout.c_str(), "dvorak") == 0) {
m_bindings['a'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), 1);
m_bindings[';'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), -1);
m_bindings['A'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), 1);
m_bindings[':'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), -1);
} else {
m_bindings['a'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), 1);
m_bindings['z'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), -1);
m_bindings['A'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), 1);
m_bindings['Z'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), -1);
}
if (strcasecmp(keyLayout.c_str(), "dvorak") != 0) {
m_bindings['s'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), 5);
m_bindings['x'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), -5);
m_bindings['S'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), 5);
m_bindings['X'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), -5);
m_bindings['d'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), 50);
m_bindings['c'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), -50);
m_bindings['D'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), 50);
m_bindings['C'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), -50);
} else {
m_bindings['o'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), 5);
m_bindings['q'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), -5);
m_bindings['O'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), 5);
m_bindings['Q'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), -5);
m_bindings['e'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), 50);
m_bindings['j'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), -50);
m_bindings['E'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), 50);
m_bindings['J'] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), -50);
}
m_bindings[keys[ 8]] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), 50);
m_bindings[keys[ 9]] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_up_throttle), -50);
m_bindings[keys[10]] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), 50);
m_bindings[keys[11]] = sigc::bind(sigc::mem_fun(*this, &Root::adjust_down_throttle), -50);
m_bindings['\x0C'] = sigc::mem_fun(m_control->display(), &display::Manager::force_redraw); // ^L
m_bindings['\x11'] = sigc::mem_fun(m_control, &Control::receive_normal_shutdown); // ^Q
+2
View File
@@ -86,6 +86,8 @@ public:
void adjust_down_throttle(int throttle);
void adjust_up_throttle(int throttle);
const char* get_throttle_keys();
void enable_input(const std::string& title, input::TextInput* input);
void disable_input();