c1d94086809f43575fb9fa47d3df2458c641e0b6
[ncurses.git] / test / ncurses.c
1 /****************************************************************************
2  * Copyright (c) 1998 Free Software Foundation, Inc.                        *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 /****************************************************************************
29
30 NAME
31    ncurses.c --- ncurses library exerciser
32
33 SYNOPSIS
34    ncurses
35
36 DESCRIPTION
37    An interactive test module for the ncurses library.
38
39 AUTHOR
40    Author: Eric S. Raymond <esr@snark.thyrsus.com> 1993
41
42 $Id: ncurses.c,v 1.108 1998/02/28 01:11:47 tom Exp $
43
44 ***************************************************************************/
45
46 #include <test.priv.h>
47
48 #include <stdio.h>
49 #include <ctype.h>
50 #include <string.h>
51 #include <assert.h>
52 #include <signal.h>
53
54 #if HAVE_LOCALE_H
55 #include <locale.h>
56 #endif
57
58 #if HAVE_GETTIMEOFDAY
59 #if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
60 #include <sys/time.h>
61 #endif
62 #if HAVE_SYS_SELECT_H
63 #include <sys/select.h>
64 #endif
65 #endif
66
67 #if HAVE_PANEL_H
68 #define USE_LIBPANEL 1
69 #include <panel.h>
70 #else
71 #define USE_LIBPANEL 0
72 #endif
73
74 #if HAVE_MENU_H && HAVE_LIBMENU
75 #define USE_LIBMENU 1
76 #include <menu.h>
77 #else
78 #define USE_LIBMENU 0
79 #endif
80
81 #if HAVE_FORM_H && HAVE_LIBFORM
82 #define USE_LIBFORM 1
83 #include <form.h>
84 #else
85 #define USE_LIBFORM 0
86 #endif
87
88 #ifdef NCURSES_VERSION
89
90 #ifdef TRACE
91 static int save_trace = TRACE_ORDINARY|TRACE_CALLS;
92 extern int _nc_tracing;
93 #endif
94
95 #if !HAVE_NAPMS
96 #define HAVE_NAPMS 1
97 #endif
98
99 #else
100
101 #define mmask_t chtype          /* not specified in XSI */
102 #define attr_t chtype           /* not specified in XSI */
103 #define ACS_S3          (acs_map['p'])  /* scan line 3 */
104 #define ACS_S7          (acs_map['r'])  /* scan line 7 */
105 #define ACS_LEQUAL      (acs_map['y'])  /* less/equal */
106 #define ACS_GEQUAL      (acs_map['z'])  /* greater/equal */
107 #define ACS_PI          (acs_map['{'])  /* Pi */
108 #define ACS_NEQUAL      (acs_map['|'])  /* not equal */
109 #define ACS_STERLING    (acs_map['}'])  /* UK pound sign */
110
111 #endif
112
113 #define P(string)       printw("%s\n", string)
114 #ifndef CTRL
115 #define CTRL(x)         ((x) & 0x1f)
116 #endif
117
118 #define QUIT            CTRL('Q')
119 #define ESCAPE          CTRL('[')
120 #define BLANK           ' '             /* this is the background character */
121
122 /* The behavior of mvhline, mvvline for negative/zero length is unspecified,
123  * though we can rely on negative x/y values to stop the macro.
124  */
125 static void do_h_line(int y, int x, chtype c, int to)
126 {
127         if ((to) > (x))
128                 mvhline(y, x, c, (to) - (x));
129 }
130
131 static void do_v_line(int y, int x, chtype c, int to)
132 {
133         if ((to) > (y))
134                 mvvline(y, x, c, (to) - (y));
135 }
136
137 /* Common function to allow ^T to toggle trace-mode in the middle of a test
138  * so that trace-files can be made smaller.
139  */
140 static int wGetchar(WINDOW *win)
141 {
142         int c;
143 #ifdef TRACE
144         while ((c = wgetch(win)) == CTRL('T')) {
145                 if (_nc_tracing) {
146                         save_trace = _nc_tracing;
147                         _tracef("TOGGLE-TRACING OFF");
148                         _nc_tracing = 0;
149                 } else {
150                         _nc_tracing = save_trace;
151                 }
152                 trace(_nc_tracing);
153                 if (_nc_tracing)
154                         _tracef("TOGGLE-TRACING ON");
155         }
156 #else
157         c = wgetch(win);
158 #endif
159         return c;
160 }
161 #define Getchar() wGetchar(stdscr)
162
163 static void Pause(void)
164 {
165         move(LINES - 1, 0);
166         addstr("Press any key to continue... ");
167         (void) Getchar();
168 }
169
170 static void Cannot(const char *what)
171 {
172         printw("\nThis %s terminal %s\n\n", getenv("TERM"), what);
173         Pause();
174 }
175
176 static void ShellOut(bool message)
177 {
178         if (message)
179                 addstr("Shelling out...");
180         def_prog_mode();
181         endwin();
182         system("sh");
183         if (message)
184                 addstr("returned from shellout.\n");
185         refresh();
186 }
187
188 /****************************************************************************
189  *
190  * Character input test
191  *
192  ****************************************************************************/
193
194 static void getch_test(void)
195 /* test the keypad feature */
196 {
197 char buf[BUFSIZ];
198 int c;
199 int incount = 0, firsttime = 0;
200 bool blocking = TRUE;
201 int y, x;
202
203     refresh();
204
205 #ifdef NCURSES_MOUSE_VERSION
206     mousemask(ALL_MOUSE_EVENTS, (mmask_t *)0);
207 #endif
208
209     (void) printw("Delay in 10ths of a second (<CR> for blocking input)? ");
210     echo();
211     getstr(buf);
212     noecho();
213     nonl();
214
215     if (isdigit(buf[0]))
216     {
217         timeout(atoi(buf) * 100);
218         blocking = FALSE;
219     }
220
221     c = '?';
222     raw();
223     for (;;)
224     {
225         if (firsttime++)
226         {
227             printw("Key pressed: %04o ", c);
228 #ifdef NCURSES_MOUSE_VERSION
229             if (c == KEY_MOUSE)
230             {
231                 MEVENT  event;
232
233                 getmouse(&event);
234                 printw("KEY_MOUSE, %s\n", _tracemouse(&event));
235             }
236             else
237 #endif  /* NCURSES_MOUSE_VERSION */
238              if (c >= KEY_MIN)
239             {
240                 (void) addstr(keyname(c));
241                 addch('\n');
242             }
243             else if (c > 0x80)
244             {
245                 int c2 = (c & 0x7f);
246                 if (isprint(c2))
247                     (void) printw("M-%c", c2);
248                 else
249                     (void) printw("M-%s", unctrl(c2));
250                 addstr(" (high-half character)\n");
251             }
252             else
253             {
254                 if (isprint(c))
255                     (void) printw("%c (ASCII printable character)\n", c);
256                 else
257                     (void) printw("%s (ASCII control character)\n", unctrl(c));
258             }
259             getyx(stdscr, y, x);
260             if (y >= LINES-1)
261                 move(0,0);
262             clrtoeol();
263         }
264
265         if (c == 'g')
266         {
267             addstr("getstr test: ");
268             echo(); getstr(buf); noecho();
269             printw("I saw `%s'.\n", buf);
270         }
271         if (c == 's')
272         {
273             ShellOut(TRUE);
274         }
275         if (c == 'x' || c == 'q' || (c == ERR && blocking))
276             break;
277         if (c == '?')
278         {
279             addstr("Type any key to see its keypad value.  Also:\n");
280             addstr("g -- triggers a getstr test\n");
281             addstr("s -- shell out\n");
282             addstr("q -- quit\n");
283             addstr("? -- repeats this help message\n");
284         }
285
286         while ((c = Getchar()) == ERR)
287             if (!blocking)
288                 (void) printw("%05d: input timed out\n", incount++);
289             else {
290                 (void) printw("%05d: input error\n", incount++);
291                 break;
292             }
293     }
294
295 #ifdef NCURSES_MOUSE_VERSION
296     mousemask(0, (mmask_t *)0);
297 #endif
298     timeout(-1);
299     erase();
300     noraw();
301     nl();
302     endwin();
303 }
304
305 static int show_attr(int row, int skip, chtype attr, const char *name, bool once)
306 {
307     mvprintw(row, 8, "%s mode:", name);
308     mvprintw(row, 24, "|");
309     if (skip) printw("%*s", skip, " ");
310     if (once)
311         attron(attr);
312     else
313         attrset(attr);
314     addstr("abcde fghij klmno pqrst uvwxy z");
315     if (once)
316         attroff(attr);
317     if (skip) printw("%*s", skip, " ");
318     printw("|");
319     if (attr != A_NORMAL && !(termattrs() & attr))
320         printw(" (N/A)");
321     return row + 2;
322 }
323
324 static bool attr_getc(int *skip, int *fg, int *bg)
325 {
326     int ch = Getchar();
327
328     if (isdigit(ch)) {
329         *skip = (ch - '0');
330         return TRUE;
331     } else if (ch == CTRL('L')) {
332         touchwin(stdscr);
333         touchwin(curscr);
334         return TRUE;
335     } else if (has_colors()) {
336         switch (ch) {
337         case 'f': *fg = (*fg + 1); break;
338         case 'F': *fg = (*fg - 1); break;
339         case 'b': *bg = (*bg + 1); break;
340         case 'B': *bg = (*bg - 1); break;
341         default:
342             return FALSE;
343         }
344         if (*fg >= COLORS) *fg = 0;
345         if (*fg <  0)      *fg = COLORS - 1;
346         if (*bg >= COLORS) *bg = 0;
347         if (*bg <  0)      *bg = COLORS - 1;
348         return TRUE;
349     }
350     return FALSE;
351 }
352
353 static void attr_test(void)
354 /* test text attributes */
355 {
356     int n;
357     int skip = tigetnum("xmc");
358     int fg = COLOR_WHITE;
359     int bg = COLOR_BLACK;
360     bool *pairs = (bool *)calloc(COLOR_PAIRS, sizeof(bool));
361     pairs[0] = TRUE;
362
363     if (skip < 0)
364         skip = 0;
365
366     n = skip;   /* make it easy */
367
368     do {
369         int row = 2;
370         int normal = A_NORMAL | BLANK;
371
372         if (has_colors()) {
373             int pair = (fg * COLORS) + bg;
374             if (!pairs[pair]) {
375                 init_pair(pair, fg, bg);
376                 pairs[pair] = TRUE;
377             }
378             normal |= COLOR_PAIR(pair);
379         }
380         bkgdset(normal);
381         erase();
382
383         mvaddstr(0, 20, "Character attribute test display");
384
385         row = show_attr(row, n, A_STANDOUT,  "STANDOUT",  TRUE);
386         row = show_attr(row, n, A_REVERSE,   "REVERSE",   TRUE);
387         row = show_attr(row, n, A_BOLD,      "BOLD",      TRUE);
388         row = show_attr(row, n, A_UNDERLINE, "UNDERLINE", TRUE);
389         row = show_attr(row, n, A_DIM,       "DIM",       TRUE);
390         row = show_attr(row, n, A_BLINK,     "BLINK",     TRUE);
391         row = show_attr(row, n, A_PROTECT,   "PROTECT",   TRUE);
392         row = show_attr(row, n, A_INVIS,     "INVISIBLE", TRUE);
393         row = show_attr(row, n, A_NORMAL,    "NORMAL",    FALSE);
394
395         mvprintw(row, 8,
396              "This terminal does %shave the magic-cookie glitch",
397              tigetnum("xmc") > -1 ? "" : "not ");
398         mvprintw(row+1, 8,
399              "Enter a digit to set gaps on each side of displayed attributes");
400         mvprintw(row+2, 8,
401              "^L = repaint");
402         if (has_colors())
403             printw(".  f/F/b/F toggle colors (now %d/%d)", fg, bg);
404
405         refresh();
406     } while (attr_getc(&n, &fg, &bg));
407
408     bkgdset(A_NORMAL | BLANK);
409     erase();
410     endwin();
411 }
412
413 /****************************************************************************
414  *
415  * Color support tests
416  *
417  ****************************************************************************/
418
419 static NCURSES_CONST char *color_names[] =
420 {
421     "black",
422     "red",
423     "green",
424     "yellow",
425     "blue",
426     "magenta",
427     "cyan",
428     "white"
429 };
430
431 static void show_color_name(int y, int x, int color)
432 {
433     if (COLORS > 8)
434         mvprintw(y, x, "%02d   ", color);
435     else
436         mvaddstr(y, x, color_names[color]);
437 }
438
439 static void color_test(void)
440 /* generate a color test pattern */
441 {
442     int i;
443     int base, top, width;
444     NCURSES_CONST char *hello;
445
446     refresh();
447     (void) printw("There are %d color pairs\n", COLOR_PAIRS);
448
449     width = (COLORS > 8) ? 4 : 8;
450     hello = (COLORS > 8) ? "Test" : "Hello";
451
452     for (base = 0; base < 2; base++)
453     {
454         top = (COLORS > 8) ? 0 : base * (COLORS+3);
455         clrtobot();
456         (void) mvprintw(top + 1, 0,
457                 "%dx%d matrix of foreground/background colors, bright *%s*\n",
458                 COLORS, COLORS,
459                 base ? "on" : "off");
460         for (i = 0; i < COLORS; i++)
461             show_color_name(top + 2, (i+1) * width, i);
462         for (i = 0; i < COLORS; i++)
463             show_color_name(top + 3 + i, 0, i);
464         for (i = 1; i < COLOR_PAIRS; i++)
465         {
466             init_pair(i, i % COLORS, i / COLORS);
467             attron((attr_t)COLOR_PAIR(i));
468             if (base)
469                 attron((attr_t)A_BOLD);
470             mvaddstr(top + 3 + (i / COLORS), (i % COLORS + 1) * width, hello);
471             attrset(A_NORMAL);
472         }
473         if ((COLORS > 8) || base)
474             Pause();
475     }
476
477     erase();
478     endwin();
479 }
480
481 static void change_color(int current, int field, int value, int usebase)
482 {
483         short   red, green, blue;
484
485         if (usebase)
486                 color_content(current, &red, &green, &blue);
487         else
488                 red = green = blue = 0;
489
490         switch (field) {
491         case 0:
492                 red += value;
493                 break;
494         case 1:
495                 green += value;
496                 break;
497         case 2:
498                 blue += value;
499                 break;
500         }
501
502         if (init_color(current, red, green, blue) == ERR)
503                 beep();
504 }
505
506 static void color_edit(void)
507 /* display the color test pattern, without trying to edit colors */
508 {
509     int i, this_c = 0, value = 0, current = 0, field = 0;
510     int last_c;
511
512     refresh();
513
514     for (i = 0; i < COLORS; i++)
515         init_pair(i, COLOR_WHITE, i);
516
517     mvprintw(LINES-2, 0, "Number: %d", value);
518
519     do {
520         short   red, green, blue;
521
522         attron(A_BOLD);
523         mvaddstr(0, 20, "Color RGB Value Editing");
524         attroff(A_BOLD);
525
526         for (i = 0; i < COLORS; i++)
527         {
528             mvprintw(2 + i, 0, "%c %-8s:",
529                      (i == current ? '>' : ' '),
530                      (i < (int) SIZEOF(color_names)
531                         ? color_names[i] : ""));
532             attrset(COLOR_PAIR(i));
533             addstr("        ");
534             attrset(A_NORMAL);
535
536             /*
537              * Note: this refresh should *not* be necessary!  It works around
538              * a bug in attribute handling that apparently causes the A_NORMAL
539              * attribute sets to interfere with the actual emission of the
540              * color setting somehow.  This needs to be fixed.
541              */
542             refresh();
543
544             color_content(i, &red, &green, &blue);
545             addstr("   R = ");
546             if (current == i && field == 0) attron(A_STANDOUT);
547             printw("%04d", red);
548             if (current == i && field == 0) attrset(A_NORMAL);
549             addstr(", G = ");
550             if (current == i && field == 1) attron(A_STANDOUT);
551             printw("%04d", green);
552             if (current == i && field == 1) attrset(A_NORMAL);
553             addstr(", B = ");
554             if (current == i && field == 2) attron(A_STANDOUT);
555             printw("%04d", blue);
556             if (current == i && field == 2) attrset(A_NORMAL);
557             attrset(A_NORMAL);
558             addstr(")");
559         }
560
561         mvaddstr(COLORS + 3, 0,
562             "Use up/down to select a color, left/right to change fields.");
563         mvaddstr(COLORS + 4, 0,
564             "Modify field by typing nnn=, nnn-, or nnn+.  ? for help.");
565
566         move(2 + current, 0);
567
568         last_c = this_c;
569         this_c = Getchar();
570         if (isdigit(this_c) && !isdigit(last_c))
571                 value = 0;
572
573         switch (this_c)
574         {
575         case KEY_UP:
576             current = (current == 0 ? (COLORS - 1) : current - 1);
577             break;
578
579         case KEY_DOWN:
580             current = (current == (COLORS - 1) ? 0 : current + 1);
581             break;
582
583         case KEY_RIGHT:
584             field = (field == 2 ? 0 : field + 1);
585             break;
586
587         case KEY_LEFT:
588             field = (field == 0 ? 2 : field - 1);
589             break;
590
591         case '0': case '1': case '2': case '3': case '4':
592         case '5': case '6': case '7': case '8': case '9':
593             value = value * 10 + (this_c - '0');
594             break;
595
596         case '+':
597             change_color(current, field, value, 1);
598             break;
599
600         case '-':
601             change_color(current, field, -value, 1);
602             break;
603
604         case '=':
605             change_color(current, field, value, 0);
606             break;
607
608         case '?':
609             erase();
610     P("                      RGB Value Editing Help");
611     P("");
612     P("You are in the RGB value editor.  Use the arrow keys to select one of");
613     P("the fields in one of the RGB triples of the current colors; the one");
614     P("currently selected will be reverse-video highlighted.");
615     P("");
616     P("To change a field, enter the digits of the new value; they are echoed");
617     P("as entered.  Finish by typing `='.  The change will take effect instantly.");
618     P("To increment or decrement a value, use the same procedure, but finish");
619     P("with a `+' or `-'.");
620     P("");
621     P("To quit, do `x' or 'q'");
622
623             Pause();
624             erase();
625             break;
626
627         case 'x':
628         case 'q':
629             break;
630
631         default:
632             beep();
633             break;
634         }
635         mvprintw(LINES-2, 0, "Number: %d", value);
636         clrtoeol();
637     } while
638         (this_c != 'x' && this_c != 'q');
639
640     erase();
641     endwin();
642 }
643
644 /****************************************************************************
645  *
646  * Soft-key label test
647  *
648  ****************************************************************************/
649
650 static void slk_test(void)
651 /* exercise the soft keys */
652 {
653     int c, fmt = 1;
654     char buf[9];
655
656     c = CTRL('l');
657     do {
658         move(0, 0);
659         switch(c)
660         {
661         case CTRL('l'):
662             erase();
663             attron(A_BOLD);
664             mvaddstr(0, 20, "Soft Key Exerciser");
665             attroff(A_BOLD);
666
667             move(2, 0);
668             P("Available commands are:");
669             P("");
670             P("^L         -- refresh screen");
671             P("a          -- activate or restore soft keys");
672             P("d          -- disable soft keys");
673             P("c          -- set centered format for labels");
674             P("l          -- set left-justified format for labels");
675             P("r          -- set right-justified format for labels");
676             P("[12345678] -- set label; labels are numbered 1 through 8");
677             P("e          -- erase stdscr (should not erase labels)");
678             P("s          -- test scrolling of shortened screen");
679             P("x, q       -- return to main menu");
680             P("");
681             P("Note: if activating the soft keys causes your terminal to");
682             P("scroll up one line, your terminal auto-scrolls when anything");
683             P("is written to the last screen position.  The ncurses code");
684             P("does not yet handle this gracefully.");
685             refresh();
686             /* fall through */
687
688         case 'a':
689             slk_restore();
690             break;
691
692         case 'e':
693             wclear(stdscr);
694             break;
695
696         case 's':
697             mvprintw(20, 0, "Press Q to stop the scrolling-test: ");
698             while ((c = Getchar()) != 'Q' && (c != ERR))
699                 addch((chtype)c);
700             break;
701
702         case 'd':
703             slk_clear();
704             break;
705
706         case 'l':
707             fmt = 0;
708             break;
709
710         case 'c':
711             fmt = 1;
712             break;
713
714         case 'r':
715             fmt = 2;
716             break;
717
718         case '1': case '2': case '3': case '4':
719         case '5': case '6': case '7': case '8':
720             (void) mvaddstr(20, 0, "Please enter the label value: ");
721             echo();
722             wgetnstr(stdscr, buf, 8);
723             noecho();
724             slk_set((c - '0'), buf, fmt);
725             slk_refresh();
726             move(20, 0); clrtoeol();
727             break;
728
729         case 'x':
730         case 'q':
731             goto done;
732
733         default:
734             beep();
735         }
736     } while
737         ((c = Getchar()) != EOF);
738
739  done:
740     erase();
741     endwin();
742 }
743
744 /****************************************************************************
745  *
746  * Alternate character-set stuff
747  *
748  ****************************************************************************/
749
750 /* ISO 6429:  codes 0x80 to 0x9f may be control characters that cause the
751  * terminal to perform functions.  The remaining codes can be graphic.
752  */
753 static void show_upper_chars(int first)
754 {
755         bool C1 = (first == 128);
756         int code;
757         int last = first + 31;
758         int reply;
759
760         erase();
761         attron(A_BOLD);
762         mvprintw(0, 20, "Display of %s Character Codes %d to %d",
763                 C1 ? "C1" : "GR", first, last);
764         attroff(A_BOLD);
765         refresh();
766
767         for (code = first; code <= last; code++) {
768                 int row = 4 + ((code - first) % 16);
769                 int col = ((code - first) / 16) * COLS / 2;
770                 char tmp[80];
771                 sprintf(tmp, "%3d (0x%x)", code, code);
772                 mvprintw(row, col, "%*s: ", COLS/4, tmp);
773                 if (C1)
774                         nodelay(stdscr, TRUE);
775                 echochar(code);
776                 if (C1) {
777                         /* (yes, this _is_ crude) */
778                         while ((reply = Getchar()) != ERR) {
779                                 addch(reply);
780                                 napms(10);
781                         }
782                         nodelay(stdscr, FALSE);
783                 }
784         }
785 }
786
787 static int show_1_acs(int n, const char *name, chtype code)
788 {
789         const int height = 16;
790         int row = 4 + (n % height);
791         int col = (n / height) * COLS / 2;
792         mvprintw(row, col, "%*s : ", COLS/4, name);
793         addch(code);
794         return n + 1;
795 }
796
797 static void show_acs_chars(void)
798 /* display the ACS character set */
799 {
800         int n;
801
802 #define BOTH(name) #name, name
803
804         erase();
805         attron(A_BOLD);
806         mvaddstr(0, 20, "Display of the ACS Character Set");
807         attroff(A_BOLD);
808         refresh();
809
810         n = show_1_acs(0, BOTH(ACS_ULCORNER));
811         n = show_1_acs(n, BOTH(ACS_LLCORNER));
812         n = show_1_acs(n, BOTH(ACS_URCORNER));
813         n = show_1_acs(n, BOTH(ACS_LRCORNER));
814         n = show_1_acs(n, BOTH(ACS_RTEE));
815         n = show_1_acs(n, BOTH(ACS_LTEE));
816         n = show_1_acs(n, BOTH(ACS_BTEE));
817         n = show_1_acs(n, BOTH(ACS_TTEE));
818         n = show_1_acs(n, BOTH(ACS_HLINE));
819         n = show_1_acs(n, BOTH(ACS_VLINE));
820         n = show_1_acs(n, BOTH(ACS_PLUS));
821         n = show_1_acs(n, BOTH(ACS_S1));
822         n = show_1_acs(n, BOTH(ACS_S9));
823         n = show_1_acs(n, BOTH(ACS_DIAMOND));
824         n = show_1_acs(n, BOTH(ACS_CKBOARD));
825         n = show_1_acs(n, BOTH(ACS_DEGREE));
826         n = show_1_acs(n, BOTH(ACS_PLMINUS));
827         n = show_1_acs(n, BOTH(ACS_BULLET));
828         n = show_1_acs(n, BOTH(ACS_LARROW));
829         n = show_1_acs(n, BOTH(ACS_RARROW));
830         n = show_1_acs(n, BOTH(ACS_DARROW));
831         n = show_1_acs(n, BOTH(ACS_UARROW));
832         n = show_1_acs(n, BOTH(ACS_BOARD));
833         n = show_1_acs(n, BOTH(ACS_LANTERN));
834         n = show_1_acs(n, BOTH(ACS_BLOCK));
835         n = show_1_acs(n, BOTH(ACS_S3));
836         n = show_1_acs(n, BOTH(ACS_S7));
837         n = show_1_acs(n, BOTH(ACS_LEQUAL));
838         n = show_1_acs(n, BOTH(ACS_GEQUAL));
839         n = show_1_acs(n, BOTH(ACS_PI));
840         n = show_1_acs(n, BOTH(ACS_NEQUAL));
841         n = show_1_acs(n, BOTH(ACS_STERLING));
842 }
843
844 static void acs_display(void)
845 {
846         int     c = 'a';
847
848         do {
849                 switch (c) {
850                 case 'a':
851                         show_acs_chars();
852                         break;
853                 case '0':
854                 case '1':
855                 case '2':
856                 case '3':
857                         show_upper_chars((c - '0') * 32 + 128);
858                         break;
859                 }
860                 mvprintw(LINES-3,0, "Note: ANSI terminals may not display C1 characters.");
861                 mvprintw(LINES-2,0, "Select: a=ACS, 0=C1, 1,2,3=GR characters, q=quit");
862                 refresh();
863         } while ((c = Getchar()) != 'x' && c != 'q');
864
865         Pause();
866         erase();
867         endwin();
868 }
869
870 /*
871  * Graphic-rendition test (adapted from vttest)
872  */
873 static void
874 test_sgr_attributes(void)
875 {
876     int pass;
877
878     for (pass = 0; pass < 2; pass++) {
879         int normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK;
880
881         /* Use non-default colors if possible to exercise bce a little */
882         if (has_colors()) {
883             init_pair(1, COLOR_WHITE, COLOR_BLUE);
884             normal |= COLOR_PAIR(1);
885         }
886         bkgdset(normal);
887         erase();
888         mvprintw( 1,20, "Graphic rendition test pattern:");
889
890         mvprintw( 4, 1, "vanilla");
891
892 #define set_sgr(mask) bkgdset((normal^(mask)));
893         set_sgr(A_BOLD);
894         mvprintw( 4,40, "bold");
895
896         set_sgr(A_UNDERLINE);
897         mvprintw( 6, 6, "underline");
898
899         set_sgr(A_BOLD|A_UNDERLINE);
900         mvprintw( 6,45, "bold underline");
901
902         set_sgr(A_BLINK);
903         mvprintw( 8, 1, "blink");
904
905         set_sgr(A_BLINK|A_BOLD);
906         mvprintw( 8,40, "bold blink");
907
908         set_sgr(A_UNDERLINE|A_BLINK);
909         mvprintw(10, 6, "underline blink");
910
911         set_sgr(A_BOLD|A_UNDERLINE|A_BLINK);
912         mvprintw(10,45, "bold underline blink");
913
914         set_sgr(A_REVERSE);
915         mvprintw(12, 1, "negative");
916
917         set_sgr(A_BOLD|A_REVERSE);
918         mvprintw(12,40, "bold negative");
919
920         set_sgr(A_UNDERLINE|A_REVERSE);
921         mvprintw(14, 6, "underline negative");
922
923         set_sgr(A_BOLD|A_UNDERLINE|A_REVERSE);
924         mvprintw(14,45, "bold underline negative");
925
926         set_sgr(A_BLINK|A_REVERSE);
927         mvprintw(16, 1, "blink negative");
928
929         set_sgr(A_BOLD|A_BLINK|A_REVERSE);
930         mvprintw(16,40, "bold blink negative");
931
932         set_sgr(A_UNDERLINE|A_BLINK|A_REVERSE);
933         mvprintw(18, 6, "underline blink negative");
934
935         set_sgr(A_BOLD|A_UNDERLINE|A_BLINK|A_REVERSE);
936         mvprintw(18,45, "bold underline blink negative");
937
938         bkgdset(normal);
939         mvprintw(LINES-2,1, "%s background. ", pass == 0 ? "Dark" : "Light");
940         clrtoeol();
941         Pause();
942     }
943
944     bkgdset(A_NORMAL | BLANK);
945     erase();
946     endwin();
947 }
948
949 /****************************************************************************
950  *
951  * Windows and scrolling tester.
952  *
953  ****************************************************************************/
954
955 #define BOTLINES        4       /* number of line stolen from screen bottom */
956
957 typedef struct
958 {
959     int y, x;
960 }
961 pair;
962
963 #define FRAME struct frame
964 FRAME
965 {
966         FRAME           *next, *last;
967         bool            do_scroll;
968         bool            do_keypad;
969         WINDOW          *wind;
970 };
971
972 /* We need to know if these flags are actually set, so don't look in FRAME.
973  * These names are known to work with SVr4 curses as well as ncurses.
974  */
975 static bool HaveKeypad(FRAME *curp)
976 {
977         WINDOW *win = (curp ? curp->wind : stdscr);
978         return win->_use_keypad;
979 }
980
981 static bool HaveScroll(FRAME *curp)
982 {
983         WINDOW *win = (curp ? curp->wind : stdscr);
984         return win->_scroll;
985 }
986
987 static void newwin_legend(FRAME *curp)
988 {
989         static const struct {
990                 const char *msg;
991                 int code;
992         } legend[] = {
993                 { "^C = create window",         0 },
994                 { "^N = next window",           0 },
995                 { "^P = previous window",       0 },
996                 { "^F = scroll forward",        0 },
997                 { "^B = scroll backward",       0 },
998                 { "^K = keypad(%s)",            1 },
999                 { "^S = scrollok(%s)",          2 },
1000                 { "^W = save window to file",   0 },
1001                 { "^R = restore window",        0 },
1002 #ifdef NCURSES_VERSION
1003                 { "^X = resize",                0 },
1004 #endif
1005                 { "^Q%s = exit",                3 }
1006         };
1007         size_t n;
1008         int y, x;
1009         bool do_keypad = HaveKeypad(curp);
1010         bool do_scroll = HaveScroll(curp);
1011         char buf[BUFSIZ];
1012
1013         move(LINES-4, 0);
1014         for (n = 0; n < SIZEOF(legend); n++) {
1015                 switch (legend[n].code) {
1016                 default:
1017                         strcpy(buf, legend[n].msg);
1018                         break;
1019                 case 1:
1020                         sprintf(buf, legend[n].msg, do_keypad ? "yes" : "no");
1021                         break;
1022                 case 2:
1023                         sprintf(buf, legend[n].msg, do_scroll ? "yes" : "no");
1024                         break;
1025                 case 3:
1026                         sprintf(buf, legend[n].msg, do_keypad ? "/ESC" : "");
1027                         break;
1028                 }
1029                 getyx(stdscr, y, x);
1030                 addstr((COLS < (x + 3 + (int)strlen(buf))) ? "\n" : (n ? ", " : ""));
1031                 addstr(buf);
1032         }
1033         clrtoeol();
1034 }
1035
1036 static void transient(FRAME *curp, NCURSES_CONST char *msg)
1037 {
1038     newwin_legend(curp);
1039     if (msg)
1040     {
1041         mvaddstr(LINES - 1, 0, msg);
1042         refresh();
1043         napms(1000);
1044     }
1045
1046     move(LINES-1, 0);
1047     printw("%s characters are echoed, window should %sscroll.",
1048         HaveKeypad(curp) ? "Non-arrow" : "All other",
1049         HaveScroll(curp) ? "" : "not " );
1050     clrtoeol();
1051 }
1052
1053 static void newwin_report(FRAME *curp)
1054 /* report on the cursor's current position, then restore it */
1055 {
1056         WINDOW *win = (curp != 0) ? curp->wind : stdscr;
1057         int y, x;
1058
1059         if (win != stdscr)
1060                 transient(curp, (char *)0);
1061         getyx(win, y, x);
1062         move(LINES - 1, COLS - 17);
1063         printw("Y = %2d X = %2d", y, x);
1064         if (win != stdscr)
1065                 refresh();
1066         else
1067                 wmove(win, y, x);
1068 }
1069
1070 static pair *selectcell(int uli, int ulj, int lri, int lrj)
1071 /* arrows keys move cursor, return location at current on non-arrow key */
1072 {
1073     static pair res;                    /* result cell */
1074     int         si = lri - uli + 1;     /* depth of the select area */
1075     int         sj = lrj - ulj + 1;     /* width of the select area */
1076     int         i = 0, j = 0;           /* offsets into the select area */
1077
1078     res.y = uli;
1079     res.x = ulj;
1080     for (;;)
1081     {
1082         move(uli + i, ulj + j);
1083         newwin_report((FRAME *)0);
1084
1085         switch(Getchar())
1086         {
1087         case KEY_UP:    i += si - 1; break;
1088         case KEY_DOWN:  i++; break;
1089         case KEY_LEFT:  j += sj - 1; break;
1090         case KEY_RIGHT: j++; break;
1091         case QUIT:
1092         case ESCAPE:    return((pair *)0);
1093 #ifdef NCURSES_MOUSE_VERSION
1094         case KEY_MOUSE:
1095             {
1096                 MEVENT  event;
1097
1098                 getmouse(&event);
1099                 if (event.y > uli && event.x > ulj) {
1100                         i = event.y - uli;
1101                         j = event.x - ulj;
1102                 } else {
1103                         beep();
1104                         break;
1105                 }
1106             }
1107             /* FALLTHRU */
1108 #endif
1109         default:        res.y = uli + i; res.x = ulj + j; return(&res);
1110         }
1111         i %= si;
1112         j %= sj;
1113     }
1114 }
1115
1116 static void outerbox(pair ul, pair lr, bool onoff)
1117 /* draw or erase a box *outside* the given pair of corners */
1118 {
1119     mvaddch(ul.y-1, lr.x-1, onoff ? ACS_ULCORNER : ' ');
1120     mvaddch(ul.y-1, lr.x+1, onoff ? ACS_URCORNER : ' ');
1121     mvaddch(lr.y+1, lr.x+1, onoff ? ACS_LRCORNER : ' ');
1122     mvaddch(lr.y+1, ul.x-1, onoff ? ACS_LLCORNER : ' ');
1123     move(ul.y-1, ul.x);   hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
1124     move(ul.y,   ul.x-1); vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
1125     move(lr.y+1, ul.x);   hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
1126     move(ul.y,   lr.x+1); vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
1127 }
1128
1129 static WINDOW *getwindow(void)
1130 /* Ask user for a window definition */
1131 {
1132     WINDOW      *rwindow;
1133     pair        ul, lr, *tmp;
1134
1135     move(0, 0); clrtoeol();
1136     addstr("Use arrows to move cursor, anything else to mark corner 1");
1137     refresh();
1138     if ((tmp = selectcell(2, 1, LINES-BOTLINES-2, COLS-2)) == (pair *)0)
1139         return((WINDOW *)0);
1140     memcpy(&ul, tmp, sizeof(pair));
1141     mvaddch(ul.y-1, ul.x-1, ACS_ULCORNER);
1142     move(0, 0); clrtoeol();
1143     addstr("Use arrows to move cursor, anything else to mark corner 2");
1144     refresh();
1145     if ((tmp = selectcell(ul.y, ul.x, LINES-BOTLINES-2, COLS-2)) == (pair *)0)
1146         return((WINDOW *)0);
1147     memcpy(&lr, tmp, sizeof(pair));
1148
1149     rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
1150
1151     outerbox(ul, lr, TRUE);
1152     refresh();
1153
1154     wrefresh(rwindow);
1155
1156     move(0, 0); clrtoeol();
1157     return(rwindow);
1158 }
1159
1160 static void newwin_move(FRAME *curp, int dy, int dx)
1161 {
1162         WINDOW *win = (curp != 0) ? curp->wind : stdscr;
1163         int cur_y, cur_x;
1164         int max_y, max_x;
1165
1166         getyx(win, cur_y, cur_x);
1167         getmaxyx(win, max_y, max_x);
1168         if ((cur_x += dx) < 0)
1169                 cur_x = 0;
1170         else if (cur_x >= max_x)
1171                 cur_x = max_x - 1;
1172         if ((cur_y += dy) < 0)
1173                 cur_y = 0;
1174         else if (cur_y >= max_y)
1175                 cur_y = max_y - 1;
1176         wmove(win, cur_y, cur_x);
1177 }
1178
1179 static FRAME *delete_framed(FRAME *fp, bool showit)
1180 {
1181         FRAME *np;
1182
1183         fp->last->next = fp->next;
1184         fp->next->last = fp->last;
1185
1186         if (showit) {
1187                 werase(fp->wind);
1188                 wrefresh(fp->wind);
1189         }
1190         delwin(fp->wind);
1191
1192         np = (fp == fp->next) ? 0 : fp->next;
1193         free(fp);
1194         return np;
1195 }
1196
1197 static void acs_and_scroll(void)
1198 /* Demonstrate windows */
1199 {
1200     int c, i;
1201     FILE *fp;
1202     FRAME *current = (FRAME *)0, *neww;
1203     WINDOW *usescr = stdscr;
1204
1205 #define DUMPFILE        "screendump"
1206
1207 #ifdef NCURSES_MOUSE_VERSION
1208     mousemask(BUTTON1_CLICKED, (mmask_t *)0);
1209 #endif
1210     c = CTRL('C');
1211     raw();
1212     do {
1213         transient((FRAME *)0, (char *)0);
1214         switch(c)
1215         {
1216         case CTRL('C'):
1217             neww = (FRAME *) calloc(1, sizeof(FRAME));
1218             if ((neww->wind = getwindow()) == (WINDOW *)0)
1219                 goto breakout;
1220
1221             if (current == 0)   /* First element,  */
1222             {
1223                 neww->next = neww; /*   so point it at itself */
1224                 neww->last = neww;
1225             }
1226             else
1227             {
1228                 neww->next = current->next;
1229                 neww->last = current;
1230                 neww->last->next = neww;
1231                 neww->next->last = neww;
1232             }
1233             current = neww;
1234             /* SVr4 curses sets the keypad on all newly-created windows to
1235              * false.  Someone reported that PDCurses makes new windows inherit
1236              * this flag.  Remove the following 'keypad()' call to test this
1237              */
1238             keypad(current->wind, TRUE);
1239             current->do_keypad = HaveKeypad(current);
1240             current->do_scroll = HaveScroll(current);
1241             break;
1242
1243         case CTRL('N'):         /* go to next window */
1244             if (current)
1245                 current = current->next;
1246             break;
1247
1248         case CTRL('P'):         /* go to previous window */
1249             if (current)
1250                 current = current->last;
1251             break;
1252
1253         case CTRL('F'):         /* scroll current window forward */
1254             if (current)
1255                 wscrl(current->wind, 1);
1256             break;
1257
1258         case CTRL('B'):         /* scroll current window backwards */
1259             if (current)
1260                 wscrl(current->wind, -1);
1261             break;
1262
1263         case CTRL('K'):         /* toggle keypad mode for current */
1264             if (current) {
1265                 current->do_keypad = !current->do_keypad;
1266                 keypad(current->wind, current->do_keypad);
1267             }
1268             break;
1269
1270         case CTRL('S'):
1271             if (current) {
1272                 current->do_scroll = !current->do_scroll;
1273                 scrollok(current->wind, current->do_scroll);
1274             }
1275             break;
1276
1277         case CTRL('W'):         /* save and delete window */
1278             if (current == current->next)
1279                 break;
1280             if ((fp = fopen(DUMPFILE, "w")) == (FILE *)0)
1281                 transient(current, "Can't open screen dump file");
1282             else
1283             {
1284                 (void) putwin(current->wind, fp);
1285                 (void) fclose(fp);
1286
1287                 current = delete_framed(current, TRUE);
1288             }
1289             break;
1290
1291         case CTRL('R'):         /* restore window */
1292             if ((fp = fopen(DUMPFILE, "r")) == (FILE *)0)
1293                 transient(current, "Can't open screen dump file");
1294             else
1295             {
1296                 neww = (FRAME *) calloc(1, sizeof(FRAME));
1297
1298                 neww->next = current->next;
1299                 neww->last = current;
1300                 neww->last->next = neww;
1301                 neww->next->last = neww;
1302
1303                 neww->wind = getwin(fp);
1304                 (void) fclose(fp);
1305
1306                 wrefresh(neww->wind);
1307             }
1308             break;
1309
1310 #ifdef NCURSES_VERSION
1311         case CTRL('X'):         /* resize window */
1312             if (current)
1313             {
1314                 pair *tmp, ul, lr;
1315                 int mx, my;
1316
1317                 move(0, 0); clrtoeol();
1318                 addstr("Use arrows to move cursor, anything else to mark new corner");
1319                 refresh();
1320
1321                 getbegyx(current->wind, ul.y, ul.x);
1322
1323                 tmp = selectcell(ul.y, ul.x, LINES-BOTLINES-2, COLS-2);
1324                 if (tmp == (pair *)0)
1325                 {
1326                     beep();
1327                     break;
1328                 }
1329
1330                 getmaxyx(current->wind, lr.y, lr.x);
1331                 lr.y += (ul.y - 1);
1332                 lr.x += (ul.x - 1);
1333                 outerbox(ul, lr, FALSE);
1334                 wnoutrefresh(stdscr);
1335
1336                 /* strictly cosmetic hack for the test */
1337                 getmaxyx(current->wind, my, mx);
1338                 if (my > tmp->y - ul.y)
1339                 {
1340                   getyx(current->wind, lr.y, lr.x);
1341                   wmove(current->wind, tmp->y - ul.y + 1, 0);
1342                   wclrtobot(current->wind);
1343                   wmove(current->wind, lr.y, lr.x);
1344                 }
1345                 if (mx > tmp->x - ul.x)
1346                   for (i = 0; i < my; i++)
1347                   {
1348                     wmove(current->wind, i, tmp->x - ul.x + 1);
1349                     wclrtoeol(current->wind);
1350                   }
1351                 wnoutrefresh(current->wind);
1352
1353                 memcpy(&lr, tmp, sizeof(pair));
1354                 (void) wresize(current->wind, lr.y-ul.y+0, lr.x-ul.x+0);
1355
1356                 getbegyx(current->wind, ul.y, ul.x);
1357                 getmaxyx(current->wind, lr.y, lr.x);
1358                 lr.y += (ul.y - 1);
1359                 lr.x += (ul.x - 1);
1360                 outerbox(ul, lr, TRUE);
1361                 wnoutrefresh(stdscr);
1362
1363                 wnoutrefresh(current->wind);
1364                 move(0, 0); clrtoeol();
1365                 doupdate();
1366             }
1367             break;
1368 #endif  /* NCURSES_VERSION */
1369
1370         case KEY_F(10): /* undocumented --- use this to test area clears */
1371             selectcell(0, 0, LINES - 1, COLS - 1);
1372             clrtobot();
1373             refresh();
1374             break;
1375
1376         case KEY_UP:
1377             newwin_move(current, -1,  0);
1378             break;
1379         case KEY_DOWN:
1380             newwin_move(current,  1,  0);
1381             break;
1382         case KEY_LEFT:
1383             newwin_move(current,  0, -1);
1384             break;
1385         case KEY_RIGHT:
1386             newwin_move(current,  0,  1);
1387             break;
1388
1389         case KEY_BACKSPACE:
1390             /* FALLTHROUGH */
1391         case KEY_DC:
1392             {
1393                 int y, x;
1394                 getyx(current->wind, y, x);
1395                 if (--x < 0) {
1396                         if (--y < 0)
1397                                 break;
1398                         x = getmaxx(current->wind) - 1;
1399                 }
1400                 mvwdelch(current->wind, y, x);
1401             }
1402             break;
1403
1404         case '\r':
1405             c = '\n';
1406             /* FALLTHROUGH */
1407
1408         default:
1409             if (current)
1410                 waddch(current->wind, (chtype)c);
1411             else
1412                 beep();
1413             break;
1414         }
1415         newwin_report(current);
1416         usescr = (current ? current->wind : stdscr);
1417         wrefresh(usescr);
1418     } while
1419         ((c = wGetchar(usescr)) != QUIT
1420          && !((c == ESCAPE) && (usescr->_use_keypad))
1421          && (c != ERR));
1422
1423  breakout:
1424     while (current != 0)
1425         current = delete_framed(current, FALSE);
1426
1427     scrollok(stdscr, TRUE);     /* reset to driver's default */
1428 #ifdef NCURSES_MOUSE_VERSION
1429     mousemask(0, (mmask_t *)0);
1430 #endif
1431     noraw();
1432     erase();
1433     endwin();
1434 }
1435
1436 /****************************************************************************
1437  *
1438  * Panels tester
1439  *
1440  ****************************************************************************/
1441
1442 #if USE_LIBPANEL
1443 static PANEL *p1;
1444 static PANEL *p2;
1445 static PANEL *p3;
1446 static PANEL *p4;
1447 static PANEL *p5;
1448 static WINDOW *w1;
1449 static WINDOW *w2;
1450 static WINDOW *w3;
1451 static WINDOW *w4;
1452 static WINDOW *w5;
1453
1454 static unsigned long nap_msec = 1;
1455
1456 static NCURSES_CONST char *mod[] =
1457 {
1458         "test ",
1459         "TEST ",
1460         "(**) ",
1461         "*()* ",
1462         "<--> ",
1463         "LAST "
1464 };
1465
1466 /*+-------------------------------------------------------------------------
1467         wait_a_while(msec)
1468 --------------------------------------------------------------------------*/
1469 static void
1470 wait_a_while(unsigned long msec GCC_UNUSED)
1471 {
1472 #if HAVE_NAPMS
1473         if(nap_msec == 1)
1474                 wGetchar(stdscr);
1475         else
1476                 napms(nap_msec);
1477 #else
1478         if(nap_msec == 1)
1479                 wGetchar(stdscr);
1480         else if(msec > 1000L)
1481                 sleep((int)msec/1000L);
1482         else
1483                 sleep(1);
1484 #endif
1485 }       /* end of wait_a_while */
1486
1487 /*+-------------------------------------------------------------------------
1488         saywhat(text)
1489 --------------------------------------------------------------------------*/
1490 static void
1491 saywhat(NCURSES_CONST char *text)
1492 {
1493         wmove(stdscr,LINES - 1,0);
1494         wclrtoeol(stdscr);
1495         waddstr(stdscr, text);
1496 }       /* end of saywhat */
1497
1498 /*+-------------------------------------------------------------------------
1499         mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
1500 --------------------------------------------------------------------------*/
1501 static PANEL *
1502 mkpanel(int rows, int cols, int tly, int tlx)
1503 {
1504 WINDOW *win = newwin(rows,cols,tly,tlx);
1505 PANEL *pan;
1506
1507         if(!win)
1508                 return((PANEL *)0);
1509         if((pan = new_panel(win)))
1510                 return(pan);
1511         delwin(win);
1512         return((PANEL *)0);
1513 }       /* end of mkpanel */
1514
1515 /*+-------------------------------------------------------------------------
1516         rmpanel(pan)
1517 --------------------------------------------------------------------------*/
1518 static void
1519 rmpanel(PANEL *pan)
1520 {
1521 WINDOW *win = panel_window(pan);
1522         del_panel(pan);
1523         delwin(win);
1524 }       /* end of rmpanel */
1525
1526 /*+-------------------------------------------------------------------------
1527         pflush()
1528 --------------------------------------------------------------------------*/
1529 static void
1530 pflush(void)
1531 {
1532         update_panels();
1533         doupdate();
1534 }       /* end of pflush */
1535
1536 /*+-------------------------------------------------------------------------
1537         fill_panel(win)
1538 --------------------------------------------------------------------------*/
1539 static void
1540 fill_panel(PANEL *pan)
1541 {
1542 WINDOW *win = panel_window(pan);
1543 int num = ((const char *)panel_userptr(pan))[1];
1544 int y,x;
1545
1546         box(win, 0, 0);
1547         wmove(win,1,1);
1548         wprintw(win,"-pan%c-", num);
1549         for(y = 2; y < getmaxy(win) - 1; y++)
1550         {
1551                 for(x = 1; x < getmaxx(win) - 1; x++)
1552                 {
1553                         wmove(win,y,x);
1554                         waddch(win,num);
1555                 }
1556         }
1557 }       /* end of fill_panel */
1558
1559 static void demo_panels(void)
1560 {
1561 int itmp;
1562 register y,x;
1563
1564         refresh();
1565
1566         for(y = 0; y < LINES - 1; y++)
1567         {
1568                 for(x = 0; x < COLS; x++)
1569                         wprintw(stdscr,"%d",(y + x) % 10);
1570         }
1571         for(y = 0; y < 5; y++)
1572         {
1573                 p1 = mkpanel(LINES/2 - 2, COLS/8 + 1, 0, 0);
1574                 w1 = panel_window(p1);
1575                 set_panel_userptr(p1,"p1");
1576
1577                 p2 = mkpanel(LINES/2 + 1, COLS/7, LINES/4, COLS/10);
1578                 w2 = panel_window(p2);
1579                 set_panel_userptr(p2,"p2");
1580
1581                 p3 = mkpanel(LINES/4, COLS/10, LINES/2, COLS/9);
1582                 w3 = panel_window(p3);
1583                 set_panel_userptr(p3,"p3");
1584
1585                 p4 = mkpanel(LINES/2 - 2, COLS/8, LINES/2 - 2, COLS/3);
1586                 w4 = panel_window(p4);
1587                 set_panel_userptr(p4,"p4");
1588
1589                 p5 = mkpanel(LINES/2 - 2, COLS/8, LINES/2, COLS/2 - 2);
1590                 w5 = panel_window(p5);
1591                 set_panel_userptr(p5,"p5");
1592
1593                 fill_panel(p1);
1594                 fill_panel(p2);
1595                 fill_panel(p3);
1596                 fill_panel(p4);
1597                 fill_panel(p5);
1598                 hide_panel(p4);
1599                 hide_panel(p5);
1600                 pflush();
1601                 saywhat("press any key to continue");
1602                 wait_a_while(nap_msec);
1603
1604                 saywhat("h3 s1 s2 s4 s5; press any key to continue");
1605                 move_panel(p1,0,0);
1606                 hide_panel(p3);
1607                 show_panel(p1);
1608                 show_panel(p2);
1609                 show_panel(p4);
1610                 show_panel(p5);
1611                 pflush();
1612                 wait_a_while(nap_msec);
1613
1614                 saywhat("s1; press any key to continue");
1615                 show_panel(p1);
1616                 pflush();
1617                 wait_a_while(nap_msec);
1618
1619                 saywhat("s2; press any key to continue");
1620                 show_panel(p2);
1621                 pflush();
1622                 wait_a_while(nap_msec);
1623
1624                 saywhat("m2; press any key to continue");
1625                 move_panel(p2, LINES/3 + 1, COLS / 8);
1626                 pflush();
1627                 wait_a_while(nap_msec);
1628
1629                 saywhat("s3;");
1630                 show_panel(p3);
1631                 pflush();
1632                 wait_a_while(nap_msec);
1633
1634                 saywhat("m3; press any key to continue");
1635                 move_panel(p3, LINES/4 + 1, COLS / 15);
1636                 pflush();
1637                 wait_a_while(nap_msec);
1638
1639                 saywhat("b3; press any key to continue");
1640                 bottom_panel(p3);
1641                 pflush();
1642                 wait_a_while(nap_msec);
1643
1644                 saywhat("s4; press any key to continue");
1645                 show_panel(p4);
1646                 pflush();
1647                 wait_a_while(nap_msec);
1648
1649                 saywhat("s5; press any key to continue");
1650                 show_panel(p5);
1651                 pflush();
1652                 wait_a_while(nap_msec);
1653
1654                 saywhat("t3; press any key to continue");
1655                 top_panel(p3);
1656                 pflush();
1657                 wait_a_while(nap_msec);
1658
1659                 saywhat("t1; press any key to continue");
1660                 top_panel(p1);
1661                 pflush();
1662                 wait_a_while(nap_msec);
1663
1664                 saywhat("t2; press any key to continue");
1665                 top_panel(p2);
1666                 pflush();
1667                 wait_a_while(nap_msec);
1668
1669                 saywhat("t3; press any key to continue");
1670                 top_panel(p3);
1671                 pflush();
1672                 wait_a_while(nap_msec);
1673
1674                 saywhat("t4; press any key to continue");
1675                 top_panel(p4);
1676                 pflush();
1677                 wait_a_while(nap_msec);
1678
1679                 for(itmp = 0; itmp < 6; itmp++)
1680                 {
1681                         saywhat("m4; press any key to continue");
1682                         wmove(w4, LINES/8, 1);
1683                         waddstr(w4,mod[itmp]);
1684                         move_panel(p4, LINES/6, itmp*(COLS/8));
1685                         wmove(w5, LINES/6, 1);
1686                         waddstr(w5,mod[itmp]);
1687                         pflush();
1688                         wait_a_while(nap_msec);
1689                         saywhat("m5; press any key to continue");
1690                         wmove(w4, LINES/6, 1);
1691                         waddstr(w4,mod[itmp]);
1692                         move_panel(p5, LINES/3 - 1,(itmp*10) + 6);
1693                         wmove(w5, LINES/8, 1);
1694                         waddstr(w5,mod[itmp]);
1695                         pflush();
1696                         wait_a_while(nap_msec);
1697                 }
1698
1699                 saywhat("m4; press any key to continue");
1700                 move_panel(p4, LINES/6, itmp*(COLS/8));
1701                 pflush();
1702                 wait_a_while(nap_msec);
1703
1704                 saywhat("t5; press any key to continue");
1705                 top_panel(p5);
1706                 pflush();
1707                 wait_a_while(nap_msec);
1708
1709                 saywhat("t2; press any key to continue");
1710                 top_panel(p2);
1711                 pflush();
1712                 wait_a_while(nap_msec);
1713
1714                 saywhat("t1; press any key to continue");
1715                 top_panel(p1);
1716                 pflush();
1717                 wait_a_while(nap_msec);
1718
1719                 saywhat("d2; press any key to continue");
1720                 rmpanel(p2);
1721                 pflush();
1722                 wait_a_while(nap_msec);
1723
1724                 saywhat("h3; press any key to continue");
1725                 hide_panel(p3);
1726                 pflush();
1727                 wait_a_while(nap_msec);
1728
1729                 saywhat("d1; press any key to continue");
1730                 rmpanel(p1);
1731                 pflush();
1732                 wait_a_while(nap_msec);
1733
1734                 saywhat("d4; press any key to continue");
1735                 rmpanel(p4);
1736                 pflush();
1737                 wait_a_while(nap_msec);
1738
1739                 saywhat("d5; press any key to continue");
1740                 rmpanel(p5);
1741                 pflush();
1742                 wait_a_while(nap_msec);
1743                 if(nap_msec == 1)
1744                         break;
1745                 nap_msec = 100L;
1746         }
1747
1748     erase();
1749     endwin();
1750 }
1751
1752 /****************************************************************************
1753  *
1754  * Pad tester
1755  *
1756  ****************************************************************************/
1757
1758 #define GRIDSIZE        3
1759
1760 static bool show_panner_legend = TRUE;
1761
1762 static int panner_legend(int line)
1763 {
1764         static const char *const legend[] = {
1765                 "Use arrow keys (or U,D,L,R) to pan, q to quit (?,t,s flags)",
1766                 "Use ! to shell-out.  Toggle legend:?, timer:t, scroll mark:s.",
1767                 "Use +,- (or j,k) to grow/shrink the panner vertically.",
1768                 "Use <,> (or h,l) to grow/shrink the panner horizontally."
1769         };
1770         int n = (SIZEOF(legend) - (LINES - line));
1771         if (line < LINES && (n >= 0)) {
1772                 move(line, 0);
1773                 if (show_panner_legend)
1774                         printw("%s", legend[n]);
1775                 clrtoeol();
1776                 return show_panner_legend;
1777         }
1778         return FALSE;
1779 }
1780
1781 static void panner_h_cleanup(int from_y, int from_x, int to_x)
1782 {
1783         if (!panner_legend(from_y))
1784                 do_h_line(from_y, from_x, ' ', to_x);
1785 }
1786
1787 static void panner_v_cleanup(int from_y, int from_x, int to_y)
1788 {
1789         if (!panner_legend(from_y))
1790                 do_v_line(from_y, from_x, ' ', to_y);
1791 }
1792
1793 static void panner(WINDOW *pad,
1794                    int top_x, int top_y, int porty, int portx,
1795                    int (*pgetc)(WINDOW *))
1796 {
1797 #if HAVE_GETTIMEOFDAY
1798     struct timeval before, after;
1799     bool timing = TRUE;
1800 #endif
1801     bool scrollers = TRUE;
1802     int basex = 0;
1803     int basey = 0;
1804     int pxmax, pymax, lowend, highend, c;
1805
1806     getmaxyx(pad, pymax, pxmax);
1807     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
1808
1809     c = KEY_REFRESH;
1810     do {
1811 #ifdef NCURSES_VERSION
1812         /*
1813          * During shell-out, the user may have resized the window.  Adjust
1814          * the port size of the pad to accommodate this.  Ncurses automatically
1815          * resizes all of the normal windows to fit on the new screen.
1816          */
1817         if (top_x > COLS)       top_x = COLS;
1818         if (portx > COLS)       portx = COLS;
1819         if (top_y > LINES)      top_y = LINES;
1820         if (porty > LINES)      porty = LINES;
1821 #endif
1822         switch(c)
1823         {
1824         case KEY_REFRESH:
1825             erase();
1826
1827             /* FALLTHRU */
1828         case '?':
1829             if (c == '?')
1830                 show_panner_legend = !show_panner_legend;
1831             panner_legend(LINES - 4);
1832             panner_legend(LINES - 3);
1833             panner_legend(LINES - 2);
1834             panner_legend(LINES - 1);
1835             break;
1836 #if HAVE_GETTIMEOFDAY
1837         case 't':
1838             timing = !timing;
1839             if (!timing)
1840                 panner_legend(LINES-1);
1841             break;
1842 #endif
1843         case 's':
1844             scrollers = !scrollers;
1845             break;
1846
1847             /* Move the top-left corner of the pad, keeping the bottom-right
1848              * corner fixed.
1849              */
1850         case 'h':       /* increase-columns: move left edge to left */
1851             if (top_x <= 0)
1852                 beep();
1853             else
1854             {
1855                 panner_v_cleanup(top_y, top_x, porty);
1856                 top_x--;
1857             }
1858             break;
1859
1860         case 'j':       /* decrease-lines: move top-edge down */
1861             if (top_y >= porty)
1862                 beep();
1863             else
1864             {
1865                 panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx);
1866                 top_y++;
1867             }
1868             break;
1869
1870         case 'k':       /* increase-lines: move top-edge up */
1871             if (top_y <= 0)
1872                 beep();
1873             else
1874             {
1875                 top_y--;
1876                 panner_h_cleanup(top_y, top_x, portx);
1877             }
1878             break;
1879
1880         case 'l':       /* decrease-columns: move left-edge to right */
1881             if (top_x >= portx)
1882                 beep();
1883             else
1884             {
1885                 panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty);
1886                 top_x++;
1887             }
1888             break;
1889
1890             /* Move the bottom-right corner of the pad, keeping the top-left
1891              * corner fixed.
1892              */
1893         case KEY_IC:    /* increase-columns: move right-edge to right */
1894             if (portx >= pxmax || portx >= COLS)
1895                 beep();
1896             else
1897             {
1898                 panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty);
1899                 ++portx;
1900             }
1901             break;
1902
1903         case KEY_IL:    /* increase-lines: move bottom-edge down */
1904             if (porty >= pymax || porty >= LINES)
1905                 beep();
1906             else
1907             {
1908                 panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx);
1909                 ++porty;
1910             }
1911             break;
1912
1913         case KEY_DC:    /* decrease-columns: move bottom edge up */
1914             if (portx <= top_x)
1915                 beep();
1916             else
1917             {
1918                 portx--;
1919                 panner_v_cleanup(top_y - (top_y > 0), portx, porty);
1920             }
1921             break;
1922
1923         case KEY_DL:    /* decrease-lines */
1924             if (porty <= top_y)
1925                 beep();
1926             else
1927             {
1928                 porty--;
1929                 panner_h_cleanup(porty, top_x - (top_x > 0), portx);
1930             }
1931             break;
1932
1933         case KEY_LEFT:  /* pan leftwards */
1934             if (basex > 0)
1935                 basex--;
1936             else
1937                 beep();
1938             break;
1939
1940         case KEY_RIGHT: /* pan rightwards */
1941             if (basex + portx - (pymax > porty) < pxmax)
1942                 basex++;
1943             else
1944                 beep();
1945             break;
1946
1947         case KEY_UP:    /* pan upwards */
1948             if (basey > 0)
1949                 basey--;
1950             else
1951                 beep();
1952             break;
1953
1954         case KEY_DOWN:  /* pan downwards */
1955             if (basey + porty - (pxmax > portx) < pymax)
1956                 basey++;
1957             else
1958                 beep();
1959             break;
1960
1961         default:
1962             beep();
1963             break;
1964         }
1965
1966         mvaddch(top_y - 1, top_x - 1, ACS_ULCORNER);
1967         do_v_line(top_y, top_x - 1, ACS_VLINE, porty);
1968         do_h_line(top_y - 1, top_x, ACS_HLINE, portx);
1969
1970         if (scrollers && (pxmax > portx - 1)) {
1971             int length  = (portx - top_x - 1);
1972             float ratio = ((float) length) / ((float) pxmax);
1973
1974             lowend  = (int)(top_x + (basex * ratio));
1975             highend = (int)(top_x + ((basex + length) * ratio));
1976
1977             do_h_line(porty - 1, top_x, ACS_HLINE, lowend);
1978             if (highend < portx) {
1979                 attron(A_REVERSE);
1980                 do_h_line(porty - 1, lowend, ' ', highend + 1);
1981                 attroff(A_REVERSE);
1982                 do_h_line(porty - 1, highend + 1, ACS_HLINE, portx);
1983             }
1984         } else
1985             do_h_line(porty - 1, top_x, ACS_HLINE, portx);
1986
1987         if (scrollers && (pymax > porty - 1)) {
1988             int length  = (porty - top_y - 1);
1989             float ratio = ((float) length) / ((float) pymax);
1990
1991             lowend  = (int)(top_y + (basey * ratio));
1992             highend = (int)(top_y + ((basey + length) * ratio));
1993
1994             do_v_line(top_y, portx - 1, ACS_VLINE, lowend);
1995                 if (highend < porty) {
1996                 attron(A_REVERSE);
1997                 do_v_line(lowend, portx - 1, ' ', highend + 1);
1998                 attroff(A_REVERSE);
1999                 do_v_line(highend + 1, portx - 1, ACS_VLINE, porty);
2000             }
2001         } else
2002             do_v_line(top_y, portx - 1, ACS_VLINE, porty);
2003
2004         mvaddch(top_y - 1, portx - 1, ACS_URCORNER);
2005         mvaddch(porty - 1, top_x - 1, ACS_LLCORNER);
2006         mvaddch(porty - 1, portx - 1, ACS_LRCORNER);
2007
2008 #if HAVE_GETTIMEOFDAY
2009         gettimeofday(&before, 0);
2010 #endif
2011         wnoutrefresh(stdscr);
2012
2013         pnoutrefresh(pad,
2014                  basey, basex,
2015                  top_y, top_x,
2016                  porty - (pxmax > portx) - 1,
2017                  portx - (pymax > porty) - 1);
2018
2019         doupdate();
2020 #if HAVE_GETTIMEOFDAY
2021         if (timing) {
2022                 double elapsed;
2023                 gettimeofday(&after, 0);
2024                 elapsed = (after.tv_sec  + after.tv_usec  / 1.0e6)
2025                         - (before.tv_sec + before.tv_usec / 1.0e6);
2026                 move(LINES-1, COLS-20);
2027                 printw("Secs: %2.03f", elapsed);
2028                 refresh();
2029         }
2030 #endif
2031
2032     } while
2033         ((c = pgetc(pad)) != KEY_EXIT);
2034
2035     scrollok(stdscr, TRUE);     /* reset to driver's default */
2036 }
2037
2038 static
2039 int padgetch(WINDOW *win)
2040 {
2041     int c;
2042
2043     switch(c = wGetchar(win))
2044     {
2045     case '!': ShellOut(FALSE); return KEY_REFRESH;
2046     case CTRL('r'): endwin(); refresh(); return KEY_REFRESH;
2047     case CTRL('l'): return KEY_REFRESH;
2048     case 'U': return(KEY_UP);
2049     case 'D': return(KEY_DOWN);
2050     case 'R': return(KEY_RIGHT);
2051     case 'L': return(KEY_LEFT);
2052     case '+': return(KEY_IL);
2053     case '-': return(KEY_DL);
2054     case '>': return(KEY_IC);
2055     case '<': return(KEY_DC);
2056     case ERR: /* FALLTHRU */
2057     case 'q': return(KEY_EXIT);
2058     default: return(c);
2059     }
2060 }
2061
2062 static void demo_pad(void)
2063 /* Demonstrate pads. */
2064 {
2065     int i, j;
2066     unsigned gridcount = 0;
2067     WINDOW *panpad = newpad(200, 200);
2068
2069     for (i = 0; i < 200; i++)
2070     {
2071         for (j = 0; j < 200; j++)
2072             if (i % GRIDSIZE == 0 && j % GRIDSIZE == 0)
2073             {
2074                 if (i == 0 || j == 0)
2075                     waddch(panpad, '+');
2076                 else
2077                     waddch(panpad, (chtype)('A' + (gridcount++ % 26)));
2078             }
2079             else if (i % GRIDSIZE == 0)
2080                 waddch(panpad, '-');
2081             else if (j % GRIDSIZE == 0)
2082                 waddch(panpad, '|');
2083             else
2084                 waddch(panpad, ' ');
2085     }
2086     panner_legend(LINES - 4);
2087     panner_legend(LINES - 3);
2088     panner_legend(LINES - 2);
2089     panner_legend(LINES - 1);
2090
2091     keypad(panpad, TRUE);
2092
2093     /* Make the pad (initially) narrow enough that a trace file won't wrap.
2094      * We'll still be able to widen it during a test, since that's required
2095      * for testing boundaries.
2096      */
2097     panner(panpad, 2, 2, LINES - 5, COLS-15, padgetch);
2098
2099     delwin(panpad);
2100     endwin();
2101     erase();
2102 }
2103 #endif /* USE_LIBPANEL */
2104
2105 /****************************************************************************
2106  *
2107  * Tests from John Burnell's PDCurses tester
2108  *
2109  ****************************************************************************/
2110
2111 static void Continue (WINDOW *win)
2112 {
2113     noecho();
2114     wmove(win, 10, 1);
2115     mvwaddstr(win, 10, 1, " Press any key to continue");
2116     wrefresh(win);
2117     wGetchar(win);
2118 }
2119
2120 static void flushinp_test(WINDOW *win)
2121 /* Input test, adapted from John Burnell's PDCurses tester */
2122 {
2123     int w, h, bx, by, sw, sh, i;
2124
2125     WINDOW *subWin;
2126     wclear (win);
2127
2128     getmaxyx(win, h,  w);
2129     getbegyx(win, by, bx);
2130     sw = w / 3;
2131     sh = h / 3;
2132     if((subWin = subwin(win, sh, sw, by + h - sh - 2, bx + w - sw - 2)) == 0)
2133         return;
2134
2135 #ifdef A_COLOR
2136     if (has_colors())
2137     {
2138         init_pair(2,COLOR_CYAN,COLOR_BLUE);
2139         wattrset(subWin, COLOR_PAIR(2) | A_BOLD);
2140     }
2141     else
2142         wattrset(subWin, A_BOLD);
2143 #else
2144     wattrset(subWin, A_BOLD);
2145 #endif
2146     box(subWin, ACS_VLINE, ACS_HLINE);
2147     mvwaddstr(subWin, 2, 1, "This is a subwindow");
2148     wrefresh(win);
2149
2150     /*
2151      * This used to set 'nocbreak()'.  However, Alexander Lukyanov says that
2152      * it only happened to "work" on SVr4 because that implementation does not
2153      * emulate nocbreak+noecho mode, whereas ncurses does.  To get the desired
2154      * test behavior, we're using 'cbreak()', which will allow a single
2155      * character to return without needing a newline. - T.Dickey 1997/10/11.
2156      */
2157     cbreak();
2158     mvwaddstr(win, 0, 1, "This is a test of the flushinp() call.");
2159
2160     mvwaddstr(win, 2, 1, "Type random keys for 5 seconds.");
2161     mvwaddstr(win, 3, 1,
2162       "These should be discarded (not echoed) after the subwindow goes away.");
2163     wrefresh(win);
2164
2165     for (i = 0; i < 5; i++)
2166     {
2167         mvwprintw (subWin, 1, 1, "Time = %d", i);
2168         wrefresh(subWin);
2169         napms(1000);
2170         flushinp();
2171     }
2172
2173     delwin (subWin);
2174     werase(win);
2175     flash();
2176     wrefresh(win);
2177     napms(1000);
2178
2179     mvwaddstr(win, 2, 1,
2180               "If you were still typing when the window timer expired,");
2181     mvwaddstr(win, 3, 1,
2182               "or else you typed nothing at all while it was running,");
2183     mvwaddstr(win, 4, 1,
2184               "test was invalid.  You'll see garbage or nothing at all. ");
2185     mvwaddstr(win, 6, 1, "Press a key");
2186     wmove(win, 9, 10);
2187     wrefresh(win);
2188     echo();
2189     wGetchar(win);
2190     flushinp();
2191     mvwaddstr(win, 12, 0,
2192               "If you see any key other than what you typed, flushinp() is broken.");
2193     Continue(win);
2194
2195     wmove(win, 9, 10);
2196     wdelch(win);
2197     wrefresh(win);
2198     wmove(win, 12, 0);
2199     clrtoeol();
2200     waddstr(win,
2201             "What you typed should now have been deleted; if not, wdelch() failed.");
2202     Continue(win);
2203
2204     cbreak();
2205 }
2206
2207 /****************************************************************************
2208  *
2209  * Menu test
2210  *
2211  ****************************************************************************/
2212
2213 #if USE_LIBMENU
2214
2215 #define MENU_Y  8
2216 #define MENU_X  8
2217
2218 static int menu_virtualize(int c)
2219 {
2220     if (c == '\n' || c == KEY_EXIT)
2221         return(MAX_COMMAND + 1);
2222     else if (c == 'n' || c == KEY_DOWN)
2223         return(REQ_NEXT_ITEM);
2224     else if (c == 'p' || c == KEY_UP)
2225         return(REQ_PREV_ITEM);
2226     else if (c == ' ')
2227         return(REQ_TOGGLE_ITEM);
2228     else
2229         return(c);
2230 }
2231
2232 static const char *animals[] =
2233 {
2234     "Lions", "Tigers", "Bears", "(Oh my!)", "Newts", "Platypi", "Lemurs",
2235     (char *)0
2236 };
2237
2238 static void menu_test(void)
2239 {
2240     MENU        *m;
2241     ITEM        *items[SIZEOF(animals)];
2242     ITEM        **ip = items;
2243     const char  **ap;
2244     int         mrows, mcols;
2245     WINDOW      *menuwin;
2246
2247     mvaddstr(0, 0, "This is the menu test:");
2248     mvaddstr(2, 0, "  Use up and down arrow to move the select bar.");
2249     mvaddstr(3, 0, "  'n' and 'p' act like arrows.");
2250     mvaddstr(4, 0, "  Press return to exit.");
2251     refresh();
2252
2253     for (ap = animals; *ap; ap++)
2254         *ip++ = new_item(*ap, "");
2255     *ip = (ITEM *)0;
2256
2257     m = new_menu(items);
2258
2259     scale_menu(m, &mrows, &mcols);
2260
2261     menuwin = newwin(mrows + 2, mcols +  2, MENU_Y, MENU_X);
2262     set_menu_win(m, menuwin);
2263     keypad(menuwin, TRUE);
2264     box(menuwin, 0, 0);
2265
2266     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
2267
2268     post_menu(m);
2269
2270     while (menu_driver(m, menu_virtualize(wGetchar(menuwin))) != E_UNKNOWN_COMMAND)
2271         continue;
2272
2273     (void) mvprintw(LINES - 2, 0,
2274                      "You chose: %s\n", item_name(current_item(m)));
2275     (void) addstr("Press any key to continue...");
2276     wGetchar(stdscr);
2277
2278     unpost_menu(m);
2279     delwin(menuwin);
2280
2281     free_menu(m);
2282     for (ip = items; *ip; ip++)
2283         free_item(*ip);
2284 }
2285
2286 #ifdef TRACE
2287 #define T_TBL(name) { #name, name }
2288 static struct {
2289         const char *name;
2290         int mask;
2291 } t_tbl[] = {
2292         T_TBL(TRACE_DISABLE),
2293         T_TBL(TRACE_TIMES),
2294         T_TBL(TRACE_TPUTS),
2295         T_TBL(TRACE_UPDATE),
2296         T_TBL(TRACE_MOVE),
2297         T_TBL(TRACE_CHARPUT),
2298         T_TBL(TRACE_ORDINARY),
2299         T_TBL(TRACE_CALLS),
2300         T_TBL(TRACE_VIRTPUT),
2301         T_TBL(TRACE_IEVENT),
2302         T_TBL(TRACE_BITS),
2303         T_TBL(TRACE_ICALLS),
2304         T_TBL(TRACE_CCALLS),
2305         T_TBL(TRACE_MAXIMUM),
2306         { (char *)0, 0 }
2307 };
2308
2309 static char *tracetrace(int tlevel)
2310 {
2311     static char *buf;
2312     int         n;
2313
2314     if (buf == 0) {
2315         size_t need = 12;
2316         for (n = 0; t_tbl[n].name != 0; n++)
2317             need += strlen(t_tbl[n].name) + 2;
2318         buf = (char *)malloc(need);
2319     }
2320     sprintf(buf, "0x%02x = {", tlevel);
2321     if (tlevel == 0) {
2322         sprintf(buf + strlen(buf), "%s, ", t_tbl[0].name);
2323     } else {
2324         for (n = 1; t_tbl[n].name != 0; n++)
2325             if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask)
2326             {
2327                 strcat(buf, t_tbl[n].name);
2328                 strcat(buf, ", ");
2329             }
2330     }
2331     if (buf[strlen(buf) - 2] == ',')
2332         buf[strlen(buf) - 2] = '\0';
2333     return(strcat(buf,"}"));
2334 }
2335
2336 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
2337  * the others
2338  */
2339 static int run_trace_menu(MENU *m)
2340 {
2341     ITEM **items;
2342     ITEM *i, **p;
2343
2344     for (;;) {
2345         bool changed = FALSE;
2346         switch (menu_driver(m, menu_virtualize(wGetchar(menu_win(m))))) {
2347         case E_UNKNOWN_COMMAND:
2348             return FALSE;
2349         default:
2350             items = menu_items(m);
2351             i = current_item(m);
2352             if (i == items[0]) {
2353                 if (item_value(i)) {
2354                     for (p = items+1; *p != 0; p++)
2355                         if (item_value(*p)) {
2356                             set_item_value(*p, FALSE);
2357                             changed = TRUE;
2358                         }
2359                 }
2360             } else {
2361                 for (p = items+1; *p != 0; p++)
2362                     if (item_value(*p)) {
2363                         set_item_value(items[0], FALSE);
2364                         changed = TRUE;
2365                         break;
2366                     }
2367             }
2368             if (!changed)
2369                 return TRUE;
2370         }
2371     }
2372 }
2373
2374 static void trace_set(void)
2375 /* interactively set the trace level */
2376 {
2377     MENU        *m;
2378     ITEM        *items[SIZEOF(t_tbl)];
2379     ITEM        **ip = items;
2380     int         mrows, mcols, newtrace;
2381     int         n;
2382     WINDOW      *menuwin;
2383
2384     mvaddstr(0, 0, "Interactively set trace level:");
2385     mvaddstr(2, 0, "  Press space bar to toggle a selection.");
2386     mvaddstr(3, 0, "  Use up and down arrow to move the select bar.");
2387     mvaddstr(4, 0, "  Press return to set the trace level.");
2388     mvprintw(6, 0, "(Current trace level is %s)", tracetrace(_nc_tracing));
2389
2390     refresh();
2391
2392     for (n = 0; t_tbl[n].name != 0; n++)
2393         *ip++ = new_item(t_tbl[n].name, "");
2394     *ip = (ITEM *)0;
2395
2396     m = new_menu(items);
2397
2398     set_menu_format(m, 0, 2);
2399     scale_menu(m, &mrows, &mcols);
2400
2401     menu_opts_off(m, O_ONEVALUE);
2402     menuwin = newwin(mrows + 2, mcols +  2, MENU_Y, MENU_X);
2403     set_menu_win(m, menuwin);
2404     keypad(menuwin, TRUE);
2405     box(menuwin, 0, 0);
2406
2407     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
2408
2409     post_menu(m);
2410
2411     for (ip = menu_items(m); *ip; ip++) {
2412         int mask = t_tbl[item_index(*ip)].mask;
2413         if (mask == 0)
2414             set_item_value(*ip, _nc_tracing == 0);
2415         else if ((mask & _nc_tracing) == mask)
2416             set_item_value(*ip, TRUE);
2417     }
2418
2419     while (run_trace_menu(m))
2420         continue;
2421
2422     newtrace = 0;
2423     for (ip = menu_items(m); *ip; ip++)
2424         if (item_value(*ip))
2425             newtrace |= t_tbl[item_index(*ip)].mask;
2426     trace(newtrace);
2427     _tracef("trace level interactively set to %s", tracetrace(_nc_tracing));
2428
2429     (void) mvprintw(LINES - 2, 0,
2430                      "Trace level is %s\n", tracetrace(_nc_tracing));
2431     (void) addstr("Press any key to continue...");
2432     wGetchar(stdscr);
2433
2434     unpost_menu(m);
2435     delwin(menuwin);
2436
2437     free_menu(m);
2438     for (ip = items; *ip; ip++)
2439         free_item(*ip);
2440 }
2441 #endif /* TRACE */
2442 #endif /* USE_LIBMENU */
2443
2444 /****************************************************************************
2445  *
2446  * Forms test
2447  *
2448  ****************************************************************************/
2449 #if USE_LIBFORM
2450 static FIELD *make_label(int frow, int fcol, NCURSES_CONST char *label)
2451 {
2452     FIELD       *f = new_field(1, strlen(label), frow, fcol, 0, 0);
2453
2454     if (f)
2455     {
2456         set_field_buffer(f, 0, label);
2457         set_field_opts(f, field_opts(f) & ~O_ACTIVE);
2458     }
2459     return(f);
2460 }
2461
2462 static FIELD *make_field(int frow, int fcol, int rows, int cols)
2463 {
2464     FIELD       *f = new_field(rows, cols, frow, fcol, 0, 0);
2465
2466     if (f)
2467         set_field_back(f, A_UNDERLINE);
2468     return(f);
2469 }
2470
2471 static void display_form(FORM *f)
2472 {
2473     WINDOW      *w;
2474     int rows, cols;
2475
2476     scale_form(f, &rows, &cols);
2477
2478     if ((w =newwin(rows+2, cols+4, 0, 0)) != (WINDOW *)0)
2479     {
2480         set_form_win(f, w);
2481         set_form_sub(f, derwin(w, rows, cols, 1, 2));
2482         box(w, 0, 0);
2483         keypad(w, TRUE);
2484     }
2485
2486     if (post_form(f) != E_OK)
2487         wrefresh(w);
2488 }
2489
2490 static void erase_form(FORM *f)
2491 {
2492     WINDOW      *w = form_win(f);
2493     WINDOW      *s = form_sub(f);
2494
2495     unpost_form(f);
2496     werase(w);
2497     wrefresh(w);
2498     delwin(s);
2499     delwin(w);
2500 }
2501
2502 static int form_virtualize(WINDOW *w)
2503 {
2504     static int  mode = REQ_INS_MODE;
2505     int         c = wGetchar(w);
2506
2507     switch(c)
2508     {
2509     case QUIT:
2510     case ESCAPE:
2511         return(MAX_FORM_COMMAND + 1);
2512
2513     /* demo doesn't use these three, leave them in anyway as sample code */
2514     case KEY_NPAGE:
2515     case CTRL('F'):
2516         return(REQ_NEXT_PAGE);
2517     case KEY_PPAGE:
2518         return(REQ_PREV_PAGE);
2519
2520     case KEY_NEXT:
2521     case CTRL('N'):
2522         return(REQ_NEXT_FIELD);
2523     case KEY_PREVIOUS:
2524     case CTRL('P'):
2525         return(REQ_PREV_FIELD);
2526
2527     case KEY_HOME:
2528         return(REQ_FIRST_FIELD);
2529     case KEY_END:
2530     case KEY_LL:
2531         return(REQ_LAST_FIELD);
2532
2533     case CTRL('L'):
2534         return(REQ_LEFT_FIELD);
2535     case CTRL('R'):
2536         return(REQ_RIGHT_FIELD);
2537     case CTRL('U'):
2538         return(REQ_UP_FIELD);
2539     case CTRL('D'):
2540         return(REQ_DOWN_FIELD);
2541
2542     case CTRL('W'):
2543         return(REQ_NEXT_WORD);
2544     case CTRL('B'):
2545         return(REQ_PREV_WORD);
2546     case CTRL('S'):
2547         return(REQ_BEG_FIELD);
2548     case CTRL('E'):
2549         return(REQ_END_FIELD);
2550
2551     case KEY_LEFT:
2552         return(REQ_LEFT_CHAR);
2553     case KEY_RIGHT:
2554         return(REQ_RIGHT_CHAR);
2555     case KEY_UP:
2556         return(REQ_UP_CHAR);
2557     case KEY_DOWN:
2558         return(REQ_DOWN_CHAR);
2559
2560     case CTRL('M'):
2561         return(REQ_NEW_LINE);
2562     case CTRL('I'):
2563         return(REQ_INS_CHAR);
2564     case CTRL('O'):
2565         return(REQ_INS_LINE);
2566     case CTRL('V'):
2567         return(REQ_DEL_CHAR);
2568
2569     case CTRL('H'):
2570     case KEY_BACKSPACE:
2571         return(REQ_DEL_PREV);
2572     case CTRL('Y'):
2573         return(REQ_DEL_LINE);
2574     case CTRL('G'):
2575         return(REQ_DEL_WORD);
2576
2577     case CTRL('C'):
2578         return(REQ_CLR_EOL);
2579     case CTRL('K'):
2580         return(REQ_CLR_EOF);
2581     case CTRL('X'):
2582         return(REQ_CLR_FIELD);
2583     case CTRL('A'):
2584         return(REQ_NEXT_CHOICE);
2585     case CTRL('Z'):
2586         return(REQ_PREV_CHOICE);
2587
2588     case CTRL(']'):
2589         if (mode == REQ_INS_MODE)
2590             return(mode = REQ_OVL_MODE);
2591         else
2592             return(mode = REQ_INS_MODE);
2593
2594     default:
2595         return(c);
2596     }
2597 }
2598
2599 static int my_form_driver(FORM *form, int c)
2600 {
2601     if (c == (MAX_FORM_COMMAND + 1)
2602                 && form_driver(form, REQ_VALIDATION) == E_OK)
2603         return(TRUE);
2604     else
2605     {
2606         beep();
2607         return(FALSE);
2608     }
2609 }
2610
2611 static void demo_forms(void)
2612 {
2613     WINDOW      *w;
2614     FORM        *form;
2615     FIELD       *f[10];
2616     int         finished = 0, c;
2617
2618     mvaddstr(10, 57, "Forms Entry Test");
2619
2620     move(18, 0);
2621     addstr("Defined form-traversal keys:   ^Q/ESC- exit form\n");
2622     addstr("^N   -- go to next field       ^P  -- go to previous field\n");
2623     addstr("Home -- go to first field      End -- go to last field\n");
2624     addstr("^L   -- go to field to left    ^R  -- go to field to right\n");
2625     addstr("^U   -- move upward to field   ^D  -- move downward to field\n");
2626     addstr("^W   -- go to next word        ^B  -- go to previous word\n");
2627     addstr("^S   -- go to start of field   ^E  -- go to end of field\n");
2628     addstr("^H   -- delete previous char   ^Y  -- delete line\n");
2629     addstr("^G   -- delete current word    ^C  -- clear to end of line\n");
2630     addstr("^K   -- clear to end of field  ^X  -- clear field\n");
2631     addstr("Arrow keys move within a field as you would expect.");
2632     refresh();
2633
2634     /* describe the form */
2635     f[0] = make_label(0, 15, "Sample Form");
2636     f[1] = make_label(2, 0, "Last Name");
2637     f[2] = make_field(3, 0, 1, 18);
2638     f[3] = make_label(2, 20, "First Name");
2639     f[4] = make_field(3, 20, 1, 12);
2640     f[5] = make_label(2, 34, "Middle Name");
2641     f[6] = make_field(3, 34, 1, 12);
2642     f[7] = make_label(5, 0, "Comments");
2643     f[8] = make_field(6, 0, 4, 46);
2644     f[9] = (FIELD *)0;
2645
2646     form = new_form(f);
2647
2648     display_form(form);
2649
2650     w = form_win(form);
2651     raw();
2652     while (!finished)
2653     {
2654         switch(form_driver(form, c = form_virtualize(w)))
2655         {
2656         case E_OK:
2657             break;
2658         case E_UNKNOWN_COMMAND:
2659             finished = my_form_driver(form, c);
2660             break;
2661         default:
2662             beep();
2663             break;
2664         }
2665     }
2666
2667     erase_form(form);
2668
2669     free_form(form);
2670     for (c = 0; f[c] != 0; c++)
2671         free_field(f[c]);
2672     noraw();
2673 }
2674 #endif  /* USE_LIBFORM */
2675
2676 /****************************************************************************
2677  *
2678  * Overlap test
2679  *
2680  ****************************************************************************/
2681
2682 static void fillwin(WINDOW *win, char ch)
2683 {
2684     int y, x;
2685     int y1, x1;
2686
2687     getmaxyx(win, y1, x1);
2688     for (y = 0; y < y1; y++)
2689     {
2690         wmove(win, y, 0);
2691         for (x = 0; x < x1; x++)
2692             waddch(win, ch);
2693     }
2694 }
2695
2696 static void crosswin(WINDOW *win, char ch)
2697 {
2698     int y, x;
2699     int y1, x1;
2700
2701     getmaxyx(win, y1, x1);
2702     for (y = 0; y < y1; y++)
2703     {
2704         for (x = 0; x < x1; x++)
2705             if (((x > (x1 - 1) / 3) && (x <= (2 * (x1 - 1)) / 3))
2706             || (((y > (y1 - 1) / 3) && (y <= (2 * (y1 - 1)) / 3))))
2707             {
2708                 wmove(win, y, x);
2709                 waddch(win, ch);
2710             }
2711     }
2712 }
2713
2714 static void overlap_test(void)
2715 /* test effects of overlapping windows */
2716 {
2717     int ch;
2718
2719     WINDOW *win1 = newwin(9, 20, 3, 3);
2720     WINDOW *win2 = newwin(9, 20, 9, 16);
2721
2722     raw();
2723     refresh();
2724     move(0, 0);
2725     printw("This test shows the behavior of wnoutrefresh() with respect to\n");
2726     printw("the shared region of two overlapping windows A and B.  The cross\n");
2727     printw("pattern in each window does not overlap the other.\n");
2728
2729
2730     move(18, 0);
2731     printw("a = refresh A, then B, then doupdate. b = refresh B, then A, then doupdaute\n");
2732     printw("c = fill window A with letter A.      d = fill window B with letter B.\n");
2733     printw("e = cross pattern in window A.        f = cross pattern in window B.\n");
2734     printw("g = clear window A.                   h = clear window B.\n");
2735     printw("i = overwrite A onto B.               j = overwrite B onto A.\n");
2736     printw("^Q/ESC = terminate test.");
2737
2738     while ((ch = Getchar()) != QUIT && ch != ESCAPE)
2739         switch (ch)
2740         {
2741         case 'a':               /* refresh window A first, then B */
2742             wnoutrefresh(win1);
2743             wnoutrefresh(win2);
2744             doupdate();
2745             break;
2746
2747         case 'b':               /* refresh window B first, then A */
2748             wnoutrefresh(win2);
2749             wnoutrefresh(win1);
2750             doupdate();
2751             break;
2752
2753         case 'c':               /* fill window A so it's visible */
2754             fillwin(win1, 'A');
2755             break;
2756
2757         case 'd':               /* fill window B so it's visible */
2758             fillwin(win2, 'B');
2759             break;
2760
2761         case 'e':               /* cross test pattern in window A */
2762             crosswin(win1, 'A');
2763             break;
2764
2765         case 'f':               /* cross test pattern in window A */
2766             crosswin(win2, 'B');
2767             break;
2768
2769         case 'g':               /* clear window A */
2770             wclear(win1);
2771             wmove(win1, 0, 0);
2772             break;
2773
2774         case 'h':               /* clear window B */
2775             wclear(win2);
2776             wmove(win2, 0, 0);
2777             break;
2778
2779         case 'i':               /* overwrite A onto B */
2780             overwrite(win1, win2);
2781             break;
2782
2783         case 'j':               /* overwrite B onto A */
2784             overwrite(win2, win1);
2785             break;
2786         }
2787
2788     delwin(win2);
2789     delwin(win1);
2790     erase();
2791     endwin();
2792 }
2793
2794 /****************************************************************************
2795  *
2796  * Main sequence
2797  *
2798  ****************************************************************************/
2799
2800 static bool
2801 do_single_test(const char c)
2802 /* perform a single specified test */
2803 {
2804     switch (c)
2805     {
2806     case 'a':
2807         getch_test();
2808         break;
2809
2810     case 'b':
2811         attr_test();
2812         break;
2813
2814     case 'c':
2815         if (!has_colors())
2816             Cannot("does not support color.");
2817         else
2818             color_test();
2819         break;
2820
2821     case 'd':
2822         if (!has_colors())
2823             Cannot("does not support color.");
2824         else if (!can_change_color())
2825             Cannot("has hardwired color values.");
2826         else
2827             color_edit();
2828         break;
2829
2830     case 'e':
2831         slk_test();
2832         break;
2833
2834     case 'f':
2835         acs_display();
2836         break;
2837
2838 #if USE_LIBPANEL
2839     case 'o':
2840         demo_panels();
2841         break;
2842 #endif
2843
2844     case 'g':
2845         acs_and_scroll();
2846         break;
2847
2848     case 'i':
2849         flushinp_test(stdscr);
2850         break;
2851
2852     case 'k':
2853         test_sgr_attributes();
2854         break;
2855
2856 #if USE_LIBMENU
2857     case 'm':
2858         menu_test();
2859         break;
2860 #endif
2861
2862 #if USE_LIBPANEL
2863     case 'p':
2864         demo_pad();
2865         break;
2866 #endif
2867
2868 #if USE_LIBFORM
2869     case 'r':
2870         demo_forms();
2871         break;
2872 #endif
2873
2874     case 's':
2875         overlap_test();
2876         break;
2877
2878 #if USE_LIBMENU && defined(TRACE)
2879     case 't':
2880         trace_set();
2881         break;
2882 #endif
2883
2884     case '?':
2885         break;
2886
2887     default:
2888         return FALSE;
2889     }
2890
2891     return TRUE;
2892 }
2893
2894 static void
2895 usage(void)
2896 {
2897     static const char *const tbl[] = {
2898          "Usage: ncurses [options]"
2899         ,""
2900         ,"Options:"
2901         ,"  -e fmt   specify format for soft-keys test (e)"
2902         ,"  -f       rip-off footer line (can repeat)"
2903         ,"  -h       rip-off header line (can repeat)"
2904         ,"  -s msec  specify nominal time for panel-demo (default: 1, to hold)"
2905 #ifdef TRACE
2906         ,"  -t mask  specify default trace-level (may toggle with ^T)"
2907 #endif
2908     };
2909     size_t n;
2910     for (n = 0; n < sizeof(tbl)/sizeof(tbl[0]); n++)
2911         fprintf(stderr, "%s\n", tbl[n]);
2912     exit(EXIT_FAILURE);
2913 }
2914
2915 static void
2916 set_terminal_modes(void)
2917 {
2918     noraw();
2919     cbreak();
2920     noecho();
2921     scrollok(stdscr, TRUE);
2922     idlok(stdscr, TRUE);
2923     keypad(stdscr, TRUE);
2924 }
2925
2926 #ifdef SIGUSR1
2927 static RETSIGTYPE announce_sig(int sig)
2928 {
2929     (void) fprintf(stderr, "Handled signal %d\r\n", sig);
2930 }
2931 #endif
2932
2933 static int rip_footer(WINDOW *win, int columns)
2934 {
2935         wbkgd(win, A_REVERSE);
2936         werase(win);
2937         wmove(win, 0, 0);
2938         wprintw(win, "footer: %d columns", columns);
2939         wnoutrefresh(win);
2940         return OK;
2941 }
2942
2943 static int rip_header(WINDOW *win, int columns)
2944 {
2945         wbkgd(win, A_REVERSE);
2946         werase(win);
2947         wmove(win, 0, 0);
2948         wprintw(win, "header: %d columns", columns);
2949         wnoutrefresh(win);
2950         return OK;
2951 }
2952
2953 /*+-------------------------------------------------------------------------
2954         main(argc,argv)
2955 --------------------------------------------------------------------------*/
2956
2957 int
2958 main(int argc, char *argv[])
2959 {
2960     int         command, c;
2961     int         my_e_param = 1;
2962
2963 #if HAVE_LOCALE_H
2964     setlocale(LC_CTYPE, "");
2965 #endif
2966
2967     while ((c = getopt(argc, argv, "e:fhs:t:")) != EOF) {
2968         switch (c) {
2969         case 'e':
2970             my_e_param = atoi(optarg);
2971 #ifdef NCURSES_VERSION
2972             if (my_e_param > 3) /* allow extended layouts */
2973                 usage();
2974 #else
2975             if (my_e_param > 1)
2976                 usage();
2977 #endif
2978             break;
2979         case 'f':
2980             ripoffline(-1, rip_footer);
2981             break;
2982         case 'h':
2983             ripoffline(1, rip_header);
2984             break;
2985 #if USE_LIBPANEL
2986         case 's':
2987             nap_msec = atol(optarg);
2988             break;
2989 #endif
2990 #ifdef TRACE
2991         case 't':
2992             save_trace = atoi(optarg);
2993             break;
2994 #endif
2995         default:
2996             usage();
2997         }
2998     }
2999
3000     /*
3001      * If there's no menus (unlikely for ncurses!), then we'll have to set
3002      * tracing on initially, just in case the user wants to test something that
3003      * doesn't involve wGetchar.
3004      */
3005 #ifdef TRACE
3006     /* enable debugging */
3007 #if !USE_LIBMENU
3008     trace(save_trace);
3009 #else
3010     if (!isatty(fileno(stdin)))
3011         trace(save_trace);
3012 #endif /* USE_LIBMENU */
3013 #endif /* TRACE */
3014
3015     /* tell it we're going to play with soft keys */
3016     slk_init(my_e_param);
3017
3018 #ifdef SIGUSR1
3019     /* set up null signal catcher so we can see what interrupts to getch do */
3020     signal(SIGUSR1, announce_sig);
3021 #endif
3022
3023     /* we must initialize the curses data structure only once */
3024     initscr();
3025     bkgdset(BLANK);
3026
3027     /* tests, in general, will want these modes */
3028     start_color();
3029     set_terminal_modes();
3030     def_prog_mode();
3031
3032     /*
3033      * Return to terminal mode, so we're guaranteed of being able to
3034      * select terminal commands even if the capabilities are wrong.
3035      */
3036     endwin();
3037
3038     (void) puts("Welcome to ncurses.  Press ? for help.");
3039
3040     do {
3041         (void) puts("This is the ncurses main menu");
3042         (void) puts("a = keyboard and mouse input test");
3043         (void) puts("b = character attribute test");
3044         (void) puts("c = color test pattern");
3045         (void) puts("d = edit RGB color values");
3046         (void) puts("e = exercise soft keys");
3047         (void) puts("f = display ACS characters");
3048         (void) puts("g = display windows and scrolling");
3049         (void) puts("i = test of flushinp()");
3050         (void) puts("k = display character attributes");
3051 #if USE_LIBMENU
3052         (void) puts("m = menu code test");
3053 #endif
3054 #if USE_LIBPANEL
3055         (void) puts("o = exercise panels library");
3056         (void) puts("p = exercise pad features");
3057         (void) puts("q = quit");
3058 #endif
3059 #if USE_LIBFORM
3060         (void) puts("r = exercise forms code");
3061 #endif
3062         (void) puts("s = overlapping-refresh test");
3063 #if USE_LIBMENU && defined(TRACE)
3064         (void) puts("t = set trace level");
3065 #endif
3066         (void) puts("? = repeat this command summary");
3067
3068         (void) fputs("> ", stdout);
3069         (void) fflush(stdout);          /* necessary under SVr4 curses */
3070
3071         /*
3072          * This used to be an 'fgets()' call.  However (on Linux, at least)
3073          * mixing stream I/O and 'read()' (used in the library) causes the
3074          * input stream to be flushed when switching between the two.
3075          */
3076         command = 0;
3077         for(;;) {
3078                 char ch;
3079                 if (read(fileno(stdin), &ch, 1) <= 0) {
3080                         if (command == 0)
3081                                 command = 'q';
3082                         break;
3083                 } else if (command == 0 && !isspace(ch)) {
3084                         command = ch;
3085                 } else if (ch == '\n' || ch == '\r') {
3086                         if (command != 0)
3087                                 break;
3088                         (void) fputs("> ", stdout);
3089                         (void) fflush(stdout);
3090                 }
3091         }
3092
3093         if (do_single_test(command)) {
3094                 /*
3095                  * This may be overkill; it's intended to reset everything back
3096                  * to the initial terminal modes so that tests don't get in
3097                  * each other's way.
3098                  */
3099                 flushinp();
3100                 set_terminal_modes();
3101                 reset_prog_mode();
3102                 clear();
3103                 refresh();
3104                 endwin();
3105                 if (command == '?') {
3106                         (void) puts("This is the ncurses capability tester.");
3107                         (void) puts("You may select a test from the main menu by typing the");
3108                         (void) puts("key letter of the choice (the letter to left of the =)");
3109                         (void) puts("at the > prompt.  The commands `x' or `q' will exit.");
3110                 }
3111                 continue;
3112         }
3113     } while
3114         (command != 'q');
3115
3116     ExitProgram(EXIT_SUCCESS);
3117 }
3118
3119 /* ncurses.c ends here */