/* Downloads progression stuff. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "elinks.h" #include "network/progress.h" #include "util/error.h" #include "util/memory.h" #include "util/time.h" #define SPD_DISP_TIME ((milliseconds_T) 100) #define CURRENT_SPD_AFTER ((milliseconds_T) 100) int has_progress(struct progress *progress) { timeval_T current_speed_after; timeval_from_milliseconds(¤t_speed_after, CURRENT_SPD_AFTER); return (timeval_cmp(&progress->elapsed, ¤t_speed_after) >= 0); } struct progress * init_progress(off_t start) { struct progress *progress = mem_calloc(1, sizeof(*progress)); if (progress) { progress->start = start; progress->timer = TIMER_ID_UNDEF; } return progress; } void done_progress(struct progress *progress) { assert(progress->timer == TIMER_ID_UNDEF); mem_free(progress); } /** Timer callback for progress.timer. As explained in install_timer(), * this function must erase the expired timer ID from all variables. */ static void progress_timeout(void *progress_voidptr) { struct progress *const progress = progress_voidptr; progress->timer = TIMER_ID_UNDEF; /* The expired timer ID has now been erased. */ progress->timer_func(progress->timer_func_data); } /* Usually called from the timer callback of @progress->timer. */ void update_progress(struct progress *progress, off_t loaded, off_t size, off_t pos) { off_t bytes_delta; timeval_T now, elapsed, dis_b_max, dis_b_interval; timeval_now(&now); timeval_sub(&elapsed, &progress->last_time, &now); timeval_copy(&progress->last_time, &now); progress->loaded = loaded; bytes_delta = progress->loaded - progress->last_loaded; progress->last_loaded = progress->loaded; timeval_add_interval(&progress->elapsed, &elapsed); timeval_add_interval(&progress->dis_b, &elapsed); timeval_from_milliseconds(&dis_b_max, mult_ms(SPD_DISP_TIME, CURRENT_SPD_SEC)); timeval_from_milliseconds(&dis_b_interval, SPD_DISP_TIME); while (timeval_cmp(&progress->dis_b, &dis_b_max) >= 0) { progress->cur_loaded -= progress->data_in_secs[0]; memmove(progress->data_in_secs, progress->data_in_secs + 1, sizeof(*progress->data_in_secs) * (CURRENT_SPD_SEC - 1)); progress->data_in_secs[CURRENT_SPD_SEC - 1] = 0; timeval_sub_interval(&progress->dis_b, &dis_b_interval); } progress->data_in_secs[CURRENT_SPD_SEC - 1] += bytes_delta; progress->cur_loaded += bytes_delta; progress->current_speed = progress->cur_loaded / (CURRENT_SPD_SEC * ((long) SPD_DISP_TIME) / 1000); progress->pos = pos; progress->size = size; if (progress->size != -1 && progress->size < progress->pos) progress->size = progress->pos; progress->average_speed = timeval_div_off_t(progress->loaded, &progress->elapsed); if (progress->average_speed) /* Division by zero risk */ timeval_from_seconds(&progress->estimated_time, (progress->size - progress->pos) / progress->average_speed); install_timer(&progress->timer, SPD_DISP_TIME, progress_timeout, progress); } /*! Unlike in install_timer(), @a timer_func need not erase the * expired timer ID from @a progress->timer. update_progress() * installs the timer with a wrapper function that takes care of * erasing the timer ID. */ void start_update_progress(struct progress *progress, void (*timer_func)(void *), void *timer_func_data) { if (!progress->valid) { struct progress tmp; /* Just copy useful fields from invalid progress. */ memset(&tmp, 0, sizeof(tmp)); tmp.start = progress->start; tmp.seek = progress->seek; tmp.valid = 1; memcpy(progress, &tmp, sizeof(*progress)); } timeval_now(&progress->last_time); progress->last_loaded = progress->loaded; progress->timer_func = timer_func; progress->timer_func_data = timer_func_data; }