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