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