summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.c820
1 files changed, 580 insertions, 240 deletions
diff --git a/src/main.c b/src/main.c
index 78c98da..e08e83f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -13,6 +13,10 @@
#include "customcellrendererrate.h"
#include "customcellrendererleft.h"
+#ifndef G_VALUE_INIT
+# define G_VALUE_INIT { 0 }
+#endif
+
static const guint UPDATE_INTERVAL_MS = 3500;
typedef enum
@@ -25,6 +29,7 @@ typedef enum
COLUMN_DOWN,
COLUMN_LEFT,
COLUMN_PROGRESSSORT,
+ COLUMN_HASH,
} column_t;
typedef struct
@@ -44,7 +49,8 @@ typedef struct
GtkTreeIter iter;
} torrent_t;
-static void torrent_update(torrent_t* torrent, torrent_data_t* data);
+static void torrent_update(const gchar* hash,
+ torrent_t* torrent, torrent_data_t* data);
static void torrent_data_init(torrent_data_t* data);
static void torrent_data_free(torrent_data_t* data);
@@ -54,8 +60,10 @@ typedef struct
gboolean connected;
GtkListStore* liststore;
+ GtkTreeSelection* listselection;
GtkWidget* top;
- GtkWidget* connectmenuitem, * disconnectmenuitem;
+ GtkAction* connectaction, * disconnectaction;
+ GtkAction* startaction, * stopaction, * rehashaction;
GtkWidget* connectdlg;
GtkWidget* connectdlg_url;
@@ -74,12 +82,15 @@ typedef struct
guint64 last_sync_time;
guint last_sync_count;
guint sync_timeout;
+
+ const gchar* item_lock;
} master_t;
typedef struct
{
GAsyncQueue* queue;
master_t* master;
+ guint64 ratio_min, ratio_max, ratio_upload;
} worker_data_t;
typedef struct
@@ -92,28 +103,34 @@ typedef struct
typedef enum
{
/* Tell worker to connect. Will generate a MSG_CONNECTRESULT as response. */
- MSG_CONNECT, /* main -> worker */
+ MSG_CONNECT,
/* Worker responding to an MSG_CONNECT */
- MSG_CONNECTRESULT, /* worker -> main */
+ MSG_CONNECTRESULT,
/* Tell worker to disconnect. No response. */
- MSG_DISCONNECT, /* main -> worker */
+ MSG_DISCONNECT,
/* Tell worker to QUIT. No response. */
- MSG_QUIT, /* main -> worker */
+ MSG_QUIT,
/* Worker reporting a fatal error to main. Main should just quit. */
- MSG_ERROR, /* worker -> main */
+ MSG_ERROR,
/* Worker reporting a status message to main. Can be generated at any time
* for any reason */
- MSG_STATUS, /* worker -> main */
+ MSG_STATUS,
/* Tell worker to update the list of torrents.
* Will generate a MSG_SYNCLIST response */
- MSG_UPDATELIST, /* main -> worker */
+ MSG_UPDATELIST,
/* Worker responding to a MSG_UPDATELIST with added and removed torrents */
- MSG_SYNCLIST, /* worker -> main */
+ MSG_SYNCLIST,
/* Tell worker to update a torrent.
* Will generate a MSG_SYNC response */
- MSG_UPDATE, /* main -> worker */
+ MSG_UPDATE,
/* Worker responding to a MSG_UPDATE with uptodate torrent data */
- MSG_SYNC, /* worker -> main */
+ MSG_SYNC,
+ /* Tell worker to send a start command for torrent */
+ MSG_START,
+ /* Tell worker to send a stop command for torrent */
+ MSG_STOP,
+ /* Tell worker to send a rehash command for torrent */
+ MSG_REHASH,
} msg_type_t;
typedef struct
@@ -181,10 +198,32 @@ typedef struct
typedef struct
{
msg_t base;
+ const gchar* hash;
torrent_t torrent;
torrent_data_t data;
} msg_sync_t;
+typedef struct
+{
+ msg_t base;
+ gchar* hash;
+ torrent_t torrent;
+} msg_start_t;
+
+typedef struct
+{
+ msg_t base;
+ gchar* hash;
+ torrent_t torrent;
+} msg_stop_t;
+
+typedef struct
+{
+ msg_t base;
+ gchar* hash;
+ torrent_t torrent;
+} msg_rehash_t;
+
typedef struct _hashlist_t
{
gchar** data;
@@ -208,7 +247,11 @@ static msg_t* msg_quit(void);
static msg_t* msg_updatelist(void);
static msg_t* msg_synclist(const gchar** added, const gchar** removed);
static msg_t* msg_update(const gchar* hash, torrent_t* torrent);
-static msg_t* msg_sync(torrent_t* torrent, torrent_data_t* data);
+static msg_t* msg_sync(const gchar* hash, torrent_t* torrent,
+ torrent_data_t* data);
+static msg_t* msg_start(const gchar* hash, torrent_t* torrent);
+static msg_t* msg_stop(const gchar* hash, torrent_t* torrent);
+static msg_t* msg_rehash(const gchar* hash, torrent_t* torrent);
static void hashlist_init(hashlist_t* hlist);
static void hashlist_clear(hashlist_t* hlist);
@@ -216,8 +259,11 @@ static void hashlist_free(hashlist_t* hlist);
static gboolean hashlist_sync(hashlist_t* hlist, xmlrpc_env * const env,
const xmlrpc_value * const result, gsize count);
-static void do_connect(GtkMenuItem* menuitem, gpointer data);
-static void do_disconnect(GtkMenuItem* menuitem, gpointer data);
+static void do_connect(GtkAction* action, gpointer data);
+static void do_disconnect(GtkAction* action, gpointer data);
+static void do_start(GtkAction* action, gpointer data);
+static void do_stop(GtkAction* action, gpointer data);
+static void do_rehash(GtkAction* action, gpointer data);
static gpointer worker_main(gpointer data);
static gboolean incoming_msg(gpointer data);
@@ -228,6 +274,9 @@ static void status(master_t* master, const char* format, ...);
static void noop_destroy(gpointer value);
static void torrent_destroy(gpointer value);
+static void listselection_changed(GtkTreeSelection* selection,
+ gpointer user_data);
+
static void liststore_sort_column_changed(GtkTreeSortable* sortable,
gpointer user_data);
static gint liststore_default_compare_func(GtkTreeModel* model,
@@ -249,6 +298,7 @@ int main(int argc, char** argv)
master_t master;
memset(&master, 0, sizeof(master));
+ memset(&worker_data, 0, sizeof(worker_data));
g_thread_init(NULL);
@@ -292,15 +342,32 @@ int main(int argc, char** argv)
g_signal_connect(gtk_builder_get_object(builder, "quitmenuitem"),
"activate", G_CALLBACK(gtk_main_quit), NULL);
- master.connectmenuitem = GTK_WIDGET(gtk_builder_get_object(builder, "connectmenuitem"));
- master.disconnectmenuitem = GTK_WIDGET(gtk_builder_get_object(builder, "disconnectmenuitem"));
- g_signal_connect(master.connectmenuitem,
+ master.connectaction = GTK_ACTION(gtk_builder_get_object(builder,
+ "connectaction"));
+ g_signal_connect(master.connectaction,
"activate", G_CALLBACK(do_connect), &master);
- g_signal_connect(master.disconnectmenuitem,
+ master.disconnectaction =
+ GTK_ACTION(gtk_builder_get_object(builder, "disconnectaction"));
+ g_signal_connect(master.disconnectaction,
"activate", G_CALLBACK(do_disconnect), &master);
+ master.startaction = GTK_ACTION(gtk_builder_get_object(builder,
+ "startaction"));
+ g_signal_connect(master.startaction,
+ "activate", G_CALLBACK(do_start), &master);
+ master.stopaction = GTK_ACTION(gtk_builder_get_object(builder,
+ "stopaction"));
+ g_signal_connect(master.stopaction,
+ "activate", G_CALLBACK(do_stop), &master);
+ master.rehashaction = GTK_ACTION(gtk_builder_get_object(builder,
+ "rehashaction"));
+ g_signal_connect(master.rehashaction,
+ "activate", G_CALLBACK(do_rehash), &master);
- gtk_widget_set_sensitive(master.connectmenuitem, TRUE);
- gtk_widget_set_sensitive(master.disconnectmenuitem, FALSE);
+ gtk_action_set_sensitive(master.connectaction, TRUE);
+ gtk_action_set_sensitive(master.disconnectaction, FALSE);
+ gtk_action_set_sensitive(master.startaction, FALSE);
+ gtk_action_set_sensitive(master.stopaction, FALSE);
+ gtk_action_set_sensitive(master.rehashaction, FALSE);
pwddlg = GTK_WIDGET(gtk_builder_get_object(builder, "pwddlg"));
gtk_dialog_set_default_response(GTK_DIALOG(pwddlg), GTK_RESPONSE_OK);
@@ -312,6 +379,11 @@ int main(int argc, char** argv)
gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(master.liststore),
liststore_default_compare_func,
&master, NULL);
+ master.listselection = gtk_tree_view_get_selection(
+ GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview")));
+ gtk_tree_selection_set_mode(master.listselection, GTK_SELECTION_SINGLE);
+ g_signal_connect(master.listselection, "changed",
+ G_CALLBACK(listselection_changed), &master);
cell = custom_cell_renderer_state_new();
column = GTK_TREE_VIEW_COLUMN(gtk_builder_get_object(builder, "statecolumn"));
@@ -512,8 +584,9 @@ gboolean incoming_msg(gpointer data)
if (m->success)
{
msg->master->connected = TRUE;
- gtk_widget_set_sensitive(msg->master->connectmenuitem, FALSE);
- gtk_widget_set_sensitive(msg->master->disconnectmenuitem, TRUE);
+ gtk_action_set_sensitive(msg->master->connectaction, FALSE);
+ gtk_action_set_sensitive(msg->master->disconnectaction, TRUE);
+ listselection_changed(msg->master->listselection, msg->master);
g_key_file_set_boolean(msg->master->config, "connect",
"auto", TRUE);
@@ -528,9 +601,10 @@ gboolean incoming_msg(gpointer data)
{
GtkWidget* dlg;
msg->master->connected = FALSE;
- gtk_widget_set_sensitive(msg->master->connectmenuitem, TRUE);
- gtk_widget_set_sensitive(msg->master->disconnectmenuitem, FALSE);
+ gtk_action_set_sensitive(msg->master->connectaction, TRUE);
+ gtk_action_set_sensitive(msg->master->disconnectaction, FALSE);
gtk_list_store_clear(msg->master->liststore);
+ listselection_changed(msg->master->listselection, msg->master);
dlg = gtk_message_dialog_new(GTK_WINDOW(msg->master->top),
GTK_DIALOG_DESTROY_WITH_PARENT,
@@ -570,6 +644,11 @@ gboolean incoming_msg(gpointer data)
const gchar** x = m->removed;
for (; *x != NULL; ++x)
{
+ if (msg->master->item_lock == *x)
+ {
+ msg->master->item_lock = NULL;
+ listselection_changed(msg->master->listselection, msg->master);
+ }
g_hash_table_remove(msg->master->torrents, *x);
}
x = m->added;
@@ -592,7 +671,13 @@ gboolean incoming_msg(gpointer data)
msg_sync_t* m = (msg_sync_t*)msg;
g_assert(msg->master->last_sync_count > 0);
- torrent_update(&m->torrent, &m->data);
+ torrent_update(m->hash, &m->torrent, &m->data);
+
+ if (msg->master->item_lock == m->hash)
+ {
+ msg->master->item_lock = NULL;
+ listselection_changed(msg->master->listselection, msg->master);
+ }
if (--msg->master->last_sync_count == 0)
{
@@ -629,13 +714,16 @@ gboolean incoming_msg(gpointer data)
case MSG_QUIT:
case MSG_UPDATE:
case MSG_UPDATELIST:
+ case MSG_START:
+ case MSG_STOP:
+ case MSG_REHASH:
g_assert(FALSE);
}
msg_free(msg);
return FALSE;
}
-void do_connect(GtkMenuItem* menuitem, gpointer data)
+void do_connect(GtkAction* action, gpointer data)
{
master_t* master = data;
gchar* url, * user, * pass = NULL;
@@ -681,7 +769,7 @@ void do_connect(GtkMenuItem* menuitem, gpointer data)
pass = NULL;
}
- gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
+ gtk_action_set_sensitive(action, FALSE);
status(master, "Connecting to %s...", url);
g_key_file_set_string(master->config, "connect", "url", url);
if (user != NULL)
@@ -700,7 +788,7 @@ void do_connect(GtkMenuItem* menuitem, gpointer data)
g_free(pass);
}
-void do_disconnect(GtkMenuItem* menuitem, gpointer data)
+void do_disconnect(GtkAction* action, gpointer data)
{
master_t* master = data;
if (!master->connected)
@@ -712,11 +800,82 @@ void do_disconnect(GtkMenuItem* menuitem, gpointer data)
g_source_remove(master->sync_timeout);
master->sync_timeout = 0;
}
- gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
+ gtk_action_set_sensitive(action, FALSE);
status(master, "Disconnecting...");
g_hash_table_remove_all(master->torrents);
g_async_queue_push(master->queue, msg_disconnect());
master->connected = FALSE;
+ listselection_changed(master->listselection, master);
+}
+
+static gboolean get_selected_torrent(master_t* master, const gchar** hash,
+ torrent_t* torrent)
+{
+ GValue value = G_VALUE_INIT;
+ GtkTreeModel* model;
+ if (!master->connected || master->item_lock)
+ {
+ return FALSE;
+ }
+ if (!gtk_tree_selection_get_selected(master->listselection, &model,
+ &torrent->iter))
+ {
+ return FALSE;
+ }
+ torrent->store = master->liststore;
+ gtk_tree_model_get_value(model, &torrent->iter,
+ COLUMN_HASH, &value);
+ *hash = g_value_get_pointer(&value);
+ g_value_unset(&value);
+ return TRUE;
+}
+
+void do_start(GtkAction* action, gpointer data)
+{
+ master_t* master = data;
+ torrent_t torrent;
+ const gchar* hash;
+ if (!get_selected_torrent(master, &hash, &torrent))
+ {
+ return;
+ }
+ g_async_queue_push(master->queue, msg_start(hash, &torrent));
+ master->item_lock = hash;
+ gtk_action_set_sensitive(master->startaction, FALSE);
+ gtk_action_set_sensitive(master->stopaction, FALSE);
+ gtk_action_set_sensitive(master->rehashaction, FALSE);
+}
+
+void do_stop(GtkAction* action, gpointer data)
+{
+ master_t* master = data;
+ torrent_t torrent;
+ const gchar* hash;
+ if (!get_selected_torrent(master, &hash, &torrent))
+ {
+ return;
+ }
+ g_async_queue_push(master->queue, msg_stop(hash, &torrent));
+ master->item_lock = hash;
+ gtk_action_set_sensitive(master->startaction, FALSE);
+ gtk_action_set_sensitive(master->stopaction, FALSE);
+ gtk_action_set_sensitive(master->rehashaction, FALSE);
+}
+
+void do_rehash(GtkAction* action, gpointer data)
+{
+ master_t* master = data;
+ torrent_t torrent;
+ const gchar* hash;
+ if (!get_selected_torrent(master, &hash, &torrent))
+ {
+ return;
+ }
+ g_async_queue_push(master->queue, msg_rehash(hash, &torrent));
+ master->item_lock = hash;
+ gtk_action_set_sensitive(master->startaction, FALSE);
+ gtk_action_set_sensitive(master->stopaction, FALSE);
+ gtk_action_set_sensitive(master->rehashaction, FALSE);
}
void save_config(master_t* master)
@@ -741,7 +900,7 @@ void status(master_t* master, const char* format, ...)
g_free(tmp);
}
-void torrent_update(torrent_t* torrent, torrent_data_t* data)
+void torrent_update(const gchar* hash, torrent_t* torrent, torrent_data_t* data)
{
gtk_list_store_set(torrent->store, &torrent->iter,
COLUMN_STATE, data->state,
@@ -754,6 +913,7 @@ void torrent_update(torrent_t* torrent, torrent_data_t* data)
COLUMN_PROGRESSSORT,
data->downloaded < 100.0f ? data->downloaded :
1000.0f + data->seeded,
+ COLUMN_HASH, hash,
-1);
}
@@ -762,6 +922,10 @@ static void worker_respond(worker_data_t* data, msg_t* msg);
static gint64 get_i64_xmlrpc(xmlrpc_env* env, xmlrpc_value* value);
static gboolean get_bool_xmlrpc(xmlrpc_env* env, xmlrpc_value* value);
+static void sync_torrent_data(worker_data_t* data, xmlrpc_env* env,
+ xmlrpc_client* client, xmlrpc_server_info* server,
+ const gchar* hash, torrent_t* torrent);
+
gpointer worker_main(gpointer _data)
{
worker_data_t* data = _data;
@@ -771,7 +935,6 @@ gpointer worker_main(gpointer _data)
gboolean quit = FALSE;
version_t version;
hashlist_t hashlist;
- guint64 ratio_min = 0, ratio_max = 0, ratio_upload = 0;
hashlist_init(&hashlist);
@@ -927,21 +1090,21 @@ gpointer worker_main(gpointer _data)
xmlrpc_client_call2(&env, client, server, "ratio.min",
params, &result);
- ratio_min = get_i64_xmlrpc(&env, result) * 10;
+ data->ratio_min = get_i64_xmlrpc(&env, result) * 10;
if (env.fault_occurred)
{
env.fault_occurred = FALSE;
}
xmlrpc_client_call2(&env, client, server, "ratio.max",
params, &result);
- ratio_max = get_i64_xmlrpc(&env, result) * 10;
+ data->ratio_max = get_i64_xmlrpc(&env, result) * 10;
if (env.fault_occurred)
{
env.fault_occurred = FALSE;
}
xmlrpc_client_call2(&env, client, server, "ratio.upload",
params, &result);
- ratio_upload = get_i64_xmlrpc(&env, result);
+ data->ratio_upload = get_i64_xmlrpc(&env, result);
if (env.fault_occurred)
{
env.fault_occurred = FALSE;
@@ -954,224 +1117,112 @@ gpointer worker_main(gpointer _data)
case MSG_UPDATE:
{
msg_update_t* m = (msg_update_t*)msg;
- xmlrpc_value* params;
- torrent_data_t torrent_data;
- do
- {
- xmlrpc_value* result = NULL, * item;
- const gchar* tmpstr;
- gint64 tmpi64;
- gboolean tmpb;
- guint64 done, up_rate, down_rate, ratio;
- torrent_data_init(&torrent_data);
- params = xmlrpc_array_new(&env);
- item = xmlrpc_string_new(&env, m->hash);
- xmlrpc_array_append_item(&env, params, item);
- xmlrpc_DECREF(item);
-
- xmlrpc_client_call2(&env, client, server, "d.is_active",
- params, &result);
- tmpb = get_bool_xmlrpc(&env, result);
- if (env.fault_occurred)
- {
- break;
- }
- if (!tmpb)
- {
- xmlrpc_client_call2(&env, client, server,
- "d.hashing_failed", params, &result);
- tmpb = get_bool_xmlrpc(&env, result);
- if (env.fault_occurred)
- {
- break;
- }
- if (tmpb)
- {
- torrent_data.state = STATE_HASHFAILED;
- }
- else
- {
- torrent_data.state = STATE_DEAD;
- }
- }
- else
- {
- xmlrpc_client_call2(&env, client, server,
- "d.is_hash_checking", params, &result);
- tmpb = get_bool_xmlrpc(&env, result);
- if (env.fault_occurred)
- {
- break;
- }
- if (tmpb)
- {
- torrent_data.state = STATE_REHASH;
- }
- else
- {
- torrent_data.state = STATE_ACTIVE;
- }
- }
-
- xmlrpc_client_call2(&env, client, server, "d.name",
- params, &result);
- if (!env.fault_occurred)
- {
- xmlrpc_read_string(&env, result, &tmpstr);
- }
- if (result) xmlrpc_DECREF(result);
- if (env.fault_occurred)
- {
- break;
- }
- torrent_data.title = (gchar*)tmpstr;
+ sync_torrent_data(data, &env, client, server, m->hash,
+ &m->torrent);
+ msg_free(msg);
+ break;
+ }
+ case MSG_QUIT:
+ quit = TRUE;
+ msg_free(msg);
+ break;
+ case MSG_START:
+ {
+ msg_start_t* m = (msg_start_t*)msg;
+ xmlrpc_value* params, * item, * result = NULL;
+ params = xmlrpc_array_new(&env);
+ item = xmlrpc_string_new(&env, m->hash);
+ xmlrpc_array_append_item(&env, params, item);
+ xmlrpc_DECREF(item);
- xmlrpc_client_call2(&env, client, server, "d.size_bytes",
- params, &result);
- tmpi64 = get_i64_xmlrpc(&env, result);
- if (env.fault_occurred)
- {
- break;
- }
- torrent_data.size = (guint64)tmpi64;
+ xmlrpc_client_call2(&env, client, server, "d.resume",
+ params, &result);
+ xmlrpc_DECREF(params);
+ if (!get_bool_xmlrpc(&env, result) || env.fault_occurred)
+ {
+ worker_respond(data,
+ msg_status("Failed to resume torrent %s (%d): %s",
+ m->hash, env.fault_code, env.fault_string));
+ env.fault_occurred = FALSE;
+ }
+ else
+ {
+ sync_torrent_data(data, &env, client, server, m->hash,
+ &m->torrent);
+ }
+ msg_free(msg);
+ break;
+ }
+ case MSG_STOP:
+ {
+ msg_stop_t* m = (msg_stop_t*)msg;
+ xmlrpc_value* params, * item, * result = NULL;
+ gboolean active;
+ params = xmlrpc_array_new(&env);
+ item = xmlrpc_string_new(&env, m->hash);
+ xmlrpc_array_append_item(&env, params, item);
+ xmlrpc_DECREF(item);
- xmlrpc_client_call2(&env, client, server, "d.bytes_done",
+ xmlrpc_client_call2(&env, client, server, "d.is_active",
+ params, &result);
+ active = get_bool_xmlrpc(&env, result);
+ if (env.fault_occurred)
+ {
+ worker_respond(data,
+ msg_status("Failed to get torrent status %s (%d): %s",
+ m->hash, env.fault_code, env.fault_string));
+ env.fault_occurred = FALSE;
+ }
+ else
+ {
+ xmlrpc_client_call2(&env, client, server,
+ active ? "d.pause" : "d.erase",
params, &result);
- tmpi64 = get_i64_xmlrpc(&env, result);
- if (env.fault_occurred)
- {
- break;
- }
- done = (guint64)tmpi64;
- if (done >= torrent_data.size)
- {
- torrent_data.downloaded = 100.0f;
- }
- else if (torrent_data.size > 0)
+ xmlrpc_DECREF(params);
+ if (!get_bool_xmlrpc(&env, result) || env.fault_occurred)
{
- torrent_data.downloaded = ((gfloat)done * 100.0f) /
- (gfloat)torrent_data.size;
+ worker_respond(data,
+ msg_status("Failed to %s torrent %s (%d): %s",
+ active ? "pause" : "remove",
+ m->hash, env.fault_code, env.fault_string));
+ env.fault_occurred = FALSE;
}
else
{
- torrent_data.downloaded = 0.0f;
- }
-
- xmlrpc_client_call2(&env, client, server, "d.ratio",
- params, &result);
- tmpi64 = get_i64_xmlrpc(&env, result);
- if (env.fault_occurred)
- {
- break;
- }
- ratio = (guint64)tmpi64;
- torrent_data.seeded = (gfloat)ratio / 1000.0f;
-
- xmlrpc_client_call2(&env, client, server, "d.down.rate",
- params, &result);
- tmpi64 = get_i64_xmlrpc(&env, result);
- if (env.fault_occurred)
- {
- break;
- }
- down_rate = (guint64)tmpi64;
- torrent_data.down = (gfloat)down_rate / 1000.0f;
-
- xmlrpc_client_call2(&env, client, server, "d.up.rate",
- params, &result);
- tmpi64 = get_i64_xmlrpc(&env, result);
- if (env.fault_occurred)
- {
- break;
- }
- up_rate = (guint64)tmpi64;
- torrent_data.up = (gfloat)up_rate / 1000.0f;
-
- if (torrent_data.downloaded < 100.0f)
- {
- if (down_rate >= 512)
- {
- torrent_data.left = (torrent_data.size - done) /
- (down_rate & ~(guint32)511);
- }
- }
- else
- {
- gint64 upload_left = -1;
- if (ratio_max > 0)
- {
- if (ratio < ratio_max)
- {
- upload_left = (torrent_data.size *
- (ratio_max - ratio)) / 1000;
- }
- else
- {
- upload_left = 0;
- }
- }
- if (ratio_min > 0)
- {
- /* Inexact but good enough */
- guint64 uploaded_bytes =
- (torrent_data.size * ratio) / 1000;
- if (uploaded_bytes >= ratio_upload)
- {
- if (ratio < ratio_min)
- {
- gint64 tmp = (torrent_data.size *
- (ratio_min - ratio)) / 1000;
- if (upload_left < 0 || tmp < upload_left)
- {
- upload_left = tmp;
- }
- }
- else
- {
- upload_left = 0;
- }
- }
- else
- {
- gint64 tmp = ratio_upload - uploaded_bytes;
- if (upload_left < 0 || tmp < upload_left)
- {
- upload_left = tmp;
- }
- }
- }
- if (upload_left >= 0)
- {
- if (up_rate >= 512)
- {
- torrent_data.left = (guint64)upload_left /
- (up_rate & ~(guint32)511);
- }
- }
+ sync_torrent_data(data, &env, client, server, m->hash,
+ &m->torrent);
}
}
- while (FALSE);
+ msg_free(msg);
+ break;
+ }
+ case MSG_REHASH:
+ {
+ msg_rehash_t* m = (msg_rehash_t*)msg;
+ xmlrpc_value* params, * item, * result = NULL;
+ params = xmlrpc_array_new(&env);
+ item = xmlrpc_string_new(&env, m->hash);
+ xmlrpc_array_append_item(&env, params, item);
+ xmlrpc_DECREF(item);
+
+ xmlrpc_client_call2(&env, client, server, "d.check_hash",
+ params, &result);
xmlrpc_DECREF(params);
- if (env.fault_occurred)
+ if (!get_bool_xmlrpc(&env, result) || env.fault_occurred)
{
worker_respond(data,
- msg_status("Failed to update torrent %s (%d): %s",
+ msg_status("Failed to rehash torrent %s (%d): %s",
m->hash, env.fault_code, env.fault_string));
env.fault_occurred = FALSE;
- torrent_data.state = STATE_DEAD;
- worker_respond(data, msg_sync(&m->torrent, &torrent_data));
- msg_free(msg);
- continue;
}
-
- worker_respond(data, msg_sync(&m->torrent, &torrent_data));
+ else
+ {
+ sync_torrent_data(data, &env, client, server, m->hash,
+ &m->torrent);
+ }
msg_free(msg);
break;
}
- case MSG_QUIT:
- quit = TRUE;
- msg_free(msg);
- break;
case MSG_CONNECTRESULT:
case MSG_ERROR:
case MSG_STATUS:
@@ -1274,6 +1325,27 @@ void msg_free(msg_t* msg)
g_slice_free(msg_sync_t, m);
break;
}
+ case MSG_START:
+ {
+ msg_start_t* m = (msg_start_t*)msg;
+ g_free(m->hash);
+ g_slice_free(msg_start_t, m);
+ break;
+ }
+ case MSG_STOP:
+ {
+ msg_stop_t* m = (msg_stop_t*)msg;
+ g_free(m->hash);
+ g_slice_free(msg_stop_t, m);
+ break;
+ }
+ case MSG_REHASH:
+ {
+ msg_rehash_t* m = (msg_rehash_t*)msg;
+ g_free(m->hash);
+ g_slice_free(msg_rehash_t, m);
+ break;
+ }
}
}
@@ -1370,15 +1442,43 @@ msg_t* msg_update(const gchar* hash, torrent_t* torrent)
return &msg->base;
}
-msg_t* msg_sync(torrent_t* torrent, torrent_data_t* data)
+msg_t* msg_sync(const gchar* hash, torrent_t* torrent, torrent_data_t* data)
{
msg_sync_t* msg = g_slice_new(msg_sync_t);
msg->base.type = MSG_SYNC;
+ msg->hash = hash;
msg->torrent = *torrent;
msg->data = *data;
return &msg->base;
}
+msg_t* msg_start(const gchar* hash, torrent_t* torrent)
+{
+ msg_start_t* msg = g_slice_new(msg_start_t);
+ msg->base.type = MSG_START;
+ msg->hash = g_strdup(hash);
+ msg->torrent = *torrent;
+ return &msg->base;
+}
+
+msg_t* msg_stop(const gchar* hash, torrent_t* torrent)
+{
+ msg_stop_t* msg = g_slice_new(msg_stop_t);
+ msg->base.type = MSG_STOP;
+ msg->hash = g_strdup(hash);
+ msg->torrent = *torrent;
+ return &msg->base;
+}
+
+msg_t* msg_rehash(const gchar* hash, torrent_t* torrent)
+{
+ msg_rehash_t* msg = g_slice_new(msg_rehash_t);
+ msg->base.type = MSG_REHASH;
+ msg->hash = g_strdup(hash);
+ msg->torrent = *torrent;
+ return &msg->base;
+}
+
void hashlist_init(hashlist_t* hlist)
{
hlist->fill = 0;
@@ -1675,10 +1775,6 @@ void liststore_sort_column_changed(GtkTreeSortable* sortable,
save_config(master);
}
-#ifndef G_VALUE_INIT
-# define G_VALUE_INIT { 0 }
-#endif
-
gint liststore_default_compare_func(GtkTreeModel* model,
GtkTreeIter* a,
GtkTreeIter* b,
@@ -1745,3 +1841,247 @@ gint liststore_default_compare_func(GtkTreeModel* model,
return ret;
}
}
+
+void sync_torrent_data(worker_data_t* data, xmlrpc_env* env,
+ xmlrpc_client* client, xmlrpc_server_info* server,
+ const gchar* hash, torrent_t* torrent)
+{
+ xmlrpc_value* params;
+ torrent_data_t torrent_data;
+ do
+ {
+ xmlrpc_value* result = NULL, * item;
+ const gchar* tmpstr;
+ gint64 tmpi64;
+ gboolean tmpb;
+ guint64 done, up_rate, down_rate, ratio;
+ torrent_data_init(&torrent_data);
+ params = xmlrpc_array_new(env);
+ item = xmlrpc_string_new(env, hash);
+ xmlrpc_array_append_item(env, params, item);
+ xmlrpc_DECREF(item);
+
+ xmlrpc_client_call2(env, client, server, "d.is_active", params,
+ &result);
+ tmpb = get_bool_xmlrpc(env, result);
+ if (env->fault_occurred)
+ {
+ break;
+ }
+ if (!tmpb)
+ {
+ xmlrpc_client_call2(env, client, server, "d.hashing_failed",
+ params, &result);
+ tmpb = get_bool_xmlrpc(env, result);
+ if (env->fault_occurred)
+ {
+ break;
+ }
+ if (tmpb)
+ {
+ torrent_data.state = STATE_HASHFAILED;
+ }
+ else
+ {
+ torrent_data.state = STATE_DEAD;
+ }
+ }
+ else
+ {
+ xmlrpc_client_call2(env, client, server, "d.is_hash_checking",
+ params, &result);
+ tmpb = get_bool_xmlrpc(env, result);
+ if (env->fault_occurred)
+ {
+ break;
+ }
+ if (tmpb)
+ {
+ torrent_data.state = STATE_REHASH;
+ }
+ else
+ {
+ torrent_data.state = STATE_ACTIVE;
+ }
+ }
+
+ xmlrpc_client_call2(env, client, server, "d.name", params, &result);
+ if (!env->fault_occurred)
+ {
+ xmlrpc_read_string(env, result, &tmpstr);
+ }
+ if (result) xmlrpc_DECREF(result);
+ if (env->fault_occurred)
+ {
+ break;
+ }
+ torrent_data.title = (gchar*)tmpstr;
+
+ xmlrpc_client_call2(env, client, server, "d.size_bytes", params,
+ &result);
+ tmpi64 = get_i64_xmlrpc(env, result);
+ if (env->fault_occurred)
+ {
+ break;
+ }
+ torrent_data.size = (guint64)tmpi64;
+
+ xmlrpc_client_call2(env, client, server, "d.bytes_done", params,
+ &result);
+ tmpi64 = get_i64_xmlrpc(env, result);
+ if (env->fault_occurred)
+ {
+ break;
+ }
+ done = (guint64)tmpi64;
+ if (done >= torrent_data.size)
+ {
+ torrent_data.downloaded = 100.0f;
+ }
+ else if (torrent_data.size > 0)
+ {
+ torrent_data.downloaded = ((gfloat)done * 100.0f) /
+ (gfloat)torrent_data.size;
+ }
+ else
+ {
+ torrent_data.downloaded = 0.0f;
+ }
+
+ xmlrpc_client_call2(env, client, server, "d.ratio", params, &result);
+ tmpi64 = get_i64_xmlrpc(env, result);
+ if (env->fault_occurred)
+ {
+ break;
+ }
+ ratio = (guint64)tmpi64;
+ torrent_data.seeded = (gfloat)ratio / 1000.0f;
+
+ xmlrpc_client_call2(env, client, server, "d.down.rate", params,
+ &result);
+ tmpi64 = get_i64_xmlrpc(env, result);
+ if (env->fault_occurred)
+ {
+ break;
+ }
+ down_rate = (guint64)tmpi64;
+ torrent_data.down = (gfloat)down_rate / 1000.0f;
+
+ xmlrpc_client_call2(env, client, server, "d.up.rate", params, &result);
+ tmpi64 = get_i64_xmlrpc(env, result);
+ if (env->fault_occurred)
+ {
+ break;
+ }
+ up_rate = (guint64)tmpi64;
+ torrent_data.up = (gfloat)up_rate / 1000.0f;
+
+ if (torrent_data.downloaded < 100.0f)
+ {
+ if (down_rate >= 512)
+ {
+ torrent_data.left = (torrent_data.size - done) /
+ (down_rate & ~(guint32)511);
+ }
+ }
+ else
+ {
+ gint64 upload_left = -1;
+ if (data->ratio_max > 0)
+ {
+ if (ratio < data->ratio_max)
+ {
+ upload_left = (torrent_data.size *
+ (data->ratio_max - ratio)) / 1000;
+ }
+ else
+ {
+ upload_left = 0;
+ }
+ }
+ if (data->ratio_min > 0)
+ {
+ /* Inexact but good enough */
+ guint64 uploaded_bytes = (torrent_data.size * ratio) / 1000;
+ if (uploaded_bytes >= data->ratio_upload)
+ {
+ if (ratio < data->ratio_min)
+ {
+ gint64 tmp = (torrent_data.size *
+ (data->ratio_min - ratio)) / 1000;
+ if (upload_left < 0 || tmp < upload_left)
+ {
+ upload_left = tmp;
+ }
+ }
+ else
+ {
+ upload_left = 0;
+ }
+ }
+ else
+ {
+ gint64 tmp = data->ratio_upload - uploaded_bytes;
+ if (upload_left < 0 || tmp < upload_left)
+ {
+ upload_left = tmp;
+ }
+ }
+ }
+ if (upload_left >= 0)
+ {
+ if (up_rate >= 512)
+ {
+ torrent_data.left = (guint64)upload_left /
+ (up_rate & ~(guint32)511);
+ }
+ }
+ }
+ }
+ while (FALSE);
+ xmlrpc_DECREF(params);
+ if (env->fault_occurred)
+ {
+ worker_respond(data,
+ msg_status("Failed to update torrent %s (%d): %s",
+ hash, env->fault_code, env->fault_string));
+ env->fault_occurred = FALSE;
+ torrent_data.state = STATE_DEAD;
+ }
+
+ worker_respond(data, msg_sync(hash, torrent, &torrent_data));
+}
+
+void listselection_changed(GtkTreeSelection* selection,
+ gpointer user_data)
+{
+ master_t* master = user_data;
+ GtkTreeIter iter;
+ GtkTreeModel* model;
+ if (master->item_lock)
+ {
+ if (!master->connected)
+ {
+ master->item_lock = NULL;
+ }
+ }
+ else if (master->connected &&
+ gtk_tree_selection_get_selected(selection, &model, &iter))
+ {
+ GValue value = G_VALUE_INIT;
+ state_t state;
+ gtk_tree_model_get_value(model, &iter, COLUMN_STATE, &value);
+ state = (state_t)g_value_get_int(&value);
+ g_value_unset(&value);
+
+ gtk_action_set_sensitive(master->startaction, state == STATE_DEAD ||
+ state == STATE_HASHFAILED);
+ gtk_action_set_sensitive(master->stopaction, state != STATE_REHASH);
+ gtk_action_set_sensitive(master->rehashaction, state != STATE_REHASH);
+ return;
+ }
+
+ gtk_action_set_sensitive(master->startaction, FALSE);
+ gtk_action_set_sensitive(master->stopaction, FALSE);
+ gtk_action_set_sensitive(master->rehashaction, FALSE);
+}