// SPDX-License-Identifier: BSD-3-Clause #include #ifdef _WIN32 #include #else #include #endif #include #include #include "qdl.h" #define UX_PROGRESS_REFRESH_RATE 10 #define UX_PROGRESS_SIZE_MAX 80 #define HASHES "################################################################################" #define DASHES "--------------------------------------------------------------------------------" static const char * const progress_hashes = HASHES; static const char * const progress_dashes = DASHES; static unsigned int ux_width; static unsigned int ux_cur_line_length; /* * Levels of output: * * error: used to signal errors to the user * info: used to inform the user about progress * logs: log prints from the device * debug: protocol logs */ /* Clear ux_cur_line_length characters of the progress bar from the screen */ static void ux_clear_line(void) { if (!ux_cur_line_length) return; printf("%*s\r", ux_cur_line_length, ""); fflush(stdout); ux_cur_line_length = 0; } #ifdef _WIN32 void ux_init(void) { CONSOLE_SCREEN_BUFFER_INFO csbi; int columns; HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); if (GetConsoleScreenBufferInfo(stdoutHandle, &csbi)) { columns = csbi.srWindow.Right - csbi.srWindow.Left + 1; ux_width = MIN(columns, UX_PROGRESS_SIZE_MAX); } } #else void ux_init(void) { struct winsize w; int ret; ret = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); if (!ret) ux_width = MIN(w.ws_col, UX_PROGRESS_SIZE_MAX); } #endif void ux_err(const char *fmt, ...) { va_list ap; ux_clear_line(); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fflush(stderr); } void ux_info(const char *fmt, ...) { va_list ap; ux_clear_line(); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); fflush(stdout); } void ux_log(const char *fmt, ...) { va_list ap; if (!qdl_debug) return; ux_clear_line(); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); fflush(stdout); } void ux_debug(const char *fmt, ...) { va_list ap; if (!qdl_debug) return; ux_clear_line(); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); fflush(stdout); } void ux_progress(const char *fmt, unsigned int value, unsigned int max, ...) { static struct timeval last_progress_update; unsigned long elapsed_us; unsigned int bar_length; unsigned int bars; unsigned int dashes; struct timeval now; char task_name[32]; float percent; va_list ap; /* Don't print progress is window is too narrow, or if stdout is redirected */ if (ux_width < 30) return; /* Avoid updating the console more than UX_PROGRESS_REFRESH_RATE per second */ if (last_progress_update.tv_sec) { gettimeofday(&now, NULL); elapsed_us = (now.tv_sec - last_progress_update.tv_sec) * 1000000 + (now.tv_usec - last_progress_update.tv_usec); if (elapsed_us < (1000000 / UX_PROGRESS_REFRESH_RATE)) return; } if (value > max) value = max; va_start(ap, max); vsnprintf(task_name, sizeof(task_name), fmt, ap); va_end(ap); bar_length = ux_width - (20 + 4 + 6); percent = (float)value / max; bars = percent * bar_length; dashes = bar_length - bars; printf("%-20.20s [%.*s%.*s] %1.2f%%%n\r", task_name, bars, progress_hashes, dashes, progress_dashes, percent * 100, &ux_cur_line_length); fflush(stdout); gettimeofday(&last_progress_update, NULL); }