3 * Progress bars for terminal programs
5 * (c) 2025 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
12 * mLib is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU Library General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or (at
15 * your option) any later version.
17 * mLib is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 * License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
28 #ifndef MLIB_TTYPROGRESS_H
29 #define MLIB_TTYPROGRESS_H
35 /*----- Header files ------------------------------------------------------*/
48 #ifndef MLIB_COMPILER_H
49 # include "compiler.h"
56 #ifndef MLIB_TTYCOLOUR_H
57 # include "ttycolour.h"
60 /*------ Highlight definitions --------------------------------------------*/
62 #define TTYPROGRESS_HIGHLIGHTS(_st, _) \
63 _(_st, BBAR, "bb", bbar_attrs) \
64 _(_st, EBAR, "be", ebar_attrs) \
65 _(_st, NOTE, "nt", note_attrs) \
66 _(_st, WARN, "wr", warn_attrs) \
67 _(_st, ERR, "er", err_attrs)
68 TTYCOLOUR_DEFENUM(TTYPROGRESS_HIGHLIGHTS, TPHL_);
70 /*----- Data structures ---------------------------------------------------*/
72 struct ttyprogress_render;
74 struct ttyprogress_item {
75 /* An item in the progress display.
77 * The `render' function is passed a pointer to the
78 * `ttyprogress_item' structure. Usually, it will need additional
79 * state: handle this by making the `ttyprogress_item' be the first
80 * member of a larger structure which holds the necessary
83 * The `render' function should limit its activities to actually
84 * writing a line of information to the terminal. In particular, it
85 * shouldn't try to calculate anything time-dependent itself.
88 struct ttyprogress *parent; /* controlling progress state */
89 struct ttyprogress_item *next, *prev; /* forward and backward links */
90 void (*render)(struct ttyprogress_item */*item*/, /* render function */
91 struct ttyprogress_render */*render*/);
93 #define TTYPROGRESS_ITEM_INIT { 0, 0, 0, 0 }
95 struct ttyprogress_buffer {
96 arena *a; /* owning arena */
97 dstr t; /* temporary string */
98 char *p; /* buffer pointer */
99 size_t sz; /* buffer size */
101 #define TTYPROGRESS_BUFFER_INIT { &arena_stdlib, DSTR_INIT, 0, 0 }
104 /* The main state for progress reporting. Here we keep track of the
105 * items which need to be displayed, and current state of the
109 struct tty *tty; /* terminal state */
110 struct ttyprogress_item *items, *end_item; /* list of progress items */
111 unsigned nitems; /* number of items */
112 unsigned last_lines; /* number written last time */
113 struct ttyprogress_buffer line; /* line buffer */
114 struct timeval tv_update; /* last update time */
115 struct tty_attr attr[TPHL__LIMIT]; /* highlight definitions */
117 #define TTYPROGRESS_INIT { 0, 0, 0, 0, 0, TTYPROGRESS_BUFFER_INIT, { 0, 0 } }
119 struct ttyprogress_render {
120 /* Information passed to rendering functions.
122 * The `linebuf' accumulates the text to be shown by
123 * `ttyprogress_showbar' or similar, which consists of left and right
124 * portions aligned left and right on the terminal line, with a
125 * variable-size cap in between. These strings are stored at the
126 * beginning and end of the `linebuf', so that (hopefully) new
127 * material can be added in the gap between them without us having to
128 * reallocate the buffer.
131 struct tty *tty; /* terminal state */
132 unsigned width; /* `effective' terminal width */
133 struct ttyprogress_buffer *line; /* line buffer */
134 size_t leftsz, rightsz; /* left and right cursors */
135 unsigned leftwd, rightwd; /* left and right widths */
136 const struct tty_attr *attr; /* highlight definitions */
139 /*----- Functions provided ------------------------------------------------*/
141 extern int ttyprogress_init(struct ttyprogress */*progress*/,
142 struct tty */*tty*/);
143 /* Initialize PROGRESS.
145 * It is safe to call this function on uninitialized data.
146 * Initialization involves opening a stream on the terminal and
147 * determining the terminal's capabilities. Returns zero on success,
148 * or -1 on failure. The structure is usable in either case (though
149 * if no terminal could be opened, then no progress output will be
153 extern void ttyprogress_free(struct ttyprogress */*progress*/);
154 /* Free any resources held by PROGRESS.
156 * It is safe to call this function on a structure that was
157 * initialized to `TTYPROGRESS_INIT', or by calling
158 * `ttyprogress_init', whether that function succeeded or not. It's
159 * also harmless to call it repeatedly on the same structure.
162 extern int ttyprogress_additem(struct ttyprogress */*progress*/,
163 struct ttyprogress_item */*item*/);
164 /* If ITEM is already associated with a progress state, then do
165 * nothing and return -1. Otherwise, add ITEM to the end of the list
166 * of active items maintained by PROGRESS, and return 0. The
167 * progress display is not updated.
170 extern int ttyprogress_removeitem(struct ttyprogress */*progress*/,
171 struct ttyprogress_item */*item*/);
172 /* If ITEM is not associated with a progress state, then do nothing
173 * and return -1. Otherwise, remove ITEM from the list of active
174 * items maintained by PROGRESS, and return 0. The progress display
178 extern int ttyprogress_clear(struct ttyprogress */*progress*/);
179 /* Clear any progress display currently shown on the terminal. Call
180 * this before doing your own output to the terminal, and call
181 * `ttyprogress_update' afterwards.
184 extern int ttyprogress_update(struct ttyprogress */*progress*/);
185 /* Update the progress display. This will call the `render'
186 * functions for all active progress items to redraw them.
189 /*----- Rendering primitives ----------------------------------------------*/
191 extern int ttyprogress_vputleft(struct ttyprogress_render */*render*/,
192 const char */*fmt*/, va_list */*ap*/);
193 extern int ttyprogress_vputright(struct ttyprogress_render */*render*/,
194 const char */*fmt*/, va_list */*ap*/);
195 extern PRINTF_LIKE(2, 3)
196 int ttyprogress_putleft(struct ttyprogress_render */*render*/,
197 const char */*fmt*/, ...);
198 extern PRINTF_LIKE(2, 3)
199 int ttyprogress_putright(struct ttyprogress_render */*render*/,
200 const char */*fmt*/, ...);
201 /* Format the `printf'-style string FMT with the supplied arguments
202 * and add it to the left or right side of the current line being
203 * built up in RENDER. Later strings are added closer to the centre
204 * than earlier strings. If there isn't enough space left to show
205 * the new string on a terminal line, or if there isn't enough memory
206 * for the necessary buffers, then do nothing and return -1. If
207 * everything worked OK, then return 0.
210 extern int ttyprogress_showbar(struct ttyprogress_render */*render*/,
212 /* Show a progress bar. The text of the progress bar will be as
213 * established by the `ttyprogress_putleft' and
214 * `ttyprogress_putright' functions called on RENDER so far, and the
215 * bar will be written to the terminal associated with RENDER. The
216 * length of the bar will be a FRAC fraction of the width of the
217 * terminal, so FRAC should be a real number between 0.0 and 1.0
221 extern int ttyprogress_shownotice(struct ttyprogress_render */*render*/,
222 const struct tty_attr */*attr*/);
223 /* Show a notice, i.e., a temporary message which doesn't actually
224 * have any progress associated with it. The text of the notice will
225 * be as established by the `ttyprogress_putleft' and
226 * `ttyprogress_putright' functions called on RENDER so far, and the
227 * notice will be written to the terminal associated with RENDER.
228 * The notice's background and foreground colours will be BG and FG
232 /*----- That's all, folks -------------------------------------------------*/