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 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 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 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 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 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 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 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 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 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 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 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 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