@@@ dvdrip-upload: change settings while i'm stealing someone else's internet
[dvdrip] / multiprogress.h
index 1db97a8..4a0d114 100644 (file)
@@ -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 <https://www.gnu.org/licenses/>.
+ */
+
 #ifndef MULTIPROGRESS_H
 #define MULTIPROGRESS_H
 
+/*----- Header files ------------------------------------------------------*/
+
 #include <stdio.h>
 #include <sys/time.h>
 
+/*----- 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