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