1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2021-2024 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, refresh;
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 refresh = 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 + refresh) {
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
rangebox_redraw(struct dialog * d,struct bar * b,int * bigchange)443 static int rangebox_redraw(struct dialog *d, struct bar *b, int *bigchange)
444 {
445 if (d->built) {
446 hide_dialog(d);
447 refresh(); /* Important for decreasing screen */
448 }
449 if (dialog_size_position(d, HBOX, MIN_WBOX, NULL) != 0)
450 return (BSDDIALOG_ERROR);
451 if (draw_dialog(d) != 0)
452 return (BSDDIALOG_ERROR);
453 if (d->built)
454 refresh(); /* Important to fix grey lines expanding screen */
455 TEXTPAD(d, HBOX + HBUTTONS);
456
457 b->w = WBAR(d);
458 *bigchange = MAX(1, b->w / 10);
459 update_barbox(d, b, true);
460 b->toupdate = true;
461
462 return (0);
463 }
464
465 int
bsddialog_rangebox(struct bsddialog_conf * conf,const char * text,int rows,int cols,int min,int max,int * value)466 bsddialog_rangebox(struct bsddialog_conf *conf, const char *text, int rows,
467 int cols, int min, int max, int *value)
468 {
469 bool loop;
470 int currvalue, retval, bigchange, positions;
471 wint_t input;
472 struct bar b;
473 struct dialog d;
474
475 CHECK_PTR(value);
476 if (min >= max)
477 RETURN_FMTERROR("min (%d) >= max (%d)", min, max);
478 if (*value < min)
479 RETURN_FMTERROR("value (%d) < min (%d)", *value, min);
480 if (*value > max)
481 RETURN_FMTERROR("value (%d) > max (%d)", *value, max);
482
483 currvalue = *value;
484 positions = max - min + 1;
485
486 if (prepare_dialog(conf, text, rows, cols, &d) != 0)
487 return (BSDDIALOG_ERROR);
488 set_buttons(&d, true, OK_LABEL, CANCEL_LABEL);
489 if ((b.win = newwin(1, 1, 1, 1)) == NULL)
490 RETURN_ERROR("Cannot build WINDOW bar");
491 b.y = b.x = 1;
492 b.fmt = "%d";
493 if (rangebox_redraw(&d, &b, &bigchange) != 0)
494 return (BSDDIALOG_ERROR);
495
496 loop = true;
497 while (loop) {
498 if (b.toupdate) {
499 b.perc = ((float)(currvalue - min)*100) / (positions-1);
500 b.label = currvalue;
501 draw_bar(&b);
502 }
503 doupdate();
504 if (get_wch(&input) == ERR)
505 continue;
506 switch(input) {
507 case KEY_ENTER:
508 case 10: /* Enter */
509 retval = BUTTONVALUE(d.bs);
510 loop = false;
511 break;
512 case 27: /* Esc */
513 if (conf->key.enable_esc) {
514 retval = BSDDIALOG_ESC;
515 loop = false;
516 }
517 break;
518 case '\t': /* TAB */
519 case KEY_CTRL('n'):
520 case KEY_RIGHT:
521 d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons;
522 DRAW_BUTTONS(d);
523 break;
524 case KEY_CTRL('p'):
525 case KEY_LEFT:
526 d.bs.curr--;
527 if (d.bs.curr < 0)
528 d.bs.curr = d.bs.nbuttons - 1;
529 DRAW_BUTTONS(d);
530 break;
531 case KEY_HOME:
532 currvalue = max;
533 b.toupdate = true;
534 break;
535 case KEY_END:
536 currvalue = min;
537 b.toupdate = true;
538 break;
539 case KEY_NPAGE:
540 currvalue -= bigchange;
541 if (currvalue < min)
542 currvalue = min;
543 b.toupdate = true;
544 break;
545 case KEY_PPAGE:
546 currvalue += bigchange;
547 if (currvalue > max)
548 currvalue = max;
549 b.toupdate = true;
550 break;
551 case '-':
552 case KEY_UP:
553 if (currvalue > min) {
554 currvalue--;
555 b.toupdate = true;
556 }
557 break;
558 case '+':
559 case KEY_DOWN:
560 if (currvalue < max) {
561 currvalue++;
562 b.toupdate = true;
563 }
564 break;
565 case KEY_F(1):
566 if (conf->key.f1_file == NULL &&
567 conf->key.f1_message == NULL)
568 break;
569 if (f1help_dialog(conf) != 0)
570 return (BSDDIALOG_ERROR);
571 if (rangebox_redraw(&d, &b, &bigchange) != 0)
572 return (BSDDIALOG_ERROR);
573 break;
574 case KEY_CTRL('l'):
575 case KEY_RESIZE:
576 if (rangebox_redraw(&d, &b, &bigchange) != 0)
577 return (BSDDIALOG_ERROR);
578 break;
579 default:
580 if (shortcut_buttons(input, &d.bs)) {
581 DRAW_BUTTONS(d);
582 doupdate();
583 retval = BUTTONVALUE(d.bs);
584 loop = false;
585 }
586 }
587 }
588
589 *value = currvalue;
590
591 delwin(b.win);
592 end_dialog(&d);
593
594 return (retval);
595 }
596
pause_redraw(struct dialog * d,struct bar * b)597 static int pause_redraw(struct dialog *d, struct bar *b)
598 {
599 if (d->built) {
600 hide_dialog(d);
601 refresh(); /* Important for decreasing screen */
602 }
603 if (dialog_size_position(d, HBOX, MIN_WBOX, NULL) != 0)
604 return (BSDDIALOG_ERROR);
605 if (draw_dialog(d) != 0)
606 return (BSDDIALOG_ERROR);
607 if (d->built)
608 refresh(); /* Important to fix grey lines expanding screen */
609 TEXTPAD(d, HBOX + HBUTTONS);
610
611 b->w = WBAR(d);
612 update_barbox(d, b, true);
613 b->toupdate = true;
614
615 return (0);
616 }
617
618 int
bsddialog_pause(struct bsddialog_conf * conf,const char * text,int rows,int cols,unsigned int * seconds)619 bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows,
620 int cols, unsigned int *seconds)
621 {
622 bool loop;
623 int retval, tout;
624 wint_t input;
625 struct bar b;
626 struct dialog d;
627
628 CHECK_PTR(seconds);
629 if (prepare_dialog(conf, text, rows, cols, &d) != 0)
630 return (BSDDIALOG_ERROR);
631 set_buttons(&d, true, OK_LABEL, CANCEL_LABEL);
632 if ((b.win = newwin(1, 1, 1, 1)) == NULL)
633 RETURN_ERROR("Cannot build WINDOW bar");
634 b.y = b.x = 1;
635 b.fmt = "%d";
636 if (pause_redraw(&d, &b) != 0)
637 return (BSDDIALOG_ERROR);
638
639 tout = *seconds;
640 nodelay(stdscr, TRUE);
641 timeout(1000);
642 loop = true;
643 while (loop) {
644 if (b.toupdate) {
645 b.perc = (float)tout * 100 / *seconds;
646 b.label = tout;
647 draw_bar(&b);
648 }
649 doupdate();
650 if (get_wch(&input) == ERR) { /* timeout */
651 tout--;
652 if (tout < 0) {
653 retval = BSDDIALOG_TIMEOUT;
654 break;
655 }
656 else {
657 b.toupdate = true;
658 continue;
659 }
660 }
661 switch(input) {
662 case KEY_ENTER:
663 case 10: /* Enter */
664 retval = BUTTONVALUE(d.bs);
665 loop = false;
666 break;
667 case 27: /* Esc */
668 if (conf->key.enable_esc) {
669 retval = BSDDIALOG_ESC;
670 loop = false;
671 }
672 break;
673 case '\t': /* TAB */
674 case KEY_RIGHT:
675 d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons;
676 DRAW_BUTTONS(d);
677 break;
678 case KEY_LEFT:
679 d.bs.curr--;
680 if (d.bs.curr < 0)
681 d.bs.curr = d.bs.nbuttons - 1;
682 DRAW_BUTTONS(d);
683 break;
684 case KEY_F(1):
685 if (conf->key.f1_file == NULL &&
686 conf->key.f1_message == NULL)
687 break;
688 if (f1help_dialog(conf) != 0)
689 return (BSDDIALOG_ERROR);
690 if (pause_redraw(&d, &b) != 0)
691 return (BSDDIALOG_ERROR);
692 break;
693 case KEY_CTRL('l'):
694 case KEY_RESIZE:
695 if (pause_redraw(&d, &b) != 0)
696 return (BSDDIALOG_ERROR);
697 break;
698 default:
699 if (shortcut_buttons(input, &d.bs)) {
700 DRAW_BUTTONS(d);
701 doupdate();
702 retval = BUTTONVALUE(d.bs);
703 loop = false;
704 }
705 }
706 }
707 nodelay(stdscr, FALSE);
708
709 *seconds = MAX(tout, 0);
710
711 delwin(b.win);
712 end_dialog(&d);
713
714 return (retval);
715 }
716