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