mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-10-04 21:29:21 +00:00
* 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:
@@ -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>
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user