Refactor RPC requests code for proper queueing (patch by intelfx @ GH-10)

This refactoring is driven by the need to be able to do true queued RPC calls
(where each successive call uses the result of the previous).

Currently, such queueing of requests is done by assigning them special "magic"
tag numbers, which are then intercepted in one big switch() statement and acted
upon. This (aside from making code greatly unclear) effectively makes each such
queue a singleton, because state passing is restricted to global variables.

We refactor RpcClient to assign an unique tag to each remote call, and then
abstract all the call<->response matching with Qt's future/promise mechanism.

Finally, we introduce a "RPC request queue" class (RpcQueue) which is built on
top of QFutureWatcher and C++11's <functional> library. This class maintains
a queue of functions, where each function receives an RPC response, does
necessary processing, performs another call and finally returns its future.
This commit is contained in:
Mike Gelfand
2016-04-19 20:41:59 +00:00
parent 66285a525b
commit 2b917de65b
14 changed files with 678 additions and 414 deletions
+17 -1
View File
@@ -1,5 +1,5 @@
/*
* This file Copyright (C) 2012-2015 Mnemosyne LLC
* This file Copyright (C) 2012-2016 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
@@ -115,3 +115,19 @@ AddData::readableName () const
return ret;
}
QString
AddData::readableShortName () const
{
switch (type)
{
case FILENAME:
return QFileInfo (filename).fileName ();
case URL:
return url.path ().split (QLatin1Char ('/')).last ();
default:
return readableName ();
}
}
+2 -1
View File
@@ -1,5 +1,5 @@
/*
* This file Copyright (C) 2012-2015 Mnemosyne LLC
* This file Copyright (C) 2012-2016 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
@@ -33,6 +33,7 @@ class AddData
QByteArray toBase64 () const;
QString readableName () const;
QString readableShortName () const;
static bool isSupported (const QString& str) { return AddData (str).type != NONE; }
+2
View File
@@ -54,6 +54,7 @@ set(${PROJECT_NAME}_SOURCES
PrefsDialog.cc
RelocateDialog.cc
RpcClient.cc
RpcQueue.cc
Session.cc
SessionDialog.cc
SqueezeLabel.cc
@@ -112,6 +113,7 @@ set(${PROJECT_NAME}_HEADERS
PrefsDialog.h
RelocateDialog.h
RpcClient.h
RpcQueue.h
Session.h
SessionDialog.h
Speed.h
+33 -38
View File
@@ -1,5 +1,5 @@
/*
* This file Copyright (C) 2013-2015 Mnemosyne LLC
* This file Copyright (C) 2013-2016 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
@@ -14,6 +14,7 @@
#include "Formatter.h"
#include "FreeSpaceLabel.h"
#include "RpcQueue.h"
#include "Session.h"
namespace
@@ -24,7 +25,6 @@ namespace
FreeSpaceLabel::FreeSpaceLabel (QWidget * parent):
QLabel (parent),
mySession (nullptr),
myTag (-1),
myTimer (this)
{
myTimer.setSingleShot (true);
@@ -39,14 +39,7 @@ FreeSpaceLabel::setSession (Session& session)
if (mySession == &session)
return;
if (mySession != nullptr)
disconnect (mySession, nullptr, this, nullptr);
mySession = &session;
connect (mySession, SIGNAL (executed (int64_t, QString, tr_variant *)),
this, SLOT (onSessionExecuted (int64_t, QString, tr_variant *)));
onTimer ();
}
@@ -73,33 +66,35 @@ FreeSpaceLabel::onTimer ()
tr_variantInitDict (&args, 1);
tr_variantDictAddStr (&args, TR_KEY_path, myPath.toUtf8 ().constData());
myTag = mySession->getUniqueTag ();
mySession->exec ("free-space", &args, myTag);
}
void
FreeSpaceLabel::onSessionExecuted (int64_t tag, const QString& result, tr_variant * arguments)
{
Q_UNUSED (result);
if (tag != myTag)
return;
QString str;
// update the label
int64_t bytes = -1;
if (tr_variantDictFindInt (arguments, TR_KEY_size_bytes, &bytes) && bytes >= 0)
setText (tr("%1 free").arg(Formatter::sizeToString (bytes)));
else
setText (QString ());
// update the tooltip
size_t len = 0;
const char * path = 0;
tr_variantDictFindStr (arguments, TR_KEY_path, &path, &len);
str = QString::fromUtf8 (path, len);
setToolTip (QDir::toNativeSeparators (str));
myTimer.start ();
RpcQueue * q = new RpcQueue ();
q->add (
[this, &args] ()
{
return mySession->exec ("free-space", &args);
});
q->add (
[this] (const RpcResponse& r)
{
QString str;
// update the label
int64_t bytes = -1;
if (tr_variantDictFindInt (r.args.get (), TR_KEY_size_bytes, &bytes) && bytes >= 0)
setText (tr ("%1 free").arg (Formatter::sizeToString (bytes)));
else
setText (QString ());
// update the tooltip
size_t len = 0;
const char * path = 0;
tr_variantDictFindStr (r.args.get (), TR_KEY_path, &path, &len);
str = QString::fromUtf8 (path, len);
setToolTip (QDir::toNativeSeparators (str));
myTimer.start ();
});
q->run ();
}
+1 -5
View File
@@ -1,5 +1,5 @@
/*
* This file Copyright (C) 2013-2015 Mnemosyne LLC
* This file Copyright (C) 2013-2016 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
@@ -9,8 +9,6 @@
#pragma once
#include <cstdint>
#include <QLabel>
#include <QString>
#include <QTimer>
@@ -34,12 +32,10 @@ class FreeSpaceLabel: public QLabel
void setPath (const QString& folder);
private slots:
void onSessionExecuted (int64_t tag, const QString& result, tr_variant * arguments);
void onTimer ();
private:
Session * mySession;
int64_t myTag;
QString myPath;
QTimer myTimer;
};
+4 -10
View File
@@ -1,5 +1,5 @@
/*
* This file Copyright (C) 2009-2015 Mnemosyne LLC
* This file Copyright (C) 2009-2016 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
@@ -285,8 +285,7 @@ MainWindow::MainWindow (Session& session, Prefs& prefs, TorrentModel& model, boo
connect (&mySession, SIGNAL (dataReadProgress ()), this, SLOT (dataReadProgress ()));
connect (&mySession, SIGNAL (dataSendProgress ()), this, SLOT (dataSendProgress ()));
connect (&mySession, SIGNAL (httpAuthenticationRequired ()), this, SLOT (wrongAuthentication ()));
connect (&mySession, SIGNAL (error (QNetworkReply::NetworkError)), this, SLOT (onError (QNetworkReply::NetworkError)));
connect (&mySession, SIGNAL (errorMessage (QString)), this, SLOT (errorMessage(QString)));
connect (&mySession, SIGNAL (networkResponse (QNetworkReply::NetworkError, QString)), this, SLOT (onNetworkResponse (QNetworkReply::NetworkError, QString)));
if (mySession.isServer ())
{
@@ -1376,13 +1375,14 @@ MainWindow::dataSendProgress ()
}
void
MainWindow::onError (QNetworkReply::NetworkError code)
MainWindow::onNetworkResponse (QNetworkReply::NetworkError code, const QString& message)
{
const bool hadError = myNetworkError;
const bool haveError = (code != QNetworkReply::NoError)
&& (code != QNetworkReply::UnknownContentError);
myNetworkError = haveError;
myErrorMessage = message;
refreshTrayIconSoon();
updateNetworkIcon();
@@ -1392,12 +1392,6 @@ MainWindow::onError (QNetworkReply::NetworkError code)
myModel.clear();
}
void
MainWindow::errorMessage (const QString& msg)
{
myErrorMessage = msg;
}
void
MainWindow::wrongAuthentication ()
{
+2 -3
View File
@@ -1,5 +1,5 @@
/*
* This file Copyright (C) 2009-2015 Mnemosyne LLC
* This file Copyright (C) 2009-2016 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
@@ -128,8 +128,7 @@ class MainWindow: public QMainWindow
void toggleSpeedMode ();
void dataReadProgress ();
void dataSendProgress ();
void onError (QNetworkReply::NetworkError);
void errorMessage (const QString&);
void onNetworkResponse (QNetworkReply::NetworkError code, const QString& message);
void toggleWindows (bool doShow);
void onSetPrefs ();
void onSetPrefs (bool);
+127 -61
View File
@@ -1,5 +1,5 @@
/*
* This file Copyright (C) 2014-2015 Mnemosyne LLC
* This file Copyright (C) 2014-2016 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
@@ -7,6 +7,7 @@
* $Id$
*/
#include <cstring>
#include <iostream>
#include <QApplication>
@@ -27,6 +28,7 @@
// #define DEBUG_HTTP
#define REQUEST_DATA_PROPERTY_KEY "requestData"
#define REQUEST_FUTUREINTERFACE_PROPERTY_KEY "requestReplyFutureInterface"
namespace
{
@@ -47,12 +49,10 @@ namespace
RpcClient::RpcClient (QObject * parent):
QObject (parent),
mySession (nullptr),
myNAM (nullptr)
myNAM (nullptr),
myNextTag (0)
{
qRegisterMetaType<TrVariantPtr> ("TrVariantPtr");
connect (this, SIGNAL (responseReceived (TrVariantPtr)),
this, SLOT (parseResponse (TrVariantPtr)));
}
void
@@ -105,64 +105,89 @@ RpcClient::url () const
return myUrl;
}
void
RpcClient::exec (tr_quark method, tr_variant * args, int64_t tag)
RpcResponseFuture
RpcClient::exec (tr_quark method, tr_variant * args)
{
exec (tr_quark_get_string (method, nullptr), args, tag);
return exec (tr_quark_get_string (method, nullptr), args);
}
void
RpcClient::exec (const char* method, tr_variant * args, int64_t tag)
RpcResponseFuture
RpcClient::exec (const char * method, tr_variant * args)
{
TrVariantPtr json = createVariant ();
tr_variantInitDict (json.get (), 3);
tr_variantDictAddStr (json.get (), TR_KEY_method, method);
if (tag >= 0)
tr_variantDictAddInt (json.get (), TR_KEY_tag, tag);
if (args != nullptr)
tr_variantDictSteal (json.get (), TR_KEY_arguments, args);
sendRequest (json);
return sendRequest (json);
}
int64_t
RpcClient::getNextTag ()
{
return myNextTag++;
}
void
RpcClient::sendRequest (TrVariantPtr json)
RpcClient::sendNetworkRequest (TrVariantPtr json, const QFutureInterface<RpcResponse> &promise)
{
if (mySession != nullptr)
{
tr_rpc_request_exec_json (mySession, json.get (), localSessionCallback, this);
}
else if (!myUrl.isEmpty ())
{
QNetworkRequest request;
request.setUrl (myUrl);
request.setRawHeader ("User-Agent", (qApp->applicationName () + QLatin1Char ('/') + QString::fromUtf8 (LONG_VERSION_STRING)).toUtf8 ());
request.setRawHeader ("Content-Type", "application/json; charset=UTF-8");
QNetworkRequest request;
request.setUrl (myUrl);
request.setRawHeader ("User-Agent", (qApp->applicationName () + QLatin1Char ('/') + QString::fromUtf8 (LONG_VERSION_STRING)).toUtf8 ());
request.setRawHeader ("Content-Type", "application/json; charset=UTF-8");
if (!mySessionId.isEmpty ())
request.setRawHeader (TR_RPC_SESSION_ID_HEADER, mySessionId.toUtf8 ());
if (!mySessionId.isEmpty ())
request.setRawHeader (TR_RPC_SESSION_ID_HEADER, mySessionId.toUtf8 ());
size_t rawJsonDataLength;
char * rawJsonData = tr_variantToStr (json.get (), TR_VARIANT_FMT_JSON_LEAN, &rawJsonDataLength);
QByteArray jsonData (rawJsonData, rawJsonDataLength);
tr_free (rawJsonData);
size_t rawJsonDataLength;
char * rawJsonData = tr_variantToStr (json.get (), TR_VARIANT_FMT_JSON_LEAN, &rawJsonDataLength);
QByteArray jsonData (rawJsonData, rawJsonDataLength);
tr_free (rawJsonData);
QNetworkReply * reply = networkAccessManager ()->post (request, jsonData);
reply->setProperty (REQUEST_DATA_PROPERTY_KEY, QVariant::fromValue (json));
connect (reply, SIGNAL (downloadProgress (qint64, qint64)), this, SIGNAL (dataReadProgress ()));
connect (reply, SIGNAL (uploadProgress (qint64, qint64)), this, SIGNAL (dataSendProgress ()));
connect (reply, SIGNAL (error (QNetworkReply::NetworkError)), this, SIGNAL (error (QNetworkReply::NetworkError)));
QNetworkReply * reply = networkAccessManager ()->post (request, jsonData);
reply->setProperty (REQUEST_DATA_PROPERTY_KEY, QVariant::fromValue (json));
reply->setProperty (REQUEST_FUTUREINTERFACE_PROPERTY_KEY, QVariant::fromValue (promise));
connect (reply, SIGNAL (downloadProgress (qint64, qint64)), this, SIGNAL (dataReadProgress ()));
connect (reply, SIGNAL (uploadProgress (qint64, qint64)), this, SIGNAL (dataSendProgress ()));
#ifdef DEBUG_HTTP
std::cerr << "sending " << "POST " << qPrintable (myUrl.path ()) << std::endl;
for (const QByteArray& b: request.rawHeaderList ())
std::cerr << b.constData ()
<< ": "
<< request.rawHeader (b).constData ()
<< std::endl;
std::cerr << "Body:\n" << jsonData.constData () << std::endl;
std::cerr << "sending " << "POST " << qPrintable (myUrl.path ()) << std::endl;
for (const QByteArray& b: request.rawHeaderList ())
std::cerr << b.constData ()
<< ": "
<< request.rawHeader (b).constData ()
<< std::endl;
std::cerr << "Body:\n" << jsonData.constData () << std::endl;
#endif
}
}
void
RpcClient::sendLocalRequest (TrVariantPtr json, const QFutureInterface<RpcResponse> &promise, int64_t tag)
{
myLocalRequests.insert (tag, promise);
tr_rpc_request_exec_json (mySession, json.get (), localSessionCallback, this);
}
RpcResponseFuture
RpcClient::sendRequest (TrVariantPtr json)
{
int64_t tag = getNextTag ();
tr_variantDictAddInt (json.get (), TR_KEY_tag, tag);
QFutureInterface<RpcResponse> promise;
promise.setExpectedResultCount (1);
promise.setProgressRange (0, 1);
promise.setProgressValue (0);
promise.reportStarted ();
if (mySession != nullptr)
sendLocalRequest (json, promise, tag);
else if (!myUrl.isEmpty ())
sendNetworkRequest (json, promise);
return promise.future ();
}
QNetworkAccessManager *
@@ -173,7 +198,7 @@ RpcClient::networkAccessManager ()
myNAM = new QNetworkAccessManager ();
connect (myNAM, SIGNAL (finished (QNetworkReply *)),
this, SLOT (onFinished (QNetworkReply *)));
this, SLOT (networkRequestFinished (QNetworkReply * )));
connect (myNAM, SIGNAL (authenticationRequired (QNetworkReply *,QAuthenticator *)),
this, SIGNAL (httpAuthenticationRequired ()));
@@ -195,12 +220,16 @@ RpcClient::localSessionCallback (tr_session * s, tr_variant * response, void * v
// this callback is invoked in the libtransmission thread, so we don't want
// to process the response here... let's push it over to the Qt thread.
self->responseReceived (json);
QMetaObject::invokeMethod (self, "localRequestFinished", Qt::QueuedConnection, Q_ARG (TrVariantPtr, json));
}
void
RpcClient::onFinished (QNetworkReply * reply)
RpcClient::networkRequestFinished (QNetworkReply *reply)
{
reply->deleteLater ();
QFutureInterface<RpcResponse> promise = reply->property (REQUEST_FUTUREINTERFACE_PROPERTY_KEY).value<QFutureInterface<RpcResponse>> ();
#ifdef DEBUG_HTTP
std::cerr << "http response header: " << std::endl;
for (const QByteArray& b: reply->rawHeaderList ())
@@ -217,40 +246,77 @@ RpcClient::onFinished (QNetworkReply * reply)
// we got a 409 telling us our session id has expired.
// update it and resubmit the request.
mySessionId = QString::fromUtf8 (reply->rawHeader (TR_RPC_SESSION_ID_HEADER));
sendRequest (reply->property (REQUEST_DATA_PROPERTY_KEY).value<TrVariantPtr> ());
sendNetworkRequest (reply->property (REQUEST_DATA_PROPERTY_KEY).value<TrVariantPtr> (), promise);
return;
}
else if (reply->error () != QNetworkReply::NoError)
emit networkResponse (reply->error(), reply->errorString());
if (reply->error () != QNetworkReply::NoError)
{
emit errorMessage (reply->errorString ());
RpcResponse result;
result.networkError = reply->error ();
promise.setProgressValueAndText (1, reply->errorString ());
promise.reportFinished (&result);
}
else
{
const QByteArray jsonData = reply->readAll ().trimmed ();
RpcResponse result;
const QByteArray jsonData = reply->readAll ().trimmed ();
TrVariantPtr json = createVariant ();
if (tr_variantFromJson (json.get (), jsonData.constData (), jsonData.size ()) == 0)
parseResponse (json);
result = parseResponseData (*json);
emit error (QNetworkReply::NoError);
promise.setProgressValue (1);
promise.reportFinished (&result);
}
reply->deleteLater ();
}
void
RpcClient::parseResponse (TrVariantPtr json)
RpcClient::localRequestFinished (TrVariantPtr response)
{
int64_t tag = parseResponseTag (*response);
RpcResponse result = parseResponseData (*response);
QFutureInterface<RpcResponse> promise = myLocalRequests.take (tag);
promise.setProgressRange (0, 1);
promise.setProgressValue (1);
promise.reportFinished (&result);
}
int64_t
RpcClient::parseResponseTag (tr_variant& json)
{
int64_t tag;
if (!tr_variantDictFindInt (json.get (), TR_KEY_tag, &tag))
if (!tr_variantDictFindInt (&json, TR_KEY_tag, &tag))
tag = -1;
return tag;
}
RpcResponse
RpcClient::parseResponseData (tr_variant& json)
{
RpcResponse ret;
const char * result;
if (!tr_variantDictFindStr (json.get (), TR_KEY_result, &result, nullptr))
result = nullptr;
if (tr_variantDictFindStr (&json, TR_KEY_result, &result, nullptr))
{
ret.result = QString::fromUtf8 (result);
ret.success = std::strcmp (result, "success") == 0;
}
tr_variant * args;
if (!tr_variantDictFindDict (json.get (), TR_KEY_arguments, &args))
args = nullptr;
if (tr_variantDictFindDict (&json, TR_KEY_arguments, &args))
{
ret.args = createVariant ();
*ret.args = *args;
tr_variantInitBool (args, false);
}
emit executed (tag, result == nullptr ? QString () : QString::fromUtf8 (result), args);
return ret;
}
+31 -12
View File
@@ -1,5 +1,5 @@
/*
* This file Copyright (C) 2014-2015 Mnemosyne LLC
* This file Copyright (C) 2014-2016 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
@@ -11,6 +11,9 @@
#include <memory>
#include <QFuture>
#include <QFutureInterface>
#include <QHash>
#include <QNetworkReply>
#include <QObject>
#include <QString>
@@ -32,6 +35,19 @@ extern "C"
struct tr_session;
}
struct RpcResponse
{
QString result;
TrVariantPtr args;
bool success = false;
QNetworkReply::NetworkError networkError = QNetworkReply::NoError;
};
Q_DECLARE_METATYPE (QFutureInterface<RpcResponse>);
// The response future -- the RPC engine returns one for each request made.
typedef QFuture<RpcResponse> RpcResponseFuture;
class RpcClient: public QObject
{
Q_OBJECT
@@ -47,34 +63,37 @@ class RpcClient: public QObject
bool isLocal () const;
const QUrl& url () const;
void exec (tr_quark method, tr_variant * args, int64_t tag = -1);
void exec (const char* method, tr_variant * args, int64_t tag = -1);
RpcResponseFuture exec (tr_quark method, tr_variant * args);
RpcResponseFuture exec (const char * method, tr_variant * args);
signals:
void httpAuthenticationRequired ();
void dataReadProgress ();
void dataSendProgress ();
void error (QNetworkReply::NetworkError code);
void errorMessage (const QString& message);
void executed (int64_t tag, const QString& result, tr_variant * args);
// private
void responseReceived (TrVariantPtr json);
void networkResponse (QNetworkReply::NetworkError code, const QString& message);
private:
void sendRequest (TrVariantPtr json);
RpcResponseFuture sendRequest (TrVariantPtr json);
QNetworkAccessManager * networkAccessManager ();
int64_t getNextTag ();
void sendNetworkRequest (TrVariantPtr json, const QFutureInterface<RpcResponse> &promise);
void sendLocalRequest (TrVariantPtr json, const QFutureInterface<RpcResponse> &promise, int64_t tag);
int64_t parseResponseTag (tr_variant& response);
RpcResponse parseResponseData (tr_variant& response);
static void localSessionCallback (tr_session * s, tr_variant * response, void * vself);
private slots:
void onFinished (QNetworkReply * reply);
void parseResponse (TrVariantPtr json);
void networkRequestFinished (QNetworkReply *reply);
void localRequestFinished (TrVariantPtr response);
private:
tr_session * mySession;
QString mySessionId;
QUrl myUrl;
QNetworkAccessManager * myNAM;
QHash<int64_t, QFutureInterface<RpcResponse>> myLocalRequests;
int64_t myNextTag;
};
+88
View File
@@ -0,0 +1,88 @@
/*
* This file Copyright (C) 2016 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
*
* $Id$
*/
#include <cassert>
#include "RpcQueue.h"
RpcQueue::RpcQueue (QObject * parent):
QObject (parent),
myTolerateErrors (false)
{
connect (&myFutureWatcher, SIGNAL (finished ()), SLOT (stepFinished ()));
}
RpcResponseFuture
RpcQueue::future ()
{
return myPromise.future ();
}
void
RpcQueue::stepFinished ()
{
RpcResponse result;
if (myFutureWatcher.future ().isResultReadyAt (0))
{
result = myFutureWatcher.result ();
RpcResponseFuture future = myFutureWatcher.future ();
// we can't handle network errors, abort queue and pass the error upwards
if (result.networkError != QNetworkReply::NoError)
{
assert (!result.success);
myPromise.reportFinished (&result);
deleteLater ();
return;
}
// call user-handler for ordinary errors
if (!result.success && myNextErrorHandler)
{
myNextErrorHandler (future);
}
// run next request, if we have one to run and there was no error (or if we tolerate errors)
if ((result.success || myTolerateErrors) && !myQueue.isEmpty ())
{
runNext (future);
return;
}
}
else
{
assert (!myNextErrorHandler);
assert (myQueue.isEmpty ());
// one way or another, the last step returned nothing.
// assume it is OK and ensure that we're not going to give an empty response object to any of the next steps.
result.success = true;
}
myPromise.reportFinished (&result);
deleteLater ();
}
void
RpcQueue::runNext (const RpcResponseFuture& response)
{
assert (!myQueue.isEmpty ());
auto next = myQueue.dequeue ();
myNextErrorHandler = next.second;
myFutureWatcher.setFuture ((next.first) (response));
}
void
RpcQueue::run ()
{
runNext (RpcResponseFuture ());
}
+122
View File
@@ -0,0 +1,122 @@
/*
* This file Copyright (C) 2016 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
*
* $Id$
*/
#pragma once
#include <functional>
#include <type_traits>
#include <QFutureInterface>
#include <QFutureWatcher>
#include <QObject>
#include <QPair>
#include <QQueue>
#include "RpcClient.h"
class RpcQueue: public QObject
{
Q_OBJECT
public:
explicit RpcQueue (QObject * parent = nullptr);
void setTolerateErrors (bool tolerateErrors = true) { myTolerateErrors = tolerateErrors; }
template <typename Func>
void add (Func func)
{
myQueue.enqueue (qMakePair (normalizeFunc (func),
ErrorHandlerFunction ()));
}
template <typename Func, typename ErrorHandler>
void add (Func func, ErrorHandler errorHandler)
{
myQueue.enqueue (qMakePair (normalizeFunc (func),
normalizeErrorHandler (errorHandler)));
}
RpcResponseFuture future ();
// The first function in queue is ran synchronously
// (hence it may be e. g. a lambda capturing local variables by reference).
void run ();
private:
// Internally queued function. Takes the last response future, makes a
// request and returns a new response future.
typedef std::function<RpcResponseFuture (const RpcResponseFuture&)> QueuedFunction;
// Internally stored error handler function. Takes the last response future and returns nothing.
typedef std::function<void (const RpcResponseFuture&)> ErrorHandlerFunction;
private slots:
void stepFinished ();
private:
void runNext (const RpcResponseFuture& response);
// These overloads convert various forms of input closures to what we store internally.
// normal closure, takes response and returns new future
template <typename Func,
typename std::enable_if<std::is_same<typename std::result_of<Func (const RpcResponse&)>::type, RpcResponseFuture>::value>::type * = nullptr>
QueuedFunction normalizeFunc (const Func& func)
{
return [func] (const RpcResponseFuture& r) { return func (r.result ()); };
}
// closure without argument (first step), takes nothing and returns new future
template <typename Func,
typename std::enable_if<std::is_same<typename std::result_of<Func ()>::type, RpcResponseFuture>::value>::type * = nullptr>
QueuedFunction normalizeFunc (const Func& func)
{
return [func] (const RpcResponseFuture&) { return func (); };
}
// closure without return value ("auxiliary"), takes response and returns nothing -- internally we reuse the last future
template <typename Func,
typename std::enable_if<std::is_same<typename std::result_of<Func (const RpcResponse&)>::type, void>::value>::type * = nullptr>
QueuedFunction normalizeFunc (const Func& func)
{
return [func] (const RpcResponseFuture& r) { func (r.result ()); return r; };
}
// closure without argument and return value, takes nothing and returns nothing -- next function will also get nothing
template <typename Func,
typename std::enable_if<std::is_same<typename std::result_of<Func ()>::type, void>::value>::type * = nullptr>
QueuedFunction normalizeFunc (const Func& func)
{
return [func] (const RpcResponseFuture& r) { func (); return r; };
}
// normal error handler, takes last response
template <typename Func,
typename std::enable_if<std::is_same<typename std::result_of<Func (const RpcResponse&)>::type, void>::value>::type * = nullptr>
ErrorHandlerFunction normalizeErrorHandler (const Func& func)
{
return [func] (const RpcResponseFuture& r) { func (r.result ()); };
}
// error handler without an argument, takes nothing
template <typename Func,
typename std::enable_if<std::is_same<typename std::result_of<Func ()>::type, void>::value>::type * = nullptr>
ErrorHandlerFunction normalizeErrorHandler (const Func& func)
{
return [func] (const RpcResponseFuture& r) { func (); };
}
private:
bool myTolerateErrors;
QFutureInterface<RpcResponse> myPromise;
QQueue<QPair<QueuedFunction, ErrorHandlerFunction>> myQueue;
ErrorHandlerFunction myNextErrorHandler;
QFutureWatcher<RpcResponse> myFutureWatcher;
};
+242 -250
View File
@@ -1,5 +1,5 @@
/*
* This file Copyright (C) 2009-2015 Mnemosyne LLC
* This file Copyright (C) 2009-2016 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
@@ -28,29 +28,12 @@
#include "AddData.h"
#include "Prefs.h"
#include "RpcQueue.h"
#include "Session.h"
#include "SessionDialog.h"
#include "Torrent.h"
#include "Utils.h"
namespace
{
enum
{
TAG_SOME_TORRENTS,
TAG_ALL_TORRENTS,
TAG_SESSION_STATS,
TAG_SESSION_INFO,
TAG_BLOCKLIST_UPDATE,
TAG_ADD_TORRENT,
TAG_PORT_TEST,
TAG_MAGNET_LINK,
TAG_RENAME_PATH,
FIRST_UNIQUE_TAG
};
}
/***
****
***/
@@ -69,59 +52,14 @@ namespace
for (const tr_quark key: keys)
tr_variantListAddQuark (list, key);
}
// If this object is passed as "ids" (compared by address), then recently active torrents are queried.
const QSet<int> recentlyActiveIds = QSet<int>() << -1;
// If this object is passed as "ids" (compared by being empty), then all torrents are queried.
const QSet<int> allIds;
}
/***
****
***/
void
FileAdded::executed (int64_t tag, const QString& result, tr_variant * arguments)
{
if (tag != myTag)
return;
if (result == QLatin1String ("success"))
{
tr_variant * dup;
const char * str;
if (tr_variantDictFindDict (arguments, TR_KEY_torrent_duplicate, &dup) &&
tr_variantDictFindStr (dup, TR_KEY_name, &str, NULL))
{
const QString myFilename = QFileInfo (myName).fileName ();
const QString name = QString::fromUtf8 (str);
QMessageBox::warning (qApp->activeWindow (),
tr ("Add Torrent"),
tr ("<p><b>Unable to add \"%1\".</b></p><p>It is a duplicate of \"%2\" which is already added.</p>").arg (myFilename).arg (name));
}
if (!myDelFile.isEmpty ())
{
QFile file (myDelFile);
file.setPermissions (QFile::ReadOwner | QFile::WriteOwner);
file.remove ();
}
}
else
{
QString text = result;
for (int i=0, n=text.size (); i<n; ++i)
if (!i || text[i-1].isSpace ())
text[i] = text[i].toUpper ();
QMessageBox::warning (qApp->activeWindow (),
tr ("Error Adding Torrent"),
QString::fromLatin1 ("<p><b>%1</b></p><p>%2</p>").arg (text).arg (myName));
}
deleteLater ();
}
/***
****
***/
void
Session::sessionSet (const tr_quark key, const QVariant& value)
{
@@ -133,7 +71,7 @@ Session::sessionSet (const tr_quark key, const QVariant& value)
case QVariant::Int: tr_variantDictAddInt (&args, key, value.toInt ()); break;
case QVariant::Double: tr_variantDictAddReal (&args, key, value.toDouble ()); break;
case QVariant::String: tr_variantDictAddStr (&args, key, value.toString ().toUtf8 ().constData ()); break;
default: assert ("unknown type");
default: assert (false);
}
exec ("session-set", &args);
@@ -142,7 +80,25 @@ Session::sessionSet (const tr_quark key, const QVariant& value)
void
Session::portTest ()
{
exec ("port-test", nullptr, TAG_PORT_TEST);
RpcQueue * q = new RpcQueue ();
q->add (
[this] ()
{
return exec ("port-test", nullptr);
});
q->add (
[this] (const RpcResponse& r)
{
bool isOpen = false;
if (r.success)
tr_variantDictFindBool (r.args.get (), TR_KEY_port_is_open, &isOpen);
emit portTested (isOpen);
});
q->run ();
}
void
@@ -153,7 +109,28 @@ Session::copyMagnetLinkToClipboard (int torrentId)
tr_variantListAddInt (tr_variantDictAddList (&args, TR_KEY_ids, 1), torrentId);
tr_variantListAddStr (tr_variantDictAddList (&args, TR_KEY_fields, 1), "magnetLink");
exec (TR_KEY_torrent_get, &args, TAG_MAGNET_LINK);
RpcQueue * q = new RpcQueue ();
q->add (
[this, &args] ()
{
return exec (TR_KEY_torrent_get, &args);
});
q->add (
[this] (const RpcResponse& r)
{
tr_variant * torrents;
tr_variant * child;
const char * str;
if (tr_variantDictFindList (r.args.get (), TR_KEY_torrents, &torrents)
&& (child = tr_variantListChild (torrents, 0))
&& tr_variantDictFindStr (child, TR_KEY_magnetLink, &str, NULL))
qApp->clipboard ()->setText (QString::fromUtf8 (str));
});
q->run ();
}
void
@@ -277,7 +254,6 @@ Session::updatePref (int key)
Session::Session (const QString& configDir, Prefs& prefs):
myConfigDir (configDir),
myPrefs (prefs),
nextUniqueTag (FIRST_UNIQUE_TAG),
myBlocklistSize (-1),
mySession (0)
{
@@ -290,14 +266,10 @@ Session::Session (const QString& configDir, Prefs& prefs):
myCumulativeStats = myStats;
connect (&myPrefs, SIGNAL (changed (int)), this, SLOT (updatePref (int)));
connect (&myRpc, SIGNAL (executed (int64_t, QString, tr_variant *)), this, SLOT (responseReceived (int64_t, QString, tr_variant *)));
connect (&myRpc, SIGNAL (httpAuthenticationRequired ()), this, SIGNAL (httpAuthenticationRequired ()));
connect (&myRpc, SIGNAL (dataReadProgress ()), this, SIGNAL (dataReadProgress ()));
connect (&myRpc, SIGNAL (dataSendProgress ()), this, SIGNAL (dataSendProgress ()));
connect (&myRpc, SIGNAL (error (QNetworkReply::NetworkError)), this, SIGNAL (error (QNetworkReply::NetworkError)));
connect (&myRpc, SIGNAL (errorMessage (QString)), this, SIGNAL (errorMessage (QString)));
connect (&myRpc, SIGNAL (networkResponse (QNetworkReply::NetworkError, QString)), this, SIGNAL (networkResponse (QNetworkReply::NetworkError, QString)));
}
Session::~Session ()
@@ -387,7 +359,11 @@ namespace
void
addOptionalIds (tr_variant * args, const QSet<int>& ids)
{
if (!ids.isEmpty ())
if (&ids == &recentlyActiveIds)
{
tr_variantDictAddStr (args, TR_KEY_ids, "recently-active");
}
else if (!ids.isEmpty ())
{
tr_variant * idList (tr_variantDictAddList (args, TR_KEY_ids, ids.size ()));
for (const int i: ids)
@@ -439,7 +415,7 @@ Session::torrentSet (const QSet<int>& ids, const tr_quark key, const QStringList
for (const QString& str: value)
tr_variantListAddStr (list, str.toUtf8 ().constData ());
exec(TR_KEY_torrent_set, &args);
exec (TR_KEY_torrent_set, &args);
}
void
@@ -489,36 +465,81 @@ Session::torrentRenamePath (const QSet<int>& ids, const QString& oldpath, const
tr_variantDictAddStr (&args, TR_KEY_path, oldpath.toUtf8 ().constData ());
tr_variantDictAddStr (&args, TR_KEY_name, newname.toUtf8 ().constData ());
exec ("torrent-rename-path", &args, TAG_RENAME_PATH);
RpcQueue * q = new RpcQueue ();
q->add (
[this, &args] ()
{
return exec ("torrent-rename-path", &args);
},
[this] (const RpcResponse& r)
{
const char * path = "(unknown)";
const char * name = "(unknown)";
tr_variantDictFindStr (r.args.get (), TR_KEY_path, &path, nullptr);
tr_variantDictFindStr (r.args.get (), TR_KEY_name, &name, nullptr);
QMessageBox * d = new QMessageBox (QMessageBox::Information,
tr ("Error Renaming Path"),
tr ("<p><b>Unable to rename \"%1\" as \"%2\": %3.</b></p> "
"<p>Please correct the errors and try again.</p>")
.arg (QString::fromUtf8 (path))
.arg (QString::fromUtf8 (name))
.arg (r.result),
QMessageBox::Close,
qApp->activeWindow ());
connect (d, SIGNAL (rejected ()), d, SLOT (deleteLater ()));
d->show ();
});
q->add (
[this] (const RpcResponse& r)
{
int64_t id = 0;
if (tr_variantDictFindInt (r.args.get (), TR_KEY_id, &id)
&& id != 0)
refreshTorrents (QSet<int> () << id,
KeyList () << TR_KEY_fileStats << TR_KEY_files << TR_KEY_id << TR_KEY_name);
});
q->run ();
}
void
Session::refreshTorrents (const QSet<int>& ids)
Session::refreshTorrents (const QSet<int>& ids, const KeyList& keys)
{
if (ids.empty ())
{
refreshAllTorrents ();
}
else
{
tr_variant args;
tr_variantInitDict (&args, 2);
addList (tr_variantDictAddList (&args, TR_KEY_fields, 0), getStatKeys ());
addOptionalIds (&args, ids);
tr_variant args;
tr_variantInitDict (&args, 2);
addList (tr_variantDictAddList (&args, TR_KEY_fields, 0), keys);
addOptionalIds (&args, ids);
exec (TR_KEY_torrent_get, &args, TAG_SOME_TORRENTS);
}
RpcQueue * q = new RpcQueue ();
q->add (
[this, &args] ()
{
return exec (TR_KEY_torrent_get, &args);
});
const bool allTorrents = ids.empty ();
q->add (
[this, allTorrents] (const RpcResponse& r)
{
tr_variant * torrents;
if (tr_variantDictFindList (r.args.get (), TR_KEY_torrents, &torrents))
emit torrentsUpdated (torrents, allTorrents);
if (tr_variantDictFindList (r.args.get (), TR_KEY_removed, &torrents))
emit torrentsRemoved (torrents);
});
q->run ();
}
void
Session::refreshExtraStats (const QSet<int>& ids)
{
tr_variant args;
tr_variantInitDict (&args, 3);
addOptionalIds (&args, ids);
addList (tr_variantDictAddList (&args, TR_KEY_fields, 0), getStatKeys () + getExtraStatKeys ());
exec (TR_KEY_torrent_get, &args, TAG_SOME_TORRENTS);
refreshTorrents (ids, getStatKeys () + getExtraStatKeys ());
}
void
@@ -528,9 +549,21 @@ Session::sendTorrentRequest (const char * request, const QSet<int>& ids)
tr_variantInitDict (&args, 1);
addOptionalIds (&args, ids);
exec (request, &args);
RpcQueue * q = new RpcQueue ();
refreshTorrents (ids);
q->add (
[this, request, &args] ()
{
return exec (request, &args);
});
q->add (
[this, ids] ()
{
refreshTorrents (ids, getStatKeys ());
});
q->run ();
}
void Session::pauseTorrents (const QSet<int>& ids) { sendTorrentRequest ("torrent-stop", ids); }
@@ -544,181 +577,97 @@ void Session::queueMoveBottom (const QSet<int>& ids) { sendTorrentRequest ("que
void
Session::refreshActiveTorrents ()
{
tr_variant args;
tr_variantInitDict (&args, 2);
tr_variantDictAddStr (&args, TR_KEY_ids, "recently-active");
addList (tr_variantDictAddList (&args, TR_KEY_fields, 0), getStatKeys ());
exec (TR_KEY_torrent_get, &args, TAG_SOME_TORRENTS);
refreshTorrents (recentlyActiveIds, getStatKeys ());
}
void
Session::refreshAllTorrents ()
{
tr_variant args;
tr_variantInitDict (&args, 1);
addList (tr_variantDictAddList (&args, TR_KEY_fields, 0), getStatKeys ());
exec (TR_KEY_torrent_get, &args, TAG_ALL_TORRENTS);
refreshTorrents (allIds, getStatKeys ());
}
void
Session::initTorrents (const QSet<int>& ids)
{
tr_variant args;
tr_variantInitDict (&args, 2);
addOptionalIds (&args, ids);
addList (tr_variantDictAddList (&args, TR_KEY_fields, 0), getStatKeys ()+getInfoKeys ());
exec ("torrent-get", &args, ids.isEmpty () ? TAG_ALL_TORRENTS : TAG_SOME_TORRENTS);
refreshTorrents (ids, getStatKeys () + getInfoKeys ());
}
void
Session::refreshSessionStats ()
{
exec ("session-stats", nullptr, TAG_SESSION_STATS);
RpcQueue * q = new RpcQueue ();
q->add (
[this] ()
{
return exec ("session-stats", nullptr);
});
q->add (
[this] (const RpcResponse& r)
{
updateStats (r.args.get ());
});
q->run ();
}
void
Session::refreshSessionInfo ()
{
exec ("session-get", nullptr, TAG_SESSION_INFO);
RpcQueue * q = new RpcQueue ();
q->add (
[this] ()
{
return exec ("session-get", nullptr);
});
q->add (
[this] (const RpcResponse& r)
{
updateInfo (r.args.get ());
});
q->run ();
}
void
Session::updateBlocklist ()
{
exec ("blocklist-update", nullptr, TAG_BLOCKLIST_UPDATE);
RpcQueue * q = new RpcQueue ();
q->add (
[this] ()
{
return exec ("blocklist-update", nullptr);
});
q->add (
[this] (const RpcResponse& r)
{
int64_t blocklistSize;
if (tr_variantDictFindInt (r.args.get (), TR_KEY_blocklist_size, &blocklistSize))
setBlocklistSize (blocklistSize);
});
q->run ();
}
/***
****
***/
void
Session::exec (tr_quark method, tr_variant * args, int64_t tag)
RpcResponseFuture
Session::exec (tr_quark method, tr_variant * args)
{
myRpc.exec (method, args, tag);
return myRpc.exec (method, args);
}
void
Session::exec (const char* method, tr_variant * args, int64_t tag)
RpcResponseFuture
Session::exec (const char * method, tr_variant * args)
{
myRpc.exec (method, args, tag);
}
void
Session::responseReceived (int64_t tag, const QString& result, tr_variant * args)
{
emit executed (tag, result, args);
if (tag < 0)
return;
switch (tag)
{
case TAG_SOME_TORRENTS:
case TAG_ALL_TORRENTS:
if (args != nullptr)
{
tr_variant * torrents;
if (tr_variantDictFindList (args, TR_KEY_torrents, &torrents))
emit torrentsUpdated (torrents, tag==TAG_ALL_TORRENTS);
if (tr_variantDictFindList (args, TR_KEY_removed, &torrents))
emit torrentsRemoved (torrents);
}
break;
case TAG_SESSION_STATS:
if (args != nullptr)
updateStats (args);
break;
case TAG_SESSION_INFO:
if (args != nullptr)
updateInfo (args);
break;
case TAG_BLOCKLIST_UPDATE:
{
int64_t intVal = 0;
if (args != nullptr)
{
if (tr_variantDictFindInt (args, TR_KEY_blocklist_size, &intVal))
setBlocklistSize (intVal);
}
break;
}
case TAG_RENAME_PATH:
{
int64_t id = 0;
if (result != QLatin1String ("success"))
{
const char * path = "";
const char * name = "";
tr_variantDictFindStr (args, TR_KEY_path, &path, 0);
tr_variantDictFindStr (args, TR_KEY_name, &name, 0);
const QString title = tr ("Error Renaming Path");
const QString text = tr ("<p><b>Unable to rename \"%1\" as \"%2\": %3.</b></p> <p>Please correct the errors and try again.</p>").arg (QString::fromUtf8 (path)).arg (QString::fromUtf8 (name)).arg (result);
QMessageBox * d = new QMessageBox (QMessageBox::Information, title, text,
QMessageBox::Close,
qApp->activeWindow ());
connect (d, SIGNAL (rejected ()), d, SLOT (deleteLater ()));
d->show ();
}
else if (tr_variantDictFindInt (args, TR_KEY_id, &id) && id)
{
tr_variant args;
tr_variantInitDict (&args, 2);
tr_variantDictAddInt (&args, TR_KEY_ids, id);
addList (tr_variantDictAddList (&args, TR_KEY_fields, 0),
KeyList () << TR_KEY_fileStats << TR_KEY_files << TR_KEY_id << TR_KEY_name);
exec ("torrent-get", &args, TAG_SOME_TORRENTS);
}
break;
}
case TAG_PORT_TEST:
{
bool isOpen;
if (args == nullptr ||
!tr_variantDictFindBool (args, TR_KEY_port_is_open, &isOpen))
isOpen = false;
emit portTested (isOpen);
break;
}
case TAG_MAGNET_LINK:
{
tr_variant * torrents;
tr_variant * child;
const char * str;
if (args != nullptr
&& tr_variantDictFindList (args, TR_KEY_torrents, &torrents)
&& ( (child = tr_variantListChild (torrents, 0)))
&& tr_variantDictFindStr (child, TR_KEY_magnetLink, &str, NULL))
qApp->clipboard ()->setText (QString::fromUtf8 (str));
break;
}
case TAG_ADD_TORRENT:
{
const char * str = "";
if (result != QLatin1String ("success"))
{
QMessageBox * d = new QMessageBox (QMessageBox::Information,
tr ("Add Torrent"),
QString::fromUtf8 (str),
QMessageBox::Close,
qApp->activeWindow ());
connect (d, SIGNAL (rejected ()), d, SLOT (deleteLater ()));
d->show ();
}
break;
}
}
return myRpc.exec (method, args);
}
void
@@ -892,16 +841,59 @@ Session::addTorrent (const AddData& addMe, tr_variant * args, bool trashOriginal
break;
}
const int64_t tag = getUniqueTag ();
RpcQueue * q = new RpcQueue ();
q->add (
[this, args] ()
{
return exec ("torrent-add", args);
},
[this, addMe] (const RpcResponse& r)
{
QMessageBox * d = new QMessageBox (QMessageBox::Warning,
tr ("Error Adding Torrent"),
QString::fromLatin1 ("<p><b>%1</b></p><p>%2</p>")
.arg (r.result)
.arg (addMe.readableName ()),
QMessageBox::Close,
qApp->activeWindow ());
connect (d, SIGNAL (rejected ()), d, SLOT (deleteLater ()));
d->show ();
});
q->add (
[this, addMe] (const RpcResponse& r)
{
tr_variant * dup;
const char * str;
if (tr_variantDictFindDict (r.args.get (), TR_KEY_torrent_duplicate, &dup) &&
tr_variantDictFindStr (dup, TR_KEY_name, &str, NULL))
{
const QString name = QString::fromUtf8 (str);
QMessageBox * d = new QMessageBox (QMessageBox::Warning,
tr ("Add Torrent"),
tr ("<p><b>Unable to add \"%1\".</b></p>"
"<p>It is a duplicate of \"%2\" which is already added.</p>")
.arg (addMe.readableShortName ())
.arg (name),
QMessageBox::Close,
qApp->activeWindow ());
connect (d, SIGNAL (rejected ()), d, SLOT (deleteLater ()));
d->show ();
}
});
// maybe delete the source .torrent
FileAdded * fileAdded = new FileAdded (tag, addMe.readableName ());
if (trashOriginal && addMe.type == AddData::FILENAME)
fileAdded->setFileToDelete (addMe.filename);
connect (this, SIGNAL (executed (int64_t, QString, tr_variant *)),
fileAdded, SLOT (executed (int64_t, QString, tr_variant *)));
q->add (
[this, addMe] ()
{
QFile original (addMe.filename);
original.setPermissions (QFile::ReadOwner | QFile::WriteOwner);
original.remove ();
});
exec ("torrent-add", args, tag);
q->run ();
}
void
+6 -33
View File
@@ -1,5 +1,5 @@
/*
* This file Copyright (C) 2009-2015 Mnemosyne LLC
* This file Copyright (C) 2009-2016 Mnemosyne LLC
*
* It may be used under the GNU GPL versions 2 or 3
* or any future license endorsed by Mnemosyne LLC.
@@ -18,6 +18,7 @@
#include <libtransmission/quark.h>
#include "RpcClient.h"
#include "Torrent.h"
class AddData;
class Prefs;
@@ -27,26 +28,6 @@ extern "C"
struct tr_variant;
}
class FileAdded: public QObject
{
Q_OBJECT
public:
FileAdded (int64_t tag, const QString& name): myTag (tag), myName (name) {}
virtual ~FileAdded () {}
void setFileToDelete (const QString& file) { myDelFile = file; }
public slots:
void executed (int64_t tag, const QString& result, tr_variant * arguments);
private:
const int64_t myTag;
const QString myName;
QString myDelFile;
};
class Session: public QObject
{
Q_OBJECT
@@ -75,10 +56,8 @@ class Session: public QObject
/** returns true if isServer () is true or if the remote address is the localhost */
bool isLocal () const;
void exec (tr_quark method, tr_variant * args, int64_t tag = -1);
void exec (const char * method, tr_variant * args, int64_t tag = -1);
int64_t getUniqueTag () { return nextUniqueTag++; }
RpcResponseFuture exec (tr_quark method, tr_variant * args);
RpcResponseFuture exec (const char * method, tr_variant * args);
void torrentSet (const QSet<int>& ids, const tr_quark key, bool val);
void torrentSet (const QSet<int>& ids, const tr_quark key, int val);
@@ -115,7 +94,6 @@ class Session: public QObject
void refreshExtraStats (const QSet<int>& ids);
signals:
void executed (int64_t tag, const QString& result, tr_variant * arguments);
void sourceChanged ();
void portTested (bool isOpen);
void statsUpdated ();
@@ -125,8 +103,7 @@ class Session: public QObject
void torrentsRemoved (tr_variant * torrentList);
void dataReadProgress ();
void dataSendProgress ();
void error (QNetworkReply::NetworkError);
void errorMessage (const QString&);
void networkResponse (QNetworkReply::NetworkError code, const QString& message);
void httpAuthenticationRequired ();
private:
@@ -138,18 +115,14 @@ class Session: public QObject
void sessionSet (const tr_quark key, const QVariant& variant);
void pumpRequests ();
void sendTorrentRequest (const char * request, const QSet<int>& torrentIds);
void refreshTorrents (const QSet<int>& torrentIds);
void refreshTorrents (const QSet<int>& torrentIds, const Torrent::KeyList& keys);
static void updateStats (tr_variant * d, tr_session_stats * stats);
private slots:
void responseReceived (int64_t tag, const QString& result, tr_variant * args);
private:
QString const myConfigDir;
Prefs& myPrefs;
int64_t nextUniqueTag;
int64_t myBlocklistSize;
tr_session * mySession;
QStringList myIdleJSON;
+1
View File
@@ -100,6 +100,7 @@ SOURCES += AboutDialog.cc \
PrefsDialog.cc \
RelocateDialog.cc \
RpcClient.cc \
RpcQueue.cc \
Session.cc \
SessionDialog.cc \
SqueezeLabel.cc \