Display transfer stats in dsh tray icon tooltip.
[doldaconnect.git] / clients / gui-shell / dsh.c
index 85f06bf..41291e9 100644 (file)
 
 struct trinfo {
     int ostate;
+    int opos, spos, speed;
+    time_t lastprog;
+    double sprog;
 };
 
 void updatewrite(void);
 
+int remote = 0;
 GtkStatusIcon *tray;
 pid_t dpid = 0, dcpid = 0;
 int connected = 0;
@@ -132,6 +136,10 @@ void inittr(struct dc_transfer *tr)
     tr->udata = tri = memset(smalloc(sizeof(*tri)), 0, sizeof(*tri));
     tr->destroycb = destroytr;
     tri->ostate = tr->state;
+    tri->spos = tri->opos = tr->curpos;
+    tri->speed = -1;
+    tri->lastprog = time(NULL);
+    tri->sprog = ntime();
 }
 
 #ifdef HAVE_NOTIFY
@@ -153,26 +161,113 @@ void notify(NotifyNotification **n, char *cat, char *title, char *body, ...)
 }
 #endif
 
+/* XXX: Achtung! Too DC-specific! */
+wchar_t *getfilename(wchar_t *path)
+{
+    wchar_t *p;
+    
+    if((p = wcsrchr(path, L'\\')) == NULL)
+       return(path);
+    else
+       return(p + 1);
+}
+
+char *bytes2si(long long bytes)
+{
+    int i;
+    double b;
+    char *sd;
+    static char ret[64];
+    
+    b = bytes;
+    for(i = 0; (b >= 1024) && (i < 4); i++)
+       b /= 1024;
+    if(i == 0)
+       sd = "B";
+    else if(i == 1)
+       sd = "kiB";
+    else if(i == 2)
+       sd = "MiB";
+    else if(i == 3)
+       sd = "GiB";
+    else
+       sd = "TiB";
+    snprintf(ret, 64, "%.1f %s", b, sd);
+    return(ret);
+}
+
+void updatetooltip(void)
+{
+    struct dc_transfer *tr;
+    struct trinfo *tri;
+    int t, i, a, st;
+    char *buf;
+    size_t bufsize, bufdata;
+    
+    t = i = a = 0;
+    st = -1;
+    for(tr = dc_transfers; tr != NULL; tr = tr->next) {
+       if(tr->dir != DC_TRNSD_DOWN)
+           continue;
+       tri = tr->udata;
+       t++;
+       if(tr->state == DC_TRNS_WAITING)
+           i++;
+       else if((tr->state == DC_TRNS_HS) || (tr->state == DC_TRNS_MAIN))
+           a++;
+       if((tr->state == DC_TRNS_MAIN) && (tri->speed != -1)) {
+           if(st == -1)
+               st = 0;
+           st += tri->speed;
+       }
+    }
+    buf = NULL;
+    bufsize = bufdata = 0;
+    bprintf(buf, "Transfers: %i", t);
+    if(t > 0)
+       bprintf(buf, " (%i/%i)", i, a);
+    if(st != -1) {
+       bprintf(buf, ", %s/s", bytes2si(st));
+    }
+    addtobuf(buf, 0);
+    gtk_status_icon_set_tooltip(tray, buf);
+    free(buf);
+}
+
 void trstatechange(struct dc_transfer *tr, int ostate)
 {
-    if(ostate == DC_TRNS_MAIN) {
+    struct trinfo *tri;
+    
+    tri = tr->udata;
+    if((ostate == DC_TRNS_MAIN) && (tr->dir == DC_TRNSD_DOWN)) {
        if(tr->state == DC_TRNS_DONE) {
 #ifdef HAVE_NOTIFY
-           notify(&trnote, "transfer.complete", _("Transfer complete"), _("Finished downloading %ls from %ls"), tr->path, tr->peernick);
+           if(dcpid == 0)
+               notify(&trnote, "transfer.complete", _("Transfer complete"), _("Finished downloading %ls from %ls"), getfilename(tr->path), tr->peernick);
 #endif
        } else {
 #ifdef HAVE_NOTIFY
-           notify(&trnote, "transfer.error", _("Transfer interrupted"), _("The transfer of %ls from %ls was interrupted from the other side"), tr->path, tr->peernick);
+           if(dcpid == 0)
+               notify(&trnote, "transfer.error", _("Transfer interrupted"), _("The transfer of %ls from %ls was interrupted from the other side"), getfilename(tr->path), tr->peernick);
 #endif
        }
     }
+    if(tr->state == DC_TRNS_MAIN) {
+       tri->speed = -1;
+       tri->spos = tr->curpos;
+       tri->sprog = ntime();
+    }
 }
 
 void updatetrinfo(void)
 {
     struct dc_transfer *tr;
     struct trinfo *tri;
+    time_t now;
+    double dnow;
     
+    now = time(NULL);
+    dnow = ntime();
     for(tr = dc_transfers; tr != NULL; tr = tr->next) {
        if(tr->udata == NULL) {
            inittr(tr);
@@ -182,8 +277,24 @@ void updatetrinfo(void)
                trstatechange(tr, tri->ostate);
                tri->ostate = tr->state;
            }
+           if(tri->opos != tr->curpos) {
+               tri->opos = tr->curpos;
+               tri->lastprog = now;
+           }
+#ifdef NOTIFY
+           if((tr->state = DC_TRNS_MAIN) && (now - tri->lastprog > 600)) {
+               if(dcpid == 0)
+                   notify(&trnote, "transfer.error", _("Transfer stalled"), _("The transfer of %ls from %ls has not made progress for 10 minutes"), getfilename(tr->path), tr->peernick);
+           }
+#endif
+           if((tr->state == DC_TRNS_MAIN) && (dnow - tri->sprog > 10)) {
+               tri->speed = ((double)(tr->curpos - tri->spos) / (dnow - tri->sprog));
+               tri->spos = tr->curpos;
+               tri->sprog = dnow;
+           }
        }
     }
+    updatetooltip();
 }
 
 void trlscb(int resp, void *data)
