12
12
13
13
#include <assert.h>
14
14
#include <limits.h>
15
+ #include <unistd.h>
15
16
#include "progressbar.h"
16
17
17
18
/// How wide we assume the screen is if termcap fails.
@@ -32,6 +33,12 @@ enum { WHITESPACE_LENGTH = 2 };
32
33
/// The amount of width taken up by the border of the bar component.
33
34
enum { BAR_BORDER_WIDTH = 2 };
34
35
36
+ /// The maximum number of bar redraws (to avoid frequent output in long runs)
37
+ enum { BAR_DRAW_COUNT_MAX = 500 };
38
+
39
+ enum { BAR_DRAW_INTERVAL = 1 ,
40
+ BAR_DRAW_INTERVAL_NOTTY = 5 };
41
+
35
42
/// Models a duration of time broken into hour/minute/second components. The number of seconds
36
43
/// should be less than the
37
44
/// number of seconds in one minute, and the number of minutes should be less than the number of
@@ -43,6 +50,7 @@ typedef struct {
43
50
} progressbar_time_components ;
44
51
45
52
static void progressbar_draw (const progressbar * bar );
53
+ static int progressbar_remaining_seconds (const progressbar * bar );
46
54
47
55
/**
48
56
* Create a new progress bar with the specified label, max number of steps, and format string.
@@ -57,6 +65,7 @@ progressbar* progressbar_new_with_format(const char* label, unsigned long max, c
57
65
58
66
new -> max = max ;
59
67
new -> value = 0 ;
68
+ new -> draw_time_interval = isatty (STDOUT_FILENO )? BAR_DRAW_INTERVAL : BAR_DRAW_INTERVAL_NOTTY ;
60
69
new -> t = 0 ;
61
70
new -> start = time (NULL );
62
71
assert (3 == strlen (format ) && "format must be 3 characters in length" );
@@ -66,6 +75,8 @@ progressbar* progressbar_new_with_format(const char* label, unsigned long max, c
66
75
67
76
progressbar_update_label (new , label );
68
77
progressbar_draw (new );
78
+ new -> prev_t = difftime (time (NULL ), new -> start );
79
+ new -> drawn_count = 1 ;
69
80
70
81
return new ;
71
82
}
@@ -90,11 +101,45 @@ void progressbar_free(progressbar* bar) {
90
101
91
102
/**
92
103
* Increment an existing progressbar by `value` steps.
104
+ * Additionally issues a redraw in case a certain time interval has elapsed (min: 1sec)
105
+ * Reasons for a larger interval are:
106
+ * - Stdout is not TTY
107
+ * - Respect BAR_DRAW_COUNT_MAX
93
108
*/
94
109
void progressbar_update (progressbar * bar , unsigned long value , double t ) {
95
110
bar -> value = value ;
96
111
bar -> t = t ;
112
+ int sim_time = difftime (time (NULL ), bar -> start );
113
+
114
+ // If there is not enough time passed to redraw the progress bar return
115
+ if ((sim_time - bar -> prev_t ) < bar -> draw_time_interval ) {
116
+ return ;
117
+ }
118
+
97
119
progressbar_draw (bar );
120
+
121
+ bar -> drawn_count ++ ;
122
+ bar -> prev_t = sim_time ;
123
+
124
+ if (bar -> drawn_count >= BAR_DRAW_COUNT_MAX || sim_time < 15 ) {
125
+ // Dont change the interval after the limit. Simulation should be over any moment and
126
+ // avoid the calc of draw_time_interval which could raise DIV/0
127
+ // Also, dont do it the first 15sec to avoid really bad estimates which could potentially
128
+ // delay a better estimate too far away in the future.
129
+ return ;
130
+ }
131
+
132
+ // Sample ETA to calculate the next interval until the redraw of the progressbar
133
+ int eta_s = progressbar_remaining_seconds (bar );
134
+ bar -> draw_time_interval = eta_s / (BAR_DRAW_COUNT_MAX - bar -> drawn_count );
135
+
136
+ if (bar -> draw_time_interval < BAR_DRAW_INTERVAL_NOTTY ) {
137
+ bar -> draw_time_interval =
138
+ isatty (STDOUT_FILENO )
139
+ ? ((bar -> draw_time_interval < BAR_DRAW_INTERVAL )? BAR_DRAW_INTERVAL
140
+ : bar -> draw_time_interval )
141
+ : BAR_DRAW_INTERVAL_NOTTY ;
142
+ }
98
143
}
99
144
100
145
/**
@@ -191,6 +236,7 @@ static void progressbar_draw(const progressbar* bar) {
191
236
fputc (' ' , stdout );
192
237
fprintf (stdout , ETA_FORMAT , bar -> t , eta .hours , eta .minutes , eta .seconds );
193
238
fputc ('\r' , stdout );
239
+ fflush (stdout );
194
240
}
195
241
196
242
/**
0 commit comments