mirror of
https://github.com/transmission/transmission.git
synced 2025-12-24 20:35:36 +00:00
Major internal restructuring for the GTK GUI,
GObject-derived wrappers are used for tr_handle_t and tr_torrent_t. Use bencoding to store prefs and state file. Make sure to always group error messages when adding multiple torrents at once. Remove some unused code. Many miscellaneous cleanups.
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
include ../Makefile.config
|
||||
include ../Makefile.common
|
||||
|
||||
SRCS = conf.c dialogs.c io.c ipc.c main.c trcellrenderertorrent.c util.c
|
||||
SRCS = conf.c dialogs.c io.c ipc.c main.c tr_backend.c tr_torrent.c \
|
||||
tr_cell_renderer_torrent.c util.c
|
||||
OBJS = $(SRCS:%.c=%.o)
|
||||
|
||||
CFLAGS += $(CFLAGS_GTK) -I../libtransmission
|
||||
|
||||
514
gtk/conf.c
514
gtk/conf.c
@@ -40,7 +40,6 @@
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "conf.h"
|
||||
#include "transmission.h"
|
||||
#include "util.h"
|
||||
|
||||
#define CONF_SUBDIR "gtk"
|
||||
@@ -61,10 +60,18 @@ static int
|
||||
lockfile(const char *file, char **errstr);
|
||||
static void
|
||||
cf_removelocks(void);
|
||||
static char *
|
||||
cf_readfile(const char *file, const char *oldfile, gsize *len,
|
||||
gboolean *usedold, char **errstr);
|
||||
static void
|
||||
cf_benc_append(benc_val_t *val, char type, int incsize);
|
||||
static void
|
||||
cf_writebenc(const char *file, const char *tmp, benc_val_t *data,
|
||||
char **errstr);
|
||||
static gboolean
|
||||
writefile_traverse(gpointer key, gpointer value, gpointer data);
|
||||
static char *
|
||||
getstateval(struct cf_torrentstate *state, char *line);
|
||||
getstateval(benc_val_t *state, char *line);
|
||||
|
||||
static char *gl_confdir = NULL;
|
||||
static char *gl_old_confdir = NULL;
|
||||
@@ -171,15 +178,59 @@ cf_sockname(void) {
|
||||
return g_build_filename(gl_confdir, FILE_SOCKET, NULL);
|
||||
}
|
||||
|
||||
gboolean
|
||||
cf_loadprefs(char **errstr) {
|
||||
char *path = g_build_filename(gl_confdir, FILE_PREFS, NULL);
|
||||
char *oldpath;
|
||||
static char *
|
||||
cf_readfile(const char *file, const char *oldfile, gsize *len,
|
||||
gboolean *usedold, char **errstr) {
|
||||
char *path;
|
||||
GIOChannel *io;
|
||||
GError *err;
|
||||
char *line, *sep;
|
||||
gsize len, termpos;
|
||||
char term = PREF_SEP_LINE;
|
||||
char *ret;
|
||||
|
||||
*errstr = NULL;
|
||||
*usedold = FALSE;
|
||||
ret = NULL;
|
||||
err = NULL;
|
||||
*len = 0;
|
||||
|
||||
path = g_build_filename(gl_confdir, file, NULL);
|
||||
io = g_io_channel_new_file(path, "r", &err);
|
||||
if(NULL != err && g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
|
||||
g_error_free(err);
|
||||
err = NULL;
|
||||
g_free(path);
|
||||
path = g_build_filename(gl_old_confdir, oldfile, NULL);
|
||||
io = g_io_channel_new_file(path, "r", &err);
|
||||
*usedold = TRUE;
|
||||
}
|
||||
if(NULL != err) {
|
||||
if(!g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT))
|
||||
*errstr = g_strdup_printf(
|
||||
_("Failed to open the file %s for reading:\n%s"), path, err->message);
|
||||
goto done;
|
||||
}
|
||||
g_io_channel_set_encoding(io, NULL, NULL);
|
||||
|
||||
if(G_IO_STATUS_ERROR == g_io_channel_read_to_end(io, &ret, len, &err)) {
|
||||
*errstr = g_strdup_printf(
|
||||
_("Error while reading from the file %s:\n%s"), path, err->message);
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
if(NULL != err)
|
||||
g_error_free(err);
|
||||
if(NULL != io)
|
||||
g_io_channel_unref(io);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
cf_loadprefs(char **errstr) {
|
||||
char *data, *line, *eol, *sep, *key;
|
||||
gsize len;
|
||||
benc_val_t val;
|
||||
gboolean usedold;
|
||||
int ii;
|
||||
|
||||
*errstr = NULL;
|
||||
|
||||
@@ -189,58 +240,103 @@ cf_loadprefs(char **errstr) {
|
||||
gl_prefs = g_tree_new_full((GCompareDataFunc)g_ascii_strcasecmp, NULL,
|
||||
g_free, g_free);
|
||||
|
||||
err = NULL;
|
||||
io = g_io_channel_new_file(path, "r", &err);
|
||||
if(NULL != err) {
|
||||
if(!g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT))
|
||||
*errstr = g_strdup_printf(
|
||||
_("Failed to open the file %s for reading:\n%s"), path, err->message);
|
||||
else {
|
||||
g_error_free(err);
|
||||
err = NULL;
|
||||
oldpath = g_build_filename(gl_old_confdir, OLD_FILE_PREFS, NULL);
|
||||
io = g_io_channel_new_file(oldpath, "r", &err);
|
||||
g_free(oldpath);
|
||||
}
|
||||
if(NULL != err)
|
||||
goto done;
|
||||
data = cf_readfile(FILE_PREFS, OLD_FILE_PREFS, &len, &usedold, errstr);
|
||||
if(NULL != *errstr) {
|
||||
g_assert(NULL == data);
|
||||
return;
|
||||
}
|
||||
g_io_channel_set_line_term(io, &term, 1);
|
||||
|
||||
err = NULL;
|
||||
for(;;) {
|
||||
assert(NULL == err) ;
|
||||
switch(g_io_channel_read_line(io, &line, &len, &termpos, &err)) {
|
||||
case G_IO_STATUS_ERROR:
|
||||
*errstr = g_strdup_printf(
|
||||
_("Error while reading from the file %s:\n%s"), path, err->message);
|
||||
goto done;
|
||||
case G_IO_STATUS_NORMAL:
|
||||
if(NULL != line) {
|
||||
if(g_utf8_validate(line, len, NULL) &&
|
||||
NULL != (sep = strchr(line, PREF_SEP_KEYVAL)) && sep > line) {
|
||||
*sep = '\0';
|
||||
line[termpos] = '\0';
|
||||
g_tree_insert(gl_prefs, g_strcompress(line), g_strcompress(sep + 1));
|
||||
}
|
||||
g_free(line);
|
||||
if(NULL == data)
|
||||
return;
|
||||
|
||||
bzero(&val, sizeof(val));
|
||||
if(!usedold && !tr_bencLoad(data, len, &val, NULL)) {
|
||||
if(TYPE_DICT == val.type) {
|
||||
key = NULL;
|
||||
for(ii = 0; ii < val.val.l.count; ii++) {
|
||||
if(NULL == key) {
|
||||
g_assert(TYPE_STR == val.val.l.vals[ii].type);
|
||||
key = val.val.l.vals[ii].val.s.s;
|
||||
} else {
|
||||
if(TYPE_INT == val.val.l.vals[ii].type)
|
||||
g_tree_insert(gl_prefs, g_strdup(key),
|
||||
g_strdup_printf("%"PRIu64, val.val.l.vals[ii].val.i));
|
||||
else if(TYPE_STR == val.val.l.vals[ii].type)
|
||||
g_tree_insert(gl_prefs, g_strdup(key),
|
||||
g_strdup(val.val.l.vals[ii].val.s.s));
|
||||
key = NULL;
|
||||
}
|
||||
break;
|
||||
case G_IO_STATUS_EOF:
|
||||
goto done;
|
||||
default:
|
||||
assert(!"unknown return code");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
/* XXX remove this in a release or two */
|
||||
for(line = data; NULL != (eol = strchr(line, PREF_SEP_LINE));
|
||||
line = eol + 1) {
|
||||
*eol = '\0';
|
||||
if(g_utf8_validate(line, -1, NULL) &&
|
||||
NULL != (sep = strchr(line, PREF_SEP_KEYVAL))) {
|
||||
*sep = '\0';
|
||||
g_tree_insert(gl_prefs, g_strcompress(line), g_strcompress(sep+1));
|
||||
}
|
||||
}
|
||||
cf_saveprefs(errstr);
|
||||
}
|
||||
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
benc_val_t *
|
||||
cf_loadstate(char **errstr) {
|
||||
char *data, *line, *eol, *prog;
|
||||
gsize len;
|
||||
gboolean usedold;
|
||||
benc_val_t *state, *torstate;
|
||||
|
||||
*errstr = NULL;
|
||||
|
||||
data = cf_readfile(FILE_STATE, OLD_FILE_STATE, &len, &usedold, errstr);
|
||||
if(NULL != *errstr) {
|
||||
g_assert(NULL == data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(NULL == data)
|
||||
return NULL;
|
||||
|
||||
state = g_new0(benc_val_t, 1);
|
||||
if(usedold || tr_bencLoad(data, len, state, NULL)) {
|
||||
/* XXX all this evil compat code should go away at some point */
|
||||
tr_bencFree(state);
|
||||
bzero(state, sizeof(benc_val_t));
|
||||
state->type = TYPE_LIST;
|
||||
for(line = data; NULL != (eol = strchr(line, PREF_SEP_LINE));
|
||||
line = eol + 1) {
|
||||
*eol = '\0';
|
||||
if(g_utf8_validate(line, -1, NULL)) {
|
||||
cf_benc_append(state, TYPE_DICT, 10);
|
||||
torstate = state->val.l.vals + state->val.l.count - 1;
|
||||
prog = line;
|
||||
while(NULL != (prog = getstateval(torstate, prog)))
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if(NULL != err)
|
||||
g_error_free(err);
|
||||
if(NULL != io)
|
||||
g_io_channel_unref(io);
|
||||
g_free(path);
|
||||
return NULL == *errstr;
|
||||
g_free(data);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static void
|
||||
cf_benc_append(benc_val_t *val, char type, int incsize) {
|
||||
if(++val->val.l.count > val->val.l.alloc) {
|
||||
val->val.l.alloc += incsize;
|
||||
val->val.l.vals = g_renew(benc_val_t, val->val.l.vals, val->val.l.alloc);
|
||||
bzero(val->val.l.vals + val->val.l.alloc - incsize,
|
||||
incsize * sizeof(benc_val_t));
|
||||
}
|
||||
val->val.l.vals[val->val.l.count-1].type = type;
|
||||
}
|
||||
|
||||
const char *
|
||||
@@ -257,176 +353,98 @@ cf_setpref(const char *name, const char *value) {
|
||||
g_tree_insert(gl_prefs, g_strdup(name), g_strdup(value));
|
||||
}
|
||||
|
||||
struct writeinfo {
|
||||
GIOChannel *io;
|
||||
GError *err;
|
||||
};
|
||||
|
||||
gboolean
|
||||
cf_saveprefs(char **errstr) {
|
||||
char *file = g_build_filename(gl_confdir, FILE_PREFS, NULL);
|
||||
char *tmpfile = g_build_filename(gl_confdir, FILE_PREFS_TMP, NULL);
|
||||
static void
|
||||
cf_writebenc(const char *file, const char *tmp, benc_val_t *data,
|
||||
char **errstr) {
|
||||
char *path = g_build_filename(gl_confdir, file, NULL);
|
||||
char *pathtmp = g_build_filename(gl_confdir, tmp, NULL);
|
||||
GIOChannel *io = NULL;
|
||||
struct writeinfo info;
|
||||
int fd;
|
||||
|
||||
assert(NULL != gl_prefs);
|
||||
assert(NULL != errstr);
|
||||
GError *err;
|
||||
char *datastr;
|
||||
size_t len;
|
||||
gsize written;
|
||||
|
||||
*errstr = NULL;
|
||||
err = NULL;
|
||||
datastr = NULL;
|
||||
|
||||
if(0 > (fd = lockfile(tmpfile, errstr))) {
|
||||
g_free(errstr);
|
||||
*errstr = g_strdup_printf(_("Failed to open or lock the file %s:\n%s"),
|
||||
tmpfile, strerror(errno));
|
||||
io = g_io_channel_new_file(pathtmp, "w", &err);
|
||||
if(NULL != err) {
|
||||
*errstr = g_strdup_printf(_("Failed to open the file %s for writing:\n%s"),
|
||||
pathtmp, err->message);
|
||||
goto done;
|
||||
}
|
||||
g_io_channel_set_encoding(io, NULL, NULL);
|
||||
|
||||
#ifdef NDEBUG
|
||||
ftruncate(fd, 0);
|
||||
#else
|
||||
assert(0 == ftruncate(fd, 0));
|
||||
#endif
|
||||
len = 0;
|
||||
datastr = tr_bencSaveMalloc(data, &len);
|
||||
|
||||
info.err = NULL;
|
||||
io = g_io_channel_unix_new(fd);
|
||||
g_io_channel_set_close_on_unref(io, TRUE);
|
||||
|
||||
info.io = io;
|
||||
info.err = NULL;
|
||||
g_tree_foreach(gl_prefs, writefile_traverse, &info);
|
||||
if(NULL != info.err ||
|
||||
G_IO_STATUS_ERROR == g_io_channel_shutdown(io, TRUE, &info.err)) {
|
||||
written = 0;
|
||||
g_io_channel_write_chars(io, datastr, len, &written, &err);
|
||||
if(NULL != err)
|
||||
g_io_channel_flush(io, &err);
|
||||
if(NULL != err) {
|
||||
*errstr = g_strdup_printf(_("Error while writing to the file %s:\n%s"),
|
||||
tmpfile, info.err->message);
|
||||
g_error_free(info.err);
|
||||
pathtmp, err->message);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if(0 > rename(tmpfile, file)) {
|
||||
if(0 > rename(pathtmp, path)) {
|
||||
*errstr = g_strdup_printf(_("Failed to rename the file %s to %s:\n%s"),
|
||||
tmpfile, file, strerror(errno));
|
||||
pathtmp, file, strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
g_free(file);
|
||||
g_free(tmpfile);
|
||||
g_free(path);
|
||||
g_free(pathtmp);
|
||||
if(NULL != io)
|
||||
g_io_channel_unref(io);
|
||||
if(NULL != datastr)
|
||||
free(datastr);
|
||||
}
|
||||
|
||||
return NULL == *errstr;
|
||||
void
|
||||
cf_saveprefs(char **errstr) {
|
||||
benc_val_t val;
|
||||
benc_val_t *ptr;
|
||||
|
||||
*errstr = NULL;
|
||||
|
||||
bzero(&val, sizeof(val));
|
||||
val.type = TYPE_DICT;
|
||||
val.val.l.alloc = val.val.l.count = g_tree_nnodes(gl_prefs) * 2;
|
||||
val.val.l.vals = g_new0(benc_val_t, val.val.l.alloc);
|
||||
|
||||
ptr = val.val.l.vals;
|
||||
g_tree_foreach(gl_prefs, writefile_traverse, &ptr);
|
||||
g_assert(ptr - val.val.l.vals == val.val.l.alloc);
|
||||
|
||||
cf_writebenc(FILE_PREFS, FILE_PREFS_TMP, &val, errstr);
|
||||
tr_bencFree(&val);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
writefile_traverse(gpointer key, gpointer value, gpointer data) {
|
||||
struct writeinfo *info = data;
|
||||
char *ekey, *eval, *line;
|
||||
char sep[2];
|
||||
int len;
|
||||
benc_val_t **ptr = data;
|
||||
benc_val_t *bkey = *ptr;
|
||||
benc_val_t *bval = (*ptr) + 1;
|
||||
|
||||
ekey = g_strescape(key, NULL);
|
||||
eval = g_strescape(value, NULL);
|
||||
sep[0] = PREF_SEP_KEYVAL;
|
||||
sep[1] = '\0';
|
||||
line = g_strjoin(sep, ekey, eval, NULL);
|
||||
len = strlen(line);
|
||||
line[len] = PREF_SEP_LINE;
|
||||
*ptr = (*ptr) + 2;
|
||||
|
||||
switch(g_io_channel_write_chars(info->io, line, len + 1, NULL, &info->err)) {
|
||||
case G_IO_STATUS_ERROR:
|
||||
goto done;
|
||||
case G_IO_STATUS_NORMAL:
|
||||
break;
|
||||
default:
|
||||
assert(!"unknown return code");
|
||||
goto done;
|
||||
}
|
||||
bkey->type = TYPE_STR;
|
||||
bkey->val.s.s = key;
|
||||
bkey->val.s.i = strlen(key);
|
||||
|
||||
done:
|
||||
g_free(ekey);
|
||||
g_free(eval);
|
||||
g_free(line);
|
||||
return NULL != info->err;
|
||||
}
|
||||
bval->type = TYPE_STR;
|
||||
bval->val.s.s = value;
|
||||
bval->val.s.i = strlen(value);
|
||||
|
||||
GList *
|
||||
cf_loadstate(char **errstr) {
|
||||
char *path = g_build_filename(gl_confdir, FILE_STATE, NULL);
|
||||
char *oldpath;
|
||||
GIOChannel *io;
|
||||
GError *err;
|
||||
char term = STATE_SEP;
|
||||
GList *ret = NULL;
|
||||
char *line, *ptr;
|
||||
gsize len, termpos;
|
||||
struct cf_torrentstate *ts;
|
||||
|
||||
err = NULL;
|
||||
io = g_io_channel_new_file(path, "r", &err);
|
||||
if(NULL != err) {
|
||||
if(!g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT))
|
||||
*errstr = g_strdup_printf(
|
||||
_("Failed to open the file %s for reading:\n%s"), path, err->message);
|
||||
else {
|
||||
g_error_free(err);
|
||||
err = NULL;
|
||||
oldpath = g_build_filename(gl_old_confdir, OLD_FILE_STATE, NULL);
|
||||
io = g_io_channel_new_file(oldpath, "r", &err);
|
||||
g_free(oldpath);
|
||||
}
|
||||
if(NULL != err)
|
||||
goto done;
|
||||
}
|
||||
g_io_channel_set_line_term(io, &term, 1);
|
||||
|
||||
err = NULL;
|
||||
for(;;) {
|
||||
assert(NULL == err);
|
||||
switch(g_io_channel_read_line(io, &line, &len, &termpos, &err)) {
|
||||
case G_IO_STATUS_ERROR:
|
||||
*errstr = g_strdup_printf(
|
||||
_("Error while reading from the file %s:\n%s"), path, err->message);
|
||||
goto done;
|
||||
case G_IO_STATUS_NORMAL:
|
||||
if(NULL != line) {
|
||||
if(g_utf8_validate(line, -1, NULL)) {
|
||||
ts = g_new0(struct cf_torrentstate, 1);
|
||||
ptr = line;
|
||||
while(NULL != (ptr = getstateval(ts, ptr)))
|
||||
;
|
||||
if(NULL != ts->ts_torrent && NULL != ts->ts_directory)
|
||||
ret = g_list_append(ret, ts);
|
||||
else
|
||||
cf_freestate(ts);
|
||||
}
|
||||
g_free(line);
|
||||
}
|
||||
break;
|
||||
case G_IO_STATUS_EOF:
|
||||
goto done;
|
||||
default:
|
||||
assert(!"unknown return code");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if(NULL != err)
|
||||
g_error_free(err);
|
||||
if(NULL != io)
|
||||
g_io_channel_unref(io);
|
||||
if(NULL != *errstr && NULL != ret) {
|
||||
g_list_foreach(ret, (GFunc)g_free, NULL);
|
||||
g_list_free(ret);
|
||||
ret = NULL;
|
||||
}
|
||||
g_free(path);
|
||||
return ret;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static char *
|
||||
getstateval(struct cf_torrentstate *state, char *line) {
|
||||
getstateval(benc_val_t *state, char *line) {
|
||||
char *start, *end;
|
||||
|
||||
/* skip any leading whitespace */
|
||||
@@ -459,103 +477,33 @@ getstateval(struct cf_torrentstate *state, char *line) {
|
||||
*end = '\0';
|
||||
|
||||
/* if it's a key we recognize then save the data */
|
||||
if(0 == strcmp(line, "torrent"))
|
||||
state->ts_torrent = g_strcompress(start);
|
||||
else if(0 == strcmp(line, "dir"))
|
||||
state->ts_directory = g_strcompress(start);
|
||||
else if(0 == strcmp(line, "paused"))
|
||||
state->ts_paused = strbool(start);
|
||||
if(0 == strcmp(line, "torrent") || 0 == strcmp(line, "dir") ||
|
||||
0 == strcmp(line, "paused")) {
|
||||
cf_benc_append(state, TYPE_STR, 6);
|
||||
state->val.l.vals[state->val.l.count-1].val.s.s = g_strdup(line);
|
||||
state->val.l.vals[state->val.l.count-1].val.s.i = strlen(line);
|
||||
if('p' == *line) {
|
||||
cf_benc_append(state, TYPE_INT, 6);
|
||||
state->val.l.vals[state->val.l.count-1].val.i = strbool(start);
|
||||
} else {
|
||||
cf_benc_append(state, TYPE_STR, 6);
|
||||
state->val.l.vals[state->val.l.count-1].val.s.s = g_strdup(start);
|
||||
state->val.l.vals[state->val.l.count-1].val.s.i = strlen(start);
|
||||
}
|
||||
}
|
||||
|
||||
/* return a pointer to just past the end of the value */
|
||||
return end + 1;
|
||||
}
|
||||
|
||||
gboolean
|
||||
cf_savestate(GList *torrents, char **errstr) {
|
||||
char *file = g_build_filename(gl_confdir, FILE_STATE, NULL);
|
||||
char *tmpfile = g_build_filename(gl_confdir, FILE_STATE_TMP, NULL);
|
||||
GIOChannel *io = NULL;
|
||||
GError *err;
|
||||
int fd;
|
||||
char *torrentfile, *torrentdir, *line;
|
||||
gsize written;
|
||||
gboolean paused;
|
||||
GIOStatus res;
|
||||
tr_stat_t *sb;
|
||||
tr_info_t *in;
|
||||
|
||||
void
|
||||
cf_savestate(benc_val_t *state, char **errstr) {
|
||||
*errstr = NULL;
|
||||
|
||||
if(0 > (fd = lockfile(tmpfile, errstr))) {
|
||||
g_free(errstr);
|
||||
*errstr = g_strdup_printf(_("Failed to open or lock the file %s:\n%s"),
|
||||
tmpfile, strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
|
||||
#ifdef NDEBUG
|
||||
ftruncate(fd, 0);
|
||||
#else
|
||||
assert(0 == ftruncate(fd, 0));
|
||||
#endif
|
||||
|
||||
io = g_io_channel_unix_new(fd);
|
||||
g_io_channel_set_close_on_unref(io, TRUE);
|
||||
|
||||
err = NULL;
|
||||
while(NULL != torrents) {
|
||||
sb = tr_torrentStat(torrents->data);
|
||||
in = tr_torrentInfo(torrents->data);
|
||||
paused = (TR_STATUS_INACTIVE & sb->status);
|
||||
torrentfile = g_strescape(in->torrent, "");
|
||||
torrentdir = g_strescape(tr_torrentGetFolder(torrents->data), "");
|
||||
/* g_strcompress */
|
||||
line = g_strdup_printf("torrent=\"%s\" dir=\"%s\" paused=\"%s\"%c",
|
||||
torrentfile, torrentdir, (paused ? "yes" : "no"),
|
||||
STATE_SEP);
|
||||
res = g_io_channel_write_chars(io, line, strlen(line), &written, &err);
|
||||
g_free(torrentfile);
|
||||
g_free(torrentdir);
|
||||
g_free(line);
|
||||
switch(res) {
|
||||
case G_IO_STATUS_ERROR:
|
||||
goto done;
|
||||
case G_IO_STATUS_NORMAL:
|
||||
break;
|
||||
default:
|
||||
assert(!"unknown return code");
|
||||
goto done;
|
||||
}
|
||||
torrents = torrents->next;
|
||||
}
|
||||
if(NULL != err ||
|
||||
G_IO_STATUS_ERROR == g_io_channel_shutdown(io, TRUE, &err)) {
|
||||
*errstr = g_strdup_printf(_("Error while writing to the file %s:\n%s"),
|
||||
tmpfile, err->message);
|
||||
g_error_free(err);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if(0 > rename(tmpfile, file)) {
|
||||
*errstr = g_strdup_printf(_("Failed to rename the file %s to %s:\n%s"),
|
||||
tmpfile, file, strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
g_free(file);
|
||||
g_free(tmpfile);
|
||||
if(NULL != io)
|
||||
g_io_channel_unref(io);
|
||||
|
||||
return NULL == *errstr;
|
||||
cf_writebenc(FILE_STATE, FILE_STATE_TMP, state, errstr);
|
||||
}
|
||||
|
||||
void
|
||||
cf_freestate(struct cf_torrentstate *state) {
|
||||
if(NULL != state->ts_torrent)
|
||||
g_free(state->ts_torrent);
|
||||
if(NULL != state->ts_directory)
|
||||
g_free(state->ts_directory);
|
||||
cf_freestate(benc_val_t *state) {
|
||||
tr_bencFree(state);
|
||||
g_free(state);
|
||||
}
|
||||
|
||||
23
gtk/conf.h
23
gtk/conf.h
@@ -28,12 +28,7 @@
|
||||
#define TG_CONF_H
|
||||
|
||||
#include "transmission.h"
|
||||
|
||||
struct cf_torrentstate {
|
||||
char *ts_torrent;
|
||||
char *ts_directory;
|
||||
gboolean ts_paused;
|
||||
};
|
||||
#include "bencode.h"
|
||||
|
||||
gboolean
|
||||
cf_init(const char *confdir, char **errstr);
|
||||
@@ -41,19 +36,19 @@ gboolean
|
||||
cf_lock(char **errstr);
|
||||
char *
|
||||
cf_sockname(void);
|
||||
gboolean
|
||||
void
|
||||
cf_loadprefs(char **errstr);
|
||||
const char *
|
||||
cf_getpref(const char *name);
|
||||
void
|
||||
cf_setpref(const char *name, const char *value);
|
||||
gboolean
|
||||
cf_saveprefs(char **errstr);
|
||||
GList *
|
||||
cf_loadstate(char **errstr);
|
||||
gboolean
|
||||
cf_savestate(GList *torrents, char **errstr);
|
||||
void
|
||||
cf_freestate(struct cf_torrentstate *state);
|
||||
cf_saveprefs(char **errstr);
|
||||
benc_val_t *
|
||||
cf_loadstate(char **errstr);
|
||||
void
|
||||
cf_savestate(benc_val_t *state, char **errstr);
|
||||
void
|
||||
cf_freestate(benc_val_t *state);
|
||||
|
||||
#endif /* TG_CONF_H */
|
||||
|
||||
@@ -45,13 +45,12 @@ struct prefdata {
|
||||
GtkSpinButton *ulimit;
|
||||
GtkFileChooser *dir;
|
||||
GtkWindow *parent;
|
||||
tr_handle_t *tr;
|
||||
TrBackend *back;
|
||||
};
|
||||
|
||||
struct addcb {
|
||||
add_torrent_func_t addfunc;
|
||||
add_torrents_func_t addfunc;
|
||||
GtkWindow *parent;
|
||||
torrents_added_func_t donefunc;
|
||||
void *data;
|
||||
gboolean autostart;
|
||||
gboolean usingaltdir;
|
||||
@@ -64,6 +63,8 @@ windclosed(GtkWidget *widget SHUTUP, gpointer gdata);
|
||||
static void
|
||||
clicklimitbox(GtkWidget *widget, gpointer gdata);
|
||||
static void
|
||||
freedata(gpointer gdata, GClosure *closure);
|
||||
static void
|
||||
clickdialog(GtkWidget *widget, int resp, gpointer gdata);
|
||||
static void
|
||||
autoclick(GtkWidget *widget, gpointer gdata);
|
||||
@@ -73,7 +74,7 @@ static void
|
||||
addresp(GtkWidget *widget, gint resp, gpointer gdata);
|
||||
|
||||
void
|
||||
makeprefwindow(GtkWindow *parent, tr_handle_t *tr, gboolean *opened) {
|
||||
makeprefwindow(GtkWindow *parent, TrBackend *back, gboolean *opened) {
|
||||
char *title = g_strdup_printf(_("%s Preferences"), g_get_application_name());
|
||||
GtkWidget *wind = gtk_dialog_new_with_buttons(title, parent,
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
|
||||
@@ -119,7 +120,8 @@ makeprefwindow(GtkWindow *parent, tr_handle_t *tr, gboolean *opened) {
|
||||
data->ulimit = GTK_SPIN_BUTTON(lim[1].num);
|
||||
data->dir = GTK_FILE_CHOOSER(dirstr);
|
||||
data->parent = parent;
|
||||
data->tr = tr;
|
||||
data->back = back;
|
||||
g_object_ref(G_OBJECT(back));
|
||||
|
||||
#define RN(n) (n), (n) + 1
|
||||
|
||||
@@ -175,7 +177,7 @@ makeprefwindow(GtkWindow *parent, tr_handle_t *tr, gboolean *opened) {
|
||||
|
||||
gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(wind)->vbox), table);
|
||||
g_signal_connect_data(wind, "response", G_CALLBACK(clickdialog),
|
||||
data, (GClosureNotify)g_free, 0);
|
||||
data, freedata, 0);
|
||||
g_signal_connect(wind, "destroy", G_CALLBACK(windclosed), opened);
|
||||
gtk_widget_show_all(wind);
|
||||
}
|
||||
@@ -197,6 +199,14 @@ clicklimitbox(GtkWidget *widget, gpointer gdata) {
|
||||
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
|
||||
}
|
||||
|
||||
static void
|
||||
freedata(gpointer gdata, GClosure *closure SHUTUP) {
|
||||
struct prefdata *data = gdata;
|
||||
|
||||
g_object_unref(G_OBJECT(data->back));
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
static void
|
||||
clickdialog(GtkWidget *widget, int resp, gpointer gdata) {
|
||||
struct prefdata *data = gdata;
|
||||
@@ -244,15 +254,17 @@ clickdialog(GtkWidget *widget, int resp, gpointer gdata) {
|
||||
cf_setpref(PREF_UPLIMIT, strnum);
|
||||
|
||||
/* save prefs */
|
||||
if(!cf_saveprefs(&errstr)) {
|
||||
cf_saveprefs(&errstr);
|
||||
if(NULL != errstr) {
|
||||
errmsg(data->parent, "%s", errstr);
|
||||
g_free(strnum);
|
||||
g_free(errstr);
|
||||
}
|
||||
|
||||
/* XXX would be nice to have errno strings, are they printed to stdout? */
|
||||
tr_setBindPort(data->tr, gtk_spin_button_get_value_as_int(data->port));
|
||||
setlimit(data->tr);
|
||||
tr_setBindPort(tr_backend_handle(data->back),
|
||||
gtk_spin_button_get_value_as_int(data->port));
|
||||
setlimit(data->back);
|
||||
}
|
||||
|
||||
if(GTK_RESPONSE_APPLY != resp)
|
||||
@@ -260,7 +272,7 @@ clickdialog(GtkWidget *widget, int resp, gpointer gdata) {
|
||||
}
|
||||
|
||||
void
|
||||
setlimit(tr_handle_t *tr) {
|
||||
setlimit(TrBackend *back) {
|
||||
struct { void (*func)(tr_handle_t*, int);
|
||||
const char *use; const char *num; gboolean defuse; long def; } lim[] = {
|
||||
{tr_setDownloadLimit, PREF_USEDOWNLIMIT, PREF_DOWNLIMIT,
|
||||
@@ -270,6 +282,7 @@ setlimit(tr_handle_t *tr) {
|
||||
};
|
||||
const char *pref;
|
||||
unsigned int ii;
|
||||
tr_handle_t *tr = tr_backend_handle(back);
|
||||
|
||||
for(ii = 0; ii < ALEN(lim); ii++) {
|
||||
pref = cf_getpref(lim[ii].use);
|
||||
@@ -283,8 +296,7 @@ setlimit(tr_handle_t *tr) {
|
||||
}
|
||||
|
||||
void
|
||||
makeaddwind(GtkWindow *parent, add_torrent_func_t addfunc,
|
||||
torrents_added_func_t donefunc, void *cbdata) {
|
||||
makeaddwind(GtkWindow *parent, add_torrents_func_t addfunc, void *cbdata) {
|
||||
GtkWidget *wind = gtk_file_chooser_dialog_new(_("Add a Torrent"), parent,
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
|
||||
@@ -303,7 +315,6 @@ makeaddwind(GtkWindow *parent, add_torrent_func_t addfunc,
|
||||
|
||||
data->addfunc = addfunc;
|
||||
data->parent = parent;
|
||||
data->donefunc = donefunc;
|
||||
data->data = cbdata;
|
||||
data->autostart = TRUE;
|
||||
data->usingaltdir = FALSE;
|
||||
@@ -360,22 +371,24 @@ static void
|
||||
addresp(GtkWidget *widget, gint resp, gpointer gdata) {
|
||||
struct addcb *data = gdata;
|
||||
GSList *files, *ii;
|
||||
gboolean added = FALSE;
|
||||
char *dir = NULL;
|
||||
GList *stupidgtk;
|
||||
gboolean paused;
|
||||
char *dir;
|
||||
|
||||
if(GTK_RESPONSE_ACCEPT == resp) {
|
||||
dir = NULL;
|
||||
if(data->usingaltdir)
|
||||
dir = gtk_file_chooser_get_filename(data->altdir);
|
||||
files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(widget));
|
||||
stupidgtk = NULL;
|
||||
for(ii = files; NULL != ii; ii = ii->next)
|
||||
if(data->addfunc(data->data, ii->data, dir,
|
||||
/* XXX need to group errors here */
|
||||
!data->autostart, NULL))
|
||||
added = TRUE;
|
||||
if(added)
|
||||
data->donefunc(data->data);
|
||||
stupidgtk = g_list_append(stupidgtk, ii->data);
|
||||
paused = !data->autostart;
|
||||
data->addfunc(data->data, NULL, stupidgtk, dir, &paused);
|
||||
if(NULL != dir)
|
||||
g_free(dir);
|
||||
g_slist_free(files);
|
||||
freestrlist(stupidgtk);
|
||||
}
|
||||
|
||||
gtk_widget_destroy(widget);
|
||||
@@ -418,7 +431,7 @@ addresp(GtkWidget *widget, gint resp, gpointer gdata) {
|
||||
} while(0)
|
||||
|
||||
void
|
||||
makeinfowind(GtkWindow *parent, tr_torrent_t *tor) {
|
||||
makeinfowind(GtkWindow *parent, TrTorrent *tor) {
|
||||
tr_stat_t *sb;
|
||||
tr_info_t *in;
|
||||
GtkWidget *wind, *label;
|
||||
@@ -427,8 +440,10 @@ makeinfowind(GtkWindow *parent, tr_torrent_t *tor) {
|
||||
const int rowcount = 14;
|
||||
GtkWidget *table = gtk_table_new(rowcount, 2, FALSE);
|
||||
|
||||
sb = tr_torrentStat(tor);
|
||||
in = tr_torrentInfo(tor);
|
||||
/* XXX should use model and update this window regularly */
|
||||
|
||||
sb = tr_torrent_stat(tor);
|
||||
in = tr_torrent_info(tor);
|
||||
str = g_strdup_printf(_("%s Properties"), in->name);
|
||||
wind = gtk_dialog_new_with_buttons(str, parent,
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
|
||||
@@ -474,7 +489,7 @@ makeinfowind(GtkWindow *parent, tr_torrent_t *tor) {
|
||||
|
||||
INFOSEP(table, ii);
|
||||
|
||||
INFOLINE(table, ii, _("Directory:"), tr_torrentGetFolder(tor));
|
||||
INFOLINE(table, ii, _("Directory:"), tr_torrentGetFolder(tr_torrent_handle(tor)));
|
||||
INFOLINEA(table, ii, _("Downloaded:"), readablesize(sb->downloaded));
|
||||
INFOLINEA(table, ii, _("Uploaded:"), readablesize(sb->uploaded));
|
||||
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
#ifndef TG_PREFS_H
|
||||
#define TG_PREFS_H
|
||||
|
||||
#include "transmission.h"
|
||||
#include "tr_backend.h"
|
||||
#include "tr_torrent.h"
|
||||
#include "util.h"
|
||||
|
||||
/* macros for names of prefs we use */
|
||||
@@ -45,19 +46,18 @@
|
||||
#define DEF_USEUPLIMIT TRUE
|
||||
|
||||
void
|
||||
makeprefwindow(GtkWindow *parent, tr_handle_t *tr, gboolean *opened);
|
||||
makeprefwindow(GtkWindow *parent, TrBackend *back, gboolean *opened);
|
||||
|
||||
/* set the upload limit based on saved prefs */
|
||||
void
|
||||
setlimit(tr_handle_t *tr);
|
||||
setlimit(TrBackend *back);
|
||||
|
||||
/* show the "add a torrent" dialog */
|
||||
void
|
||||
makeaddwind(GtkWindow *parent, add_torrent_func_t addfunc,
|
||||
torrents_added_func_t donefunc, void *cbdata);
|
||||
makeaddwind(GtkWindow *parent, add_torrents_func_t addfunc, void *cbdata);
|
||||
|
||||
/* show the info window for a torrent */
|
||||
void
|
||||
makeinfowind(GtkWindow *parent, tr_torrent_t *tor);
|
||||
makeinfowind(GtkWindow *parent, TrTorrent *tor);
|
||||
|
||||
#endif /* TG_PREFS_H */
|
||||
|
||||
45
gtk/ipc.c
45
gtk/ipc.c
@@ -37,7 +37,9 @@
|
||||
#include <gtk/gtk.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#include "transmission.h"
|
||||
#include "bencode.h"
|
||||
|
||||
#include "conf.h"
|
||||
#include "io.h"
|
||||
#include "ipc.h"
|
||||
@@ -54,8 +56,7 @@ enum contype { CON_SERV, CON_ADDFILE };
|
||||
|
||||
struct constate_serv {
|
||||
void *wind;
|
||||
add_torrent_func_t addfunc;
|
||||
torrents_added_func_t donefunc;
|
||||
add_torrents_func_t addfunc;
|
||||
void *cbdata;
|
||||
};
|
||||
|
||||
@@ -82,8 +83,7 @@ struct constate {
|
||||
};
|
||||
|
||||
void
|
||||
ipc_socket_setup(void *parent, add_torrent_func_t addfunc,
|
||||
torrents_added_func_t donefunc, void *cbdata);
|
||||
ipc_socket_setup(void *parent, add_torrents_func_t addfunc, void *cbdata);
|
||||
gboolean
|
||||
ipc_sendfiles_blocking(GList *files);
|
||||
static void
|
||||
@@ -126,8 +126,7 @@ static const struct handlerdef gl_funcs_addfile[] = {
|
||||
static char *gl_sockpath = NULL;
|
||||
|
||||
void
|
||||
ipc_socket_setup(void *parent, add_torrent_func_t addfunc,
|
||||
torrents_added_func_t donefunc, void *cbdata) {
|
||||
ipc_socket_setup(void *parent, add_torrents_func_t addfunc, void *cbdata) {
|
||||
struct constate *con;
|
||||
|
||||
con = g_new0(struct constate, 1);
|
||||
@@ -137,7 +136,6 @@ ipc_socket_setup(void *parent, add_torrent_func_t addfunc,
|
||||
con->type = CON_SERV;
|
||||
con->u.serv.wind = parent;
|
||||
con->u.serv.addfunc = addfunc;
|
||||
con->u.serv.donefunc = donefunc;
|
||||
con->u.serv.cbdata = cbdata;
|
||||
|
||||
serv_bind(con);
|
||||
@@ -378,33 +376,18 @@ all_io_closed(GSource *source SHUTUP, void *vdata) {
|
||||
static void
|
||||
srv_addfile(struct constate *con, const char *name SHUTUP, benc_val_t *val) {
|
||||
struct constate_serv *srv = &con->u.serv;
|
||||
GList *errs = NULL;
|
||||
char *str;
|
||||
GList *files;
|
||||
int ii;
|
||||
gboolean added;
|
||||
benc_val_t *file;
|
||||
|
||||
if(TYPE_LIST == val->type) {
|
||||
added = FALSE;
|
||||
for(ii = 0; ii < val->val.l.count; ii++) {
|
||||
file = &val->val.l.vals[ii];
|
||||
if(TYPE_STR == file->type && g_utf8_validate(file->val.s.s, -1, NULL)) {
|
||||
/* XXX somehow escape invalid utf-8 */
|
||||
added = TRUE;
|
||||
srv->addfunc(srv->cbdata, file->val.s.s, NULL, FALSE, &errs);
|
||||
}
|
||||
}
|
||||
|
||||
if(NULL != errs) {
|
||||
str = joinstrlist(errs, "\n");
|
||||
errmsg(srv->wind, ngettext("Failed to load the torrent file %s",
|
||||
"Failed to load the torrent files:\n%s",
|
||||
g_list_length(errs)), str);
|
||||
freestrlist(errs);
|
||||
g_free(str);
|
||||
}
|
||||
if(added)
|
||||
srv->donefunc(srv->cbdata);
|
||||
files = NULL;
|
||||
for(ii = 0; ii < val->val.l.count; ii++)
|
||||
if(TYPE_STR == val->val.l.vals[ii].type &&
|
||||
/* XXX somehow escape invalid utf-8 */
|
||||
g_utf8_validate(val->val.l.vals[ii].val.s.s, -1, NULL))
|
||||
files = g_list_append(files, val->val.l.vals[ii].val.s.s);
|
||||
srv->addfunc(srv->cbdata, NULL, files, NULL, NULL);
|
||||
g_list_free(files);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,8 +30,7 @@
|
||||
#include "util.h"
|
||||
|
||||
void
|
||||
ipc_socket_setup(void *wind, add_torrent_func_t addfunc,
|
||||
torrents_added_func_t donefunc, void *cbdata);
|
||||
ipc_socket_setup(void *wind, add_torrents_func_t addfunc, void *cbdata);
|
||||
|
||||
gboolean
|
||||
ipc_sendfiles_blocking(GList *files);
|
||||
|
||||
630
gtk/main.c
630
gtk/main.c
File diff suppressed because it is too large
Load Diff
238
gtk/tr_backend.c
Normal file
238
gtk/tr_backend.c
Normal file
@@ -0,0 +1,238 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#define TR_WANT_TORRENT_PRIVATE
|
||||
|
||||
#include "transmission.h"
|
||||
#include "bencode.h"
|
||||
|
||||
#include "conf.h"
|
||||
#include "tr_backend.h"
|
||||
#include "tr_torrent.h"
|
||||
#include "util.h"
|
||||
|
||||
/*
|
||||
enum {
|
||||
TR_BACKEND_HANDLE = 1,
|
||||
};
|
||||
*/
|
||||
|
||||
static void
|
||||
tr_backend_init(GTypeInstance *instance, gpointer g_class);
|
||||
static void
|
||||
tr_backend_set_property(GObject *object, guint property_id,
|
||||
const GValue *value, GParamSpec *pspec);
|
||||
static void
|
||||
tr_backend_get_property(GObject *object, guint property_id,
|
||||
GValue *value, GParamSpec *pspec);
|
||||
static void
|
||||
tr_backend_class_init(gpointer g_class, gpointer g_class_data);
|
||||
static void
|
||||
tr_backend_dispose(GObject *obj);
|
||||
static void
|
||||
tr_backend_finalize(GObject *obj);
|
||||
static void
|
||||
tr_backend_torrent_finalized(gpointer gdata, GObject *tor);
|
||||
|
||||
GType
|
||||
tr_backend_get_type(void) {
|
||||
static GType type = 0;
|
||||
|
||||
if(0 == type) {
|
||||
static const GTypeInfo info = {
|
||||
sizeof (TrBackendClass),
|
||||
NULL, /* base_init */
|
||||
NULL, /* base_finalize */
|
||||
tr_backend_class_init, /* class_init */
|
||||
NULL, /* class_finalize */
|
||||
NULL, /* class_data */
|
||||
sizeof (TrBackend),
|
||||
0, /* n_preallocs */
|
||||
tr_backend_init, /* instance_init */
|
||||
NULL,
|
||||
};
|
||||
type = g_type_register_static(G_TYPE_OBJECT, "TrBackendType", &info, 0);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
static void
|
||||
tr_backend_class_init(gpointer g_class, gpointer g_class_data SHUTUP) {
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS(g_class);
|
||||
//GParamSpec *pspec;
|
||||
|
||||
gobject_class->set_property = tr_backend_set_property;
|
||||
gobject_class->get_property = tr_backend_get_property;
|
||||
gobject_class->dispose = tr_backend_dispose;
|
||||
gobject_class->finalize = tr_backend_finalize;
|
||||
|
||||
/*
|
||||
pspec = g_param_spec_pointer("backend-handle", _("Backend handle"),
|
||||
_("Backend handle from libtransmission"),
|
||||
G_PARAM_READWRITE);
|
||||
g_object_class_install_property(gobject_class, TR_BACKEND_HANDLE, pspec);
|
||||
*/
|
||||
}
|
||||
|
||||
static void
|
||||
tr_backend_init(GTypeInstance *instance, gpointer g_class SHUTUP) {
|
||||
TrBackend *self = (TrBackend *)instance;
|
||||
|
||||
self->handle = tr_init();
|
||||
self->disposed = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
tr_backend_set_property(GObject *object, guint property_id,
|
||||
const GValue *value SHUTUP, GParamSpec *pspec) {
|
||||
TrBackend *self = (TrBackend*)object;
|
||||
|
||||
if(self->disposed)
|
||||
return;
|
||||
|
||||
switch(property_id) {
|
||||
/*
|
||||
case TR_BACKEND_HANDLE:
|
||||
g_assert(NULL == self->handle);
|
||||
self->handle = g_value_get_pointer(value);
|
||||
break;
|
||||
*/
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tr_backend_get_property(GObject *object, guint property_id,
|
||||
GValue *value SHUTUP, GParamSpec *pspec) {
|
||||
TrBackend *self = (TrBackend*)object;
|
||||
|
||||
if(self->disposed)
|
||||
return;
|
||||
|
||||
switch(property_id) {
|
||||
/*
|
||||
case TR_BACKEND_HANDLE:
|
||||
g_value_set_pointer(value, self->handle);
|
||||
break;
|
||||
*/
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tr_backend_dispose(GObject *obj) {
|
||||
GObjectClass *parent = g_type_class_peek(g_type_parent(TR_BACKEND_TYPE));
|
||||
TrBackend *self = (TrBackend*)obj;
|
||||
GList *ii;
|
||||
|
||||
if(self->disposed)
|
||||
return;
|
||||
self->disposed = TRUE;
|
||||
|
||||
fprintf(stderr, "back dispose %p\n", self);
|
||||
|
||||
if(NULL != self->torrents) {
|
||||
for(ii = self->torrents; NULL != ii; ii = ii->next)
|
||||
g_object_weak_unref(ii->data, tr_backend_torrent_finalized, self);
|
||||
g_list_free(self->torrents);
|
||||
self->torrents = NULL;
|
||||
}
|
||||
|
||||
/* Chain up to the parent class */
|
||||
parent->dispose(obj);
|
||||
}
|
||||
|
||||
static void
|
||||
tr_backend_finalize(GObject *obj) {
|
||||
GObjectClass *parent = g_type_class_peek(g_type_parent(TR_BACKEND_TYPE));
|
||||
TrBackend *self = (TrBackend *)obj;
|
||||
|
||||
fprintf(stderr, "back finalize %p\n", self);
|
||||
|
||||
if(NULL != self->handle)
|
||||
tr_close(self->handle);
|
||||
|
||||
/* Chain up to the parent class */
|
||||
parent->finalize(obj);
|
||||
}
|
||||
|
||||
TrBackend *
|
||||
tr_backend_new(void) {
|
||||
return g_object_new(TR_BACKEND_TYPE, NULL);
|
||||
}
|
||||
|
||||
tr_handle_t *
|
||||
tr_backend_handle(TrBackend *back) {
|
||||
TR_IS_BACKEND(back);
|
||||
|
||||
return back->handle;
|
||||
}
|
||||
|
||||
void
|
||||
tr_backend_save_state(TrBackend *back, char **errstr) {
|
||||
benc_val_t state;
|
||||
int ii;
|
||||
GList *jj;
|
||||
|
||||
TR_IS_BACKEND(back);
|
||||
|
||||
bzero(&state, sizeof(state));
|
||||
state.type = TYPE_LIST;
|
||||
state.val.l.alloc = state.val.l.count = g_list_length(back->torrents);
|
||||
state.val.l.vals = g_new0(benc_val_t, state.val.l.alloc);
|
||||
|
||||
for(ii = 0, jj = back->torrents; NULL != jj; ii++, jj = jj->next)
|
||||
tr_torrent_get_state(jj->data, state.val.l.vals + ii);
|
||||
|
||||
cf_savestate(&state, errstr);
|
||||
tr_bencFree(&state);
|
||||
}
|
||||
|
||||
GList *
|
||||
tr_backend_load_state(TrBackend *back, benc_val_t *state, GList **errors) {
|
||||
GList *ret = NULL;
|
||||
int ii;
|
||||
TrTorrent *tor;
|
||||
char *errstr;
|
||||
|
||||
TR_IS_BACKEND(back);
|
||||
|
||||
if(TYPE_LIST != state->type)
|
||||
return NULL;
|
||||
|
||||
for(ii = 0; ii < state->val.l.count; ii++) {
|
||||
errstr = NULL;
|
||||
tor = tr_torrent_new_with_state(G_OBJECT(back), state->val.l.vals + ii,
|
||||
&errstr);
|
||||
if(NULL != errstr)
|
||||
*errors = g_list_append(*errors, errstr);
|
||||
if(NULL != tor)
|
||||
ret = g_list_append(ret, tor);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
tr_backend_add_torrent(TrBackend *back, GObject *tor) {
|
||||
TR_IS_BACKEND(back);
|
||||
TR_IS_TORRENT(tor);
|
||||
|
||||
g_object_weak_ref(tor, tr_backend_torrent_finalized, back);
|
||||
back->torrents = g_list_append(back->torrents, tor);
|
||||
}
|
||||
|
||||
static void
|
||||
tr_backend_torrent_finalized(gpointer gdata, GObject *tor) {
|
||||
TrBackend *back = gdata;
|
||||
|
||||
TR_IS_BACKEND(back);
|
||||
|
||||
back->torrents = g_list_remove(back->torrents, tor);
|
||||
}
|
||||
56
gtk/tr_backend.h
Normal file
56
gtk/tr_backend.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#ifndef TR_BACKEND_H
|
||||
#define TR_BACKEND_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "transmission.h"
|
||||
#include "bencode.h"
|
||||
|
||||
#define TR_BACKEND_TYPE (tr_backend_get_type ())
|
||||
#define TR_BACKEND(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST ((obj), TR_BACKEND_TYPE, TrBackend))
|
||||
#define TR_BACKEND_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST ((klass), TR_BACKEND_TYPE, TrBackendClass))
|
||||
#define TR_IS_BACKEND(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), TR_BACKEND_TYPE))
|
||||
#define TR_IS_BACKEND_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE ((klass), TR_BACKEND_TYPE))
|
||||
#define TR_BACKEND_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS ((obj), TR_BACKEND_TYPE, TrBackendClass))
|
||||
|
||||
typedef struct _TrBackend TrBackend;
|
||||
typedef struct _TrBackendClass TrBackendClass;
|
||||
|
||||
/* treat the contents of this structure as private */
|
||||
struct _TrBackend {
|
||||
GObject parent;
|
||||
tr_handle_t *handle;
|
||||
GList *torrents;
|
||||
gboolean disposed;
|
||||
};
|
||||
|
||||
struct _TrBackendClass {
|
||||
GObjectClass parent;
|
||||
};
|
||||
|
||||
GType
|
||||
tr_backend_get_type(void);
|
||||
|
||||
TrBackend *
|
||||
tr_backend_new(void);
|
||||
|
||||
tr_handle_t *
|
||||
tr_backend_handle(TrBackend *back);
|
||||
|
||||
void
|
||||
tr_backend_save_state(TrBackend *back, char **errstr);
|
||||
|
||||
GList *
|
||||
tr_backend_load_state(TrBackend *back, benc_val_t *state, GList **errors);
|
||||
|
||||
#ifdef TR_WANT_BACKEND_PRIVATE
|
||||
void
|
||||
tr_backend_add_torrent(TrBackend *back, GObject *tor);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "trcellrenderertorrent.h"
|
||||
#include "tr_cell_renderer_torrent.h"
|
||||
#include "util.h"
|
||||
|
||||
enum { PROP_0, PROP_VALUE, PROP_TEXT, PROP_LABEL };
|
||||
368
gtk/tr_torrent.c
Normal file
368
gtk/tr_torrent.c
Normal file
@@ -0,0 +1,368 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#define TR_WANT_BACKEND_PRIVATE
|
||||
|
||||
#include "transmission.h"
|
||||
#include "bencode.h"
|
||||
|
||||
#include "tr_backend.h"
|
||||
#include "tr_torrent.h"
|
||||
#include "util.h"
|
||||
|
||||
enum {
|
||||
TR_TORRENT_HANDLE = 1,
|
||||
TR_TORRENT_BACKEND,
|
||||
TR_TORRENT_DIR,
|
||||
TR_TORRENT_PAUSED,
|
||||
};
|
||||
|
||||
static void
|
||||
tr_torrent_init(GTypeInstance *instance, gpointer g_class);
|
||||
static void
|
||||
tr_torrent_set_property(GObject *object, guint property_id,
|
||||
const GValue *value, GParamSpec *pspec);
|
||||
static void
|
||||
tr_torrent_get_property(GObject *object, guint property_id,
|
||||
GValue *value, GParamSpec *pspec);
|
||||
static void
|
||||
tr_torrent_class_init(gpointer g_class, gpointer g_class_data);
|
||||
static void
|
||||
tr_torrent_dispose(GObject *obj);
|
||||
static void
|
||||
tr_torrent_finalize(GObject *obj);
|
||||
static void
|
||||
tr_torrent_set_folder(TrTorrent *tor);
|
||||
static gboolean
|
||||
tr_torrent_paused(TrTorrent *tor);
|
||||
|
||||
GType
|
||||
tr_torrent_get_type(void) {
|
||||
static GType type = 0;
|
||||
|
||||
if(0 == type) {
|
||||
static const GTypeInfo info = {
|
||||
sizeof (TrTorrentClass),
|
||||
NULL, /* base_init */
|
||||
NULL, /* base_finalize */
|
||||
tr_torrent_class_init, /* class_init */
|
||||
NULL, /* class_finalize */
|
||||
NULL, /* class_data */
|
||||
sizeof (TrTorrent),
|
||||
0, /* n_preallocs */
|
||||
tr_torrent_init, /* instance_init */
|
||||
NULL,
|
||||
};
|
||||
type = g_type_register_static(G_TYPE_OBJECT, "TrTorrentType", &info, 0);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
static void
|
||||
tr_torrent_class_init(gpointer g_class, gpointer g_class_data SHUTUP) {
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS(g_class);
|
||||
//TrTorrentClass *klass = TR_TORRENT_CLASS(g_class);
|
||||
GParamSpec *pspec;
|
||||
|
||||
gobject_class->set_property = tr_torrent_set_property;
|
||||
gobject_class->get_property = tr_torrent_get_property;
|
||||
gobject_class->dispose = tr_torrent_dispose;
|
||||
gobject_class->finalize = tr_torrent_finalize;
|
||||
|
||||
pspec = g_param_spec_pointer("torrent-handle", "Torrent handle",
|
||||
"Torrent handle from libtransmission",
|
||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
|
||||
g_object_class_install_property(gobject_class, TR_TORRENT_HANDLE, pspec);
|
||||
|
||||
pspec = g_param_spec_object("backend", "Backend",
|
||||
"Libtransmission backend object",
|
||||
TR_BACKEND_TYPE,
|
||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
|
||||
g_object_class_install_property(gobject_class, TR_TORRENT_BACKEND, pspec);
|
||||
|
||||
pspec = g_param_spec_string("download-directory", "Download directory",
|
||||
"Directory to download files to", NULL,
|
||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
|
||||
g_object_class_install_property(gobject_class, TR_TORRENT_DIR, pspec);
|
||||
|
||||
pspec = g_param_spec_boolean("paused", "Paused",
|
||||
"Is the torrent paused or running", TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
g_object_class_install_property(gobject_class, TR_TORRENT_PAUSED, pspec);
|
||||
}
|
||||
|
||||
static void
|
||||
tr_torrent_init(GTypeInstance *instance, gpointer g_class SHUTUP) {
|
||||
TrTorrent *self = (TrTorrent *)instance;
|
||||
|
||||
self->handle = NULL;
|
||||
self->back = NULL;
|
||||
self->dir = NULL;
|
||||
self->disposed = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
tr_torrent_set_property(GObject *object, guint property_id,
|
||||
const GValue *value, GParamSpec *pspec) {
|
||||
TrTorrent *self = (TrTorrent*)object;
|
||||
|
||||
if(self->disposed)
|
||||
return;
|
||||
|
||||
switch(property_id) {
|
||||
case TR_TORRENT_HANDLE:
|
||||
g_assert(NULL == self->handle);
|
||||
self->handle = g_value_get_pointer(value);
|
||||
if(NULL != self->handle && NULL != self->dir)
|
||||
tr_torrent_set_folder(self);
|
||||
break;
|
||||
case TR_TORRENT_BACKEND:
|
||||
g_assert(NULL == self->back);
|
||||
self->back = g_object_ref(g_value_get_object(value));
|
||||
break;
|
||||
case TR_TORRENT_DIR:
|
||||
g_assert(NULL == self->dir);
|
||||
self->dir = g_value_dup_string(value);
|
||||
if(NULL != self->handle && NULL != self->dir)
|
||||
tr_torrent_set_folder(self);
|
||||
break;
|
||||
case TR_TORRENT_PAUSED:
|
||||
g_assert(NULL != self->handle);
|
||||
if(tr_torrent_paused(self) != g_value_get_boolean(value))
|
||||
(g_value_get_boolean(value) ? tr_torrentStop : tr_torrentStart)
|
||||
(self->handle);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tr_torrent_get_property(GObject *object, guint property_id,
|
||||
GValue *value, GParamSpec *pspec) {
|
||||
TrTorrent *self = (TrTorrent*)object;
|
||||
|
||||
if(self->disposed)
|
||||
return;
|
||||
|
||||
switch(property_id) {
|
||||
case TR_TORRENT_HANDLE:
|
||||
g_value_set_pointer(value, self->handle);
|
||||
break;
|
||||
case TR_TORRENT_BACKEND:
|
||||
g_value_set_object(value, self->back);
|
||||
break;
|
||||
case TR_TORRENT_DIR:
|
||||
g_value_set_string(value, (NULL != self->dir ? self->dir :
|
||||
tr_torrentGetFolder(self->handle)));
|
||||
break;
|
||||
case TR_TORRENT_PAUSED:
|
||||
g_value_set_boolean(value, tr_torrent_paused(self));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tr_torrent_dispose(GObject *obj) {
|
||||
GObjectClass *parent = g_type_class_peek(g_type_parent(TR_TORRENT_TYPE));
|
||||
TrTorrent *self = (TrTorrent*)obj;
|
||||
|
||||
if(self->disposed)
|
||||
return;
|
||||
self->disposed = TRUE;
|
||||
|
||||
fprintf(stderr, "tor dispose %p\n", self);
|
||||
|
||||
if(NULL != self->handle) {
|
||||
if(!tr_torrent_paused(self))
|
||||
tr_torrentStop(self->handle);
|
||||
tr_torrentClose(tr_backend_handle(TR_BACKEND(self->back)), self->handle);
|
||||
self->handle = NULL;
|
||||
}
|
||||
|
||||
if(NULL != self->back) {
|
||||
g_object_unref(self->back);
|
||||
self->back = NULL;
|
||||
}
|
||||
|
||||
/* Chain up to the parent class */
|
||||
parent->dispose(obj);
|
||||
}
|
||||
|
||||
static void
|
||||
tr_torrent_finalize(GObject *obj) {
|
||||
GObjectClass *parent = g_type_class_peek(g_type_parent(TR_TORRENT_TYPE));
|
||||
TrTorrent *self = (TrTorrent *)obj;
|
||||
|
||||
fprintf(stderr, "tor finalize %p\n", self);
|
||||
|
||||
/* Chain up to the parent class */
|
||||
parent->finalize(obj);
|
||||
}
|
||||
|
||||
tr_torrent_t *
|
||||
tr_torrent_handle(TrTorrent *tor) {
|
||||
TR_IS_TORRENT(tor);
|
||||
|
||||
if(tor->disposed)
|
||||
return NULL;
|
||||
|
||||
return tor->handle;
|
||||
}
|
||||
|
||||
tr_stat_t *
|
||||
tr_torrent_stat(TrTorrent *tor) {
|
||||
TR_IS_TORRENT(tor);
|
||||
|
||||
if(tor->disposed)
|
||||
return NULL;
|
||||
|
||||
return tr_torrentStat(tor->handle);
|
||||
}
|
||||
|
||||
tr_info_t *
|
||||
tr_torrent_info(TrTorrent *tor) {
|
||||
TR_IS_TORRENT(tor);
|
||||
|
||||
if(tor->disposed)
|
||||
return NULL;
|
||||
|
||||
return tr_torrentInfo(tor->handle);
|
||||
}
|
||||
|
||||
TrTorrent *
|
||||
tr_torrent_new(GObject *backend, const char *torrent, const char *dir,
|
||||
gboolean *paused, char **err) {
|
||||
TrTorrent *ret;
|
||||
tr_torrent_t *handle;
|
||||
int errcode;
|
||||
|
||||
TR_IS_BACKEND(backend);
|
||||
g_assert(NULL != dir);
|
||||
|
||||
*err = NULL;
|
||||
|
||||
errcode = -1;
|
||||
handle = tr_torrentInit(tr_backend_handle(TR_BACKEND(backend)),
|
||||
torrent, &errcode);
|
||||
if(NULL == handle) {
|
||||
switch(errcode) {
|
||||
case TR_EINVALID:
|
||||
*err = g_strdup_printf(_("%s: not a valid torrent file"), torrent);
|
||||
break;
|
||||
case TR_EDUPLICATE:
|
||||
*err = g_strdup_printf(_("%s: torrent is already open"), torrent);
|
||||
break;
|
||||
default:
|
||||
*err = g_strdup(torrent);
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = g_object_new(TR_TORRENT_TYPE, "torrent-handle", handle,
|
||||
"backend", backend, "download-directory", dir, NULL);
|
||||
tr_backend_add_torrent(TR_BACKEND(backend), G_OBJECT(ret));
|
||||
|
||||
g_object_set(ret, "paused", (NULL == paused ? FALSE : *paused), NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
TrTorrent *
|
||||
tr_torrent_new_with_state(GObject *backend, benc_val_t *state, char **err) {
|
||||
int ii;
|
||||
benc_val_t *name, *data;
|
||||
char *torrent, *dir;
|
||||
gboolean hadpaused, paused;
|
||||
|
||||
*err = NULL;
|
||||
|
||||
if(TYPE_DICT != state->type)
|
||||
return NULL;
|
||||
|
||||
torrent = dir = NULL;
|
||||
hadpaused = FALSE;
|
||||
|
||||
for(ii = 0; ii + 1 < state->val.l.count; ii += 2) {
|
||||
name = state->val.l.vals + ii;
|
||||
data = state->val.l.vals + ii + 1;
|
||||
if(TYPE_STR == name->type &&
|
||||
(TYPE_STR == data->type || TYPE_INT == data->type)) {
|
||||
if(0 == strcmp("torrent", name->val.s.s))
|
||||
torrent = data->val.s.s;
|
||||
else if(0 == strcmp("dir", name->val.s.s))
|
||||
dir = data->val.s.s;
|
||||
else if(0 == strcmp("paused", name->val.s.s)) {
|
||||
hadpaused = TRUE;
|
||||
paused = (data->val.i ? TRUE : FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(NULL == torrent || NULL == dir)
|
||||
return NULL;
|
||||
|
||||
return tr_torrent_new(backend, torrent, dir,
|
||||
(hadpaused ? &paused : NULL), err);
|
||||
}
|
||||
|
||||
void
|
||||
tr_torrent_get_state(TrTorrent *tor, benc_val_t *state) {
|
||||
tr_info_t *in = tr_torrentInfo(tor->handle);
|
||||
const char *strs[] = {
|
||||
"torrent", in->torrent, "dir", tr_torrentGetFolder(tor->handle), "paused",
|
||||
};
|
||||
unsigned int ii;
|
||||
const unsigned int len = 6;
|
||||
|
||||
TR_IS_TORRENT(tor);
|
||||
|
||||
if(tor->disposed)
|
||||
return;
|
||||
|
||||
state->type = TYPE_DICT;
|
||||
state->val.l.vals = g_new0(benc_val_t, len);
|
||||
state->val.l.alloc = state->val.l.count = len;
|
||||
|
||||
g_assert(len > ALEN(strs));
|
||||
for(ii = 0; ii < ALEN(strs); ii++) {
|
||||
state->val.l.vals[ii].type = TYPE_STR;
|
||||
state->val.l.vals[ii].val.s.s = g_strdup(strs[ii]);
|
||||
state->val.l.vals[ii].val.s.i = strlen(strs[ii]);
|
||||
}
|
||||
|
||||
state->val.l.vals[ii].type = TYPE_INT;
|
||||
state->val.l.vals[ii].val.i = tr_torrent_paused(tor);
|
||||
ii++;
|
||||
|
||||
g_assert(len == ii);
|
||||
}
|
||||
|
||||
static void
|
||||
tr_torrent_set_folder(TrTorrent *tor) {
|
||||
char *wd;
|
||||
|
||||
if(NULL != tor->dir)
|
||||
tr_torrentSetFolder(tor->handle, tor->dir);
|
||||
else {
|
||||
wd = g_new(char, MAX_PATH_LENGTH + 1);
|
||||
tr_torrentSetFolder(tor->handle,
|
||||
(NULL == getcwd(wd, MAX_PATH_LENGTH + 1) ? "." : wd));
|
||||
g_free(wd);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
tr_torrent_paused(TrTorrent *tor) {
|
||||
tr_stat_t *st = tr_torrentStat(tor->handle);
|
||||
|
||||
return (TR_STATUS_INACTIVE & st->status ? TRUE : FALSE);
|
||||
}
|
||||
61
gtk/tr_torrent.h
Normal file
61
gtk/tr_torrent.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef TR_TORRENT_H
|
||||
#define TR_TORRENT_H
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "transmission.h"
|
||||
#include "bencode.h"
|
||||
|
||||
#define TR_TORRENT_TYPE (tr_torrent_get_type ())
|
||||
#define TR_TORRENT(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST ((obj), TR_TORRENT_TYPE, TrTorrent))
|
||||
#define TR_TORRENT_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST ((klass), TR_TORRENT_TYPE, TrTorrentClass))
|
||||
#define TR_IS_TORRENT(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), TR_TORRENT_TYPE))
|
||||
#define TR_IS_TORRENT_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE ((klass), TR_TORRENT_TYPE))
|
||||
#define TR_TORRENT_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS ((obj), TR_TORRENT_TYPE, TrTorrentClass))
|
||||
|
||||
typedef struct _TrTorrent TrTorrent;
|
||||
typedef struct _TrTorrentClass TrTorrentClass;
|
||||
|
||||
/* treat the contents of this structure as private */
|
||||
struct _TrTorrent {
|
||||
GObject parent;
|
||||
tr_torrent_t *handle;
|
||||
GObject *back;
|
||||
char *dir;
|
||||
gboolean disposed;
|
||||
};
|
||||
|
||||
struct _TrTorrentClass {
|
||||
GObjectClass parent;
|
||||
};
|
||||
|
||||
GType
|
||||
tr_torrent_get_type(void);
|
||||
|
||||
tr_torrent_t *
|
||||
tr_torrent_handle(TrTorrent *tor);
|
||||
|
||||
tr_stat_t *
|
||||
tr_torrent_stat(TrTorrent *tor);
|
||||
|
||||
tr_info_t *
|
||||
tr_torrent_info(TrTorrent *tor);
|
||||
|
||||
TrTorrent *
|
||||
tr_torrent_new(GObject *backend, const char *torrent, const char *dir,
|
||||
gboolean *paused, char **err);
|
||||
|
||||
TrTorrent *
|
||||
tr_torrent_new_with_state(GObject *backend, benc_val_t *state, char **err);
|
||||
|
||||
#ifdef TR_WANT_TORRENT_PRIVATE
|
||||
void
|
||||
tr_torrent_get_state(TrTorrent *tor, benc_val_t *state);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -37,8 +37,7 @@
|
||||
#define SHUTUP
|
||||
#endif
|
||||
|
||||
typedef gboolean (*add_torrent_func_t)(void *, const char *, const char *, gboolean, GList **);
|
||||
typedef void (*torrents_added_func_t)(void *);
|
||||
typedef void (*add_torrents_func_t)(void*,void*,GList*,const char*,gboolean*);
|
||||
|
||||
/* return number of items in array */
|
||||
#define ALEN(a) (sizeof(a) / sizeof((a)[0]))
|
||||
|
||||
Reference in New Issue
Block a user