@@ -191,13 +302,18 @@ void trlscb(int resp, void *data)
     updatetrinfo();
 }
 
+gint trupdatecb(gpointer data)
+{
+    updatetrinfo();
+    return(TRUE);
+}
+
 void logincb(int err, wchar_t *reason, void *data)
 {
     if(err != DC_LOGIN_ERR_SUCCESS) {
        msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server"));
        exit(1);
     }
-    gtk_status_icon_set_tooltip(tray, "Dolda Connect");
     dc_queuecmd(NULL, NULL, L"notify", L"trans:act", L"on", L"trans:prog", L"on", NULL);
     dc_gettrlistasync(trlscb, NULL);
     connected = 1;
@@ -233,6 +349,7 @@ void dcfdcb(gpointer data, gint source, GdkInputCondition cond)
            dc_uimisc_handlenotify(resp);
            updatetrinfo();
        }
+       dc_freeresp(resp);
     }
     updatewrite();
 }
@@ -254,7 +371,7 @@ void updatewrite(void)
 
 void connectdc(void)
 {
-    if((dcfd = dc_connect(dc_srv_local)) < 0) {
+    if((dcfd = dc_connect(remote?NULL:dc_srv_local)) < 0) {
        msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server: %s"), strerror(errno));
        exit(1);
     }
@@ -337,12 +454,28 @@ void dolcon(void)
     if((dcpid = fork()) == 0) {
        for(i = 3; i < FD_SETSIZE; i++)
            close(i);
-       execlp("dolcon", "dolcon", NULL);
+       if(remote)
+           execlp("dolcon", "dolcon", NULL);
+       else
+           execlp("dolcon", "dolcon", "-l", NULL);
        perror("dolcon");
        exit(127);
     }
 }
 
+void cb_shm_dolconf_activate(GtkWidget *uu1, gpointer uu2)
+{
+    int i;
+    
+    if((dcpid = fork()) == 0) {
+       for(i = 3; i < FD_SETSIZE; i++)
+           close(i);
+       execlp("dolconf", "dolconf", NULL);
+       perror("dolconf");
+       exit(127);
+    }
+}
+
 void cb_shm_dolcon_activate(GtkWidget *uu1, gpointer uu2)
 {
     dolcon();
@@ -396,6 +529,8 @@ void inittray(void)
 
 int main(int argc, char **argv)
 {
+    int c;
+    
     setlocale(LC_ALL, "");
     bindtextdomain(PACKAGE, LOCALEDIR);
     textdomain(PACKAGE);
@@ -405,13 +540,32 @@ int main(int argc, char **argv)
 #ifdef HAVE_NOTIFY
     notify_init("Dolda Connect");
 #endif
+    while((c = getopt(argc, argv, "rh")) != -1) {
+       switch(c) {
+       case 'r':
+           remote = 1;
+           break;
+       case 'h':
+           printf("usage: doldacond-shell [-hr]\n");
+           printf("\t-h\tDisplay this help message\n");
+           printf("\t-r\tConnect to a remote host\n");
+           exit(0);
+       default:
+           fprintf(stderr, "usage: doldacond-shell [-hr]\n");
+           exit(1);
+       }
+    }
 
     create_shm_wnd();
     dcicon = gdk_pixbuf_new_from_xpm_data((const char **)dolda_icon_xpm);
     gtk_window_set_default_icon(dcicon);
     inittray();
-    startdaemon();
+    if(remote)
+       connectdc();
+    else
+       startdaemon();
     
+    g_timeout_add(10000, trupdatecb, NULL);
     gtk_main();
 
     return(0);