2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2021-2023 Alfonso Sabato Siciliano
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include "bsddialog.h"
35 #include "bsddialog_progressview.h"
36 #include "bsddialog_theme.h"
39 #define BARPADDING 2 /* widget border | BARPADDING | box bar */
42 #define MIN_WBOX (BARPADDING + BOXBORDERS + MIN_WBAR + BARPADDING)
44 #define MIN_WMGBOX (BARPADDING + BOXBORDERS + MIN_WMGBAR + BARPADDING)
46 #define WBOX(d) ((d)->w - BORDERS - BARPADDING - BARPADDING)
47 #define WBAR(d) (WBOX(d) - BOXBORDERS)
49 bool bsddialog_interruptprogview;
50 bool bsddialog_abortprogview;
51 long long int bsddialog_total_progview;
53 static const char states[12][14] = {
54 " Succeeded ", /* -1 */
57 " Completed ", /* -4 */
61 " In Progress ", /* -8 */
64 " Pending ", /* -11 */
65 " UNKNOWN ", /* < -11, no API */
71 int y; /* bar y in win */
72 int x; /* bar x in win */
73 int w; /* width in win */
74 int perc; /* barlen = (w * perc) / 100 */
75 const char* fmt; /* format for label */
76 int label; /* rangebox and pause perc!=label */
79 static void draw_bar(struct bar *b)
85 barlen = b->perc > 0 ? (b->perc * b->w) / 100 : 0;
87 ch = ' ' | t.bar.f_color;
88 mvwhline(b->win, b->y, b->x, ch, barlen);
89 ch = ' ' | t.bar.color;
90 mvwhline(b->win, b->y, b->x + barlen, ch, b->w - barlen);
92 sprintf(label, b->fmt, b->label);
93 xlabel = b->x + b->w/2 - (int)strlen(label)/2; /* 1-byte-char string */
94 wattron(b->win, t.bar.color); /* x+barlen < xlabel */
95 mvwaddstr(b->win, b->y, xlabel, label);
96 wattroff(b->win, t.bar.color);
97 wattron(b->win, t.bar.f_color); /* x+barlen >= xlabel */
98 mvwaddnstr(b->win, b->y, xlabel, label, MAX((b->x+barlen) - xlabel, 0));
99 wattroff(b->win, t.bar.f_color);
102 wnoutrefresh(b->win);
106 static void update_barbox(struct dialog *d, struct bar *b, bool buttons)
110 y = d->y + d->h - BORDER - HBOX;
113 update_box(d->conf, b->win, y, d->x + BORDER + BARPADDING, HBOX,
118 bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows,
119 int cols, unsigned int perc, int fd, const char *sep, const char *end)
124 char inputbuf[2048], ntext[2048], *pntext;
128 if (prepare_dialog(conf, text, rows, cols, &d) != 0)
129 return (BSDDIALOG_ERROR);
130 if ((b.win = newwin(1, 1, 1, 1)) == NULL)
131 RETURN_ERROR("Cannot build WINDOW bar");
141 if ((input = fdopen(fd2, "r")) == NULL)
142 RETURN_FMTERROR("Cannot build FILE* from fd %d", fd);
145 perc = MIN(perc, 100);
150 refresh(); /* Important for decreasing screen */
152 if (dialog_size_position(&d, HBOX, MIN_WBOX, NULL) != 0)
153 return (BSDDIALOG_ERROR);
155 return (BSDDIALOG_ERROR);
157 refresh(); /* fix grey lines expanding screen */
159 update_barbox(&d, &b, false);
161 b.perc = b.label = perc;
165 if (input == NULL) /* that is fd < 0 */
169 fscanf(input, "%s", inputbuf);
170 if (strcmp(inputbuf, end) == 0) {
174 if (strcmp(inputbuf, sep) == 0)
177 if (mainloop == false)
179 fscanf(input, "%d", &perc);
180 perc = MIN(perc, 100);
184 fscanf(input, "%s", inputbuf);
185 if (strcmp(inputbuf, end) == 0) {
189 if (strcmp(inputbuf, sep) == 0)
191 strcpy(pntext, inputbuf);
192 pntext += strlen(inputbuf); /* end string, no strlen */
205 return (BSDDIALOG_OK);
210 do_mixedgauge(struct bsddialog_conf *conf, const char *text, int rows, int cols,
211 unsigned int mainperc, unsigned int nminibars, const char **minilabels,
212 int *minipercs, bool color)
214 int i, miniperc, max_minibarlen;
216 int minicolor, red, green;
220 CHECK_ARRAY(nminibars, minilabels);
221 CHECK_ARRAY(nminibars, minipercs);
223 red = bsddialog_color(BSDDIALOG_WHITE,BSDDIALOG_RED, BSDDIALOG_BOLD);
224 green = bsddialog_color(BSDDIALOG_WHITE,BSDDIALOG_GREEN,BSDDIALOG_BOLD);
227 for (i = 0; i < (int)nminibars; i++)
228 max_minibarlen = MAX(max_minibarlen,
229 (int)strcols(CHECK_STR(minilabels[i])));
230 max_minibarlen += 3 + 16; /* seps + [...] */
231 max_minibarlen = MAX(max_minibarlen, MIN_WMGBOX); /* mainbar */
233 if (prepare_dialog(conf, text, rows, cols, &d) != 0)
234 return (BSDDIALOG_ERROR);
235 if (dialog_size_position(&d, nminibars + HBOX, max_minibarlen,
237 return (BSDDIALOG_ERROR);
238 if (draw_dialog(&d) != 0)
239 return (BSDDIALOG_ERROR);
243 b.x = 1 + d.w - 2 - 15;
247 for (i = 0; i < (int)nminibars; i++) {
248 miniperc = minipercs[i];
250 if (color && miniperc >= 0)
251 wattron(d.widget, A_BOLD);
252 mvwaddstr(d.widget, i+1, 2, CHECK_STR(minilabels[i]));
253 if (color && miniperc >= 0)
254 wattroff(d.widget, A_BOLD);
256 if (miniperc == BSDDIALOG_MG_BLANK)
258 mvwaddstr(d.widget, i+1, d.w-2-15, "[ ]");
261 b.perc = b.label = MIN(miniperc, 100);
263 } else { /* miniperc < 0 */
264 if (miniperc < BSDDIALOG_MG_PENDING)
265 miniperc = -12; /* UNKNOWN */
266 minicolor = t.dialog.color;
267 if (color && miniperc == BSDDIALOG_MG_FAILED)
269 else if (color && miniperc == BSDDIALOG_MG_DONE)
271 wattron(d.widget, minicolor);
272 miniperc = abs(miniperc + 1);
273 mvwaddstr(d.widget, i+1, 1+d.w-2-15, states[miniperc]);
274 wattroff(d.widget, minicolor);
277 wnoutrefresh(d.widget);
280 ystext = MAX(d.h - BORDERS - htext - HBOX, (int)nminibars);
281 rtextpad(&d, 0, 0, ystext, HBOX);
284 if ((b.win = newwin(1, 1, 1, 1)) == NULL)
285 RETURN_ERROR("Cannot build WINDOW bar");
286 update_barbox(&d, &b, false);
287 wattron(b.win, t.bar.color);
288 mvwaddstr(b.win, 0, 2, "Overall Progress");
289 wattroff(b.win, t.bar.color);
294 b.perc = b.label = MIN(mainperc, 100);
299 /* getch(); to test with "alternate mode" */
304 return (BSDDIALOG_OK);
308 bsddialog_mixedgauge(struct bsddialog_conf *conf, const char *text, int rows,
309 int cols, unsigned int mainperc, unsigned int nminibars,
310 const char **minilabels, int *minipercs)
314 retval = do_mixedgauge(conf, text, rows, cols, mainperc, nminibars,
315 minilabels, minipercs, false);
321 bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows,
322 int cols, struct bsddialog_progviewconf *pvconf, unsigned int nminibar,
323 struct bsddialog_fileminibar *minibar)
326 int perc, retval, *minipercs;
327 unsigned int i, mainperc, totaltodo;
329 const char **minilabels;
330 time_t tstart, told, tnew, refresh;
332 if ((minilabels = calloc(nminibar, sizeof(char*))) == NULL)
333 RETURN_ERROR("Cannot allocate memory for minilabels");
334 if ((minipercs = calloc(nminibar, sizeof(int))) == NULL)
335 RETURN_ERROR("Cannot allocate memory for minipercs");
338 for (i = 0; i < nminibar; i++) {
339 totaltodo += minibar[i].size;
340 minilabels[i] = minibar[i].label;
341 minipercs[i] = minibar[i].status;
344 refresh = pvconf->refresh == 0 ? 0 : pvconf->refresh - 1;
345 retval = BSDDIALOG_OK;
350 while (!(bsddialog_interruptprogview || bsddialog_abortprogview)) {
351 if (bsddialog_total_progview == 0 || totaltodo == 0)
354 mainperc = (bsddialog_total_progview * 100) / totaltodo;
357 if (update || tnew > told + refresh) {
358 retval = do_mixedgauge(conf, text, rows, cols, mainperc,
359 nminibar, minilabels, minipercs, true);
360 if (retval == BSDDIALOG_ERROR)
361 return (BSDDIALOG_ERROR);
363 move(SCREENLINES - 1, 2);
365 readforsec = ((tnew - tstart) == 0) ? 0 :
366 bsddialog_total_progview / (float)(tnew - tstart);
367 printw(pvconf->fmtbottomstr, bsddialog_total_progview,
377 if (minibar[i].status == BSDDIALOG_MG_FAILED)
380 perc = pvconf->callback(&minibar[i]);
382 if (minibar[i].status == BSDDIALOG_MG_DONE) { /*||perc >= 100)*/
383 minipercs[i] = BSDDIALOG_MG_DONE;
386 } else if (minibar[i].status == BSDDIALOG_MG_FAILED ||
388 minipercs[i] = BSDDIALOG_MG_FAILED;
390 } else /* perc >= 0 */
399 static int rangebox_redraw(struct dialog *d, struct bar *b, int *bigchange)
403 refresh(); /* Important for decreasing screen */
405 if (dialog_size_position(d, HBOX, MIN_WBOX, NULL) != 0)
406 return (BSDDIALOG_ERROR);
407 if (draw_dialog(d) != 0)
408 return (BSDDIALOG_ERROR);
410 refresh(); /* Important to fix grey lines expanding screen */
411 TEXTPAD(d, HBOX + HBUTTONS);
414 *bigchange = MAX(1, b->w / 10);
415 update_barbox(d, b, true);
422 bsddialog_rangebox(struct bsddialog_conf *conf, const char *text, int rows,
423 int cols, int min, int max, int *value)
426 int currvalue, retval, bigchange, positions;
433 RETURN_FMTERROR("min (%d) >= max (%d)", min, max);
435 RETURN_FMTERROR("value (%d) < min (%d)", *value, min);
437 RETURN_FMTERROR("value (%d) > max (%d)", *value, max);
440 positions = max - min + 1;
442 if (prepare_dialog(conf, text, rows, cols, &d) != 0)
443 return (BSDDIALOG_ERROR);
444 set_buttons(&d, true, OK_LABEL, CANCEL_LABEL);
445 if ((b.win = newwin(1, 1, 1, 1)) == NULL)
446 RETURN_ERROR("Cannot build WINDOW bar");
449 if (rangebox_redraw(&d, &b, &bigchange) != 0)
450 return (BSDDIALOG_ERROR);
455 b.perc = ((float)(currvalue - min)*100) / (positions-1);
460 if (get_wch(&input) == ERR)
465 retval = BUTTONVALUE(d.bs);
469 if (conf->key.enable_esc) {
470 retval = BSDDIALOG_ESC;
476 d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons;
482 d.bs.curr = d.bs.nbuttons - 1;
494 currvalue -= bigchange;
500 currvalue += bigchange;
506 if (currvalue < max) {
512 if (currvalue > min) {
518 if (conf->key.f1_file == NULL &&
519 conf->key.f1_message == NULL)
521 if (f1help_dialog(conf) != 0)
522 return (BSDDIALOG_ERROR);
523 if (rangebox_redraw(&d, &b, &bigchange) != 0)
524 return (BSDDIALOG_ERROR);
527 if (rangebox_redraw(&d, &b, &bigchange) != 0)
528 return (BSDDIALOG_ERROR);
531 if (shortcut_buttons(input, &d.bs)) {
534 retval = BUTTONVALUE(d.bs);
548 static int pause_redraw(struct dialog *d, struct bar *b)
552 refresh(); /* Important for decreasing screen */
554 if (dialog_size_position(d, HBOX, MIN_WBOX, NULL) != 0)
555 return (BSDDIALOG_ERROR);
556 if (draw_dialog(d) != 0)
557 return (BSDDIALOG_ERROR);
559 refresh(); /* Important to fix grey lines expanding screen */
560 TEXTPAD(d, HBOX + HBUTTONS);
563 update_barbox(d, b, true);
570 bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows,
571 int cols, unsigned int *seconds)
580 if (prepare_dialog(conf, text, rows, cols, &d) != 0)
581 return (BSDDIALOG_ERROR);
582 set_buttons(&d, true, OK_LABEL, CANCEL_LABEL);
583 if ((b.win = newwin(1, 1, 1, 1)) == NULL)
584 RETURN_ERROR("Cannot build WINDOW bar");
587 if (pause_redraw(&d, &b) != 0)
588 return (BSDDIALOG_ERROR);
591 nodelay(stdscr, TRUE);
596 b.perc = (float)tout * 100 / *seconds;
601 if (get_wch(&input) == ERR) { /* timeout */
604 retval = BSDDIALOG_TIMEOUT;
615 retval = BUTTONVALUE(d.bs);
619 if (conf->key.enable_esc) {
620 retval = BSDDIALOG_ESC;
626 d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons;
632 d.bs.curr = d.bs.nbuttons - 1;
636 if (conf->key.f1_file == NULL &&
637 conf->key.f1_message == NULL)
639 if (f1help_dialog(conf) != 0)
640 return (BSDDIALOG_ERROR);
641 if (pause_redraw(&d, &b) != 0)
642 return (BSDDIALOG_ERROR);
645 if (pause_redraw(&d, &b) != 0)
646 return (BSDDIALOG_ERROR);
649 if (shortcut_buttons(input, &d.bs)) {
652 retval = BUTTONVALUE(d.bs);
657 nodelay(stdscr, FALSE);
659 *seconds = MAX(tout, 0);