diff options
Diffstat (limited to 'src/main.c')
| -rw-r--r-- | src/main.c | 820 |
1 files changed, 580 insertions, 240 deletions
@@ -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); +} |
