xref: /aosp_15_r20/external/coreboot/payloads/libpayload/curses/PDCurses/demos/tui.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /********************************* tui.c ************************************/
2 /*
3  * 'textual user interface'
4  *
5  * $Id: tui.c,v 1.34 2008/07/14 12:35:23 wmcbrine Exp $
6  *
7  * Author : P.J. Kunst <[email protected]>
8  * Date   : 25-02-93
9  */
10 
11 #include <ctype.h>
12 #include <curses.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <time.h>
17 #include "tui.h"
18 
19 void statusmsg(char *);
20 int waitforkey(void);
21 void rmerror(void);
22 
23 #if defined(__unix) && !defined(__DJGPP__)
24 #include <unistd.h>
25 #endif
26 
27 #ifdef A_COLOR
28 # define TITLECOLOR       1       /* color pair indices */
29 # define MAINMENUCOLOR    (2 | A_BOLD)
30 # define MAINMENUREVCOLOR (3 | A_BOLD | A_REVERSE)
31 # define SUBMENUCOLOR     (4 | A_BOLD)
32 # define SUBMENUREVCOLOR  (5 | A_BOLD | A_REVERSE)
33 # define BODYCOLOR        6
34 # define STATUSCOLOR      (7 | A_BOLD)
35 # define INPUTBOXCOLOR    8
36 # define EDITBOXCOLOR     (9 | A_BOLD | A_REVERSE)
37 #else
38 # define TITLECOLOR       0       /* color pair indices */
39 # define MAINMENUCOLOR    (A_BOLD)
40 # define MAINMENUREVCOLOR (A_BOLD | A_REVERSE)
41 # define SUBMENUCOLOR     (A_BOLD)
42 # define SUBMENUREVCOLOR  (A_BOLD | A_REVERSE)
43 # define BODYCOLOR        0
44 # define STATUSCOLOR      (A_BOLD)
45 # define INPUTBOXCOLOR    0
46 # define EDITBOXCOLOR     (A_BOLD | A_REVERSE)
47 #endif
48 
49 #define th 1     /* title window height */
50 #define mh 1     /* main menu height */
51 #define sh 2     /* status window height */
52 #define bh (LINES - th - mh - sh)   /* body window height */
53 #define bw COLS  /* body window width */
54 
55 /******************************* STATIC ************************************/
56 
57 static WINDOW *wtitl, *wmain, *wbody, *wstat; /* title, menu, body, status win*/
58 static int nexty, nextx;
59 static int key = ERR, ch = ERR;
60 static bool quit = FALSE;
61 static bool incurses = FALSE;
62 
63 #ifndef PDCURSES
wordchar(void)64 static char wordchar(void)
65 {
66     return 0x17;    /* ^W */
67 }
68 #endif
69 
padstr(char * s,int length)70 static char *padstr(char *s, int length)
71 {
72     static char buf[MAXSTRLEN];
73     char fmt[10];
74 
75     sprintf(fmt, (int)strlen(s) > length ? "%%.%ds" : "%%-%ds", length);
76     sprintf(buf, fmt, s);
77 
78     return buf;
79 }
80 
prepad(char * s,int length)81 static char *prepad(char *s, int length)
82 {
83     int i;
84     char *p = s;
85 
86     if (length > 0)
87     {
88         memmove((void *)(s + length), (const void *)s, strlen(s) + 1);
89 
90         for (i = 0; i < length; i++)
91             *p++ = ' ';
92     }
93 
94     return s;
95 }
96 
rmline(WINDOW * win,int nr)97 static void rmline(WINDOW *win, int nr)   /* keeps box lines intact */
98 {
99     mvwaddstr(win, nr, 1, padstr(" ", bw - 2));
100     wrefresh(win);
101 }
102 
initcolor(void)103 static void initcolor(void)
104 {
105 #ifdef A_COLOR
106     if (has_colors())
107         start_color();
108 
109     /* foreground, background */
110 
111     init_pair(TITLECOLOR       & ~A_ATTR, COLOR_BLACK, COLOR_CYAN);
112     init_pair(MAINMENUCOLOR    & ~A_ATTR, COLOR_WHITE, COLOR_CYAN);
113     init_pair(MAINMENUREVCOLOR & ~A_ATTR, COLOR_WHITE, COLOR_BLACK);
114     init_pair(SUBMENUCOLOR     & ~A_ATTR, COLOR_WHITE, COLOR_CYAN);
115     init_pair(SUBMENUREVCOLOR  & ~A_ATTR, COLOR_WHITE, COLOR_BLACK);
116     init_pair(BODYCOLOR        & ~A_ATTR, COLOR_WHITE, COLOR_BLUE);
117     init_pair(STATUSCOLOR      & ~A_ATTR, COLOR_WHITE, COLOR_CYAN);
118     init_pair(INPUTBOXCOLOR    & ~A_ATTR, COLOR_BLACK, COLOR_CYAN);
119     init_pair(EDITBOXCOLOR     & ~A_ATTR, COLOR_WHITE, COLOR_BLACK);
120 #endif
121 }
122 
setcolor(WINDOW * win,chtype color)123 static void setcolor(WINDOW *win, chtype color)
124 {
125     chtype attr = color & A_ATTR;  /* extract Bold, Reverse, Blink bits */
126 
127 #ifdef A_COLOR
128     attr &= ~A_REVERSE;  /* ignore reverse, use colors instead! */
129     wattrset(win, COLOR_PAIR(color & A_CHARTEXT) | attr);
130 #else
131     attr &= ~A_BOLD;     /* ignore bold, gives messy display on HP-UX */
132     wattrset(win, attr);
133 #endif
134 }
135 
colorbox(WINDOW * win,chtype color,int hasbox)136 static void colorbox(WINDOW *win, chtype color, int hasbox)
137 {
138     int maxy;
139 #ifndef PDCURSES
140     int maxx;
141 #endif
142     chtype attr = color & A_ATTR;  /* extract Bold, Reverse, Blink bits */
143 
144     setcolor(win, color);
145 
146 #ifdef A_COLOR
147     if (has_colors())
148         wbkgd(win, COLOR_PAIR(color & A_CHARTEXT) | (attr & ~A_REVERSE));
149     else
150 #endif
151         wbkgd(win, attr);
152 
153     werase(win);
154 
155 #ifdef PDCURSES
156     maxy = getmaxy(win);
157 #else
158     getmaxyx(win, maxy, maxx);
159 #endif
160     if (hasbox && (maxy > 2))
161         box(win, 0, 0);
162 
163     touchwin(win);
164     wrefresh(win);
165 }
166 
idle(void)167 static void idle(void)
168 {
169     char buf[MAXSTRLEN];
170     time_t t;
171     struct tm *tp;
172 
173     if (time (&t) == -1)
174         return;  /* time not available */
175 
176     tp = localtime(&t);
177     sprintf(buf, " %.2d-%.2d-%.4d  %.2d:%.2d:%.2d",
178             tp->tm_mday, tp->tm_mon + 1, tp->tm_year + 1900,
179             tp->tm_hour, tp->tm_min, tp->tm_sec);
180 
181     mvwaddstr(wtitl, 0, bw - strlen(buf) - 2, buf);
182     wrefresh(wtitl);
183 }
184 
menudim(menu * mp,int * lines,int * columns)185 static void menudim(menu *mp, int *lines, int *columns)
186 {
187     int n, l, mmax = 0;
188 
189     for (n=0; mp->func; n++, mp++)
190         if ((l = strlen(mp->name)) > mmax) mmax = l;
191 
192     *lines = n;
193     *columns = mmax + 2;
194 }
195 
setmenupos(int y,int x)196 static void setmenupos(int y, int x)
197 {
198     nexty = y;
199     nextx = x;
200 }
201 
getmenupos(int * y,int * x)202 static void getmenupos(int *y, int *x)
203 {
204     *y = nexty;
205     *x = nextx;
206 }
207 
hotkey(const char * s)208 static int hotkey(const char *s)
209 {
210     int c0 = *s;    /* if no upper case found, return first char */
211 
212     for (; *s; s++)
213         if (isupper((unsigned char)*s))
214             break;
215 
216     return *s ? *s : c0;
217 }
218 
repaintmenu(WINDOW * wmenu,menu * mp)219 static void repaintmenu(WINDOW *wmenu, menu *mp)
220 {
221     int i;
222     menu *p = mp;
223 
224     for (i = 0; p->func; i++, p++)
225         mvwaddstr(wmenu, i + 1, 2, p->name);
226 
227     touchwin(wmenu);
228     wrefresh(wmenu);
229 }
230 
repaintmainmenu(int width,menu * mp)231 static void repaintmainmenu(int width, menu *mp)
232 {
233     int i;
234     menu *p = mp;
235 
236     for (i = 0; p->func; i++, p++)
237         mvwaddstr(wmain, 0, i * width, prepad(padstr(p->name, width - 1), 1));
238 
239     touchwin(wmain);
240     wrefresh(wmain);
241 }
242 
mainhelp(void)243 static void mainhelp(void)
244 {
245 #ifdef ALT_X
246     statusmsg("Use arrow keys and Enter to select (Alt-X to quit)");
247 #else
248     statusmsg("Use arrow keys and Enter to select");
249 #endif
250 }
251 
mainmenu(menu * mp)252 static void mainmenu(menu *mp)
253 {
254     int nitems, barlen, old = -1, cur = 0, c, cur0;
255 
256     menudim(mp, &nitems, &barlen);
257     repaintmainmenu(barlen, mp);
258 
259     while (!quit)
260     {
261         if (cur != old)
262         {
263             if (old != -1)
264             {
265                 mvwaddstr(wmain, 0, old * barlen,
266                           prepad(padstr(mp[old].name, barlen - 1), 1));
267 
268                 statusmsg(mp[cur].desc);
269             }
270             else
271                 mainhelp();
272 
273             setcolor(wmain, MAINMENUREVCOLOR);
274 
275             mvwaddstr(wmain, 0, cur * barlen,
276                       prepad(padstr(mp[cur].name, barlen - 1), 1));
277 
278             setcolor(wmain, MAINMENUCOLOR);
279             old = cur;
280             wrefresh(wmain);
281         }
282 
283         switch (c = (key != ERR ? key : waitforkey()))
284         {
285         case KEY_DOWN:
286         case '\n':              /* menu item selected */
287             touchwin(wbody);
288             wrefresh(wbody);
289             rmerror();
290             setmenupos(th + mh, cur * barlen);
291             curs_set(1);
292             (mp[cur].func)();   /* perform function */
293             curs_set(0);
294 
295             switch (key)
296             {
297             case KEY_LEFT:
298                 cur = (cur + nitems - 1) % nitems;
299                 key = '\n';
300                 break;
301 
302             case KEY_RIGHT:
303                 cur = (cur + 1) % nitems;
304                 key = '\n';
305                 break;
306 
307             default:
308                 key = ERR;
309             }
310 
311             repaintmainmenu(barlen, mp);
312             old = -1;
313             break;
314 
315         case KEY_LEFT:
316             cur = (cur + nitems - 1) % nitems;
317             break;
318 
319         case KEY_RIGHT:
320             cur = (cur + 1) % nitems;
321             break;
322 
323         case KEY_ESC:
324             mainhelp();
325             break;
326 
327         default:
328             cur0 = cur;
329 
330             do
331             {
332                 cur = (cur + 1) % nitems;
333 
334             } while ((cur != cur0) && (hotkey(mp[cur].name) != toupper(c)));
335 
336             if (hotkey(mp[cur].name) == toupper(c))
337                 key = '\n';
338         }
339 
340     }
341 
342     rmerror();
343     touchwin(wbody);
344     wrefresh(wbody);
345 }
346 
cleanup(void)347 static void cleanup(void)   /* cleanup curses settings */
348 {
349     if (incurses)
350     {
351         delwin(wtitl);
352         delwin(wmain);
353         delwin(wbody);
354         delwin(wstat);
355         curs_set(1);
356         endwin();
357         incurses = FALSE;
358     }
359 }
360 
361 /******************************* EXTERNAL **********************************/
362 
clsbody(void)363 void clsbody(void)
364 {
365     werase(wbody);
366     wmove(wbody, 0, 0);
367 }
368 
bodylen(void)369 int bodylen(void)
370 {
371 #ifdef PDCURSES
372     return getmaxy(wbody);
373 #else
374     int maxy, maxx;
375 
376     getmaxyx(wbody, maxy, maxx);
377     return maxy;
378 #endif
379 }
380 
bodywin(void)381 WINDOW *bodywin(void)
382 {
383     return wbody;
384 }
385 
rmerror(void)386 void rmerror(void)
387 {
388     rmline(wstat, 0);
389 }
390 
rmstatus(void)391 void rmstatus(void)
392 {
393     rmline(wstat, 1);
394 }
395 
titlemsg(char * msg)396 void titlemsg(char *msg)
397 {
398     mvwaddstr(wtitl, 0, 2, padstr(msg, bw - 3));
399     wrefresh(wtitl);
400 }
401 
bodymsg(char * msg)402 void bodymsg(char *msg)
403 {
404     waddstr(wbody, msg);
405     wrefresh(wbody);
406 }
407 
errormsg(char * msg)408 void errormsg(char *msg)
409 {
410     beep();
411     mvwaddstr(wstat, 0, 2, padstr(msg, bw - 3));
412     wrefresh(wstat);
413 }
414 
statusmsg(char * msg)415 void statusmsg(char *msg)
416 {
417     mvwaddstr(wstat, 1, 2, padstr(msg, bw - 3));
418     wrefresh(wstat);
419 }
420 
keypressed(void)421 bool keypressed(void)
422 {
423     ch = wgetch(wbody);
424 
425     return ch != ERR;
426 }
427 
getkey(void)428 int getkey(void)
429 {
430     int c = ch;
431 
432     ch = ERR;
433 #ifdef ALT_X
434     quit = (c == ALT_X);    /* PC only ! */
435 #endif
436     return c;
437 }
438 
waitforkey(void)439 int waitforkey(void)
440 {
441     do idle(); while (!keypressed());
442     return getkey();
443 }
444 
DoExit(void)445 void DoExit(void)   /* terminate program */
446 {
447     quit = TRUE;
448 }
449 
domenu(menu * mp)450 void domenu(menu *mp)
451 {
452     int y, x, nitems, barlen, mheight, mw, old = -1, cur = 0, cur0;
453     bool stop = FALSE;
454     WINDOW *wmenu;
455 
456     curs_set(0);
457     getmenupos(&y, &x);
458     menudim(mp, &nitems, &barlen);
459     mheight = nitems + 2;
460     mw = barlen + 2;
461     wmenu = newwin(mheight, mw, y, x);
462     colorbox(wmenu, SUBMENUCOLOR, 1);
463     repaintmenu(wmenu, mp);
464 
465     key = ERR;
466 
467     while (!stop && !quit)
468     {
469         if (cur != old)
470         {
471             if (old != -1)
472                 mvwaddstr(wmenu, old + 1, 1,
473                           prepad(padstr(mp[old].name, barlen - 1), 1));
474 
475             setcolor(wmenu, SUBMENUREVCOLOR);
476             mvwaddstr(wmenu, cur + 1, 1,
477                       prepad(padstr(mp[cur].name, barlen - 1), 1));
478 
479             setcolor(wmenu, SUBMENUCOLOR);
480             statusmsg(mp[cur].desc);
481 
482             old = cur;
483             wrefresh(wmenu);
484         }
485 
486         switch (key = ((key != ERR) ? key : waitforkey()))
487         {
488         case '\n':          /* menu item selected */
489             touchwin(wbody);
490             wrefresh(wbody);
491             setmenupos(y + 1, x + 1);
492             rmerror();
493 
494             key = ERR;
495             curs_set(1);
496             (mp[cur].func)();   /* perform function */
497             curs_set(0);
498 
499             repaintmenu(wmenu, mp);
500 
501             old = -1;
502             break;
503 
504         case KEY_UP:
505             cur = (cur + nitems - 1) % nitems;
506             key = ERR;
507             break;
508 
509         case KEY_DOWN:
510             cur = (cur + 1) % nitems;
511             key = ERR;
512             break;
513 
514         case KEY_ESC:
515         case KEY_LEFT:
516         case KEY_RIGHT:
517             if (key == KEY_ESC)
518                 key = ERR;  /* return to prev submenu */
519 
520             stop = TRUE;
521             break;
522 
523         default:
524             cur0 = cur;
525 
526             do
527             {
528                 cur = (cur + 1) % nitems;
529 
530             } while ((cur != cur0) &&
531                      (hotkey(mp[cur].name) != toupper((int)key)));
532 
533             key = (hotkey(mp[cur].name) == toupper((int)key)) ? '\n' : ERR;
534         }
535 
536     }
537 
538     rmerror();
539     delwin(wmenu);
540     touchwin(wbody);
541     wrefresh(wbody);
542 }
543 
startmenu(menu * mp,char * mtitle)544 void startmenu(menu *mp, char *mtitle)
545 {
546     initscr();
547     incurses = TRUE;
548     initcolor();
549 
550     wtitl = subwin(stdscr, th, bw, 0, 0);
551     wmain = subwin(stdscr, mh, bw, th, 0);
552     wbody = subwin(stdscr, bh, bw, th + mh, 0);
553     wstat = subwin(stdscr, sh, bw, th + mh + bh, 0);
554 
555     colorbox(wtitl, TITLECOLOR, 0);
556     colorbox(wmain, MAINMENUCOLOR, 0);
557     colorbox(wbody, BODYCOLOR, 0);
558     colorbox(wstat, STATUSCOLOR, 0);
559 
560     if (mtitle)
561         titlemsg(mtitle);
562 
563     cbreak();              /* direct input (no newline required)... */
564     noecho();              /* ... without echoing */
565     curs_set(0);           /* hide cursor (if possible) */
566     nodelay(wbody, TRUE);  /* don't wait for input... */
567     halfdelay(10);         /* ...well, no more than a second, anyway */
568     keypad(wbody, TRUE);   /* enable cursor keys */
569     scrollok(wbody, TRUE); /* enable scrolling in main window */
570 
571     leaveok(stdscr, TRUE);
572     leaveok(wtitl, TRUE);
573     leaveok(wmain, TRUE);
574     leaveok(wstat, TRUE);
575 
576     mainmenu(mp);
577 
578     cleanup();
579 }
580 
repainteditbox(WINDOW * win,int x,char * buf)581 static void repainteditbox(WINDOW *win, int x, char *buf)
582 {
583 #ifndef PDCURSES
584     int maxy;
585 #endif
586     int maxx;
587 
588 #ifdef PDCURSES
589     maxx = getmaxx(win);
590 #else
591     getmaxyx(win, maxy, maxx);
592 #endif
593     werase(win);
594     mvwprintw(win, 0, 0, "%s", padstr(buf, maxx));
595     wmove(win, 0, x);
596     wrefresh(win);
597 }
598 
599 /*
600 
601   weditstr()     - edit string
602 
603   Description:
604     The initial value of 'str' with a maximum length of 'field' - 1,
605     which is supplied by the calling routine, is editted. The user's
606     erase (^H), kill (^U) and delete word (^W) chars are interpreted.
607     The PC insert or Tab keys toggle between insert and edit mode.
608     Escape aborts the edit session, leaving 'str' unchanged.
609     Enter, Up or Down Arrow are used to accept the changes to 'str'.
610     NOTE: editstr(), mveditstr(), and mvweditstr() are macros.
611 
612   Return Value:
613     Returns the input terminating character on success (Escape,
614     Enter, Up or Down Arrow) and ERR on error.
615 
616   Errors:
617     It is an error to call this function with a NULL window pointer.
618     The length of the initial 'str' must not exceed 'field' - 1.
619 
620 */
621 
weditstr(WINDOW * win,char * buf,int field)622 int weditstr(WINDOW *win, char *buf, int field)
623 {
624     char org[MAXSTRLEN], *tp, *bp = buf;
625     bool defdisp = TRUE, stop = FALSE, insert = FALSE;
626     int cury, curx, begy, begx, oldattr;
627     WINDOW *wedit;
628     int c = 0;
629 
630     if ((field >= MAXSTRLEN) || (buf == NULL) ||
631         ((int)strlen(buf) > field - 1))
632         return ERR;
633 
634     strcpy(org, buf);   /* save original */
635 
636     wrefresh(win);
637     getyx(win, cury, curx);
638     getbegyx(win, begy, begx);
639 
640     wedit = subwin(win, 1, field, begy + cury, begx + curx);
641     oldattr = wedit->_attrs;
642     colorbox(wedit, EDITBOXCOLOR, 0);
643 
644     keypad(wedit, TRUE);
645     curs_set(1);
646 
647     while (!stop)
648     {
649         idle();
650         repainteditbox(wedit, bp - buf, buf);
651 
652         switch (c = wgetch(wedit))
653         {
654         case ERR:
655             break;
656 
657         case KEY_ESC:
658             strcpy(buf, org);   /* restore original */
659             stop = TRUE;
660             break;
661 
662         case '\n':
663         case KEY_UP:
664         case KEY_DOWN:
665             stop = TRUE;
666             break;
667 
668         case KEY_LEFT:
669             if (bp > buf)
670                 bp--;
671             break;
672 
673         case KEY_RIGHT:
674             defdisp = FALSE;
675             if (bp - buf < (int)strlen(buf))
676                 bp++;
677             break;
678 
679         case '\t':            /* TAB -- because insert
680                                   is broken on HPUX */
681         case KEY_IC:          /* enter insert mode */
682         case KEY_EIC:         /* exit insert mode */
683             defdisp = FALSE;
684             insert = !insert;
685 
686             curs_set(insert ? 2 : 1);
687             break;
688 
689         default:
690             if (c == erasechar())       /* backspace, ^H */
691             {
692                 if (bp > buf)
693                 {
694                     memmove((void *)(bp - 1), (const void *)bp, strlen(bp) + 1);
695                     bp--;
696                 }
697             }
698             else if (c == killchar())   /* ^U */
699             {
700                 bp = buf;
701                 *bp = '\0';
702             }
703             else if (c == wordchar())   /* ^W */
704             {
705                 tp = bp;
706 
707                 while ((bp > buf) && (*(bp - 1) == ' '))
708                     bp--;
709                 while ((bp > buf) && (*(bp - 1) != ' '))
710                     bp--;
711 
712                 memmove((void *)bp, (const void *)tp, strlen(tp) + 1);
713             }
714             else if (isprint(c))
715             {
716                 if (defdisp)
717                 {
718                     bp = buf;
719                     *bp = '\0';
720                     defdisp = FALSE;
721                 }
722 
723                 if (insert)
724                 {
725                     if ((int)strlen(buf) < field - 1)
726                     {
727                         memmove((void *)(bp + 1), (const void *)bp,
728                                 strlen(bp) + 1);
729 
730                         *bp++ = c;
731                     }
732                 }
733                 else if (bp - buf < field - 1)
734                 {
735                     /* append new string terminator */
736 
737                     if (!*bp)
738                         bp[1] = '\0';
739 
740                     *bp++ = c;
741                 }
742             }
743         }
744     }
745 
746     curs_set(0);
747 
748     wattrset(wedit, oldattr);
749     repainteditbox(wedit, bp - buf, buf);
750     delwin(wedit);
751 
752     return c;
753 }
754 
winputbox(WINDOW * win,int nlines,int ncols)755 WINDOW *winputbox(WINDOW *win, int nlines, int ncols)
756 {
757     WINDOW *winp;
758     int cury, curx, begy, begx;
759 
760     getyx(win, cury, curx);
761     getbegyx(win, begy, begx);
762 
763     winp = newwin(nlines, ncols, begy + cury, begx + curx);
764     colorbox(winp, INPUTBOXCOLOR, 1);
765 
766     return winp;
767 }
768 
getstrings(char * desc[],char * buf[],int field)769 int getstrings(char *desc[], char *buf[], int field)
770 {
771     WINDOW *winput;
772     int oldy, oldx, maxy, maxx, nlines, ncols, i, n, l, mmax = 0;
773     int c = 0;
774     bool stop = FALSE;
775 
776     for (n = 0; desc[n]; n++)
777         if ((l = strlen(desc[n])) > mmax)
778             mmax = l;
779 
780     nlines = n + 2; ncols = mmax + field + 4;
781     getyx(wbody, oldy, oldx);
782     getmaxyx(wbody, maxy, maxx);
783 
784     winput = mvwinputbox(wbody, (maxy - nlines) / 2, (maxx - ncols) / 2,
785         nlines, ncols);
786 
787     for (i = 0; i < n; i++)
788         mvwprintw(winput, i + 1, 2, "%s", desc[i]);
789 
790     i = 0;
791 
792     while (!stop)
793     {
794         switch (c = mvweditstr(winput, i+1, mmax+3, buf[i], field))
795         {
796         case KEY_ESC:
797             stop = TRUE;
798             break;
799 
800         case KEY_UP:
801             i = (i + n - 1) % n;
802             break;
803 
804         case '\n':
805         case '\t':
806         case KEY_DOWN:
807             if (++i == n)
808                 stop = TRUE;    /* all passed? */
809         }
810     }
811 
812     delwin(winput);
813     touchwin(wbody);
814     wmove(wbody, oldy, oldx);
815     wrefresh(wbody);
816 
817     return c;
818 }
819