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