1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2021 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 <sys/param.h> 29 30 #include <ctype.h> 31 #include <stdlib.h> 32 #include <string.h> 33 34 #ifdef PORTNCURSES 35 #include <ncurses/ncurses.h> 36 #else 37 #include <ncurses.h> 38 #endif 39 40 #include "bsddialog.h" 41 #include "lib_util.h" 42 #include "bsddialog_theme.h" 43 44 #define BARMARGIN 3 45 #define MINBARWIDTH 10 46 #define MINWIDTH (VBORDERS + MINBARWIDTH + BARMARGIN * 2) 47 #define MINHEIGHT 7 /* without text */ 48 49 /* "Bar": gauge - mixedgauge - rangebox - pause */ 50 51 extern struct bsddialog_theme t; 52 53 static void 54 draw_perc_bar(WINDOW *win, int y, int x, int size, int perc, bool withlabel, 55 int label) 56 { 57 char labelstr[128]; 58 int i, blue_x, color; 59 60 blue_x = (int)((perc*(size))/100); 61 62 wmove(win, y, x); 63 for (i = 0; i < size; i++) { 64 color = (i <= blue_x) ? t.bar.f_color : t.bar.color; 65 wattron(win, color); 66 waddch(win, ' '); 67 wattroff(win, color); 68 } 69 70 if (withlabel) 71 sprintf(labelstr, "%d", label); 72 else 73 sprintf(labelstr, "%3d%%", perc); 74 wmove(win, y, x + size/2 - 2); 75 for (i=0; i < (int) strlen(labelstr); i++) { 76 color = (blue_x + 1 <= size/2 - (int)strlen(labelstr)/2 + i ) ? 77 t.bar.color : t.bar.f_color; 78 wattron(win, color); 79 waddch(win, labelstr[i]); 80 wattroff(win, color); 81 } 82 } 83 84 static int 85 bar_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, 86 char *text, struct buttons *bs) 87 { 88 int maxword, maxline, nlines, buttonswidth; 89 90 if (get_text_properties(conf, text, &maxword, &maxline, &nlines) != 0) 91 return BSDDIALOG_ERROR; 92 93 buttonswidth = 0; 94 if (bs != NULL) { /* gauge has not buttons */ 95 buttonswidth= bs->nbuttons * bs->sizebutton; 96 if (bs->nbuttons > 0) 97 buttonswidth += (bs->nbuttons-1) * t.button.space; 98 } 99 100 if (cols == BSDDIALOG_AUTOSIZE) { 101 *w = VBORDERS; 102 /* buttons size */ 103 *w += buttonswidth; 104 /* bar size */ 105 *w = MAX(*w, MINWIDTH); 106 /* text size*/ 107 *w = MAX((int)(maxline + VBORDERS + t.text.hmargin * 2), *w); 108 /* conf.auto_minwidth */ 109 *w = MAX(*w, (int)conf->auto_minwidth); 110 /* avoid terminal overflow */ 111 *w = MIN(*w, widget_max_width(conf)); 112 } 113 114 if (rows == BSDDIALOG_AUTOSIZE) { 115 *h = MINHEIGHT; 116 if (maxword > 0) 117 *h += 1; 118 /* conf.auto_minheight */ 119 *h = MAX(*h, (int)conf->auto_minheight); 120 /* avoid terminal overflow */ 121 *h = MIN(*h, widget_max_height(conf)); 122 } 123 124 return (0); 125 } 126 127 static int 128 bar_checksize(char *text, int rows, int cols, struct buttons *bs) 129 { 130 int minheight, minwidth; 131 132 minwidth = 0; 133 if (bs != NULL) { /* gauge has not buttons */ 134 minwidth = bs->nbuttons * bs->sizebutton; 135 if (bs->nbuttons > 0) 136 minwidth += (bs->nbuttons-1) * t.button.space; 137 } 138 minwidth = MAX(minwidth + VBORDERS, MINBARWIDTH); 139 140 if (cols< minwidth) 141 RETURN_ERROR("Few cols for this widget"); 142 143 minheight = MINHEIGHT + ((text != NULL && strlen(text) > 0) ? 1 : 0); 144 if (rows < minheight) 145 RETURN_ERROR("Few rows for this mixedgauge"); 146 147 return 0; 148 } 149 150 int 151 bsddialog_gauge(struct bsddialog_conf *conf, char* text, int rows, int cols, 152 unsigned int perc) 153 { 154 WINDOW *widget, *textpad, *bar, *shadow; 155 char input[2048], ntext[2048], *pntext; 156 int y, x, h, w, htextpad; 157 bool mainloop; 158 159 if (set_widget_size(conf, rows, cols, &h, &w) != 0) 160 return BSDDIALOG_ERROR; 161 if (bar_autosize(conf, rows, cols, &h, &w, text, NULL) != 0) 162 return BSDDIALOG_ERROR; 163 if (bar_checksize(text, h, w, NULL) != 0) 164 return BSDDIALOG_ERROR; 165 if (set_widget_position(conf, &y, &x, h, w) != 0) 166 return BSDDIALOG_ERROR; 167 168 if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED, 169 &textpad, &htextpad, text, false) != 0) 170 return BSDDIALOG_ERROR; 171 172 bar = new_boxed_window(conf, y+h-4, x+3, 3, w-6, RAISED); 173 174 mainloop = true; 175 while (mainloop) { 176 wrefresh(widget); 177 prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-4, 178 x+w-1-t.text.hmargin); 179 draw_perc_bar(bar, 1, 1, w-8, perc, false, -1 /*unused*/); 180 wrefresh(bar); 181 182 while (true) { 183 scanf("%s", input); 184 if (strcmp(input,"EOF") == 0) { 185 mainloop = false; 186 break; 187 } 188 if (strcmp(input,"XXX") == 0) 189 break; 190 } 191 scanf("%d", &perc); 192 perc = perc < 0 ? 0 : perc; 193 perc = perc > 100 ? 100 : perc; 194 htextpad = 1; 195 wclear(textpad); 196 pntext = &ntext[0]; 197 ntext[0] = '\0'; 198 while (true) { 199 scanf("%s", input); 200 if (strcmp(input,"EOF") == 0) { 201 mainloop = false; 202 break; 203 } 204 if (strcmp(input,"XXX") == 0) 205 break; 206 pntext[0] = ' '; 207 pntext++; 208 strcpy(pntext, input); 209 pntext += strlen(input); 210 } 211 print_textpad(conf, textpad, &htextpad, w-2-t.text.hmargin*2, 212 ntext); 213 } 214 215 delwin(bar); 216 end_widget_withtextpad(conf, widget, h, w, textpad, shadow); 217 218 return BSDDIALOG_OK; 219 } 220 221 int 222 bsddialog_mixedgauge(struct bsddialog_conf *conf, char* text, int rows, 223 int cols, unsigned int mainperc, unsigned int nminibars, char **minilabels, 224 int *minipercs) 225 { 226 WINDOW *widget, *textpad, *bar, *shadow; 227 int i, output, miniperc, y, x, h, w, max_minbarlen; 228 int maxword, maxline, nlines, htextpad, ypad; 229 char states[12][16] = { 230 "[ Succeeded ]", /* 0 */ 231 "[ Failed ]", /* 1 */ 232 "[ Passed ]", /* 2 */ 233 "[ Completed ]", /* 3 */ 234 "[ Checked ]", /* 4 */ 235 "[ Done ]", /* 5 */ 236 "[ Skipped ]", /* 6 */ 237 "[ In Progress ]", /* 7 */ 238 "(blank) ", /* 8 */ 239 "[ N/A ]", /* 9 */ 240 "[ Pending ]", /* 10 */ 241 "[ UNKNOWN ]", /* 10+ */ 242 }; 243 244 max_minbarlen = 0; 245 for (i=0; i < (int)nminibars; i++) 246 max_minbarlen = MAX(max_minbarlen, (int)strlen(minilabels[i])); 247 max_minbarlen += 3 + 16 /* seps + [...] or mainbar */; 248 249 if (set_widget_size(conf, rows, cols, &h, &w) != 0) 250 return BSDDIALOG_ERROR; 251 252 /* mixedgauge autosize */ 253 if (get_text_properties(conf, text, &maxword, &maxline, &nlines) != 0) 254 return BSDDIALOG_ERROR; 255 256 if (cols == BSDDIALOG_AUTOSIZE) { 257 w = max_minbarlen + HBORDERS; 258 w = MAX(max_minbarlen, maxline + 4); 259 w = MAX(w, (int)conf->auto_minwidth); 260 w = MIN(w, widget_max_width(conf) - 1); 261 } 262 if (rows == BSDDIALOG_AUTOSIZE) { 263 h = 5; /* borders + mainbar */ 264 h += nminibars; 265 h += (strlen(text) > 0 ? 3 : 0); 266 h = MAX(h, (int)conf->auto_minheight); 267 h = MIN(h, widget_max_height(conf) -1); 268 } 269 270 /* mixedgauge checksize */ 271 if (w < max_minbarlen + 2) 272 RETURN_ERROR("Few cols for this mixedgauge"); 273 if (h < 5 + (int)nminibars + (strlen(text) > 0 ? 1 : 0)) 274 RETURN_ERROR("Few rows for this mixedgauge"); 275 276 if (set_widget_position(conf, &y, &x, h, w) != 0) 277 return BSDDIALOG_ERROR; 278 279 output = new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, 280 RAISED, &textpad, &htextpad, text, false); 281 if (output == BSDDIALOG_ERROR) 282 return output; 283 284 /* mini bars */ 285 for (i=0; i < (int)nminibars; i++) { 286 miniperc = minipercs[i]; 287 if (miniperc == 8) 288 continue; 289 mvwaddstr(widget, i+1, 2, minilabels[i]); 290 if (miniperc > 10) 291 mvwaddstr(widget, i+1, w-2-15, states[11]); 292 else if (miniperc >= 0 && miniperc <= 10) 293 mvwaddstr(widget, i+1, w-2-15, states[miniperc]); 294 else { /* miniperc < 0 */ 295 miniperc = abs(miniperc); 296 mvwaddstr(widget, i+1, w-2-15, "[ ]"); 297 draw_perc_bar(widget, i+1, 1+w-2-15, 13, miniperc, 298 false, -1 /*unused*/); 299 } 300 } 301 302 wrefresh(widget); 303 ypad = y + h - 5 - htextpad; 304 ypad = ypad < y+(int)nminibars ? y+nminibars : ypad; 305 prefresh(textpad, 0, 0, ypad, x+2, y+h-4, x+w-2); 306 307 /* main bar */ 308 bar = new_boxed_window(conf, y+h -4, x+3, 3, w-6, RAISED); 309 310 draw_perc_bar(bar, 1, 1, w-8, mainperc, false, -1 /*unused*/); 311 312 wattron(bar, t.bar.color); 313 mvwaddstr(bar, 0, 2, "Overall Progress"); 314 wattroff(bar, t.bar.color); 315 316 wrefresh(bar); 317 318 /* getch(); port ncurses shows nothing */ 319 320 delwin(bar); 321 end_widget_withtextpad(conf, widget, h, w, textpad, shadow); 322 323 return BSDDIALOG_OK; 324 } 325 326 int 327 bsddialog_rangebox(struct bsddialog_conf *conf, char* text, int rows, int cols, 328 int min, int max, int *value) 329 { 330 WINDOW *widget, *textpad, *bar, *shadow; 331 int i, y, x, h, w, htextpad; 332 bool loop, buttupdate, barupdate; 333 int input, currvalue, output, sizebar, bigchange, positions; 334 float perc; 335 struct buttons bs; 336 337 if (value == NULL) 338 RETURN_ERROR("*value cannot be NULL"); 339 340 if (min >= max) 341 RETURN_ERROR("min >= max"); 342 343 currvalue = *value; 344 positions = max - min + 1; 345 346 get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label), 347 BUTTONLABEL(cancel_label), BUTTONLABEL(help_label)); 348 349 if (set_widget_size(conf, rows, cols, &h, &w) != 0) 350 return BSDDIALOG_ERROR; 351 if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0) 352 return BSDDIALOG_ERROR; 353 if (bar_checksize(text, h, w, &bs) != 0) 354 return BSDDIALOG_ERROR; 355 if (set_widget_position(conf, &y, &x, h, w) != 0) 356 return BSDDIALOG_ERROR; 357 358 if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED, 359 &textpad, &htextpad, text, true) != 0) 360 return BSDDIALOG_ERROR; 361 362 prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-7, 363 x+w-1-t.text.hmargin); 364 365 sizebar = w - HBORDERS - 2 - BARMARGIN * 2; 366 bigchange = MAX(1, sizebar/10); 367 368 bar = new_boxed_window(conf, y + h - 6, x + 1 + BARMARGIN, 3, 369 sizebar + 2, RAISED); 370 371 loop = buttupdate = barupdate = true; 372 while(loop) { 373 if (buttupdate) { 374 draw_buttons(widget, h-2, w, bs, true); 375 wrefresh(widget); 376 buttupdate = false; 377 } 378 if (barupdate) { 379 perc = ((float)(currvalue - min)*100) / (positions-1); 380 draw_perc_bar(bar, 1, 1, sizebar, perc, true, currvalue); 381 barupdate = false; 382 wrefresh(bar); 383 } 384 385 input = getch(); 386 switch(input) { 387 case KEY_ENTER: 388 case 10: /* Enter */ 389 output = bs.value[bs.curr]; 390 *value = currvalue; 391 loop = false; 392 break; 393 case 27: /* Esc */ 394 output = BSDDIALOG_ESC; 395 loop = false; 396 break; 397 case '\t': /* TAB */ 398 bs.curr = (bs.curr + 1) % bs.nbuttons; 399 buttupdate = true; 400 break; 401 case KEY_LEFT: 402 if (bs.curr > 0) { 403 bs.curr--; 404 buttupdate = true; 405 } 406 break; 407 case KEY_RIGHT: 408 if (bs.curr < (int) bs.nbuttons - 1) { 409 bs.curr++; 410 buttupdate = true; 411 } 412 break; 413 case KEY_HOME: 414 currvalue = max; 415 barupdate = true; 416 break; 417 case KEY_END: 418 currvalue = min; 419 barupdate = true; 420 break; 421 case KEY_NPAGE: 422 currvalue -= bigchange; 423 if (currvalue < min) 424 currvalue = min; 425 barupdate = true; 426 break; 427 case KEY_PPAGE: 428 currvalue += bigchange; 429 if (currvalue > max) 430 currvalue = max; 431 barupdate = true; 432 break; 433 case KEY_UP: 434 if (currvalue < max) { 435 currvalue++; 436 barupdate = true; 437 } 438 break; 439 case KEY_DOWN: 440 if (currvalue > min) { 441 currvalue--; 442 barupdate = true; 443 } 444 break; 445 case KEY_F(1): 446 if (conf->f1_file == NULL && conf->f1_message == NULL) 447 break; 448 if (f1help(conf) != 0) 449 return BSDDIALOG_ERROR; 450 /* No break! the terminal size can change */ 451 case KEY_RESIZE: 452 hide_widget(y, x, h, w,conf->shadow); 453 454 /* 455 * Unnecessary, but, when the columns decrease the 456 * following "refresh" seem not work 457 */ 458 refresh(); 459 460 if (set_widget_size(conf, rows, cols, &h, &w) != 0) 461 return BSDDIALOG_ERROR; 462 if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0) 463 return BSDDIALOG_ERROR; 464 if (bar_checksize(text, h, w, &bs) != 0) 465 return BSDDIALOG_ERROR; 466 if (set_widget_position(conf, &y, &x, h, w) != 0) 467 return BSDDIALOG_ERROR; 468 469 wclear(shadow); 470 mvwin(shadow, y + t.shadow.h, x + t.shadow.w); 471 wresize(shadow, h, w); 472 473 wclear(widget); 474 mvwin(widget, y, x); 475 wresize(widget, h, w); 476 477 htextpad = 1; 478 wclear(textpad); 479 wresize(textpad, 1, w - HBORDERS - t.text.hmargin * 2); 480 481 sizebar = w - HBORDERS - 2 - BARMARGIN * 2; 482 bigchange = MAX(1, sizebar/10); 483 wclear(bar); 484 mvwin(bar, y + h - 6, x + 1 + BARMARGIN); 485 wresize(bar, 3, sizebar + 2); 486 487 if(update_widget_withtextpad(conf, shadow, widget, h, w, 488 RAISED, textpad, &htextpad, text, true) != 0) 489 return BSDDIALOG_ERROR; 490 491 prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-7, 492 x+w-1-t.text.hmargin); 493 494 draw_borders(conf, bar, 3, sizebar + 2, RAISED); 495 496 barupdate = true; 497 buttupdate = true; 498 break; 499 default: 500 for (i = 0; i < (int) bs.nbuttons; i++) 501 if (tolower(input) == tolower((bs.label[i])[0])) { 502 output = bs.value[i]; 503 loop = false; 504 } 505 } 506 } 507 508 delwin(bar); 509 end_widget_withtextpad(conf, widget, h, w, textpad, shadow); 510 511 return output; 512 } 513 514 int 515 bsddialog_pause(struct bsddialog_conf *conf, char* text, int rows, int cols, 516 unsigned int sec) 517 { 518 WINDOW *widget, *textpad, *bar, *shadow; 519 int i, output, y, x, h, w, htextpad; 520 bool loop, buttupdate, barupdate; 521 int input, tout, sizebar; 522 float perc; 523 struct buttons bs; 524 525 get_buttons(conf, &bs, BUTTONLABEL(ok_label), BUTTONLABEL(extra_label), 526 BUTTONLABEL(cancel_label), BUTTONLABEL(help_label)); 527 528 if (set_widget_size(conf, rows, cols, &h, &w) != 0) 529 return BSDDIALOG_ERROR; 530 if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0) 531 return BSDDIALOG_ERROR; 532 if (bar_checksize(text, h, w, &bs) != 0) 533 return BSDDIALOG_ERROR; 534 if (set_widget_position(conf, &y, &x, h, w) != 0) 535 return BSDDIALOG_ERROR; 536 537 if (new_widget_withtextpad(conf, &shadow, &widget, y, x, h, w, RAISED, 538 &textpad, &htextpad, text, true) != 0) 539 return BSDDIALOG_ERROR; 540 541 prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-7, 542 x+w-1-t.text.hmargin); 543 544 sizebar = w - HBORDERS - 2 - BARMARGIN * 2; 545 bar = new_boxed_window(conf, y + h - 6, x + 1 + BARMARGIN, 3, 546 sizebar + 2, RAISED); 547 548 tout = sec; 549 nodelay(stdscr, TRUE); 550 timeout(1000); 551 loop = buttupdate = barupdate = true; 552 while(loop) { 553 if (barupdate) { 554 perc = (float)tout * 100 / sec; 555 draw_perc_bar(bar, 1, 1, sizebar, perc, true, tout); 556 barupdate = false; 557 wrefresh(bar); 558 } 559 560 if (buttupdate) { 561 draw_buttons(widget, h-2, w, bs, true); 562 wrefresh(widget); 563 buttupdate = false; 564 } 565 566 input = getch(); 567 if(input < 0) { /* timeout */ 568 tout--; 569 if (tout < 0) { 570 output = BSDDIALOG_TIMEOUT; 571 break; 572 } 573 else { 574 barupdate = true; 575 continue; 576 } 577 } 578 switch(input) { 579 case KEY_ENTER: 580 case 10: /* Enter */ 581 output = bs.value[bs.curr]; 582 loop = false; 583 break; 584 case 27: /* Esc */ 585 output = BSDDIALOG_ESC; 586 loop = false; 587 break; 588 case '\t': /* TAB */ 589 bs.curr = (bs.curr + 1) % bs.nbuttons; 590 buttupdate = true; 591 break; 592 case KEY_LEFT: 593 if (bs.curr > 0) { 594 bs.curr--; 595 buttupdate = true; 596 } 597 break; 598 case KEY_RIGHT: 599 if (bs.curr < (int) bs.nbuttons - 1) { 600 bs.curr++; 601 buttupdate = true; 602 } 603 break; 604 case KEY_F(1): 605 if (conf->f1_file == NULL && conf->f1_message == NULL) 606 break; 607 if (f1help(conf) != 0) 608 return BSDDIALOG_ERROR; 609 /* No break! the terminal size can change */ 610 case KEY_RESIZE: 611 hide_widget(y, x, h, w,conf->shadow); 612 613 /* 614 * Unnecessary, but, when the columns decrease the 615 * following "refresh" seem not work 616 */ 617 refresh(); 618 619 if (set_widget_size(conf, rows, cols, &h, &w) != 0) 620 return BSDDIALOG_ERROR; 621 if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0) 622 return BSDDIALOG_ERROR; 623 if (bar_checksize(text, h, w, &bs) != 0) 624 return BSDDIALOG_ERROR; 625 if (set_widget_position(conf, &y, &x, h, w) != 0) 626 return BSDDIALOG_ERROR; 627 628 wclear(shadow); 629 mvwin(shadow, y + t.shadow.h, x + t.shadow.w); 630 wresize(shadow, h, w); 631 632 wclear(widget); 633 mvwin(widget, y, x); 634 wresize(widget, h, w); 635 636 htextpad = 1; 637 wclear(textpad); 638 wresize(textpad, 1, w - HBORDERS - t.text.hmargin * 2); 639 640 sizebar = w - HBORDERS - 2 - BARMARGIN * 2; 641 wclear(bar); 642 mvwin(bar, y + h - 6, x + 1 + BARMARGIN); 643 wresize(bar, 3, sizebar + 2); 644 645 if(update_widget_withtextpad(conf, shadow, widget, h, w, 646 RAISED, textpad, &htextpad, text, true) != 0) 647 return BSDDIALOG_ERROR; 648 649 prefresh(textpad, 0, 0, y+1, x+1+t.text.hmargin, y+h-7, 650 x+w-1-t.text.hmargin); 651 652 draw_borders(conf, bar, 3, sizebar + 2, RAISED); 653 654 barupdate = true; 655 buttupdate = true; 656 break; 657 default: 658 for (i = 0; i < (int) bs.nbuttons; i++) 659 if (tolower(input) == tolower((bs.label[i])[0])) { 660 output = bs.value[i]; 661 loop = false; 662 } 663 } 664 } 665 666 nodelay(stdscr, FALSE); 667 668 delwin(bar); 669 end_widget_withtextpad(conf, widget, h, w, textpad, shadow); 670 671 return output; 672 } 673