diff options
author | トトも <85485984+ElectronicsArchiver@users.noreply.github.com> | 2022-04-16 20:18:56 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-16 14:18:56 -0400 |
commit | 33cc3b9dd7bf3dae1c6cf86e46bb4923f96e7fff (patch) | |
tree | 02fb808d19aee560ac9d381ca5a52509881cdd44 /src/bar.c | |
parent | 8f5a73a6585bd425807430fd80ce1e3a737f4c5f (diff) | |
download | bfs-33cc3b9dd7bf3dae1c6cf86e46bb4923f96e7fff.tar.xz |
Source / Include Folder (#88)
Moved Source Files Into `src` Folder
Diffstat (limited to 'src/bar.c')
-rw-r--r-- | src/bar.c | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/src/bar.c b/src/bar.c new file mode 100644 index 0000000..b0e595e --- /dev/null +++ b/src/bar.c @@ -0,0 +1,248 @@ +/**************************************************************************** + * bfs * + * Copyright (C) 2020-2022 Tavian Barnes <tavianator@tavianator.com> * + * * + * Permission to use, copy, modify, and/or distribute this software for any * + * purpose with or without fee is hereby granted. * + * * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * + ****************************************************************************/ + +#include "bar.h" +#include "dstring.h" +#include "util.h" +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <sys/ioctl.h> +#include <unistd.h> + +struct bfs_bar { + int fd; + volatile sig_atomic_t width; + volatile sig_atomic_t height; +}; + +/** The global status bar instance. */ +static struct bfs_bar the_bar = { + .fd = -1, +}; + +/** Get the terminal size, if possible. */ +static int bfs_bar_getsize(struct bfs_bar *bar) { +#ifdef TIOCGWINSZ + struct winsize ws; + if (ioctl(bar->fd, TIOCGWINSZ, &ws) != 0) { + return -1; + } + + bar->width = ws.ws_col; + bar->height = ws.ws_row; + return 0; +#else + errno = ENOTSUP; + return -1; +#endif +} + +/** Async Signal Safe puts(). */ +static int ass_puts(int fd, const char *str) { + size_t len = strlen(str); + return xwrite(fd, str, len) == len ? 0 : -1; +} + +/** Number of decimal digits needed for terminal sizes. */ +#define ITOA_DIGITS ((sizeof(unsigned short) * CHAR_BIT + 2) / 3) + +/** Async Signal Safe itoa(). */ +static char *ass_itoa(char *str, unsigned int n) { + char *end = str + ITOA_DIGITS; + *end = '\0'; + + char *c = end; + do { + *--c = '0' + (n % 10); + n /= 10; + } while (n); + + size_t len = end - c; + memmove(str, c, len + 1); + return str + len; +} + +/** Update the size of the scrollable region. */ +static int bfs_bar_resize(struct bfs_bar *bar) { + char esc_seq[12 + ITOA_DIGITS] = + "\0337" // DECSC: Save cursor + "\033[;"; // DECSTBM: Set scrollable region + + // DECSTBM takes the height as the second argument + char *ptr = esc_seq + strlen(esc_seq); + ptr = ass_itoa(ptr, bar->height - 1); + + strcpy(ptr, + "r" // DECSTBM + "\0338" // DECRC: Restore the cursor + "\033[J" // ED: Erase display from cursor to end + ); + + return ass_puts(bar->fd, esc_seq); +} + +#ifdef SIGWINCH +/** SIGWINCH handler. */ +static void sighand_winch(int sig) { + int error = errno; + + bfs_bar_getsize(&the_bar); + bfs_bar_resize(&the_bar); + + errno = error; +} +#endif + +/** Reset the scrollable region and hide the bar. */ +static int bfs_bar_reset(struct bfs_bar *bar) { + return ass_puts(bar->fd, + "\0337" // DECSC: Save cursor + "\033[r" // DECSTBM: Reset scrollable region + "\0338" // DECRC: Restore cursor + "\033[J" // ED: Erase display from cursor to end + ); +} + +/** Signal handler for process-terminating signals. */ +static void sighand_reset(int sig) { + bfs_bar_reset(&the_bar); + raise(sig); +} + +/** Register sighand_reset() for a signal. */ +static void reset_before_death_by(int sig) { + struct sigaction sa = { + .sa_handler = sighand_reset, + .sa_flags = SA_RESETHAND, + }; + sigemptyset(&sa.sa_mask); + sigaction(sig, &sa, NULL); +} + +/** printf() to the status bar with a single write(). */ +BFS_FORMATTER(2, 3) +static int bfs_bar_printf(struct bfs_bar *bar, const char *format, ...) { + va_list args; + va_start(args, format); + char *str = dstrvprintf(format, args); + va_end(args); + + if (!str) { + return -1; + } + + int ret = ass_puts(bar->fd, str); + dstrfree(str); + return ret; +} + +struct bfs_bar *bfs_bar_show(void) { + if (the_bar.fd >= 0) { + errno = EBUSY; + goto fail; + } + + char term[L_ctermid]; + ctermid(term); + if (strlen(term) == 0) { + errno = ENOTTY; + goto fail; + } + + the_bar.fd = open(term, O_RDWR | O_CLOEXEC); + if (the_bar.fd < 0) { + goto fail; + } + + if (bfs_bar_getsize(&the_bar) != 0) { + goto fail_close; + } + + reset_before_death_by(SIGABRT); + reset_before_death_by(SIGINT); + reset_before_death_by(SIGPIPE); + reset_before_death_by(SIGQUIT); + reset_before_death_by(SIGTERM); + +#ifdef SIGWINCH + struct sigaction sa = { + .sa_handler = sighand_winch, + .sa_flags = SA_RESTART, + }; + sigemptyset(&sa.sa_mask); + sigaction(SIGWINCH, &sa, NULL); +#endif + + bfs_bar_printf(&the_bar, + "\n" // Make space for the bar + "\0337" // DECSC: Save cursor + "\033[;%ur" // DECSTBM: Set scrollable region + "\0338" // DECRC: Restore cursor + "\033[1A", // CUU: Move cursor up 1 row + (unsigned int)(the_bar.height - 1) + ); + + return &the_bar; + +fail_close: + close_quietly(the_bar.fd); + the_bar.fd = -1; +fail: + return NULL; +} + +unsigned int bfs_bar_width(const struct bfs_bar *bar) { + return bar->width; +} + +int bfs_bar_update(struct bfs_bar *bar, const char *str) { + return bfs_bar_printf(bar, + "\0337" // DECSC: Save cursor + "\033[%u;0f" // HVP: Move cursor to row, column + "\033[K" // EL: Erase line + "\033[7m" // SGR reverse video + "%s" + "\033[27m" // SGR reverse video off + "\0338", // DECRC: Restore cursor + (unsigned int)bar->height, + str + ); +} + +void bfs_bar_hide(struct bfs_bar *bar) { + if (!bar) { + return; + } + + signal(SIGABRT, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGPIPE, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); +#ifdef SIGWINCH + signal(SIGWINCH, SIG_DFL); +#endif + + bfs_bar_reset(bar); + + xclose(bar->fd); + bar->fd = -1; +} |