X-Git-Url: https://git.distorted.org.uk/~mdw/dvdrip/blobdiff_plain/dc53ebfaa3fb887f962b574c6bafa45b160fc765..refs/heads/mdw/cleanup:/multiprogress.h diff --git a/multiprogress.h b/multiprogress.h index 1db97a8..4a0d114 100644 --- a/multiprogress.h +++ b/multiprogress.h @@ -1,24 +1,80 @@ +/* -*-c-*- + * + * Progress bars for terminal programs + * + * (c) 2022 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This library is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + #ifndef MULTIPROGRESS_H #define MULTIPROGRESS_H +/*----- Header files ------------------------------------------------------*/ + #include #include +/*----- Compiler-specific magic -------------------------------------------*/ + +#if (defined(__GNUC__) && (__GNUC__ > 2 || \ + (__GNUC__ == 2 && __GNUC_MINOR__ >= 5))) || \ + (defined(__clang__) && (__clang_major__ > 3 || \ + (__clang_major__ == 3 && __clang_minor__ >= 3))) +# define MULTIPROGRESS__PRINTF_LIKE(farg, aarg) \ + __attribute__((format(printf, farg, aarg))) +#else +# define MULTIPROGRESS__PRINTF_LIKE(farg, aarg) +#endif + +/*----- Data structures ---------------------------------------------------*/ + struct progress_ttyinfo { - FILE *fp; /* terminal stream */ + /* Information about the terminal we're going to write to. This is + * maintained as part of the `progress_state' (see below) and + * published to renderers as part of the `progress_render_state'. + * + * The `fp' may be null, if no terminal could be opened, or it's just + * too deficient in terms of its capabilities. Capabilities are + * named following `termcap' conventions, even though we might well + * actually be using `terminfo' instead. + */ + + FILE *fp; /* terminal stream, or null */ char *termbuf, *capbuf; /* buffers for termcap */ struct { /* terminal capabilities */ unsigned f; /* various flags */ -#define TCF_BCE 1u /* erases to background colour */ - const char *cr, *up, *ce, *cd; /* cursor motion */ +#define TCF_BCE 1u /* erases to background colour */ + const char *cr, *nw, *up, *ce, *cd; /* cursor motion and erasure */ const char *mr, *md, *me; /* reverse video, bold */ const char *af, *ab, *op; /* colour */ + char pc; /* pad character (termcap) */ } cap; - unsigned defwd, defht; /* default width and height */ + unsigned defwd, defht; /* default width and height */ }; -#define PROGRESS_TTYINFO_INIT { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } } +#define PROGRESS_TTYINFO_INIT \ + { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 80, 25 } struct progress_state { + /* The main state for progress reporting. Here we keep track of the + * items which need to be displayed, and current state of the + * display. + */ + struct progress_ttyinfo tty; /* terminal state */ struct progress_item *items, *end_item; /* list of progress items */ unsigned nitems; /* number of items */ @@ -28,16 +84,39 @@ struct progress_state { #define PROGRESS_STATE_INIT { PROGRESS_TTYINFO_INIT, 0, 0, 0, 0, { 0, 0 } } struct progress_render_state { + /* Information passed to rendering functions. + * + * The `linebuf' accumulates the text to be shown by + * `progress_showbar' or similar, which consists of left and right + * portions aligned left and right on the terminal line, with a + * variable-size cap in between. These strings are stored at the + * beginning and end of the `linebuf', so that (hopefully) new + * material can be added in the gap between them without us having to + * reallocate the buffer. + */ + const struct progress_ttyinfo *tty; /* terminal state */ unsigned width, height; /* terminal size, in characters */ char *linebuf; size_t linesz; /* output buffer */ char *tempbuf; size_t tempsz; /* scratch buffer */ size_t leftsz, rightsz; /* left and right cursors */ unsigned leftwd, rightwd; /* left and right widths */ - char *old_bc, *old_up; /* old fixup strings */ + char *old_bc, *old_up, old_pc; /* saved `termcap' globals */ }; struct progress_item { + /* An item in the progress display. + * + * The `render' function is passed a pointer to the `progress_item' + * structure. Usually, it will need additional state: handle this by + * making the `progress_item' be the first member of a larger + * structure which holds the necessary information. + * + * The `render' function should limit its activities to actually + * writing a line of information to the terminal. In particular, it + * shouldn't try to calculate anything time-dependent itself. + */ + struct progress_state *parent; /* controlling progress state */ struct progress_item *next, *prev; /* forward and backward links */ void (*render)(struct progress_item */*item*/, /* render function */ @@ -45,38 +124,111 @@ struct progress_item { }; #define PROGRESS_ITEM_INIT { 0, 0, 0, 0 } -extern int progress_init(struct progress_state */*progress*/); -extern void progress_free(struct progress_state */*progress*/); +/*----- Functions provided ------------------------------------------------*/ -extern int progress_clear(struct progress_state */*progress*/); +extern int progress_init(struct progress_state */*progress*/); + /* Initialize PROGRESS. + * + * It is safe to call this function on uninitialized data. This + * involves opening a stream on the terminal, and determining the + * terminal's capabilities. Returns zero on success, or -1 on + * failure. The structure is usable in either case (though if no + * terminal could be opened, then no progress output will be + * produced). + */ -extern int progress_update(struct progress_state */*progress*/); +extern void progress_free(struct progress_state */*progress*/); + /* Free any resources held by PROGRESS. + * + * It is safe to call this function on a structure that was + * initialized to `PROGRESS_STATE_INIT', or by calling + * `progress_init', whether that function succeeded or not. It's + * also harmless to call it repeatedly on the same structure. + */ extern int progress_additem(struct progress_state */*progress*/, struct progress_item */*item*/); + /* If ITEM is already associated with a progress state, then do + * nothing and return -1. Otherwise, add ITEM to the end of the list + * of active items maintained by PROGRESS, and return 0. The + * progress display is not updated. + */ extern int progress_removeitem(struct progress_state */*progress*/, struct progress_item */*item*/); + /* If ITEM is not associated with a progress state, then do nothing + * and return -1. Otherwise, remove ITEM from the list of active + * items maintained by PROGRESS, and return 0. The progress display + * is not updated. + */ + +extern int progress_clear(struct progress_state */*progress*/); + /* Clear any progress display currently shown on the terminal. Call + * this before doing your own output to the terminal, and call + * `progress_update' afterwards. + */ +extern int progress_update(struct progress_state */*progress*/); + /* Update the progress display. This will call the `render' + * functions for all active progress items to redraw them. + */ extern int progress_vputleft(struct progress_render_state */*render*/, const char */*fmt*/, va_list /*ap*/); - extern int progress_vputright(struct progress_render_state */*render*/, const char */*fmt*/, va_list /*ap*/); - -__attribute__((format(printf, 2, 3))) +MULTIPROGRESS__PRINTF_LIKE(2, 3) extern int progress_putleft(struct progress_render_state */*render*/, const char */*fmt*/, ...); - -__attribute__((format(printf, 2, 3))) +MULTIPROGRESS__PRINTF_LIKE(2, 3) extern int progress_putright(struct progress_render_state */*render*/, const char */*fmt*/, ...); + /* Format the `printf'-style string FMT with the supplied arguments + * and add it to the left or right side of the current line being + * built up in RENDER. Later strings are added closer to the centre + * than earlier strings. If there isn't enough space left to show + * the new string on a terminal line, or if there isn't enough memory + * for the necessary buffers, then do nothing and return -1. If + * everything worked OK, then return 0. + */ + +extern void progress_put_sequence(const struct progress_ttyinfo */*tty*/, + const char */*p*/, unsigned /*nlines*/); + /* Send a sequence P -- one of the capability strings from TTY -- to + * the terminal TTY, padding it as necessary based on the fact that + * NLINES of the display are affected. (See the `tputs' function for + * the details.) + */ + +extern void progress_set_fgcolour(const struct progress_ttyinfo */*tty*/, + int /*colour*/); +extern void progress_set_bgcolour(const struct progress_ttyinfo */*tty*/, + int /*colour*/); + /* Set COLOUR as the foreground (`set_fgcolour') or background + * (`set_bgcolour') colour for subsequent output to TTY. + */ extern int progress_showbar(struct progress_render_state */*render*/, double /*frac*/); + /* Show a progress bar. The text of the progress bar will be as + * established by the `progress_putleft' and `progress_putright' + * functions called on RENDER so far, and the bar will be written to + * the terminal associated with RENDER. The length of the bar will + * be a FRAC fraction of the width of the terminal, so FRAC should be + * a real number between 0.0 and 1.0 inclusive. + */ extern int progress_shownotice(struct progress_render_state */*render*/, int /*bg*/, int /*fg*/); + /* Show a notice, i.e., a temporary message which doesn't actually + * have any progress associated with it. The text of the notice will + * be as established by the `progress_putleft' and + * `progress_putright' functions called on RENDER so far, and the + * notice will be written to the terminal associated with RENDER. + * The notice's background and foreground colours will be BG and FG + * respectively. + */ + +/*----- That's all, folks -------------------------------------------------*/ #endif