multiprogress.[ch]: Add commentary.
[dvdrip] / multiprogress.h
CommitLineData
293c860f
MW
1/* -*-c-*-
2 *
3 * Progress bars for terminal programs
4 *
5 * (c) 2022 Mark Wooding
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This library is free software: you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or (at your
13 * option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
18 * License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this library. If not, see <https://www.gnu.org/licenses/>.
22 */
23
dc53ebfa
MW
24#ifndef MULTIPROGRESS_H
25#define MULTIPROGRESS_H
26
293c860f
MW
27/*----- Header files ------------------------------------------------------*/
28
dc53ebfa
MW
29#include <stdio.h>
30#include <sys/time.h>
31
293c860f
MW
32/*----- Data structures ---------------------------------------------------*/
33
dc53ebfa 34struct progress_ttyinfo {
293c860f
MW
35 /* Information about the terminal we're going to write to. This is
36 * maintained as part of the `progress_state' (see below) and
37 * published to renderers as part of the `progress_render_state'.
38 *
39 * The `fp' may be null, if no terminal could be opened, or it's just
40 * too deficient in terms of its capabilities. Capabilities are
41 * named following `termcap' conventions, even though we might well
42 * actually be using `terminfo' instead.
43 */
44
45 FILE *fp; /* terminal stream, or null */
dc53ebfa
MW
46 char *termbuf, *capbuf; /* buffers for termcap */
47 struct { /* terminal capabilities */
48 unsigned f; /* various flags */
293c860f
MW
49#define TCF_BCE 1u /* erases to background colour */
50 const char *cr, *nw, *up, *ce, *cd; /* cursor motion and erasure */
dc53ebfa
MW
51 const char *mr, *md, *me; /* reverse video, bold */
52 const char *af, *ab, *op; /* colour */
35951074 53 char pc; /* pad character (termcap) */
dc53ebfa 54 } cap;
168cc0a9 55 unsigned defwd, defht; /* default width and height */
dc53ebfa 56};
85218b42 57#define PROGRESS_TTYINFO_INIT \
35951074 58 { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 80, 25 }
dc53ebfa
MW
59
60struct progress_state {
293c860f
MW
61 /* The main state for progress reporting. Here we keep track of the
62 * items which need to be displayed, and current state of the
63 * display.
64 */
65
dc53ebfa
MW
66 struct progress_ttyinfo tty; /* terminal state */
67 struct progress_item *items, *end_item; /* list of progress items */
68 unsigned nitems; /* number of items */
69 unsigned last_lines; /* number written last time */
70 struct timeval tv_update; /* last update time */
71};
72#define PROGRESS_STATE_INIT { PROGRESS_TTYINFO_INIT, 0, 0, 0, 0, { 0, 0 } }
73
74struct progress_render_state {
293c860f
MW
75 /* Information passed to rendering functions.
76 *
77 * The `linebuf' accumulates the text to be shown by
78 * `progress_showbar' or similar, which consists of left and right
79 * portions aligned left and right on the terminal line, with a
80 * variable-size cap in between. These strings are stored at the
81 * beginning and end of the `linebuf', so that (hopefully) new
82 * material can be added in the gap between them without us having to
83 * reallocate the buffer.
84 */
85
dc53ebfa
MW
86 const struct progress_ttyinfo *tty; /* terminal state */
87 unsigned width, height; /* terminal size, in characters */
88 char *linebuf; size_t linesz; /* output buffer */
89 char *tempbuf; size_t tempsz; /* scratch buffer */
90 size_t leftsz, rightsz; /* left and right cursors */
91 unsigned leftwd, rightwd; /* left and right widths */
35951074 92 char *old_bc, *old_up, old_pc; /* saved `termcap' globals */
dc53ebfa
MW
93};
94
95struct progress_item {
293c860f
MW
96 /* An item in the progress display.
97 *
98 * The `render' function is passed a pointer to the `progress_item'
99 * structure. Usually, it will need additional state: handle this by
100 * making the `progress_item' be the first member of a larger
101 * structure which holds the necessary information.
102 *
103 * The `render' function should limit its activities to actually
104 * writing a line of information to the terminal. In particular, it
105 * shouldn't try to calculate anything time-dependent itself.
106 */
107
dc53ebfa
MW
108 struct progress_state *parent; /* controlling progress state */
109 struct progress_item *next, *prev; /* forward and backward links */
110 void (*render)(struct progress_item */*item*/, /* render function */
111 struct progress_render_state */*rs*/);
112};
113#define PROGRESS_ITEM_INIT { 0, 0, 0, 0 }
114
293c860f
MW
115/*----- Functions provided ------------------------------------------------*/
116
dc53ebfa 117extern int progress_init(struct progress_state */*progress*/);
293c860f
MW
118 /* Initialize PROGRESS.
119 *
120 * It is safe to call this function on uninitialized data. This
121 * involves opening a stream on the terminal, and determining the
122 * terminal's capabilities. Returns zero on success, or -1 on
123 * failure. The structure is usable in either case (though if no
124 * terminal could be opened, then no progress output will be
125 * produced).
126 */
127
dc53ebfa 128extern void progress_free(struct progress_state */*progress*/);
293c860f
MW
129 /* Free any resources held by PROGRESS.
130 *
131 * It is safe to call this function on a structure that was
132 * initialized to `PROGRESS_STATE_INIT', or by calling
133 * `progress_init', whether that function succeeded or not. It's
134 * also harmless to call it repeatedly on the same structure.
135 */
dc53ebfa 136
dc53ebfa
MW
137extern int progress_additem(struct progress_state */*progress*/,
138 struct progress_item */*item*/);
293c860f
MW
139 /* If ITEM is already associated with a progress state, then do
140 * nothing and return -1. Otherwise, add ITEM to the end of the list
141 * of active items maintained by PROGRESS, and return 0. The
142 * progress display is not updated.
143 */
dc53ebfa
MW
144
145extern int progress_removeitem(struct progress_state */*progress*/,
146 struct progress_item */*item*/);
293c860f
MW
147 /* If ITEM is not associated with a progress state, then do nothing
148 * and return -1. Otherwise, remove ITEM from the list of active
149 * items maintained by PROGRESS, and return 0. The progress display
150 * is not updated.
151 */
dc53ebfa 152
44d94f48 153extern int progress_clear(struct progress_state */*progress*/);
293c860f
MW
154 /* Clear any progress display currently shown on the terminal. Call
155 * this before doing your own output to the terminal, and call
156 * `progress_update' afterwards.
157 */
44d94f48
MW
158
159extern int progress_update(struct progress_state */*progress*/);
293c860f
MW
160 /* Update the progress display. This will call the `render'
161 * functions for all active progress items to redraw them.
162 */
dc53ebfa
MW
163
164extern int progress_vputleft(struct progress_render_state */*render*/,
165 const char */*fmt*/, va_list /*ap*/);
dc53ebfa
MW
166extern int progress_vputright(struct progress_render_state */*render*/,
167 const char */*fmt*/, va_list /*ap*/);
dc53ebfa
MW
168__attribute__((format(printf, 2, 3)))
169extern int progress_putleft(struct progress_render_state */*render*/,
170 const char */*fmt*/, ...);
dc53ebfa
MW
171__attribute__((format(printf, 2, 3)))
172extern int progress_putright(struct progress_render_state */*render*/,
173 const char */*fmt*/, ...);
293c860f
MW
174 /* Format the `printf'-style string FMT with the supplied arguments
175 * and add it to the left or right side of the current line being
176 * built up in RENDER. Later strings are added closer to the centre
177 * than earlier strings. If there isn't enough space left to show
178 * the new string on a terminal line, or if there isn't enough memory
179 * for the necessary buffers, then do nothing and return -1. If
180 * everything worked OK, then return 0.
181 */
dc53ebfa 182
c45831b0
MW
183extern void progress_put_sequence(const struct progress_ttyinfo */*tty*/,
184 const char */*p*/, unsigned /*nlines*/);
293c860f
MW
185 /* Send a sequence P -- one of the capability strings from TTY -- to
186 * the terminal TTY, padding it as necessary based on the fact that
187 * NLINES of the display are affected. (See the `tputs' function for
188 * the details.)
189 */
c45831b0
MW
190
191extern void progress_set_fgcolour(const struct progress_ttyinfo */*tty*/,
192 int /*colour*/);
193extern void progress_set_bgcolour(const struct progress_ttyinfo */*tty*/,
194 int /*colour*/);
293c860f
MW
195 /* Set COLOUR as the foreground (`set_fgcolour') or background
196 * (`set_bgcolour') colour for subsequent output to TTY.
197 */
c45831b0 198
dc53ebfa
MW
199extern int progress_showbar(struct progress_render_state */*render*/,
200 double /*frac*/);
293c860f
MW
201 /* Show a progress bar. The text of the progress bar will be as
202 * established by the `progress_putleft' and `progress_putright'
203 * functions called on RENDER so far, and the bar will be written to
204 * the terminal associated with RENDER. The length of the bar will
205 * be a FRAC fraction of the width of the terminal, so FRAC should be
206 * a real number between 0.0 and 1.0 inclusive.
207 */
dc53ebfa
MW
208
209extern int progress_shownotice(struct progress_render_state */*render*/,
210 int /*bg*/, int /*fg*/);
293c860f
MW
211 /* Show a notice, i.e., a temporary message which doesn't actually
212 * have any progress associated with it. The text of the notice will
213 * be as established by the `progress_putleft' and
214 * `progress_putright' functions called on RENDER so far, and the
215 * notice will be written to the terminal associated with RENDER.
216 * The notice's background and foreground colours will be BG and FG
217 * respectively.
218 */
219
220/*----- That's all, folks -------------------------------------------------*/
dc53ebfa
MW
221
222#endif