#include <pakfire/progressbar.h>
#include <pakfire/util.h>
-#define REDRAW_TIMEOUT 250
+#define DRAWS_PER_SECOND 20
+
+static const struct itimerspec TIMER = {
+ .it_interval = {
+ .tv_sec = 0,
+ .tv_nsec = 1000000000 / DRAWS_PER_SECOND,
+ },
+ .it_value = {
+ .tv_sec = 0,
+ .tv_nsec = 1000000000 / DRAWS_PER_SECOND,
+ },
+};
struct pakfire_progressbar_widget {
STAILQ_ENTRY(pakfire_progressbar_widget) nodes;
// The progress
unsigned long value;
unsigned long value_max;
- unsigned long value_redraw;
- unsigned long update_interval;
struct timespec time_start;
- struct timespec time_redraw;
+ timer_t timer;
// Widgets
STAILQ_HEAD(widgets, pakfire_progressbar_widget) widgets;
}
static void pakfire_progressbar_free(struct pakfire_progressbar* p) {
+ // Delete the timer
+ if (p->timer)
+ timer_delete(p->timer);
+
pakfire_progressbar_free_widgets(p);
free(p);
}
+static int pakfire_progressbar_draw(struct pakfire_progressbar* p) {
+ struct pakfire_progressbar_widget* widget;
+
+ // Update terminal size if not set
+ if (!terminal.cols)
+ pakfire_progressbar_update_terminal_size(SIGWINCH);
+
+ unsigned int cols_left = terminal.cols - p->num_widgets - 2;
+ unsigned int i = 0;
+
+ // Create an array with the result of all print functions
+ const char* elements[p->num_widgets];
+
+ // Reset all elements
+ for (i = 0; i < p->num_widgets; i++)
+ elements[i] = NULL;
+
+ // Reset i
+ i = 0;
+
+ // Process all non-expandable widgets in the first pass
+ STAILQ_FOREACH(widget, &p->widgets, nodes) {
+ const char* element = NULL;
+
+ // Clear any previous content
+ if (*widget->buffer)
+ memset(widget->buffer, '\0', sizeof(widget->buffer));
+
+ if (!widget->expandable) {
+ element = widget->print(p, widget, 0, widget->data);
+ if (element)
+ cols_left -= strlen(element);
+ }
+
+ elements[i++] = element;
+ }
+
+ // How many expandable widgets are left?
+ int num_expandables = p->num_widgets - i;
+
+ // How much space do we allocate to each of them?
+ int width = cols_left - num_expandables;
+
+ i = 0;
+
+ // Process all expandable widgets
+ STAILQ_FOREACH(widget, &p->widgets, nodes) {
+ const char* element = elements[i];
+
+ if (widget->expandable) {
+ element = widget->print(p, widget, width, widget->data);
+ }
+
+ elements[i++] = element;
+ }
+
+ // Reset the line
+ fputs("\r", p->f);
+
+ // Print all elements
+ for (i = 0; i < p->num_widgets; i++) {
+ // Skip anything that returned nothing
+ if (!elements[i])
+ continue;
+
+ fputs(" ", p->f);
+ fputs(elements[i], p->f);
+ }
+
+ // Flush everything
+ fflush(p->f);
+
+ return 0;
+}
+
+static void __pakfire_progressbar_draw(union sigval data) {
+ struct pakfire_progressbar* p = (struct pakfire_progressbar*)data.sival_ptr;
+
+ // Draw unconditionally
+ pakfire_progressbar_draw(p);
+}
+
PAKFIRE_EXPORT int pakfire_progressbar_create(
struct pakfire_progressbar** progressbar, FILE* f) {
struct pakfire_progressbar* p = calloc(1, sizeof(*p));
// Setup widgets
STAILQ_INIT(&p->widgets);
+ struct sigevent sigevent = {
+ .sigev_notify = SIGEV_THREAD,
+ .sigev_notify_function = __pakfire_progressbar_draw,
+ .sigev_value = {
+ .sival_ptr = p,
+ },
+ };
+
+ // Create timer
+ int r = timer_create(CLOCK_REALTIME, &sigevent, &p->timer);
+ if (r)
+ goto ERROR;
+
// Done
*progressbar = p;
return 0;
+
+ERROR:
+ pakfire_progressbar_free(p);
+
+ return r;
}
PAKFIRE_EXPORT struct pakfire_progressbar* pakfire_progressbar_ref(struct pakfire_progressbar* p) {
static time_t pakfire_progressbar_elapsed_time(struct pakfire_progressbar* p) {
struct timespec now;
- int r = clock_gettime(CLOCK_MONOTONIC, &now);
+ int r = clock_gettime(CLOCK_REALTIME, &now);
if (r)
return r;
// Set maximum value
pakfire_progressbar_set_max(p, value);
+ // Start timer
+ int r = timer_settime(p->timer, 0, &TIMER, NULL);
+ if (r)
+ return r;
+
// Set start time
- int r = clock_gettime(CLOCK_MONOTONIC, &p->time_start);
+ r = clock_gettime(CLOCK_REALTIME, &p->time_start);
if (r)
return r;
- return pakfire_progressbar_update(p, 0);
-}
+ // Set initial value
+ r = pakfire_progressbar_update(p, 0);
+ if (r)
+ return r;
-static int pakfire_progressbar_redraw(struct pakfire_progressbar* p);
+ // Perform an initial draw
+ r = pakfire_progressbar_draw(p);
+ if (r)
+ return r;
+
+ return r;
+}
PAKFIRE_EXPORT int pakfire_progressbar_update(struct pakfire_progressbar* p, unsigned long value) {
if (p->status == PAKFIRE_PROGRESSBAR_INIT)
return EINVAL;
p->value = value;
-
- return pakfire_progressbar_redraw(p);
+ return 0;
}
PAKFIRE_EXPORT int pakfire_progressbar_increment(struct pakfire_progressbar* p, unsigned long value) {
if (r)
return r;
+ // Perform a final draw
+ pakfire_progressbar_draw(p);
+
// Finish line
r = fputs("\n", p->f);
if (r <= 0 || r == EOF)
// Reset all values
p->value = 0;
p->value_max = 0;
- p->value_redraw = 0;
- p->update_interval = 0;
p->time_start.tv_sec = 0;
p->time_start.tv_nsec = 0;
- p->time_redraw.tv_sec = 0;
- p->time_redraw.tv_nsec = 0;
return 0;
}
PAKFIRE_EXPORT void pakfire_progressbar_set_max(
struct pakfire_progressbar* p, unsigned long value) {
- // Do nothing if nothing has changed
- if (p->value_max == value)
- return;
-
// Store maximum value
p->value_max = value;
-
- // Redraw immediately
- p->value_redraw = 0;
-
- // Update terminal size if not set
- if (!terminal.cols)
- pakfire_progressbar_update_terminal_size(SIGWINCH);
-
- // Calculate update interval
- p->update_interval = p->value_max / terminal.cols;
}
static int pakfire_progressbar_add_widget(struct pakfire_progressbar* p,
return 0;
}
-/*
- This functions determines whether this progressbar needs to be redrawn
- and sets any markers to determine the next redraw.
-*/
-static int pakfire_progressbar_needs_redraw(struct pakfire_progressbar* p) {
- struct timespec now;
-
- // Fetch the current time
- int r = clock_gettime(CLOCK_MONOTONIC, &now);
- if (r)
- return r;
-
- // Redraw when we surpassed the next redraw value
- if (p->value >= p->value_redraw)
- goto REDRAW;
-
- // Redraw when we hit the timeout
- if (timespec_lt(&p->time_redraw, &now))
- goto REDRAW;
-
- // Redraw when we are finished
- if (p->status == PAKFIRE_PROGRESSBAR_FINISHED)
- goto REDRAW;
-
- // No need to redraw
- return 0;
-
-REDRAW:
- // Compute next redraw steps
- p->value_redraw = p->value + p->update_interval;
-
- struct timespec timeout = timespec_from_ms(REDRAW_TIMEOUT);
- p->time_redraw = timespec_add(&now, &timeout);
-
- return 1;
-}
-
-static int pakfire_progressbar_redraw(struct pakfire_progressbar* p) {
- struct pakfire_progressbar_widget* widget;
-
- // Return when we should not be redrawing
- if (!pakfire_progressbar_needs_redraw(p))
- return 0;
-
- // Update terminal size if not set
- if (!terminal.cols)
- pakfire_progressbar_update_terminal_size(SIGWINCH);
-
- unsigned int cols_left = terminal.cols - p->num_widgets - 2;
- unsigned int i = 0;
-
- // Create an array with the result of all print functions
- const char* elements[p->num_widgets];
-
- // Reset all elements
- for (i = 0; i < p->num_widgets; i++)
- elements[i] = NULL;
-
- // Reset i
- i = 0;
-
- // Process all non-expandable widgets in the first pass
- STAILQ_FOREACH(widget, &p->widgets, nodes) {
- const char* element = NULL;
-
- // Clear any previous content
- if (*widget->buffer)
- memset(widget->buffer, '\0', sizeof(widget->buffer));
-
- if (!widget->expandable) {
- element = widget->print(p, widget, 0, widget->data);
- if (element)
- cols_left -= strlen(element);
- }
-
- elements[i++] = element;
- }
-
- // How many expandable widgets are left?
- int num_expandables = p->num_widgets - i;
-
- // How much space do we allocate to each of them?
- int width = cols_left - num_expandables;
-
- i = 0;
- // Process all expandable widgets
- STAILQ_FOREACH(widget, &p->widgets, nodes) {
- const char* element = elements[i];
-
- if (widget->expandable) {
- element = widget->print(p, widget, width, widget->data);
- }
-
- elements[i++] = element;
- }
-
- // Reset the line
- fputs("\r", p->f);
-
- // Print all elements
- for (i = 0; i < p->num_widgets; i++) {
- // Skip anything that returned nothing
- if (!elements[i])
- continue;
-
- fputs(" ", p->f);
- fputs(elements[i], p->f);
- }
-
- return 0;
-}
// String widget