xref: /freebsd/contrib/bsddialog/lib/barbox.c (revision 3e42d7194bdcd4fbfd781518258f919d6ac803d3)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2021-2025 Alfonso Sabato Siciliano
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
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.
14  *
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
25  * SUCH DAMAGE.
26  */
27 
28 #include <curses.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <time.h>
32 #include <unistd.h>
33 
34 #include "bsddialog.h"
35 #include "bsddialog_progressview.h"
36 #include "bsddialog_theme.h"
37 #include "lib_util.h"
38 
39 #define BARPADDING   2  /* Dialog border | BARPADDING | box bar */
40 #define BOXBORDERS   2
41 #define MIN_WBAR     15
42 #define MIN_WBOX     (BARPADDING + BOXBORDERS + MIN_WBAR + BARPADDING)
43 #define MIN_WMGBAR   18 /* Mixedgauge main bar */
44 #define MIN_WMGBOX   (BARPADDING + BOXBORDERS + MIN_WMGBAR + BARPADDING)
45 #define HBOX         3
46 #define WBOX(d)      ((d)->w - BORDERS - BARPADDING - BARPADDING)
47 #define WBAR(d)      (WBOX(d) - BOXBORDERS)
48 
49 bool bsddialog_interruptprogview;
50 bool bsddialog_abortprogview;
51 long long int bsddialog_total_progview;
52 
53 static const char states[12][14] = {
54 	"  Succeeded  ", /* -1  */
55 	"   Failed    ", /* -2  */
56 	"   Passed    ", /* -3  */
57 	"  Completed  ", /* -4  */
58 	"   Checked   ", /* -5  */
59 	"    Done     ", /* -6  */
60 	"   Skipped   ", /* -7  */
61 	" In Progress ", /* -8  */
62 	"(blank)      ", /* -9  */
63 	"     N/A     ", /* -10 */
64 	"   Pending   ", /* -11 */
65 	"   UNKNOWN   ", /* < -11, no API */
66 };
67 
68 struct bar {
69 	bool toupdate;
70 	WINDOW *win;
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 */
77 };
78 
draw_bar(struct bar * b)79 static void draw_bar(struct bar *b)
80 {
81 	int barlen, xlabel;
82 	chtype ch;
83 	char label[128];
84 
85 	barlen = b->perc > 0 ? (b->perc * b->w) / 100 : 0;
86 
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);
91 
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);
100 
101 	if (b->toupdate)
102 		wnoutrefresh(b->win);
103 	b->toupdate = false;
104 }
105 
update_barbox(struct dialog * d,struct bar * b,bool buttons)106 static void update_barbox(struct dialog *d, struct bar *b, bool buttons)
107 {
108 	int y;
109 
110 	y = d->y + d->h - BORDER - HBOX;
111 	if (buttons)
112 		y -= HBUTTONS;
113 	update_box(d->conf, b->win, y, d->x + BORDER + BARPADDING, HBOX,
114 	    WBOX(d), RAISED);
115 }
116 
117 int
bsddialog_gauge(struct bsddialog_conf * conf,const char * text,int rows,int cols,unsigned int perc,int fd,const char * sep,const char * end)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)
120 {
121 	bool mainloop;
122 	int fd2;
123 	FILE *input;
124 	char inputbuf[2048], ntext[2048], *pntext;
125 	struct bar b;
126 	struct dialog d;
127 
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");
132 	b.y = b.x = 1;
133 	b.fmt = "%3d%%";
134 
135 	input = NULL;
136 	if (fd >= 0) {
137 		CHECK_PTR(sep);
138 		CHECK_PTR(end);
139 
140 		fd2 = dup(fd);
141 		if ((input = fdopen(fd2, "r")) == NULL)
142 			RETURN_FMTERROR("Cannot build FILE* from fd %d", fd);
143 	}
144 
145 	perc = MIN(perc, 100);
146 	mainloop = true;
147 	while (mainloop) {
148 		if (d.built) {
149 			hide_dialog(&d);
150 			refresh(); /* Important for decreasing screen */
151 		}
152 		if (dialog_size_position(&d, HBOX, MIN_WBOX, NULL) != 0)
153 			return (BSDDIALOG_ERROR);
154 		if (draw_dialog(&d))
155 			return (BSDDIALOG_ERROR);
156 		if (d.built)
157 			refresh(); /* fix grey lines expanding screen */
158 		TEXTPAD(&d, HBOX);
159 		update_barbox(&d, &b, false);
160 		b.w = WBAR(&d);
161 		b.perc = b.label = perc;
162 		b.toupdate = true;
163 		draw_bar(&b);
164 		doupdate();
165 		if (input == NULL) /* that is fd < 0 */
166 			break;
167 
168 		while (true) {
169 			fscanf(input, "%s", inputbuf);
170 			if (strcmp(inputbuf, end) == 0) {
171 				mainloop = false;
172 				break;
173 			}
174 			if (strcmp(inputbuf, sep) == 0)
175 				break;
176 		}
177 		if (mainloop == false)
178 			break;
179 		fscanf(input, "%d", &perc);
180 		perc = MIN(perc, 100);
181 		pntext = &ntext[0];
182 		ntext[0] = '\0';
183 		while (true) {
184 			fscanf(input, "%s", inputbuf);
185 			if (strcmp(inputbuf, end) == 0) {
186 				mainloop = false;
187 				break;
188 			}
189 			if (strcmp(inputbuf, sep) == 0)
190 				break;
191 			strcpy(pntext, inputbuf);
192 			pntext += strlen(inputbuf); /* end string, no strlen */
193 			pntext[0] = ' ';
194 			pntext++;
195 		}
196 		pntext[0] = '\0';
197 		d.text = ntext;
198 	}
199 
200 	if (input != NULL)
201 		fclose(input);
202 	delwin(b.win);
203 	end_dialog(&d);
204 
205 	return (BSDDIALOG_OK);
206 }
207 
208 /* Mixedgauge */
209 static void
mvwaddcstr(WINDOW * win,int y,int x,const char * mbstring,unsigned int cols)210 mvwaddcstr(WINDOW *win, int y, int x, const char *mbstring, unsigned int cols)
211 {
212 	size_t charlen, n, w;
213 	mbstate_t mbs;
214 	const char *pmbstring;
215 	wchar_t wch;
216 
217 	w = n = 0;
218 	pmbstring = mbstring;
219 	memset(&mbs, 0, sizeof(mbs));
220 	while ((charlen = mbrlen(pmbstring, MB_CUR_MAX, &mbs)) != 0 &&
221 	    charlen != (size_t)-1 && charlen != (size_t)-2) {
222 		mbtowc(&wch, pmbstring, charlen);
223 		w += (wch == L'\t') ? TABSIZE : wcwidth(wch);
224 		if (w > cols)
225 			break;
226 		pmbstring += charlen;
227 		n += charlen;
228 	}
229 	mvwaddnstr(win, y, x, mbstring, n);
230 	if(w > cols)
231 		mvwaddstr(win, y, (x + cols) - 3, "...");
232 }
233 
234 static int
mixedgauge_size_position(struct dialog * d,int nminibars,const char ** minilabels,int * htext)235 mixedgauge_size_position(struct dialog *d, int nminibars,
236     const char **minilabels, int *htext)
237 {
238 	int i, max_minibarlen;
239 
240 	max_minibarlen = 0;
241 	for (i = 0; i < (int)nminibars; i++)
242 		max_minibarlen = MAX(max_minibarlen,
243 		    (int)strcols(CHECK_STR(minilabels[i])));
244 	max_minibarlen += 18; /* ' '<max_minibarlen>' ['13'] ' */
245 	max_minibarlen = MAX(max_minibarlen, MIN_WMGBOX); /* mainbar */
246 
247 	if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0)
248 		return (BSDDIALOG_ERROR);
249 	if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w,
250 	    d->text, htext, &d->bs, nminibars + HBOX, max_minibarlen) != 0)
251 		return (BSDDIALOG_ERROR);
252 	if (widget_checksize(d->h, d->w, &d->bs, nminibars + HBOX,
253 	    MIN_WMGBOX) != 0)
254 		return (BSDDIALOG_ERROR);
255 	if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0)
256 		return (BSDDIALOG_ERROR);
257 
258 	return (0);
259 }
260 
261 static int
do_mixedgauge(struct bsddialog_conf * conf,const char * text,int rows,int cols,unsigned int mainperc,unsigned int nminibars,const char ** minilabels,int * minipercs,bool color)262 do_mixedgauge(struct bsddialog_conf *conf, const char *text, int rows, int cols,
263     unsigned int mainperc, unsigned int nminibars, const char **minilabels,
264     int *minipercs, bool color)
265 {
266 	int i, miniperc;
267 	int ystext, htext;
268 	int minicolor, red, green;
269 	struct bar b;
270 	struct dialog d;
271 
272 	CHECK_ARRAY(nminibars, minilabels);
273 	CHECK_ARRAY(nminibars, minipercs);
274 
275 	red   = bsddialog_color(BSDDIALOG_WHITE,BSDDIALOG_RED,  BSDDIALOG_BOLD);
276 	green = bsddialog_color(BSDDIALOG_WHITE,BSDDIALOG_GREEN,BSDDIALOG_BOLD);
277 
278 	if (prepare_dialog(conf, text, rows, cols, &d) != 0)
279 		return (BSDDIALOG_ERROR);
280 	if (mixedgauge_size_position(&d, nminibars, minilabels, &htext) != 0)
281 		return (BSDDIALOG_ERROR);
282 	if (draw_dialog(&d) != 0)
283 		return (BSDDIALOG_ERROR);
284 
285 	/* mini bars */
286 	b.win = d.widget;
287 	b.x = 1 + d.w - 2 - 15;
288 	b.w = 13;
289 	b.fmt = "%3d%%";
290 	b.toupdate = false;
291 	for (i = 0; i < (int)nminibars; i++) {
292 		miniperc = minipercs[i];
293 		/* label */
294 		if (color && miniperc >= 0)
295 			wattron(d.widget, A_BOLD);
296 		mvwaddcstr(d.widget, i+1, 2, CHECK_STR(minilabels[i]), d.w-20);
297 		if (color && miniperc >= 0)
298 			wattroff(d.widget, A_BOLD);
299 		/* perc */
300 		if (miniperc == BSDDIALOG_MG_BLANK)
301 			continue;
302 		mvwaddstr(d.widget, i+1, d.w-2-15, "[             ]");
303 		if (miniperc >= 0) {
304 			b.y = i + 1;
305 			b.perc = b.label = MIN(miniperc, 100);
306 			draw_bar(&b);
307 		} else { /* miniperc < 0 */
308 			if (miniperc < BSDDIALOG_MG_PENDING)
309 				miniperc = -12; /* UNKNOWN */
310 			minicolor = t.dialog.color;
311 			if (color && miniperc == BSDDIALOG_MG_FAILED)
312 				minicolor = red;
313 			else if (color && miniperc == BSDDIALOG_MG_DONE)
314 				minicolor = green;
315 			wattron(d.widget, minicolor);
316 			miniperc = abs(miniperc + 1);
317 			mvwaddstr(d.widget, i+1, 1+d.w-2-15, states[miniperc]);
318 			wattroff(d.widget, minicolor);
319 		}
320 	}
321 	wnoutrefresh(d.widget);
322 
323 	/* text */
324 	ystext = MAX(d.h - BORDERS - htext - HBOX, (int)nminibars);
325 	rtextpad(&d, 0, 0, ystext, HBOX);
326 
327 	/* main bar */
328 	if ((b.win = newwin(1, 1, 1, 1)) == NULL)
329 		RETURN_ERROR("Cannot build WINDOW bar");
330 	update_barbox(&d, &b, false);
331 	wattron(b.win, t.bar.color);
332 	mvwaddstr(b.win, 0, 2, "Overall Progress");
333 	wattroff(b.win, t.bar.color);
334 
335 	b.y = b.x = 1;
336 	b.w = WBAR(&d);
337 	b.fmt = "%3d%%";
338 	b.perc = b.label = MIN(mainperc, 100);
339 	b.toupdate = true;
340 	draw_bar(&b);
341 
342 	doupdate();
343 	/* getch(); to test with "alternate mode" */
344 
345 	delwin(b.win);
346 	end_dialog(&d);
347 
348 	return (BSDDIALOG_OK);
349 }
350 
351 int
bsddialog_mixedgauge(struct bsddialog_conf * conf,const char * text,int rows,int cols,unsigned int mainperc,unsigned int nminibars,const char ** minilabels,int * minipercs)352 bsddialog_mixedgauge(struct bsddialog_conf *conf, const char *text, int rows,
353     int cols, unsigned int mainperc, unsigned int nminibars,
354     const char **minilabels, int *minipercs)
355 {
356 	int retval;
357 
358 	retval = do_mixedgauge(conf, text, rows, cols, mainperc, nminibars,
359 	    minilabels, minipercs, false);
360 
361 	return (retval);
362 }
363 
364 int
bsddialog_progressview(struct bsddialog_conf * conf,const char * text,int rows,int cols,struct bsddialog_progviewconf * pvconf,unsigned int nminibar,struct bsddialog_fileminibar * minibar)365 bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows,
366     int cols, struct bsddialog_progviewconf *pvconf, unsigned int nminibar,
367     struct bsddialog_fileminibar *minibar)
368 {
369 	bool update;
370 	int perc, retval, *minipercs;
371 	unsigned int i, mainperc, totaltodo;
372 	float readforsec;
373 	const char **minilabels;
374 	time_t tstart, told, tnew, trefresh;
375 
376 	if ((minilabels = calloc(nminibar, sizeof(char*))) == NULL)
377 		RETURN_ERROR("Cannot allocate memory for minilabels");
378 	if ((minipercs = calloc(nminibar, sizeof(int))) == NULL)
379 		RETURN_ERROR("Cannot allocate memory for minipercs");
380 
381 	totaltodo = 0;
382 	for (i = 0; i < nminibar; i++) {
383 		totaltodo += minibar[i].size;
384 		minilabels[i] = minibar[i].label;
385 		minipercs[i] = minibar[i].status;
386 	}
387 
388 	trefresh = pvconf->refresh == 0 ? 0 : pvconf->refresh - 1;
389 	retval = BSDDIALOG_OK;
390 	i = 0;
391 	update = true;
392 	time(&told);
393 	tstart = told;
394 	while (!(bsddialog_interruptprogview || bsddialog_abortprogview)) {
395 		if (bsddialog_total_progview == 0 || totaltodo == 0)
396 			mainperc = 0;
397 		else
398 			mainperc = (bsddialog_total_progview * 100) / totaltodo;
399 
400 		time(&tnew);
401 		if (update || tnew > told + trefresh) {
402 			retval = do_mixedgauge(conf, text, rows, cols, mainperc,
403 			    nminibar, minilabels, minipercs, true);
404 			if (retval == BSDDIALOG_ERROR)
405 				return (BSDDIALOG_ERROR);
406 
407 			move(SCREENLINES - 1, 2);
408 			clrtoeol();
409 			readforsec = ((tnew - tstart) == 0) ? 0 :
410 			    bsddialog_total_progview / (float)(tnew - tstart);
411 			printw(pvconf->fmtbottomstr, bsddialog_total_progview,
412 			    readforsec);
413 			refresh();
414 
415 			time(&told);
416 			update = false;
417 		}
418 
419 		if (i >= nminibar)
420 			break;
421 		if (minibar[i].status == BSDDIALOG_MG_FAILED)
422 			break;
423 
424 		perc = pvconf->callback(&minibar[i]);
425 
426 		if (minibar[i].status == BSDDIALOG_MG_DONE) { /*||perc >= 100)*/
427 			minipercs[i] = BSDDIALOG_MG_DONE;
428 			update = true;
429 			i++;
430 		} else if (minibar[i].status == BSDDIALOG_MG_FAILED ||
431 		    perc < 0) {
432 			minipercs[i] = BSDDIALOG_MG_FAILED;
433 			update = true;
434 		} else /* perc >= 0 */
435 			minipercs[i] = perc;
436 	}
437 
438 	free(minilabels);
439 	free(minipercs);
440 	return (retval);
441 }
442 
443 static int
rangebox_redraw(struct dialog * d,bool redraw,struct bar * b,int * bigchange)444 rangebox_redraw(struct dialog *d, bool redraw, struct bar *b, int *bigchange)
445 {
446 	if (redraw) {
447 		hide_dialog(d);
448 		refresh(); /* Important for decreasing screen */
449 	}
450 	if (dialog_size_position(d, HBOX, MIN_WBOX, NULL) != 0)
451 		return (BSDDIALOG_ERROR);
452 	if (draw_dialog(d) != 0) /* doupdate() in main loop */
453 		return (BSDDIALOG_ERROR);
454 	if (redraw)
455 		refresh(); /* Important to fix grey lines expanding screen */
456 	TEXTPAD(d, HBOX + HBUTTONS);
457 
458 	b->w = WBAR(d);
459 	*bigchange = MAX(1, b->w  / 10);
460 	update_barbox(d, b, true);
461 	b->toupdate = true;
462 
463 	return (0);
464 }
465 
466 int
bsddialog_rangebox(struct bsddialog_conf * conf,const char * text,int rows,int cols,int min,int max,int * value)467 bsddialog_rangebox(struct bsddialog_conf *conf, const char *text, int rows,
468     int cols, int min, int max, int *value)
469 {
470 	bool loop;
471 	int currvalue, retval, bigchange, positions;
472 	wint_t input;
473 	struct bar b;
474 	struct dialog d;
475 
476 	CHECK_PTR(value);
477 	if (min >= max)
478 		RETURN_FMTERROR("min (%d) >= max (%d)", min, max);
479 	if (*value < min)
480 		RETURN_FMTERROR("value (%d) < min (%d)", *value, min);
481 	if (*value > max)
482 		RETURN_FMTERROR("value (%d) > max (%d)", *value, max);
483 
484 	currvalue = *value;
485 	positions = max - min + 1;
486 
487 	if (prepare_dialog(conf, text, rows, cols, &d) != 0)
488 		return (BSDDIALOG_ERROR);
489 	set_buttons(&d, true, OK_LABEL, CANCEL_LABEL);
490 	if ((b.win = newwin(1, 1, 1, 1)) == NULL)
491 		RETURN_ERROR("Cannot build WINDOW bar");
492 	b.y = b.x = 1;
493 	b.fmt = "%d";
494 	if (rangebox_redraw(&d, false, &b, &bigchange) != 0)
495 		return (BSDDIALOG_ERROR);
496 
497 	loop = true;
498 	while (loop) {
499 		if (b.toupdate) {
500 			b.perc = ((float)(currvalue - min)*100) / (positions-1);
501 			b.label = currvalue;
502 			draw_bar(&b);
503 		}
504 		doupdate();
505 		if (get_wch(&input) == ERR)
506 			continue;
507 		switch(input) {
508 		case KEY_ENTER:
509 		case 10: /* Enter */
510 			retval = BUTTONVALUE(d.bs);
511 			loop = false;
512 			break;
513 		case 27: /* Esc */
514 			if (conf->key.enable_esc) {
515 				retval = BSDDIALOG_ESC;
516 				loop = false;
517 			}
518 			break;
519 		case '\t': /* TAB */
520 		case KEY_CTRL('n'):
521 		case KEY_RIGHT:
522 			d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons;
523 			DRAW_BUTTONS(d);
524 			break;
525 		case KEY_CTRL('p'):
526 		case KEY_LEFT:
527 			d.bs.curr--;
528 			if (d.bs.curr < 0)
529 				 d.bs.curr = d.bs.nbuttons - 1;
530 			DRAW_BUTTONS(d);
531 			break;
532 		case KEY_HOME:
533 			currvalue = max;
534 			b.toupdate = true;
535 			break;
536 		case KEY_END:
537 			currvalue = min;
538 			b.toupdate = true;
539 			break;
540 		case KEY_NPAGE:
541 			currvalue -= bigchange;
542 			if (currvalue < min)
543 				currvalue = min;
544 			b.toupdate = true;
545 			break;
546 		case KEY_PPAGE:
547 			currvalue += bigchange;
548 			if (currvalue > max)
549 				currvalue = max;
550 			b.toupdate = true;
551 			break;
552 		case '-':
553 		case KEY_UP:
554 			if (currvalue > min) {
555 				currvalue--;
556 				b.toupdate = true;
557 			}
558 			break;
559 		case '+':
560 		case KEY_DOWN:
561 			if (currvalue < max) {
562 				currvalue++;
563 				b.toupdate = true;
564 			}
565 			break;
566 		case KEY_F(1):
567 			if (conf->key.f1_file == NULL &&
568 			    conf->key.f1_message == NULL)
569 				break;
570 			if (f1help_dialog(conf) != 0)
571 				return (BSDDIALOG_ERROR);
572 			if (rangebox_redraw(&d, true, &b, &bigchange) != 0)
573 				return (BSDDIALOG_ERROR);
574 			break;
575 		case KEY_CTRL('l'):
576 		case KEY_RESIZE:
577 			if (rangebox_redraw(&d, true, &b, &bigchange) != 0)
578 				return (BSDDIALOG_ERROR);
579 			break;
580 		default:
581 			if (shortcut_buttons(input, &d.bs)) {
582 				DRAW_BUTTONS(d);
583 				doupdate();
584 				retval = BUTTONVALUE(d.bs);
585 				loop = false;
586 			}
587 		}
588 	}
589 
590 	*value = currvalue;
591 
592 	delwin(b.win);
593 	end_dialog(&d);
594 
595 	return (retval);
596 }
597 
pause_redraw(struct dialog * d,bool redraw,struct bar * b)598 static int pause_redraw(struct dialog *d, bool redraw, struct bar *b)
599 {
600 	if (redraw) {
601 		hide_dialog(d);
602 		refresh(); /* Important for decreasing screen */
603 	}
604 	if (dialog_size_position(d, HBOX, MIN_WBOX, NULL) != 0)
605 		return (BSDDIALOG_ERROR);
606 	if (draw_dialog(d) != 0) /* doupdate() in main loop */
607 		return (BSDDIALOG_ERROR);
608 	if (redraw)
609 		refresh(); /* Important to fix grey lines expanding screen */
610 	TEXTPAD(d, HBOX + HBUTTONS);
611 
612 	b->w = WBAR(d);
613 	update_barbox(d, b, true);
614 	b->toupdate = true;
615 
616 	return (0);
617 }
618 
619 int
bsddialog_pause(struct bsddialog_conf * conf,const char * text,int rows,int cols,unsigned int * seconds)620 bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows,
621     int cols, unsigned int *seconds)
622 {
623 	bool loop;
624 	int retval, tout;
625 	wint_t input;
626 	struct bar b;
627 	struct dialog d;
628 
629 	CHECK_PTR(seconds);
630 	if (prepare_dialog(conf, text, rows, cols, &d) != 0)
631 		return (BSDDIALOG_ERROR);
632 	set_buttons(&d, true, OK_LABEL, CANCEL_LABEL);
633 	if ((b.win = newwin(1, 1, 1, 1)) == NULL)
634 		RETURN_ERROR("Cannot build WINDOW bar");
635 	b.y = b.x = 1;
636 	b.fmt = "%d";
637 	if (pause_redraw(&d, false, &b) != 0)
638 		return (BSDDIALOG_ERROR);
639 
640 	tout = *seconds;
641 	nodelay(stdscr, TRUE);
642 	timeout(1000);
643 	loop = true;
644 	while (loop) {
645 		if (b.toupdate) {
646 			b.perc = (float)tout * 100 / *seconds;
647 			b.label = tout;
648 			draw_bar(&b);
649 		}
650 		doupdate();
651 		if (get_wch(&input) == ERR) { /* timeout */
652 			tout--;
653 			if (tout < 0) {
654 				retval = BSDDIALOG_TIMEOUT;
655 				break;
656 			}
657 			else {
658 				b.toupdate = true;
659 				continue;
660 			}
661 		}
662 		switch(input) {
663 		case KEY_ENTER:
664 		case 10: /* Enter */
665 			retval = BUTTONVALUE(d.bs);
666 			loop = false;
667 			break;
668 		case 27: /* Esc */
669 			if (conf->key.enable_esc) {
670 				retval = BSDDIALOG_ESC;
671 				loop = false;
672 			}
673 			break;
674 		case '\t': /* TAB */
675 		case KEY_RIGHT:
676 			d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons;
677 			DRAW_BUTTONS(d);
678 			break;
679 		case KEY_LEFT:
680 			d.bs.curr--;
681 			if (d.bs.curr < 0)
682 				 d.bs.curr = d.bs.nbuttons - 1;
683 			DRAW_BUTTONS(d);
684 			break;
685 		case KEY_F(1):
686 			if (conf->key.f1_file == NULL &&
687 			    conf->key.f1_message == NULL)
688 				break;
689 			if (f1help_dialog(conf) != 0)
690 				return (BSDDIALOG_ERROR);
691 			if (pause_redraw(&d, true, &b) != 0)
692 				return (BSDDIALOG_ERROR);
693 			break;
694 		case KEY_CTRL('l'):
695 		case KEY_RESIZE:
696 			if (pause_redraw(&d, true, &b) != 0)
697 				return (BSDDIALOG_ERROR);
698 			break;
699 		default:
700 			if (shortcut_buttons(input, &d.bs)) {
701 				DRAW_BUTTONS(d);
702 				doupdate();
703 				retval = BUTTONVALUE(d.bs);
704 				loop = false;
705 			}
706 		}
707 	}
708 	nodelay(stdscr, FALSE);
709 
710 	*seconds = MAX(tout, 0);
711 
712 	delwin(b.win);
713 	end_dialog(&d);
714 
715 	return (retval);
716 }
717