chiark / gitweb /
@@@ tty commentary
[mLib] / ui / ttyprogress.h
1 /* -*-c-*-
2  *
3  * Progress bars for terminal programs
4  *
5  * (c) 2025 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of the mLib utilities library.
11  *
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.
16  *
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.
21  *
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,
25  * USA.
26  */
27
28 #ifndef MLIB_TTYPROGRESS_H
29 #define MLIB_TTYPROGRESS_H
30
31 #ifdef __cplusplus
32   extern "C" {
33 #endif
34
35 /*----- Header files ------------------------------------------------------*/
36
37 #include <stdio.h>
38 #include <sys/time.h>
39
40 #ifndef MLIB_ARENA_H
41 #  include "arena.h"
42 #endif
43
44 #ifndef MLIB_DSTR_H
45 #  include "dstr.h"
46 #endif
47
48 #ifndef MLIB_COMPILER_H
49 #  include "compiler.h"
50 #endif
51
52 #ifndef MLIB_TTY_H
53 #  include "tty.h"
54 #endif
55
56 #ifndef MLIB_TTYCOLOUR_H
57 #  include "ttycolour.h"
58 #endif
59
60 /*------ Highlight definitions --------------------------------------------*/
61
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_);
69
70 /*----- Data structures ---------------------------------------------------*/
71
72 struct ttyprogress_render;
73
74 struct ttyprogress_item {
75         /* An item in the progress display.
76          *
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
81          * information.
82          *
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.
86          */
87
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*/);
92 };
93 #define TTYPROGRESS_ITEM_INIT { 0, 0, 0, 0 }
94
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 */
100 };
101 #define TTYPROGRESS_BUFFER_INIT { &arena_stdlib, DSTR_INIT, 0, 0 }
102
103 struct ttyprogress {
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
106          * display.
107          */
108
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 */
116 };
117 #define TTYPROGRESS_INIT { 0, 0, 0, 0, 0, TTYPROGRESS_BUFFER_INIT, { 0, 0 } }
118
119 struct ttyprogress_render {
120         /* Information passed to rendering functions.
121          *
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.
129          */
130
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 */
137 };
138
139 /*----- Functions provided ------------------------------------------------*/
140
141 extern int ttyprogress_init(struct ttyprogress */*progress*/,
142                             struct tty */*tty*/);
143         /* Initialize PROGRESS.
144          *
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
150          * produced).
151          */
152
153 extern void ttyprogress_free(struct ttyprogress */*progress*/);
154         /* Free any resources held by PROGRESS.
155          *
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.
160          */
161
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.
168          */
169
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
175          * is not updated.
176          */
177
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.
182          */
183
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.
187          */
188
189 /*----- Rendering primitives ----------------------------------------------*/
190
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.
208          */
209
210 extern int ttyprogress_showbar(struct ttyprogress_render */*render*/,
211                                double /*frac*/);
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
218          * inclusive.
219          */
220
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
229          * respectively.
230          */
231
232 /*----- That's all, folks -------------------------------------------------*/
233
234 #ifdef __cplusplus
235   }
236 #endif
237
238 #endif