From 9f1b84b51d1f0434239f530b838a3bf507295506 Mon Sep 17 00:00:00 2001 From: Alexandre Abadie Date: Sun, 6 Oct 2019 15:34:29 +0200 Subject: [PATCH] sys/progress_bar: add module for configurable progress bars --- sys/include/progress_bar.h | 132 ++++++++++++++++++++++++++++++++ sys/progress_bar/Makefile | 1 + sys/progress_bar/doc.txt | 5 ++ sys/progress_bar/progress_bar.c | 106 +++++++++++++++++++++++++ 4 files changed, 244 insertions(+) create mode 100644 sys/include/progress_bar.h create mode 100644 sys/progress_bar/Makefile create mode 100644 sys/progress_bar/doc.txt create mode 100644 sys/progress_bar/progress_bar.c diff --git a/sys/include/progress_bar.h b/sys/include/progress_bar.h new file mode 100644 index 0000000000..d1f3855654 --- /dev/null +++ b/sys/include/progress_bar.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2019 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_progress_bar + * @{ + * + * @file + * @brief A simple CLI progress bar + * + * @author Alexandre Abadie + */ + +#ifndef PROGRESS_BAR_H +#define PROGRESS_BAR_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Progress bar maximum characters length + */ +#ifndef PROGRESS_BAR_LENGTH +#define PROGRESS_BAR_LENGTH (25U) +#endif + +/** + * @brief Progress bar character + */ +#ifndef PROGRESS_BAR_FULL_CHARACTER +#define PROGRESS_BAR_FULL_CHARACTER "█" +#endif + +/** + * @brief Progress bar empty character + */ +#ifndef PROGRESS_BAR_EMPTY_CHARACTER +#define PROGRESS_BAR_EMPTY_CHARACTER " " +#endif + +/** + * @brief Character displayed on the left of the progress bar + */ +#ifndef PROGRESS_BAR_PREFIX_CHARACTER +#define PROGRESS_BAR_PREFIX_CHARACTER "|" +#endif + +/** + * @brief Character displayed on the left of the progress bar + */ +#ifndef PROGRESS_BAR_SUFFIX_CHARACTER +#define PROGRESS_BAR_SUFFIX_CHARACTER "|" +#endif + +/** + * @brief Progress bar prefix max length + */ +#ifndef PROGRESS_BAR_PREFIX_MAX_LENGTH +#define PROGRESS_BAR_PREFIX_MAX_LENGTH (32U) +#endif + +/** + * @brief Progress bar suffix max length + */ +#ifndef PROGRESS_BAR_SUFFIX_MAX_LENGTH +#define PROGRESS_BAR_SUFFIX_MAX_LENGTH (32U) +#endif + +/** + * @brief Progress bar descriptor + */ +typedef struct { + /** Current value of the progress bar. Must be between 0 and 100 (included) */ + uint8_t value; + /** Prefix displayed on the left of the progress bar */ + char prefix[PROGRESS_BAR_PREFIX_MAX_LENGTH]; + /** Suffix displayed on the right of the progress bar */ + char suffix[PROGRESS_BAR_SUFFIX_MAX_LENGTH]; +} progress_bar_t; + +/** + * @brief Print a progress bar in the terminal + * + * @param[in] prefix String displayed on the left of the progress bar + * @param[in] suffix String displayed on the right of the progress bar + * @param[in] value Value of the progress bar + */ +void progress_bar_print(char *prefix, char *suffix, uint8_t value); + +/** + * @brief Update the progress bar display in the terminal + * + * @param[in] progress_bar Pointer to the progress bar descriptor + */ +void progress_bar_update(progress_bar_t *progress_bar); + +/** + * @brief Prepare the output for displaying multiple progress bars. + * + * This function is just adding enough empty lines to give enough space to + * print the list of progress bars. + * + * This function must be called only once and before starting the progress bar + * list updates with. + * + * @param[in] len The length of the progress bar array + */ +void progress_bar_prepare_multi(uint8_t len); + +/** + * @brief Update all progress bar displays of the given progress bars list + * + * @param[in] progress_bar_list An array of progress bars + * @param[in] len The length of the progress bar array + */ +void progress_bar_update_multi(progress_bar_t *progress_bar_list, uint8_t len); + +#ifdef __cplusplus +} +#endif + +/** @} */ +#endif /* PROGRESS_BAR_H */ diff --git a/sys/progress_bar/Makefile b/sys/progress_bar/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/progress_bar/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/progress_bar/doc.txt b/sys/progress_bar/doc.txt new file mode 100644 index 0000000000..3473a6c3a9 --- /dev/null +++ b/sys/progress_bar/doc.txt @@ -0,0 +1,5 @@ +/** + * @defgroup sys_progress_bar A terminal progress bar + * @ingroup sys + * @brief Manage a progress bar on the standard output + */ diff --git a/sys/progress_bar/progress_bar.c b/sys/progress_bar/progress_bar.c new file mode 100644 index 0000000000..70ee709885 --- /dev/null +++ b/sys/progress_bar/progress_bar.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2019 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup sys_progress_bar + * @{ + * + * @file + * @brief Progress bar implementation + * + * @author Alexandre Abadie + * + * @} + */ + +#include +#include +#include + +#include "progress_bar.h" + +void progress_bar_print(char *prefix, char *suffix, uint8_t value) +{ + if (value > 100) { + value = 100; + } + + /* Hide cursor */ + printf("\033[?25l"); + + /* Hack for pyterm: prepare space for the progress bar */ + putchar('\n'); + printf("\033[1A"); + + /* Move cursor at the beginning of the line */ + putchar('\r'); + + /* Display progress bar prefix if any */ + if (prefix) { + printf("%s", prefix); + } + + printf(PROGRESS_BAR_PREFIX_CHARACTER); + + /* Fully reprint the progress bar */ + for (unsigned i = 0; i < PROGRESS_BAR_LENGTH; ++i) { + if (100 * i < (uint16_t)(value * PROGRESS_BAR_LENGTH)) { + printf(PROGRESS_BAR_FULL_CHARACTER); + } + else { + printf(PROGRESS_BAR_EMPTY_CHARACTER); + } + } + + printf(PROGRESS_BAR_SUFFIX_CHARACTER); + + /* Display progress bar suffix if any */ + if (suffix) { + printf("%s", suffix); + } + + /* Hack for pyterm */ + printf("\033[s"); + putchar('\n'); + printf("\033[u"); + + /* show cursor */ + printf("\033[?25h"); + +#ifdef MODULE_NEWLIB + fflush(stdout); +#endif +} + +void progress_bar_update(progress_bar_t *progress_bar) +{ + progress_bar_print(progress_bar->prefix, progress_bar->suffix, + progress_bar->value); +} + +void progress_bar_prepare_multi(uint8_t len) +{ + /* Give enough space to print all progress bars. */ + for (uint8_t i = 0; i < len; ++i) { + putchar('\n'); + } +} + +void progress_bar_update_multi(progress_bar_t *progress_bar_list, uint8_t len) +{ + /* Move cursor to the line of the first progress bar. */ + printf("\033[%dA", len); + + for (uint8_t i = 0; i < len; ++i) { + /* Display each progress bar as usual */ + progress_bar_update(&progress_bar_list[i]); + + /* Move cursor to next progress bar line. */ + putchar('\n'); + } +}