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