#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,
- },
+static const struct timespec TIMER = {
+ .tv_sec = 0,
+ .tv_nsec = 1000000000 / DRAWS_PER_SECOND,
};
struct pakfire_progressbar_widget {
int nrefs;
FILE* f;
- enum pakfire_progressbar_status {
+ volatile enum pakfire_progressbar_status {
PAKFIRE_PROGRESSBAR_INIT = 0,
PAKFIRE_PROGRESSBAR_RUNNING,
PAKFIRE_PROGRESSBAR_FINISHED,
} status;
// The progress
- unsigned long value;
- unsigned long value_max;
+ volatile unsigned long value;
+ volatile unsigned long value_max;
struct timespec time_start;
- timer_t timer;
- pthread_mutex_t drawlock;
+
+ // Reference to the thread that is drawing the progress bar
+ pthread_t renderer;
// 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);
-
- // Free muted
- pthread_mutex_destroy(&p->drawlock);
-
pakfire_progressbar_free_widgets(p);
free(p);
}
static int pakfire_progressbar_draw(struct pakfire_progressbar* p) {
struct pakfire_progressbar_widget* widget;
- int r;
-
- // Acquire the lock
- r = pthread_mutex_lock(&p->drawlock);
- if (r)
- return r;
// Update terminal size if not set
if (!terminal.cols)
// Flush everything
fflush(p->f);
- // Release lock
- pthread_mutex_unlock(&p->drawlock);
-
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) {
- int r;
-
// Allocate main object
struct pakfire_progressbar* p = calloc(1, sizeof(*p));
if (!p)
// Store output
p->f = stdout;
- // Acquire lock
- r = pthread_mutex_init(&p->drawlock, NULL);
- if (r)
- goto ERROR;
-
// Register signal handler to update terminal size
signal(SIGWINCH, pakfire_progressbar_update_terminal_size);
// 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
- r = timer_create(CLOCK_REALTIME, &sigevent, &p->timer);
- if (r)
- goto ERROR;
-
// Done
*progressbar = p;
- return 0;
-ERROR:
- pakfire_progressbar_free(p);
-
- return r;
+ return 0;
}
PAKFIRE_EXPORT struct pakfire_progressbar* pakfire_progressbar_ref(struct pakfire_progressbar* p) {
return now.tv_sec - p->time_start.tv_sec;
}
+static void* __pakfire_progressbar_renderer(void* __p) {
+ struct pakfire_progressbar* p = (struct pakfire_progressbar*)__p;
+ int r;
+
+ do {
+ // Redraw the progress bar
+ r = pakfire_progressbar_draw(p);
+ if (r)
+ goto ERROR;
+
+ // Sleep for a short moment
+ nanosleep(&TIMER, NULL);
+
+ // Keep doing this for as long as we are running
+ } while (p->status == PAKFIRE_PROGRESSBAR_RUNNING);
+
+ // Set to maximum value
+ r = pakfire_progressbar_update(p, p->value_max);
+ if (r)
+ goto ERROR;
+
+ // Perform a final draw
+ r = pakfire_progressbar_draw(p);
+ if (r)
+ goto ERROR;
+
+ // Finish line
+ r = fputs("\n", p->f);
+ if (r <= 0 || r == EOF) {
+ r = 1;
+ goto ERROR;
+ }
+
+ // Success
+ r = 0;
+
+ERROR:
+ return (void *)(intptr_t)r;
+}
+
PAKFIRE_EXPORT int pakfire_progressbar_start(struct pakfire_progressbar* p, unsigned long value) {
+ int r;
+
if (p->status == PAKFIRE_PROGRESSBAR_RUNNING)
return EINVAL;
// 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
r = clock_gettime(CLOCK_REALTIME, &p->time_start);
if (r)
if (r)
return r;
- // Perform an initial draw
- r = pakfire_progressbar_draw(p);
- if (r)
- return r;
-
- return r;
+ // Launch the renderer thread
+ return pthread_create(&p->renderer, NULL, __pakfire_progressbar_renderer, p);
}
PAKFIRE_EXPORT int pakfire_progressbar_update(struct pakfire_progressbar* p, unsigned long value) {
}
PAKFIRE_EXPORT int pakfire_progressbar_finish(struct pakfire_progressbar* p) {
+ int r;
+ int retval = 0;
+
if (p->status != PAKFIRE_PROGRESSBAR_RUNNING)
return EINVAL;
// Set status
p->status = PAKFIRE_PROGRESSBAR_FINISHED;
- // Set to maximum value
- int r = pakfire_progressbar_update(p, p->value_max);
+ // Wait until the render thread is done
+ r = pthread_join(p->renderer, (void**)&retval);
if (r)
return r;
- // Perform a final draw
- pakfire_progressbar_draw(p);
-
- // Finish line
- r = fputs("\n", p->f);
- if (r <= 0 || r == EOF)
- return 1;
-
- return 0;
+ return retval;
}
PAKFIRE_EXPORT int pakfire_progressbar_reset(struct pakfire_progressbar* p) {
return 0;
}
-
-
// String widget
static const char* pakfire_progressbar_string(struct pakfire_progressbar* p,