From cc1394b1d6ebed9a5cb630a3404b1452359011b7 Mon Sep 17 00:00:00 2001 From: Josh Elsasser Date: Wed, 23 May 2007 17:59:35 +0000 Subject: [PATCH] Add signal mechanism for reporting error from TrCore. Use error signal for errors when adding torrents and saving state. --- gtk/main.c | 91 ++++++++++++++++++++++-------------- gtk/tr_core.c | 118 ++++++++++++++++++++++++++++++++++++++--------- gtk/tr_core.h | 33 +++++++++---- gtk/tr_torrent.c | 58 ++++++++++++----------- gtk/tr_torrent.h | 4 +- 5 files changed, 209 insertions(+), 95 deletions(-) diff --git a/gtk/main.c b/gtk/main.c index 35ba1cfc6..1681199cc 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -72,6 +72,7 @@ struct cbdata { guint timer; gboolean msgwinopen; gboolean closing; + GList * errqueue; }; struct exitdata { @@ -164,6 +165,9 @@ gotdrag(GtkWidget *widget, GdkDragContext *dc, gint x, gint y, GtkSelectionData *sel, guint info, guint time, gpointer gdata); static void +coreerr( TrCore * core, enum tr_core_err code, const char * msg, + gpointer gdata ); +static void readinitialprefs( struct cbdata * cbdata ); static void prefschanged( GtkWidget * widget, int id, gpointer data ); @@ -184,8 +188,6 @@ static void addtorrents(void *vdata, void *state, GList *files, const char *dir, guint flags); static void -savetorrents(struct cbdata *data); -static void safepipe(void); static void setupsighandlers(void); @@ -382,6 +384,10 @@ appsetup( TrWindow * wind, benc_val_t * state, GList * args, gboolean paused ) cbdata->timer = 0; cbdata->msgwinopen = FALSE; cbdata->closing = FALSE; + cbdata->errqueue = NULL; + + /* set up error message handler */ + g_signal_connect( cbdata->core, "error", G_CALLBACK( coreerr ), cbdata ); /* apply a few prefs */ readinitialprefs( cbdata ); @@ -575,6 +581,11 @@ exitcheck( gpointer gdata ) g_object_unref( cbdata->icon ); } g_assert( 0 == cbdata->timer ); + if( NULL != cbdata->errqueue ) + { + g_list_foreach( cbdata->errqueue, (GFunc) g_free, NULL ); + g_list_free( cbdata->errqueue ); + } g_free( cbdata ); gtk_main_quit(); @@ -679,6 +690,42 @@ setupdrag(GtkWidget *widget, struct cbdata *data) { ALEN(targets), GDK_ACTION_COPY | GDK_ACTION_MOVE); } +static void +coreerr( TrCore * core SHUTUP, enum tr_core_err code, const char * msg, + gpointer gdata ) +{ + struct cbdata * cbdata = gdata; + char * joined; + + switch( code ) + { + case TR_CORE_ERR_ADD_TORRENT: + cbdata->errqueue = g_list_append( cbdata->errqueue, + g_strdup( msg ) ); + return; + case TR_CORE_ERR_NO_MORE_TORRENTS: + if( NULL != cbdata->errqueue ) + { + joined = joinstrlist( cbdata->errqueue, "\n" ); + errmsg( cbdata->wind, + ngettext( "Failed to load torrent file:\n%s", + "Failed to load torrent files:\n%s", + g_list_length( cbdata->errqueue ) ), + joined ); + g_list_foreach( cbdata->errqueue, (GFunc) g_free, NULL ); + g_list_free( cbdata->errqueue ); + cbdata->errqueue = NULL; + g_free( joined ); + } + return; + case TR_CORE_ERR_SAVE_STATE: + errmsg( cbdata->wind, "%s", msg ); + return; + } + + g_assert_not_reached(); +} + static void readinitialprefs( struct cbdata * cbdata ) { @@ -958,8 +1005,8 @@ handleaction( struct cbdata * data, int act ) g_list_free(rows); if(changed) { - savetorrents(data); updatemodel(data); + tr_core_save( data->core ); } } @@ -967,17 +1014,14 @@ static void addtorrents(void *vdata, void *state, GList *files, const char *dir, guint flags) { struct cbdata *data = vdata; - GList *errlist, *ii; - char *errstr; const char * pref; int added; - errlist = NULL; added = 0; if( NULL != state ) { - added += tr_core_load( data->core, state, &errlist ); + added += tr_core_load( data->core, state ); } if(NULL != files) { @@ -991,47 +1035,24 @@ addtorrents(void *vdata, void *state, GList *files, } dir = getdownloaddir(); } - for(ii = g_list_first(files); NULL != ii; ii = ii->next) { - errstr = NULL; - if( tr_core_add_torrent( data->core, ii->data, dir, flags, &errstr ) ) + for( files = g_list_first( files ); NULL != files; files = files->next ) + { + if( tr_core_add_torrent( data->core, files->data, dir, flags ) ) { added++; } - if(NULL != errstr) - errlist = g_list_append(errlist, errstr); } } - if(NULL != errlist) { - errstr = joinstrlist(errlist, "\n"); - errmsg( data->wind, ngettext( "Failed to load torrent file:\n%s", - "Failed to load torrent files:\n%s", - g_list_length( errlist ) ), errstr ); - g_list_foreach(errlist, (GFunc)g_free, NULL); - g_list_free(errlist); - g_free(errstr); - } + tr_core_torrents_added( data->core ); if( 0 < added ) { updatemodel(data); - savetorrents(data); + tr_core_save( data->core ); } } -static void -savetorrents( struct cbdata *data ) -{ - char * errstr; - - tr_core_save( data->core, &errstr ); - if( NULL != errstr ) - { - errmsg( data->wind, "%s", errstr ); - g_free( errstr ); - } -} - static void safepipe(void) { struct sigaction sa; diff --git a/gtk/tr_core.c b/gtk/tr_core.c index e498ccc74..41312d165 100644 --- a/gtk/tr_core.c +++ b/gtk/tr_core.c @@ -43,6 +43,9 @@ tr_core_init( GTypeInstance * instance, gpointer g_class ); static void tr_core_class_init( gpointer g_class, gpointer g_class_data ); static void +tr_core_marshal_err( GClosure * closure, GValue * ret, guint count, + const GValue * vals, gpointer hint, gpointer marshal ); +static void tr_core_dispose( GObject * obj ); static int tr_core_check_torrents( TrCore * self ); @@ -50,6 +53,8 @@ static int tr_core_check_zombies( TrCore * self ); static void tr_core_insert( TrCore * self, TrTorrent * tor ); +static void +tr_core_errsig( TrCore * self, enum tr_core_err type, const char * msg ); GType tr_core_get_type( void ) @@ -81,9 +86,41 @@ void tr_core_class_init( gpointer g_class, gpointer g_class_data SHUTUP ) { GObjectClass * gobject_class; + TrCoreClass * core_class; gobject_class = G_OBJECT_CLASS( g_class ); gobject_class->dispose = tr_core_dispose; + + core_class = TR_CORE_CLASS( g_class ); + core_class->errsig = g_signal_new( "error", G_TYPE_FROM_CLASS( g_class ), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + tr_core_marshal_err, G_TYPE_NONE, + 2, G_TYPE_INT, G_TYPE_STRING ); +} + +void +tr_core_marshal_err( GClosure * closure, GValue * ret SHUTUP, guint count, + const GValue * vals, gpointer hint SHUTUP, + gpointer marshal ) +{ + typedef void (*TRMarshalErr) ( gpointer, enum tr_core_err, const char *, + gpointer ); + TRMarshalErr callback; + GCClosure * cclosure = (GCClosure*) closure; + enum tr_core_err errcode; + const char * errstr; + gpointer inst, gdata; + + g_return_if_fail( 3 == count ); + + inst = g_value_peek_pointer( vals ); + errcode = g_value_get_int( vals + 1 ); + errstr = g_value_get_string( vals + 2 ); + gdata = closure->data; + + callback = (TRMarshalErr) ( NULL == marshal ? + cclosure->callback : marshal ); + callback( inst, errcode, errstr, gdata ); } void @@ -325,12 +362,14 @@ tr_core_check_zombies( TrCore * self ) } void -tr_core_save( TrCore * self, char ** error ) +tr_core_save( TrCore * self ) { - benc_val_t state; + benc_val_t state, * item; int count; GtkTreeIter iter; TrTorrent * tor; + char * errstr; + GList * saved, * ii; TR_IS_CORE( self ); @@ -346,41 +385,54 @@ tr_core_save( TrCore * self, char ** error ) tr_bencInit( &state, TYPE_LIST ); if( tr_bencListReserve( &state, count ) ) { - if( NULL != error ) - { - *error = g_strdup( "malloc failure" ); - } + tr_core_errsig( self, TR_CORE_ERR_SAVE_STATE, "malloc failure" ); return; } + saved = NULL; if( gtk_tree_model_get_iter_first( self->model, &iter) ) { do { + item = tr_bencListAdd( &state ); gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 ); - tr_torrent_get_state( tor, tr_bencListAdd( &state ) ); + if( tr_torrent_get_state( tor, item ) ) + { + saved = g_list_append( saved, tor ); + } + else + { + tr_bencFree( item ); + tr_bencInitStr( item, NULL, 0, 1 ); + } g_object_unref( tor ); } while( gtk_tree_model_iter_next( self->model, &iter ) ); } - cf_savestate( &state, error ); + errstr = NULL; + cf_savestate( &state, &errstr ); tr_bencFree( &state ); - - if( gtk_tree_model_get_iter_first( self->model, &iter) ) + if( NULL != errstr ) { - do + tr_core_errsig( self, TR_CORE_ERR_SAVE_STATE, errstr ); + g_free( errstr ); + } + else + { + for( ii = saved; NULL != ii; ii = ii->next ) { - gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 ); - tr_torrent_state_saved( tor ); - g_object_unref( tor ); + tr_torrent_state_saved( ii->data ); } - while( gtk_tree_model_iter_next( self->model, &iter ) ); + } + if( NULL != saved ) + { + g_list_free( saved ); } } int -tr_core_load( TrCore * self, benc_val_t * state, GList ** errors ) +tr_core_load( TrCore * self, benc_val_t * state ) { int ii, count; char * errstr; @@ -399,12 +451,14 @@ tr_core_load( TrCore * self, benc_val_t * state, GList ** errors ) errstr = NULL; tor = tr_torrent_new_with_state( self->handle, state->val.l.vals + ii, 0, &errstr ); - if( NULL != errstr ) + if( NULL == tor ) { - *errors = g_list_append( *errors, errstr ); + tr_core_errsig( self, TR_CORE_ERR_ADD_TORRENT, errstr ); + g_free( errstr ); } - if( NULL != tor ) + else { + g_assert( NULL == errstr ); tr_core_insert( self, tor ); count++; } @@ -415,23 +469,36 @@ tr_core_load( TrCore * self, benc_val_t * state, GList ** errors ) gboolean tr_core_add_torrent( TrCore * self, const char * torrent, const char * dir, - guint flags, char ** err ) + guint flags ) { TrTorrent * tor; + char * errstr; TR_IS_CORE( self ); - tor = tr_torrent_new( self->handle, torrent, dir, flags, err ); + errstr = NULL; + tor = tr_torrent_new( self->handle, torrent, dir, flags, &errstr ); if( NULL == tor ) { + tr_core_errsig( self, TR_CORE_ERR_ADD_TORRENT, errstr ); + g_free( errstr ); return FALSE; } + g_assert( NULL == errstr ); tr_core_insert( self, tor ); return TRUE; } +void +tr_core_torrents_added( TrCore * self ) +{ + TR_IS_CORE( self ); + + tr_core_errsig( self, TR_CORE_ERR_NO_MORE_TORRENTS, NULL ); +} + void tr_core_delete_torrent( TrCore * self, GtkTreeIter * iter ) { @@ -523,3 +590,12 @@ tr_core_update( TrCore * self ) tr_core_check_zombies( self ); } } + +void +tr_core_errsig( TrCore * self, enum tr_core_err type, const char * msg ) +{ + TrCoreClass * class; + + class = g_type_class_peek( TR_CORE_TYPE ); + g_signal_emit( self, class->errsig, 0, type, msg ); +} diff --git a/gtk/tr_core.h b/gtk/tr_core.h index 32fa17411..a06b93d08 100644 --- a/gtk/tr_core.h +++ b/gtk/tr_core.h @@ -66,6 +66,17 @@ struct _TrCore struct _TrCoreClass { GObjectClass parent; + /* "error" signal: + void handler( TrCore *, enum tr_core_err, const char *, gpointer ) */ + int errsig; +}; + +enum tr_core_err +{ + TR_CORE_ERR_ADD_TORRENT, /* adding a torrent failed */ + /* no more torrents to be added, used for grouping torrent add errors */ + TR_CORE_ERR_NO_MORE_TORRENTS, + TR_CORE_ERR_SAVE_STATE /* error saving state */ }; GType @@ -90,22 +101,24 @@ tr_core_shutdown( TrCore * self ); gboolean tr_core_quiescent( TrCore * self ); -/* Save state. If error is not NULL is will be set to any error which - occurs, this must be freed. */ +/* Save state. May trigger "error" signal with TR_CORE_ERR_SAVE_STATE */ void -tr_core_save( TrCore * self, char ** error ); +tr_core_save( TrCore * self ); -/* Load saved state, return number of torrents added. If errors is not - NULL then a char* is appended for each error which occurs, these - must be freed. */ +/* Load saved state, return number of torrents added. May trigger one + or more "error" signals with TR_CORE_ERR_ADD_TORRENT */ int -tr_core_load( TrCore * self, benc_val_t * state, GList ** errors ); +tr_core_load( TrCore * self, benc_val_t * state ); -/* Add a torrent. Torrent, dir, flags, and err arguments are the same - as tr_torrent_new() */ +/* Add a torrent. Torrent, dir and flags arguments are the same as + tr_torrent_new(). May trigger "error" signal with TR_CORE_ERR_ADD_TORRENT */ gboolean tr_core_add_torrent( TrCore * self, const char * torrent, const char * dir, - guint flags, char ** err ); + guint flags ); + +/* Trigger "error" signal with TR_CORE_ERR_NO_MORE_TORRENTS */ +void +tr_core_torrents_added( TrCore * self ); /* remove a torrent, waiting for it to pause if necessary */ void diff --git a/gtk/tr_torrent.c b/gtk/tr_torrent.c index 34b82666b..e1e04558e 100644 --- a/gtk/tr_torrent.c +++ b/gtk/tr_torrent.c @@ -401,38 +401,42 @@ tr_torrent_new_with_state( tr_handle_t * back, benc_val_t * state, return tr_torrent_new( back, torrent, dir, flags, err ); } -#define SETSTRVAL(vv, ss) \ - do { \ - (vv)->type = TYPE_STR; \ - (vv)->val.s.s = g_strdup((ss)); \ - (vv)->val.s.i = strlen((ss)); \ - } while(0) +gboolean +tr_torrent_get_state( TrTorrent * tor, benc_val_t * state ) +{ + tr_info_t * inf; -void -tr_torrent_get_state(TrTorrent *tor, benc_val_t *state) { - tr_info_t *in = tr_torrentInfo(tor->handle); + TR_IS_TORRENT( tor ); - TR_IS_TORRENT(tor); + if( tor->severed ) + { + return FALSE; + } - if(tor->severed) - return; + inf = tr_torrentInfo( tor->handle ); - state->type = TYPE_DICT; - state->val.l.vals = g_new0(benc_val_t, 6); - state->val.l.alloc = state->val.l.count = 6; + tr_bencInit( state, TYPE_DICT ); + if( tr_bencDictReserve( state, 3 ) ) + { + return FALSE; + } - if(TR_FLAG_SAVE & in->flags) { - SETSTRVAL(state->val.l.vals + 0, "hash"); - SETSTRVAL(state->val.l.vals + 1, in->hashString); - } else { - SETSTRVAL(state->val.l.vals + 0, "torrent"); - SETSTRVAL(state->val.l.vals + 1, in->torrent); - } - SETSTRVAL(state->val.l.vals + 2, "dir"); - SETSTRVAL(state->val.l.vals + 3, tr_torrentGetFolder(tor->handle)); - SETSTRVAL(state->val.l.vals + 4, "paused"); - state->val.l.vals[5].type = TYPE_INT; - state->val.l.vals[5].val.i = tr_torrent_paused(tor); + if( TR_FLAG_SAVE & inf->flags ) + { + tr_bencInitStr( tr_bencDictAdd( state, "hash" ), + inf->hashString, -1, 1 ); + } + else + { + tr_bencInitStr( tr_bencDictAdd( state, "torrent" ), + inf->torrent, -1, 1 ); + } + tr_bencInitStr( tr_bencDictAdd( state, "dir" ), + tr_torrentGetFolder( tor->handle ), -1, 1 ); + tr_bencInitInt( tr_bencDictAdd( state, "paused" ), + ( tr_torrent_paused( tor ) ? 1 : 0 ) ); + + return TRUE; } /* XXX this should probably be done with a signal */ diff --git a/gtk/tr_torrent.h b/gtk/tr_torrent.h index 374a1ac1f..e0bc4f503 100644 --- a/gtk/tr_torrent.h +++ b/gtk/tr_torrent.h @@ -96,8 +96,8 @@ TrTorrent * tr_torrent_new_with_state( tr_handle_t * handle, benc_val_t * state, guint flags, char ** err ); -void -tr_torrent_get_state(TrTorrent *tor, benc_val_t *state); +gboolean +tr_torrent_get_state( TrTorrent * tor, benc_val_t * state ); void tr_torrent_state_saved(TrTorrent *tor);