source: trunk/gtk/tr_core.c @ 2369

Last change on this file since 2369 was 2369, checked in by charles, 15 years ago

disambiguate some of the tr_stat_t variables' names

  • Property svn:keywords set to Date Rev Author Id
File size: 20.4 KB
Line 
1/******************************************************************************
2 * $Id: tr_core.c 2369 2007-07-16 00:50:43Z charles $
3 *
4 * Copyright (c) 2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <stdint.h>
26#include <string.h>
27
28#include <gtk/gtk.h>
29#include <glib/gi18n.h>
30
31#include "bencode.h"
32#include "transmission.h"
33
34#include "conf.h"
35#include "tr_core.h"
36#include "tr_prefs.h"
37#include "tr_torrent.h"
38#include "util.h"
39
40static void
41tr_core_init( GTypeInstance * instance, gpointer g_class );
42static void
43tr_core_class_init( gpointer g_class, gpointer g_class_data );
44static void
45tr_core_marshal_err( GClosure * closure, GValue * ret, guint count,
46                     const GValue * vals, gpointer hint, gpointer marshal );
47void
48tr_core_marshal_prompt( GClosure * closure, GValue * ret, guint count,
49                        const GValue * vals, gpointer hint, gpointer marshal );
50void
51tr_core_marshal_data( GClosure * closure, GValue * ret, guint count,
52                      const GValue * vals, gpointer hint, gpointer marshal );
53static void
54tr_core_dispose( GObject * obj );
55static void
56tr_core_insert( TrCore * self, TrTorrent * tor );
57static void
58tr_core_errsig( TrCore * self, enum tr_core_err type, const char * msg );
59
60GType
61tr_core_get_type( void )
62{
63    static GType type = 0;
64
65    if( 0 == type )
66    {
67        static const GTypeInfo info =
68        {
69            sizeof( TrCoreClass ),
70            NULL,                       /* base_init */
71            NULL,                       /* base_finalize */
72            tr_core_class_init,         /* class_init */
73            NULL,                       /* class_finalize */
74            NULL,                       /* class_data */
75            sizeof( TrCore ),
76            0,                          /* n_preallocs */
77            tr_core_init,               /* instance_init */
78            NULL,
79        };
80        type = g_type_register_static( G_TYPE_OBJECT, "TrCore", &info, 0 );
81    }
82
83    return type;
84}
85
86void
87tr_core_class_init( gpointer g_class, gpointer g_class_data SHUTUP )
88{
89    GObjectClass * gobject_class;
90    TrCoreClass  * core_class;
91
92    gobject_class = G_OBJECT_CLASS( g_class );
93    gobject_class->dispose = tr_core_dispose;
94
95    core_class = TR_CORE_CLASS( g_class );
96    core_class->errsig = g_signal_new( "error", G_TYPE_FROM_CLASS( g_class ),
97                                       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
98                                       tr_core_marshal_err, G_TYPE_NONE,
99                                       2, G_TYPE_INT, G_TYPE_STRING );
100    core_class->promptsig = g_signal_new( "directory-prompt",
101                                          G_TYPE_FROM_CLASS( g_class ),
102                                          G_SIGNAL_RUN_LAST, 0, NULL, NULL,
103                                          tr_core_marshal_prompt, G_TYPE_NONE,
104                                          3, G_TYPE_POINTER, G_TYPE_INT,
105                                          G_TYPE_BOOLEAN );
106    core_class->promptdatasig = g_signal_new( "directory-prompt-data",
107                                              G_TYPE_FROM_CLASS( g_class ),
108                                              G_SIGNAL_RUN_LAST, 0, NULL, NULL,
109                                              tr_core_marshal_data,
110                                              G_TYPE_NONE, 3, G_TYPE_STRING,
111                                              G_TYPE_UINT, G_TYPE_BOOLEAN );
112    core_class->quitsig = g_signal_new( "quit", G_TYPE_FROM_CLASS( g_class ),
113                                        G_SIGNAL_RUN_LAST, 0, NULL, NULL,
114                                        g_cclosure_marshal_VOID__VOID,
115                                        G_TYPE_NONE, 0 );
116    core_class->prefsig = g_signal_new( "prefs-changed",
117                                        G_TYPE_FROM_CLASS( g_class ),
118                                        G_SIGNAL_RUN_LAST, 0, NULL, NULL,
119                                        g_cclosure_marshal_VOID__INT,
120                                        G_TYPE_NONE, 1, G_TYPE_INT );
121}
122
123void
124tr_core_marshal_err( GClosure * closure, GValue * ret SHUTUP, guint count,
125                     const GValue * vals, gpointer hint SHUTUP,
126                     gpointer marshal )
127{
128    typedef void (*TRMarshalErr)
129        ( gpointer, enum tr_core_err, const char *, gpointer );
130    TRMarshalErr     callback;
131    GCClosure      * cclosure = (GCClosure*) closure;
132    enum tr_core_err errcode;
133    const char     * errstr;
134    gpointer         inst, gdata;
135
136    g_return_if_fail( 3 == count );
137
138    inst    = g_value_peek_pointer( vals );
139    errcode = g_value_get_int( vals + 1 );
140    errstr  = g_value_get_string( vals + 2 );
141    gdata   = closure->data;
142
143    callback = (TRMarshalErr) ( NULL == marshal ?
144                                cclosure->callback : marshal );
145    callback( inst, errcode, errstr, gdata );
146}
147
148void
149tr_core_marshal_prompt( GClosure * closure, GValue * ret SHUTUP, guint count,
150                        const GValue * vals, gpointer hint SHUTUP,
151                        gpointer marshal )
152{
153    typedef void (*TRMarshalPrompt)
154        ( gpointer, GList *, enum tr_torrent_action, gboolean, gpointer );
155    TRMarshalPrompt        callback;
156    GCClosure            * cclosure = (GCClosure*) closure;
157    GList                * paths;
158    enum tr_torrent_action action;
159    gboolean               paused;
160    gpointer               inst, gdata;
161
162    g_return_if_fail( 4 == count );
163
164    inst    = g_value_peek_pointer( vals );
165    paths   = g_value_get_pointer( vals + 1 );
166    action  = g_value_get_int( vals + 2 );
167    paused  = g_value_get_boolean( vals + 3 );
168    gdata   = closure->data;
169
170    callback = (TRMarshalPrompt) ( NULL == marshal ?
171                                   cclosure->callback : marshal );
172    callback( inst, paths, action, paused, gdata );
173}
174
175void
176tr_core_marshal_data( GClosure * closure, GValue * ret SHUTUP, guint count,
177                      const GValue * vals, gpointer hint SHUTUP,
178                      gpointer marshal )
179{
180    typedef void (*TRMarshalPrompt)
181        ( gpointer, uint8_t *, size_t, gboolean, gpointer );
182    TRMarshalPrompt        callback;
183    GCClosure            * cclosure = (GCClosure*) closure;
184    uint8_t              * data;
185    size_t                 size;
186    gboolean               paused;
187    gpointer               inst, gdata;
188
189    g_return_if_fail( 4 == count );
190
191    inst    = g_value_peek_pointer( vals );
192    data    = (uint8_t *) g_value_get_string( vals + 1 );
193    size    = g_value_get_uint( vals + 2 );
194    paused  = g_value_get_boolean( vals + 3 );
195    gdata   = closure->data;
196
197    callback = (TRMarshalPrompt) ( NULL == marshal ?
198                                   cclosure->callback : marshal );
199    callback( inst, data, size, paused, gdata );
200}
201
202void
203tr_core_init( GTypeInstance * instance, gpointer g_class SHUTUP )
204{
205    TrCore * self = (TrCore *) instance;
206    GtkListStore * store;
207
208    /* column types for the model used to store torrent information */
209    /* keep this in sync with the enum near the bottom of tr_core.h */
210    GType types[] =
211    {
212        /* info->name, info->totalSize, info->hashString, status, */
213        G_TYPE_STRING, G_TYPE_UINT64,   G_TYPE_STRING,    G_TYPE_INT,
214        /* error,   errorString,   percentComplete, percentDone,  rateDownload, rateUpload, */
215        G_TYPE_INT, G_TYPE_STRING, G_TYPE_FLOAT,    G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_FLOAT,
216        /* eta,     peersConnected, peersUploading, peersDownloading, seeders, */
217        G_TYPE_INT, G_TYPE_INT,     G_TYPE_INT,     G_TYPE_INT,       G_TYPE_INT,
218        /* leechers, completedFromTracker, downloaded,    uploaded */
219        G_TYPE_INT,  G_TYPE_INT,           G_TYPE_UINT64, G_TYPE_UINT64,
220        /* left,       tracker,               TrTorrent object, ID for IPC */
221        G_TYPE_UINT64, TR_TRACKER_BOXED_TYPE, TR_TORRENT_TYPE,  G_TYPE_INT,
222    };
223
224#ifdef REFDBG
225    fprintf( stderr, "core    %p init\n", self );
226#endif
227
228    /* create the model used to store torrent data */
229    g_assert( ALEN( types ) == MC_ROW_COUNT );
230    store = gtk_list_store_newv( MC_ROW_COUNT, types );
231
232    self->model    = GTK_TREE_MODEL( store );
233    self->handle   = tr_init( "gtk" );
234    self->nextid   = 1;
235    self->quitting = FALSE;
236    self->disposed = FALSE;
237}
238
239void
240tr_core_dispose( GObject * obj )
241{
242    TrCore       * self = (TrCore *) obj;
243    GObjectClass * parent;
244    GtkTreeIter    iter;
245    TrTorrent    * tor;
246
247    if( self->disposed )
248    {
249        return;
250    }
251    self->disposed = TRUE;
252
253#ifdef REFDBG
254    fprintf( stderr, "core    %p dispose\n", self );
255#endif
256
257    /* sever all remaining torrents in the model */
258    if( gtk_tree_model_get_iter_first( self->model, &iter ) ) do
259    {
260        gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 );
261        tr_torrent_sever( tor );
262        g_object_unref( tor );
263    }
264    while( gtk_tree_model_iter_next( self->model, &iter ) );
265    g_object_unref( self->model );
266
267#ifdef REFDBG
268    fprintf( stderr, "core    %p dead\n", self );
269#endif
270
271    /* close the libtransmission instance */
272    tr_close( self->handle );
273
274    /* Chain up to the parent class */
275    parent = g_type_class_peek( g_type_parent( TR_CORE_TYPE ) );
276    parent->dispose( obj );
277}
278
279TrCore *
280tr_core_new( void )
281{
282    return g_object_new( TR_CORE_TYPE, NULL );
283}
284
285GtkTreeModel *
286tr_core_model( TrCore * self )
287{
288    g_return_val_if_fail (TR_IS_CORE(self), NULL);
289
290    return self->disposed ? NULL : self->model;
291}
292
293tr_handle_t *
294tr_core_handle( TrCore * self )
295{
296    g_return_val_if_fail (TR_IS_CORE(self), NULL);
297
298    return self->disposed ? NULL : self->handle;
299}
300
301void
302tr_core_shutdown( TrCore * self )
303{
304    GtkTreeIter iter;
305
306    TR_IS_CORE( self );
307
308    if( self->disposed )
309        return;
310
311    g_assert( !self->quitting );
312    self->quitting = TRUE;
313
314    /* try to stop all the torrents nicely */
315    if ( gtk_tree_model_get_iter_first( self->model, &iter) ) do {
316        TrTorrent * tor;
317        gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 );
318        tr_torrent_sever( tor );
319        g_object_unref( tor );
320    } while( gtk_list_store_remove( GTK_LIST_STORE(self->model), &iter ) );
321
322    /* shut down nat traversal */
323    tr_natTraversalEnable( self->handle, 0 );
324}
325
326gboolean
327tr_core_quiescent( TrCore * self )
328{
329    const tr_handle_status_t * hstat;
330
331    TR_IS_CORE( self );
332    g_assert( self->quitting );
333
334    if( self->disposed )
335        return TRUE;
336
337    if ( tr_torrentCount( self->handle ) != 0 )
338        return FALSE;
339
340    hstat = tr_handleStatus( self->handle );
341    return TR_NAT_TRAVERSAL_DISABLED == hstat->natTraversalStatus;
342}
343
344void
345tr_core_save( TrCore * self )
346{
347    benc_val_t  state;
348    int         count;
349    GtkTreeIter iter;
350    TrTorrent * tor;
351    char      * errstr;
352    GList     * saved, * ii;
353
354    TR_IS_CORE( self );
355
356    count = gtk_tree_model_iter_n_children( self->model, NULL );
357
358    tr_bencInit( &state, TYPE_LIST );
359    if( tr_bencListReserve( &state, count ) )
360    {
361        tr_core_errsig( self, TR_CORE_ERR_SAVE_STATE, "malloc failure" );
362        return;
363    }
364
365    saved = NULL;
366    if( gtk_tree_model_get_iter_first( self->model, &iter) ) do
367    {
368        benc_val_t * item = tr_bencListAdd( &state );
369        gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 );
370        if( tr_torrent_get_state( tor, item ) )
371        {
372            saved = g_list_append( saved, tor );
373        }
374        else
375        {
376            tr_bencFree( item );
377            tr_bencInitStr( item, NULL, 0, 1 );
378        }
379        g_object_unref( tor );
380    }
381    while( gtk_tree_model_iter_next( self->model, &iter ) );
382
383    errstr = NULL;
384    cf_savestate( &state, &errstr );
385    tr_bencFree( &state );
386    if( NULL != errstr )
387    {
388        tr_core_errsig( self, TR_CORE_ERR_SAVE_STATE, errstr );
389        g_free( errstr );
390    }
391    else
392    {
393        for( ii = saved; NULL != ii; ii = ii->next )
394        {
395            tr_torrent_state_saved( ii->data );
396        }
397    }
398
399    g_list_free( saved );
400}
401
402int
403tr_core_load( TrCore * self, const benc_val_t * state, gboolean forcepaused )
404{
405    int count = 0;
406
407    TR_IS_CORE( self );
408
409    if( TYPE_LIST == state->type )
410    {
411        int ii;
412        for( ii = 0; ii < state->val.l.count; ii++ )
413        {
414            char * errstr = NULL;
415            TrTorrent * tor = tr_torrent_new_with_state( self->handle,
416                                                     state->val.l.vals + ii,
417                                                     forcepaused, &errstr );
418            if( NULL == tor )
419            {
420                tr_core_errsig( self, TR_CORE_ERR_ADD_TORRENT, errstr );
421                g_free( errstr );
422            }
423            else
424            {
425                g_assert( NULL == errstr );
426                tr_core_insert( self, tor );
427                count++;
428            }
429        }
430    }
431
432    return count;
433}
434
435gboolean
436tr_core_add( TrCore * self, const char * path, enum tr_torrent_action act,
437             gboolean paused )
438{
439    GList * list;
440    int     ret;
441
442    TR_IS_CORE( self );
443
444    list = g_list_append( NULL, (void *) path );
445    ret  = tr_core_add_list( self, list, act, paused );
446    g_list_free( list );
447
448    return 1 == ret;
449}
450
451gboolean
452tr_core_add_dir( TrCore * self, const char * path, const char * dir,
453                 enum tr_torrent_action act, gboolean paused )
454{
455    TrTorrent * tor;
456    char      * errstr;
457
458    TR_IS_CORE( self );
459
460    errstr = NULL;
461    tor = tr_torrent_new( self->handle, path, dir, act, paused, &errstr );
462    if( NULL == tor )
463    {
464        tr_core_errsig( self, TR_CORE_ERR_ADD_TORRENT, errstr );
465        g_free( errstr );
466        return FALSE;
467    }
468    g_assert( NULL == errstr );
469
470    tr_core_insert( self, tor );
471
472    return TRUE;
473}
474
475int
476tr_core_add_list( TrCore * self, GList * paths, enum tr_torrent_action act,
477                  gboolean paused )
478{
479    const char  * pref = tr_prefs_get( PREF_ID_ASKDIR );
480    TrCoreClass * class;
481    int           count;
482
483    TR_IS_CORE( self );
484
485    if( strbool( pref ) )
486    {
487        class = g_type_class_peek( TR_CORE_TYPE );
488        g_signal_emit( self, class->promptsig, 0, paths, act, paused );
489        return 0;
490    }
491
492    pref = getdownloaddir();
493    count = 0;
494    for( ; paths; paths=paths->next )
495        if( tr_core_add_dir( self, paths->data, pref, act, paused ) )
496            count++;
497
498    return count;
499}
500
501gboolean
502tr_core_add_data( TrCore * self, uint8_t * data, size_t size, gboolean paused )
503{
504    const char  * pref = tr_prefs_get( PREF_ID_ASKDIR );
505
506    TR_IS_CORE( self );
507
508    if( strbool( pref ) )
509    {
510        TrCoreClass * class = g_type_class_peek( TR_CORE_TYPE );
511        g_signal_emit( self, class->promptdatasig, 0, data, size, paused );
512        return FALSE;
513    }
514
515    return tr_core_add_data_dir( self, data, size, getdownloaddir(), paused );
516}
517
518gboolean
519tr_core_add_data_dir( TrCore * self, uint8_t * data, size_t size,
520                      const char * dir, gboolean paused )
521{
522    TrTorrent * tor;
523    char      * errstr = NULL;
524
525    TR_IS_CORE( self );
526
527    tor = tr_torrent_new_with_data( self->handle, data, size, dir,
528                                    paused, &errstr );
529    if( NULL == tor )
530    {
531        tr_core_errsig( self, TR_CORE_ERR_ADD_TORRENT, errstr );
532        g_free( errstr );
533        return FALSE;
534    }
535    g_assert( NULL == errstr );
536
537    tr_core_insert( self, tor );
538
539    return TRUE;
540}
541
542void
543tr_core_torrents_added( TrCore * self )
544{
545    TR_IS_CORE( self );
546
547    tr_core_update( self );
548    tr_core_save( self );
549    tr_core_errsig( self, TR_CORE_ERR_NO_MORE_TORRENTS, NULL );
550}
551
552void
553tr_core_delete_torrent( TrCore * self, GtkTreeIter * iter )
554{
555    TrTorrent * tor;
556
557    TR_IS_CORE( self );
558
559    gtk_tree_model_get( self->model, iter, MC_TORRENT, &tor, -1 );
560    gtk_list_store_remove( GTK_LIST_STORE( self->model ), iter );
561
562    if( TR_FLAG_SAVE & tr_torrent_info( tor )->flags )
563        tr_torrentRemoveSaved( tr_torrent_handle( tor ) );
564
565    tr_torrent_sever( tor );
566}
567
568void
569tr_core_insert( TrCore * self, TrTorrent * tor )
570{
571    GtkTreeIter iter;
572    const tr_info_t * inf;
573
574    gtk_list_store_append( GTK_LIST_STORE( self->model ), &iter );
575    inf = tr_torrent_info( tor );
576    gtk_list_store_set( GTK_LIST_STORE( self->model ), &iter,
577                        MC_NAME,    inf->name,
578                        MC_SIZE,    inf->totalSize,
579                        MC_HASH,    inf->hashString,
580                        MC_TORRENT, tor,
581                        MC_ID,      self->nextid,
582                        -1);
583    g_object_unref( tor );
584    self->nextid++;
585}
586
587void
588tr_core_update( TrCore * self )
589{
590    GtkTreeIter iter;
591    TrTorrent * tor;
592    const tr_stat_t * st;
593
594    TR_IS_CORE( self );
595
596    if( gtk_tree_model_get_iter_first( self->model, &iter ) ) do
597    {
598        gtk_tree_model_get( self->model, &iter, MC_TORRENT, &tor, -1 );
599        st = tr_torrent_stat( tor );
600        g_object_unref( tor );
601        tr_torrent_check_seeding_cap ( tor );
602
603        /* XXX find out if setting the same data emits changed signal */
604        gtk_list_store_set( GTK_LIST_STORE( self->model ), &iter,
605                            MC_STAT,        st->status,
606                            MC_ERR,         st->error,
607                            MC_TERR,        st->errorString,
608                            MC_PROG_C,      st->percentComplete,
609                            MC_PROG_D,      st->percentDone,
610                            MC_DRATE,       st->rateDownload,
611                            MC_URATE,       st->rateUpload,
612                            MC_ETA,         st->eta,
613                            MC_PEERS,       st->peersConnected,
614                            MC_UPEERS,      st->peersGettingFromUs,
615                            MC_DPEERS,      st->peersSendingToUs,
616                            MC_SEED,        st->seeders,
617                            MC_LEECH,       st->leechers,
618                            MC_DONE,        st->completedFromTracker,
619                            MC_TRACKER,     st->tracker,
620                            MC_DOWN,        st->downloaded,
621                            MC_UP,          st->uploaded,
622                            MC_LEFT,        st->left,
623                            -1 );
624    }
625    while( gtk_tree_model_iter_next( self->model, &iter ) );
626}
627
628void
629tr_core_errsig( TrCore * self, enum tr_core_err type, const char * msg )
630{
631    TrCoreClass * class;
632
633    class = g_type_class_peek( TR_CORE_TYPE );
634    g_signal_emit( self, class->errsig, 0, type, msg );
635}
636
637void
638tr_core_quit( TrCore * self )
639{
640    TrCoreClass * class;
641
642    TR_IS_CORE( self );
643
644    class = g_type_class_peek( TR_CORE_TYPE );
645    g_signal_emit( self, class->quitsig, 0 );
646}
647
648void
649tr_core_set_pref( TrCore * self, int id, const char * val )
650{
651    const char  * name, * old;
652    char        * errstr;
653    TrCoreClass * class;
654
655    TR_IS_CORE( self );
656
657    /* don't change anything if the new value is the same as the old one */
658    name = tr_prefs_name( id );
659    old = cf_getpref( name );
660    if( !tr_strcmp( old, val ) )
661        return;
662
663    cf_setpref( name, val );
664
665    /* write prefs to disk */
666    cf_saveprefs( &errstr );
667    if( NULL != errstr )
668    {
669        tr_core_errsig( self, TR_CORE_ERR_SAVE_STATE, errstr );
670        g_free( errstr );
671    }
672
673    /* signal a pref change */
674    class = g_type_class_peek( TR_CORE_TYPE );
675    g_signal_emit( self, class->prefsig, 0, id );
676}
677
678void
679tr_core_set_pref_bool( TrCore * self, int id, gboolean val )
680{
681    tr_core_set_pref( self, id, ( val ? "yes" : "no" ) );
682}
683
684void
685tr_core_set_pref_int( TrCore * self, int id, int val )
686{
687    char buf[32];
688
689    snprintf( buf, sizeof buf, "%i", val );
690
691    tr_core_set_pref( self, id, buf );
692}
Note: See TracBrowser for help on using the repository browser.