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