]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/ncurses.c
ncurses 5.6 - patch 20080804
[ncurses.git] / test / ncurses.c
1 /****************************************************************************
2  * Copyright (c) 1998-2007,2008 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.324 2008/08/04 16:27:54 tom Exp $
44
45 ***************************************************************************/
46
47 #include <test.priv.h>
48
49 #ifdef __hpux
50 #undef mvwdelch                 /* HPUX 11.23 macro will not compile */
51 #endif
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 USE_LIBPANEL
63 #include <panel.h>
64 #endif
65
66 #if USE_LIBMENU
67 #include <menu.h>
68 #endif
69
70 #if USE_LIBFORM
71 #include <form.h>
72 #endif
73
74 #ifdef NCURSES_VERSION
75
76 #define NCURSES_CONST_PARAM const void
77
78 #ifdef TRACE
79 static unsigned save_trace = TRACE_ORDINARY | TRACE_ICALLS | TRACE_CALLS;
80 extern unsigned _nc_tracing;
81 #endif
82
83 #else
84
85 #define NCURSES_CONST_PARAM char
86
87 #define mmask_t chtype          /* not specified in XSI */
88
89 #ifndef ACS_S3
90 #ifdef CURSES_ACS_ARRAY
91 #define ACS_S3          (CURSES_ACS_ARRAY['p'])         /* scan line 3 */
92 #define ACS_S7          (CURSES_ACS_ARRAY['r'])         /* scan line 7 */
93 #define ACS_LEQUAL      (CURSES_ACS_ARRAY['y'])         /* less/equal */
94 #define ACS_GEQUAL      (CURSES_ACS_ARRAY['z'])         /* greater/equal */
95 #define ACS_PI          (CURSES_ACS_ARRAY['{'])         /* Pi */
96 #define ACS_NEQUAL      (CURSES_ACS_ARRAY['|'])         /* not equal */
97 #define ACS_STERLING    (CURSES_ACS_ARRAY['}'])         /* UK pound sign */
98 #else
99 #define ACS_S3          (A_ALTCHARSET + 'p')    /* scan line 3 */
100 #define ACS_S7          (A_ALTCHARSET + 'r')    /* scan line 7 */
101 #define ACS_LEQUAL      (A_ALTCHARSET + 'y')    /* less/equal */
102 #define ACS_GEQUAL      (A_ALTCHARSET + 'z')    /* greater/equal */
103 #define ACS_PI          (A_ALTCHARSET + '{')    /* Pi */
104 #define ACS_NEQUAL      (A_ALTCHARSET + '|')    /* not equal */
105 #define ACS_STERLING    (A_ALTCHARSET + '}')    /* UK pound sign */
106 #endif
107 #endif /* ACS_S3 */
108
109 #ifdef CURSES_WACS_ARRAY
110 #define WACS_S3         (&(CURSES_WACS_ARRAY['p']))     /* scan line 3 */
111 #define WACS_S7         (&(CURSES_WACS_ARRAY['r']))     /* scan line 7 */
112 #define WACS_LEQUAL     (&(CURSES_WACS_ARRAY['y']))     /* less/equal */
113 #define WACS_GEQUAL     (&(CURSES_WACS_ARRAY['z']))     /* greater/equal */
114 #define WACS_PI         (&(CURSES_WACS_ARRAY['{']))     /* Pi */
115 #define WACS_NEQUAL     (&(CURSES_WACS_ARRAY['|']))     /* not equal */
116 #define WACS_STERLING   (&(CURSES_WACS_ARRAY['}']))     /* UK pound sign */
117 #endif
118
119 #endif
120
121 #define ToggleAcs(temp,real) temp = ((temp == real) ? 0 : real)
122
123 #define P(string)       printw("%s\n", string)
124
125 #define BLANK           ' '     /* this is the background character */
126
127 #undef max_colors
128 static int max_colors;          /* the actual number of colors we'll use */
129 static int min_colors;          /* the minimum color code */
130 static bool use_colors;         /* true if we use colors */
131
132 #undef max_pairs
133 static int max_pairs;           /* ...and the number of color pairs */
134
135 typedef struct {
136     short red;
137     short green;
138     short blue;
139 } RGB_DATA;
140
141 static RGB_DATA *all_colors;
142
143 static void main_menu(bool);
144
145 /* The behavior of mvhline, mvvline for negative/zero length is unspecified,
146  * though we can rely on negative x/y values to stop the macro.
147  */
148 static void
149 do_h_line(int y, int x, chtype c, int to)
150 {
151     if ((to) > (x))
152         mvhline(y, x, c, (to) - (x));
153 }
154
155 static void
156 do_v_line(int y, int x, chtype c, int to)
157 {
158     if ((to) > (y))
159         mvvline(y, x, c, (to) - (y));
160 }
161
162 static void
163 Repaint(void)
164 {
165     touchwin(stdscr);
166     touchwin(curscr);
167     wrefresh(curscr);
168 }
169
170 static bool
171 isQuit(int c)
172 {
173     return ((c) == QUIT || (c) == ESCAPE);
174 }
175 #define case_QUIT       QUIT: case ESCAPE
176
177 /* Common function to allow ^T to toggle trace-mode in the middle of a test
178  * so that trace-files can be made smaller.
179  */
180 static int
181 wGetchar(WINDOW *win)
182 {
183     int c;
184 #ifdef TRACE
185     while ((c = wgetch(win)) == CTRL('T')) {
186         if (_nc_tracing) {
187             save_trace = _nc_tracing;
188             Trace(("TOGGLE-TRACING OFF"));
189             _nc_tracing = 0;
190         } else {
191             _nc_tracing = save_trace;
192         }
193         trace(_nc_tracing);
194         if (_nc_tracing)
195             Trace(("TOGGLE-TRACING ON"));
196     }
197 #else
198     c = wgetch(win);
199 #endif
200     return c;
201 }
202 #define Getchar() wGetchar(stdscr)
203
204 /* replaces wgetnstr(), since we want to be able to edit values */
205 static void
206 wGetstring(WINDOW *win, char *buffer, int limit)
207 {
208     int y0, x0, x, ch;
209     bool done = FALSE;
210
211     echo();
212     getyx(win, y0, x0);
213     wattrset(win, A_REVERSE);
214
215     x = (int) strlen(buffer);
216     while (!done) {
217         if (x > (int) strlen(buffer))
218             x = (int) strlen(buffer);
219         wmove(win, y0, x0);
220         wprintw(win, "%-*s", limit, buffer);
221         wmove(win, y0, x0 + x);
222         switch (ch = wGetchar(win)) {
223         case '\n':
224         case KEY_ENTER:
225             done = TRUE;
226             break;
227         case CTRL('U'):
228             *buffer = '\0';
229             break;
230         case '\b':
231         case KEY_BACKSPACE:
232         case KEY_DC:
233             if (x > 0) {
234                 int j;
235                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
236                     ;
237                 }
238             } else {
239                 beep();
240             }
241             break;
242         case KEY_LEFT:
243             if (x > 0) {
244                 --x;
245             } else {
246                 flash();
247             }
248             break;
249         case KEY_RIGHT:
250             ++x;
251             break;
252         default:
253             if (!isprint(ch) || ch >= KEY_MIN) {
254                 beep();
255             } else if ((int) strlen(buffer) < limit) {
256                 int j;
257                 for (j = (int) strlen(buffer) + 1; j > x; --j) {
258                     buffer[j] = buffer[j - 1];
259                 }
260                 buffer[x++] = (char) ch;
261             } else {
262                 flash();
263             }
264         }
265     }
266
267     wattroff(win, A_REVERSE);
268     wmove(win, y0, x0);
269     noecho();
270 }
271
272 #if USE_WIDEC_SUPPORT
273 static wchar_t
274 fullwidth_of(int ch)
275 {
276     return (ch + 0xff10 - '0');
277 }
278
279 static void
280 make_fullwidth_text(wchar_t *target, const char *source)
281 {
282     int ch;
283     while ((ch = *source++) != 0) {
284         *target++ = fullwidth_of(ch);
285     }
286     *target = 0;
287 }
288
289 static void
290 make_narrow_text(wchar_t *target, const char *source)
291 {
292     int ch;
293     while ((ch = *source++) != 0) {
294         *target++ = ch;
295     }
296     *target = 0;
297 }
298
299 static void
300 make_fullwidth_digit(cchar_t *target, int digit)
301 {
302     wchar_t source[2];
303
304     source[0] = fullwidth_of(digit + '0');
305     source[1] = 0;
306     setcchar(target, source, A_NORMAL, 0, 0);
307 }
308
309 static int
310 wGet_wchar(WINDOW *win, wint_t *result)
311 {
312     int c;
313 #ifdef TRACE
314     while ((c = wget_wch(win, result)) == CTRL('T')) {
315         if (_nc_tracing) {
316             save_trace = _nc_tracing;
317             Trace(("TOGGLE-TRACING OFF"));
318             _nc_tracing = 0;
319         } else {
320             _nc_tracing = save_trace;
321         }
322         trace(_nc_tracing);
323         if (_nc_tracing)
324             Trace(("TOGGLE-TRACING ON"));
325     }
326 #else
327     c = wget_wch(win, result);
328 #endif
329     return c;
330 }
331 #define Get_wchar(result) wGet_wchar(stdscr, result)
332
333 /* replaces wgetn_wstr(), since we want to be able to edit values */
334 static void
335 wGet_wstring(WINDOW *win, wchar_t *buffer, int limit)
336 {
337     int y0, x0, x;
338     wint_t ch;
339     bool done = FALSE;
340     bool fkey = FALSE;
341
342     echo();
343     getyx(win, y0, x0);
344     wattrset(win, A_REVERSE);
345
346     x = (int) wcslen(buffer);
347     while (!done) {
348         if (x > (int) wcslen(buffer))
349             x = (int) wcslen(buffer);
350
351         /* clear the "window' */
352         wmove(win, y0, x0);
353         wprintw(win, "%*s", limit, " ");
354
355         /* write the existing buffer contents */
356         wmove(win, y0, x0);
357         waddnwstr(win, buffer, limit);
358
359         /* positions the cursor past character 'x' */
360         wmove(win, y0, x0);
361         waddnwstr(win, buffer, x);
362
363         switch (wGet_wchar(win, &ch)) {
364         case KEY_CODE_YES:
365             fkey = TRUE;
366             switch (ch) {
367             case KEY_ENTER:
368                 ch = '\n';
369                 fkey = FALSE;
370                 break;
371             case KEY_BACKSPACE:
372             case KEY_DC:
373                 ch = '\b';
374                 fkey = FALSE;
375                 break;
376             case KEY_LEFT:
377             case KEY_RIGHT:
378                 break;
379             default:
380                 ch = (wint_t) -1;
381                 break;
382             }
383             break;
384         case OK:
385             fkey = FALSE;
386             break;
387         default:
388             ch = (wint_t) -1;
389             fkey = TRUE;
390             break;
391         }
392
393         switch (ch) {
394         case '\n':
395             done = TRUE;
396             break;
397         case CTRL('U'):
398             *buffer = '\0';
399             break;
400         case '\b':
401             if (x > 0) {
402                 int j;
403                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
404                     ;
405                 }
406             } else {
407                 beep();
408             }
409             break;
410         case KEY_LEFT:
411             if (x > 0) {
412                 --x;
413             } else {
414                 beep();
415             }
416             break;
417         case KEY_RIGHT:
418             ++x;
419             break;
420         default:
421             if (fkey) {
422                 beep();
423             } else if ((int) wcslen(buffer) < limit) {
424                 int j;
425                 for (j = (int) wcslen(buffer) + 1; j > x; --j) {
426                     buffer[j] = buffer[j - 1];
427                 }
428                 buffer[x++] = (wchar_t) ch;
429             } else {
430                 beep();
431             }
432         }
433     }
434
435     wattroff(win, A_REVERSE);
436     wmove(win, y0, x0);
437     noecho();
438 }
439
440 #endif
441
442 static void
443 Pause(void)
444 {
445     move(LINES - 1, 0);
446     addstr("Press any key to continue... ");
447     (void) Getchar();
448 }
449
450 static void
451 Cannot(const char *what)
452 {
453     printw("\nThis %s terminal %s\n\n", getenv("TERM"), what);
454     Pause();
455 }
456
457 static void
458 ShellOut(bool message)
459 {
460     if (message)
461         addstr("Shelling out...");
462     def_prog_mode();
463     endwin();
464     system("sh");
465     if (message)
466         addstr("returned from shellout.\n");
467     refresh();
468 }
469
470 #ifdef NCURSES_MOUSE_VERSION
471 /*
472  * This function is the same as _tracemouse(), but we cannot count on that
473  * being available in the non-debug library.
474  */
475 static const char *
476 mouse_decode(MEVENT const *ep)
477 {
478     static char buf[80 + (5 * 10) + (32 * 15)];
479
480     (void) sprintf(buf, "id %2d  at (%2d, %2d, %2d) state %4lx = {",
481                    ep->id, ep->x, ep->y, ep->z, (unsigned long) ep->bstate);
482
483 #define SHOW(m, s) if ((ep->bstate & m)==m) {strcat(buf,s); strcat(buf, ", ");}
484
485     SHOW(BUTTON1_RELEASED, "release-1");
486     SHOW(BUTTON1_PRESSED, "press-1");
487     SHOW(BUTTON1_CLICKED, "click-1");
488     SHOW(BUTTON1_DOUBLE_CLICKED, "doubleclick-1");
489     SHOW(BUTTON1_TRIPLE_CLICKED, "tripleclick-1");
490 #if NCURSES_MOUSE_VERSION == 1
491     SHOW(BUTTON1_RESERVED_EVENT, "reserved-1");
492 #endif
493
494     SHOW(BUTTON2_RELEASED, "release-2");
495     SHOW(BUTTON2_PRESSED, "press-2");
496     SHOW(BUTTON2_CLICKED, "click-2");
497     SHOW(BUTTON2_DOUBLE_CLICKED, "doubleclick-2");
498     SHOW(BUTTON2_TRIPLE_CLICKED, "tripleclick-2");
499 #if NCURSES_MOUSE_VERSION == 1
500     SHOW(BUTTON2_RESERVED_EVENT, "reserved-2");
501 #endif
502
503     SHOW(BUTTON3_RELEASED, "release-3");
504     SHOW(BUTTON3_PRESSED, "press-3");
505     SHOW(BUTTON3_CLICKED, "click-3");
506     SHOW(BUTTON3_DOUBLE_CLICKED, "doubleclick-3");
507     SHOW(BUTTON3_TRIPLE_CLICKED, "tripleclick-3");
508 #if NCURSES_MOUSE_VERSION == 1
509     SHOW(BUTTON3_RESERVED_EVENT, "reserved-3");
510 #endif
511
512     SHOW(BUTTON4_RELEASED, "release-4");
513     SHOW(BUTTON4_PRESSED, "press-4");
514     SHOW(BUTTON4_CLICKED, "click-4");
515     SHOW(BUTTON4_DOUBLE_CLICKED, "doubleclick-4");
516     SHOW(BUTTON4_TRIPLE_CLICKED, "tripleclick-4");
517 #if NCURSES_MOUSE_VERSION == 1
518     SHOW(BUTTON4_RESERVED_EVENT, "reserved-4");
519 #endif
520
521 #if NCURSES_MOUSE_VERSION == 2
522     SHOW(BUTTON5_RELEASED, "release-5");
523     SHOW(BUTTON5_PRESSED, "press-5");
524     SHOW(BUTTON5_CLICKED, "click-5");
525     SHOW(BUTTON5_DOUBLE_CLICKED, "doubleclick-5");
526     SHOW(BUTTON5_TRIPLE_CLICKED, "tripleclick-5");
527 #endif
528
529     SHOW(BUTTON_CTRL, "ctrl");
530     SHOW(BUTTON_SHIFT, "shift");
531     SHOW(BUTTON_ALT, "alt");
532     SHOW(ALL_MOUSE_EVENTS, "all-events");
533     SHOW(REPORT_MOUSE_POSITION, "position");
534
535 #undef SHOW
536
537     if (buf[strlen(buf) - 1] == ' ')
538         buf[strlen(buf) - 2] = '\0';
539     (void) strcat(buf, "}");
540     return (buf);
541 }
542 #endif /* NCURSES_MOUSE_VERSION */
543
544 /****************************************************************************
545  *
546  * Character input test
547  *
548  ****************************************************************************/
549
550 static void
551 setup_getch(WINDOW *win, bool flags[])
552 {
553     keypad(win, flags['k']);    /* should be redundant, but for testing */
554     meta(win, flags['m']);      /* force this to a known state */
555     if (flags['e'])
556         echo();
557     else
558         noecho();
559 }
560
561 static void
562 wgetch_help(WINDOW *win, bool flags[])
563 {
564     static const char *help[] =
565     {
566         "e  -- toggle echo mode"
567         ,"g  -- triggers a getstr test"
568         ,"k  -- toggle keypad/literal mode"
569         ,"m  -- toggle meta (7-bit/8-bit) mode"
570         ,"^q -- quit"
571         ,"s  -- shell out\n"
572         ,"w  -- create a new window"
573 #ifdef SIGTSTP
574         ,"z  -- suspend this process"
575 #endif
576     };
577     int y, x;
578     unsigned chk = ((SIZEOF(help) + 1) / 2);
579     unsigned n;
580
581     getyx(win, y, x);
582     move(0, 0);
583     printw("Type any key to see its %s value.  Also:\n",
584            flags['k'] ? "keypad" : "literal");
585     for (n = 0; n < SIZEOF(help); ++n) {
586         int row = 1 + (int) (n % chk);
587         int col = (n >= chk) ? COLS / 2 : 0;
588         int flg = ((strstr(help[n], "toggle") != 0)
589                    && (flags[UChar(*help[n])] != FALSE));
590         if (flg)
591             standout();
592         mvprintw(row, col, "%s", help[n]);
593         if (col == 0)
594             clrtoeol();
595         if (flg)
596             standend();
597     }
598     wrefresh(stdscr);
599     wmove(win, y, x);
600 }
601
602 static void
603 wgetch_wrap(WINDOW *win, int first_y)
604 {
605     int last_y = getmaxy(win) - 1;
606     int y = getcury(win) + 1;
607
608     if (y >= last_y)
609         y = first_y;
610     wmove(win, y, 0);
611     wclrtoeol(win);
612 }
613
614 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
615 typedef struct {
616     WINDOW *text;
617     WINDOW *frame;
618 } WINSTACK;
619
620 static WINSTACK *winstack = 0;
621 static unsigned len_winstack = 0;
622
623 static void
624 forget_boxes(void)
625 {
626     if (winstack != 0) {
627         free(winstack);
628     }
629     winstack = 0;
630     len_winstack = 0;
631 }
632
633 static void
634 remember_boxes(unsigned level, WINDOW *txt_win, WINDOW *box_win)
635 {
636     unsigned need = (level + 1) * 2;
637
638     assert(level < COLS);
639
640     if (winstack == 0) {
641         len_winstack = 20;
642         winstack = typeMalloc(WINSTACK, len_winstack);
643     } else if (need >= len_winstack) {
644         len_winstack = need;
645         winstack = typeRealloc(WINSTACK, len_winstack, winstack);
646     }
647     winstack[level].text = txt_win;
648     winstack[level].frame = box_win;
649 }
650
651 #if USE_SOFTKEYS && (NCURSES_VERSION_PATCH < 20071229) && NCURSES_EXT_FUNCS
652 static void
653 slk_repaint(void)
654 {
655     /* this chunk is now done in resize_term() */
656     slk_touch();
657     slk_clear();
658     slk_noutrefresh();
659 }
660
661 #else
662 #define slk_repaint()           /* nothing */
663 #endif
664
665 /*
666  * For wgetch_test(), we create pairs of windows - one for a box, one for text.
667  * Resize both and paint the box in the parent.
668  */
669 static void
670 resize_boxes(unsigned level, WINDOW *win)
671 {
672     unsigned n;
673     int base = 5;
674     int high = LINES - base;
675     int wide = COLS;
676
677     touchwin(stdscr);
678     wnoutrefresh(stdscr);
679
680     slk_repaint();
681
682     for (n = 0; n < level; ++n) {
683         wresize(winstack[n].frame, high, wide);
684         wresize(winstack[n].text, high - 2, wide - 2);
685         high -= 2;
686         wide -= 2;
687         werase(winstack[n].text);
688         box(winstack[n].frame, 0, 0);
689         wnoutrefresh(winstack[n].frame);
690         wprintw(winstack[n].text,
691                 "size %dx%d\n",
692                 getmaxy(winstack[n].text),
693                 getmaxx(winstack[n].text));
694         wnoutrefresh(winstack[n].text);
695         if (winstack[n].text == win)
696             break;
697     }
698     doupdate();
699 }
700 #else
701 #define forget_boxes()          /* nothing */
702 #define remember_boxes(level,text,frame)        /* nothing */
703 #endif
704
705 static void
706 wgetch_test(unsigned level, WINDOW *win, int delay)
707 {
708     char buf[BUFSIZ];
709     int first_y, first_x;
710     int c;
711     int incount = 0;
712     bool flags[256];
713     bool blocking = (delay < 0);
714
715     memset(flags, FALSE, sizeof(flags));
716     flags[UChar('k')] = (win == stdscr);
717
718     setup_getch(win, flags);
719     wtimeout(win, delay);
720     getyx(win, first_y, first_x);
721
722     wgetch_help(win, flags);
723     wsetscrreg(win, first_y, getmaxy(win) - 1);
724     scrollok(win, TRUE);
725
726     for (;;) {
727         while ((c = wGetchar(win)) == ERR) {
728             incount++;
729             if (blocking) {
730                 (void) wprintw(win, "%05d: input error", incount);
731                 break;
732             } else {
733                 (void) wprintw(win, "%05d: input timed out", incount);
734             }
735             wgetch_wrap(win, first_y);
736         }
737         if (c == ERR && blocking) {
738             wprintw(win, "ERR");
739             wgetch_wrap(win, first_y);
740         } else if (isQuit(c)) {
741             break;
742         } else if (c == 'e') {
743             flags[UChar('e')] = !flags[UChar('e')];
744             setup_getch(win, flags);
745             wgetch_help(win, flags);
746         } else if (c == 'g') {
747             waddstr(win, "getstr test: ");
748             echo();
749             wgetnstr(win, buf, sizeof(buf) - 1);
750             noecho();
751             wprintw(win, "I saw %d characters:\n\t`%s'.", (int) strlen(buf), buf);
752             wclrtoeol(win);
753             wgetch_wrap(win, first_y);
754         } else if (c == 'k') {
755             flags[UChar('k')] = !flags[UChar('k')];
756             setup_getch(win, flags);
757             wgetch_help(win, flags);
758         } else if (c == 'm') {
759             flags[UChar('m')] = !flags[UChar('m')];
760             setup_getch(win, flags);
761             wgetch_help(win, flags);
762         } else if (c == 's') {
763             ShellOut(TRUE);
764         } else if (c == 'w') {
765             int high = getmaxy(win) - 1 - first_y + 1;
766             int wide = getmaxx(win) - first_x;
767             int old_y, old_x;
768             int new_y = first_y + getbegy(win);
769             int new_x = first_x + getbegx(win);
770
771             getyx(win, old_y, old_x);
772             if (high > 2 && wide > 2) {
773                 WINDOW *wb = newwin(high, wide, new_y, new_x);
774                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
775
776                 box(wb, 0, 0);
777                 wrefresh(wb);
778                 wmove(wi, 0, 0);
779                 remember_boxes(level, wi, wb);
780                 wgetch_test(level + 1, wi, delay);
781                 delwin(wi);
782                 delwin(wb);
783
784                 wgetch_help(win, flags);
785                 wmove(win, old_y, old_x);
786                 touchwin(win);
787                 wrefresh(win);
788                 doupdate();
789             }
790 #ifdef SIGTSTP
791         } else if (c == 'z') {
792             kill(getpid(), SIGTSTP);
793 #endif
794         } else {
795             wprintw(win, "Key pressed: %04o ", c);
796 #ifdef NCURSES_MOUSE_VERSION
797             if (c == KEY_MOUSE) {
798                 int y, x;
799                 MEVENT event;
800
801                 getmouse(&event);
802                 wprintw(win, "KEY_MOUSE, %s", mouse_decode(&event));
803                 getyx(win, y, x);
804                 move(event.y, event.x);
805                 addch('*');
806                 wmove(win, y, x);
807             } else
808 #endif /* NCURSES_MOUSE_VERSION */
809             if (c >= KEY_MIN) {
810 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
811                 if (c == KEY_RESIZE) {
812                     resize_boxes(level, win);
813                 }
814 #endif
815                 (void) waddstr(win, keyname(c));
816             } else if (c > 0x80) {
817                 unsigned c2 = (unsigned) (c & 0x7f);
818                 if (isprint(c2))
819                     (void) wprintw(win, "M-%c", UChar(c2));
820                 else
821                     (void) wprintw(win, "M-%s", unctrl(c2));
822                 waddstr(win, " (high-half character)");
823             } else {
824                 if (isprint(c))
825                     (void) wprintw(win, "%c (ASCII printable character)", c);
826                 else
827                     (void) wprintw(win, "%s (ASCII control character)",
828                                    unctrl(UChar(c)));
829             }
830             wgetch_wrap(win, first_y);
831         }
832     }
833
834     wtimeout(win, -1);
835 }
836
837 static int
838 begin_getch_test(void)
839 {
840     char buf[BUFSIZ];
841     int delay;
842
843     refresh();
844
845 #ifdef NCURSES_MOUSE_VERSION
846     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
847 #endif
848
849     (void) printw("Delay in 10ths of a second (<CR> for blocking input)? ");
850     echo();
851     getnstr(buf, sizeof(buf) - 1);
852     noecho();
853     nonl();
854
855     if (isdigit(UChar(buf[0]))) {
856         delay = atoi(buf) * 100;
857     } else {
858         delay = -1;
859     }
860     raw();
861     move(5, 0);
862     return delay;
863 }
864
865 static void
866 finish_getch_test(void)
867 {
868 #ifdef NCURSES_MOUSE_VERSION
869     mousemask(0, (mmask_t *) 0);
870 #endif
871     erase();
872     noraw();
873     nl();
874     endwin();
875 }
876
877 static void
878 getch_test(void)
879 {
880     int delay = begin_getch_test();
881
882     slk_restore();
883     wgetch_test(0, stdscr, delay);
884     forget_boxes();
885     finish_getch_test();
886 }
887
888 #if USE_WIDEC_SUPPORT
889 /*
890  * For wget_wch_test(), we create pairs of windows - one for a box, one for text.
891  * Resize both and paint the box in the parent.
892  */
893 #if defined(KEY_RESIZE) && HAVE_WRESIZE
894 static void
895 resize_wide_boxes(unsigned level, WINDOW *win)
896 {
897     unsigned n;
898     int base = 5;
899     int high = LINES - base;
900     int wide = COLS;
901
902     touchwin(stdscr);
903     wnoutrefresh(stdscr);
904
905     slk_repaint();
906
907     for (n = 0; n < level; ++n) {
908         wresize(winstack[n].frame, high, wide);
909         wresize(winstack[n].text, high - 2, wide - 2);
910         high -= 2;
911         wide -= 2;
912         werase(winstack[n].text);
913         box_set(winstack[n].frame, 0, 0);
914         wnoutrefresh(winstack[n].frame);
915         wprintw(winstack[n].text,
916                 "size %dx%d\n",
917                 getmaxy(winstack[n].text),
918                 getmaxx(winstack[n].text));
919         wnoutrefresh(winstack[n].text);
920         if (winstack[n].text == win)
921             break;
922     }
923     doupdate();
924 }
925 #endif /* KEY_RESIZE */
926
927 static char *
928 wcstos(const wchar_t *src)
929 {
930     int need;
931     mbstate_t state;
932     char *result = 0;
933     const wchar_t *tmp = src;
934
935     memset(&state, 0, sizeof(state));
936     if ((need = (int) wcsrtombs(0, &tmp, 0, &state)) > 0) {
937         unsigned have = (unsigned) need;
938         if ((result = typeCalloc(char, have + 1)) != 0) {
939             tmp = src;
940             if (wcsrtombs(result, &tmp, have, &state) != have) {
941                 free(result);
942                 result = 0;
943             }
944         }
945     }
946     return result;
947 }
948
949 static void
950 wget_wch_test(unsigned level, WINDOW *win, int delay)
951 {
952     wchar_t wchar_buf[BUFSIZ];
953     wint_t wint_buf[BUFSIZ];
954     int first_y, first_x;
955     wint_t c;
956     int incount = 0;
957     bool flags[256];
958     bool blocking = (delay < 0);
959     int y, x, code;
960     char *temp;
961
962     memset(flags, FALSE, sizeof(flags));
963     flags[UChar('k')] = (win == stdscr);
964
965     setup_getch(win, flags);
966     wtimeout(win, delay);
967     getyx(win, first_y, first_x);
968
969     wgetch_help(win, flags);
970     wsetscrreg(win, first_y, getmaxy(win) - 1);
971     scrollok(win, TRUE);
972
973     for (;;) {
974         while ((code = wGet_wchar(win, &c)) == ERR) {
975             incount++;
976             if (blocking) {
977                 (void) wprintw(win, "%05d: input error", incount);
978                 break;
979             } else {
980                 (void) wprintw(win, "%05d: input timed out", incount);
981             }
982             wgetch_wrap(win, first_y);
983         }
984         if (code == ERR && blocking) {
985             wprintw(win, "ERR");
986             wgetch_wrap(win, first_y);
987         } else if (isQuit((int) c)) {
988             break;
989         } else if (c == 'e') {
990             flags[UChar('e')] = !flags[UChar('e')];
991             setup_getch(win, flags);
992             wgetch_help(win, flags);
993         } else if (c == 'g') {
994             waddstr(win, "getstr test: ");
995             echo();
996             code = wgetn_wstr(win, wint_buf, sizeof(wint_buf) - 1);
997             noecho();
998             if (code == ERR) {
999                 wprintw(win, "wgetn_wstr returns an error.");
1000             } else {
1001                 int n;
1002                 for (n = 0; (wchar_buf[n] = (wchar_t) wint_buf[n]) != 0; ++n) {
1003                     ;
1004                 }
1005                 if ((temp = wcstos(wchar_buf)) != 0) {
1006                     wprintw(win, "I saw %d characters:\n\t`%s'.",
1007                             (int) wcslen(wchar_buf), temp);
1008                     free(temp);
1009                 } else {
1010                     wprintw(win, "I saw %d characters (cannot convert).",
1011                             (int) wcslen(wchar_buf));
1012                 }
1013             }
1014             wclrtoeol(win);
1015             wgetch_wrap(win, first_y);
1016         } else if (c == 'k') {
1017             flags[UChar('k')] = !flags[UChar('k')];
1018             setup_getch(win, flags);
1019             wgetch_help(win, flags);
1020         } else if (c == 'm') {
1021             flags[UChar('m')] = !flags[UChar('m')];
1022             setup_getch(win, flags);
1023             wgetch_help(win, flags);
1024         } else if (c == 's') {
1025             ShellOut(TRUE);
1026         } else if (c == 'w') {
1027             int high = getmaxy(win) - 1 - first_y + 1;
1028             int wide = getmaxx(win) - first_x;
1029             int old_y, old_x;
1030             int new_y = first_y + getbegy(win);
1031             int new_x = first_x + getbegx(win);
1032
1033             getyx(win, old_y, old_x);
1034             if (high > 2 && wide > 2) {
1035                 WINDOW *wb = newwin(high, wide, new_y, new_x);
1036                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
1037
1038                 box_set(wb, 0, 0);
1039                 wrefresh(wb);
1040                 wmove(wi, 0, 0);
1041                 remember_boxes(level, wi, wb);
1042                 wget_wch_test(level + 1, wi, delay);
1043                 delwin(wi);
1044                 delwin(wb);
1045
1046                 wgetch_help(win, flags);
1047                 wmove(win, old_y, old_x);
1048                 touchwin(win);
1049                 wrefresh(win);
1050             }
1051 #ifdef SIGTSTP
1052         } else if (c == 'z') {
1053             kill(getpid(), SIGTSTP);
1054 #endif
1055         } else {
1056             wprintw(win, "Key pressed: %04o ", (int) c);
1057 #ifdef NCURSES_MOUSE_VERSION
1058             if (c == KEY_MOUSE) {
1059                 MEVENT event;
1060
1061                 getmouse(&event);
1062                 wprintw(win, "KEY_MOUSE, %s", mouse_decode(&event));
1063                 getyx(win, y, x);
1064                 move(event.y, event.x);
1065                 addch('*');
1066                 wmove(win, y, x);
1067             } else
1068 #endif /* NCURSES_MOUSE_VERSION */
1069             if (code == KEY_CODE_YES) {
1070 #if defined(KEY_RESIZE) && HAVE_WRESIZE
1071                 if (c == KEY_RESIZE) {
1072                     resize_wide_boxes(level, win);
1073                 }
1074 #endif
1075                 (void) waddstr(win, key_name((wchar_t) c));
1076             } else {
1077                 if (c < 256 && iscntrl(c)) {
1078                     (void) wprintw(win, "%s (control character)", unctrl(c));
1079                 } else {
1080                     wchar_t c2 = (wchar_t) c;
1081                     waddnwstr(win, &c2, 1);
1082                     (void) wprintw(win, " = %#x (printable character)", c);
1083                 }
1084             }
1085             wgetch_wrap(win, first_y);
1086         }
1087     }
1088
1089     wtimeout(win, -1);
1090 }
1091
1092 static void
1093 get_wch_test(void)
1094 {
1095     int delay = begin_getch_test();
1096
1097     slk_restore();
1098     wget_wch_test(0, stdscr, delay);
1099     forget_boxes();
1100     finish_getch_test();
1101 }
1102 #endif
1103
1104 /****************************************************************************
1105  *
1106  * Character attributes test
1107  *
1108  ****************************************************************************/
1109
1110 #if HAVE_SETUPTERM || HAVE_TGETENT
1111 #define get_ncv() TIGETNUM("ncv","NC")
1112 #define get_xmc() TIGETNUM("xmc","sg")
1113 #else
1114 #define get_ncv() -1
1115 #define get_xmc() -1
1116 #endif
1117
1118 #if !HAVE_TERMATTRS
1119 static chtype
1120 my_termattrs(void)
1121 {
1122     static int first = TRUE;
1123     static chtype result = 0;
1124
1125     if (first) {
1126 #if !HAVE_TIGETSTR
1127         char buffer[4096];
1128         char parsed[4096];
1129         char *area_pointer = parsed;
1130
1131         tgetent(buffer, getenv("TERM"));
1132 #endif
1133
1134         if (TIGETSTR("smso", "so"))
1135             result |= A_STANDOUT;
1136         if (TIGETSTR("smul", "us"))
1137             result |= A_UNDERLINE;
1138         if (TIGETSTR("rev", "mr"))
1139             result |= A_REVERSE;
1140         if (TIGETSTR("blink", "mb"))
1141             result |= A_BLINK;
1142         if (TIGETSTR("dim", "mh"))
1143             result |= A_DIM;
1144         if (TIGETSTR("bold", "md"))
1145             result |= A_BOLD;
1146         if (TIGETSTR("smacs", "ac"))
1147             result |= A_ALTCHARSET;
1148
1149         first = FALSE;
1150     }
1151     return result;
1152 }
1153 #define termattrs() my_termattrs()
1154 #endif
1155
1156 #define MAX_ATTRSTRING 31
1157 #define LEN_ATTRSTRING 26
1158
1159 static char attr_test_string[MAX_ATTRSTRING + 1];
1160
1161 static void
1162 attr_legend(WINDOW *helpwin)
1163 {
1164     int row = 1;
1165     int col = 1;
1166
1167     mvwprintw(helpwin, row++, col,
1168               "ESC to exit.");
1169     mvwprintw(helpwin, row++, col,
1170               "^L repaints.");
1171     ++row;
1172     mvwprintw(helpwin, row++, col,
1173               "Modify the test strings:");
1174     mvwprintw(helpwin, row++, col,
1175               "  A digit sets gaps on each side of displayed attributes");
1176     mvwprintw(helpwin, row++, col,
1177               "  </> shifts the text left/right. ");
1178     ++row;
1179     mvwprintw(helpwin, row++, col,
1180               "Toggles:");
1181     if (use_colors) {
1182         mvwprintw(helpwin, row++, col,
1183                   "  f/F/b/F toggle foreground/background background color");
1184         mvwprintw(helpwin, row++, col,
1185                   "  t/T     toggle text/background color attribute");
1186     }
1187     mvwprintw(helpwin, row++, col,
1188               "  a/A     toggle ACS (alternate character set) mapping");
1189     mvwprintw(helpwin, row++, col,
1190               "  v/V     toggle video attribute to combine with each line");
1191 }
1192
1193 static void
1194 show_color_attr(int fg, int bg, int tx)
1195 {
1196     if (use_colors) {
1197         printw("  Colors (fg %d, bg %d", fg, bg);
1198         if (tx >= 0)
1199             printw(", text %d", tx);
1200         printw("),");
1201     }
1202 }
1203
1204 static bool
1205 cycle_color_attr(int ch, short *fg, short *bg, short *tx)
1206 {
1207     bool error = FALSE;
1208
1209     if (use_colors) {
1210         switch (ch) {
1211         case 'f':
1212             *fg = (short) (*fg + 1);
1213             break;
1214         case 'F':
1215             *fg = (short) (*fg - 1);
1216             break;
1217         case 'b':
1218             *bg = (short) (*bg + 1);
1219             break;
1220         case 'B':
1221             *bg = (short) (*bg - 1);
1222             break;
1223         case 't':
1224             *tx = (short) (*tx + 1);
1225             break;
1226         case 'T':
1227             *tx = (short) (*tx - 1);
1228             break;
1229         default:
1230             beep();
1231             error = TRUE;
1232             break;
1233         }
1234         if (*fg >= COLORS)
1235             *fg = (short) min_colors;
1236         if (*fg < min_colors)
1237             *fg = (short) (COLORS - 1);
1238         if (*bg >= COLORS)
1239             *bg = (short) min_colors;
1240         if (*bg < min_colors)
1241             *bg = (short) (COLORS - 1);
1242         if (*tx >= COLORS)
1243             *tx = -1;
1244         if (*tx < -1)
1245             *tx = (short) (COLORS - 1);
1246     } else {
1247         beep();
1248         error = TRUE;
1249     }
1250     return error;
1251 }
1252
1253 static void
1254 adjust_attr_string(int adjust)
1255 {
1256     int first = ((int) UChar(attr_test_string[0])) + adjust;
1257     int last = first + LEN_ATTRSTRING;
1258
1259     if (first >= ' ' && last <= '~') {  /* 32..126 */
1260         int j, k;
1261         for (j = 0, k = first; j < MAX_ATTRSTRING && k <= last; ++j, ++k) {
1262             attr_test_string[j] = (char) k;
1263             if (((k + 1 - first) % 5) == 0) {
1264                 if (++j >= MAX_ATTRSTRING)
1265                     break;
1266                 attr_test_string[j] = ' ';
1267             }
1268         }
1269         while (j < MAX_ATTRSTRING)
1270             attr_test_string[j++] = ' ';
1271         attr_test_string[j] = '\0';
1272     } else {
1273         beep();
1274     }
1275 }
1276
1277 static void
1278 init_attr_string(void)
1279 {
1280     attr_test_string[0] = 'a';
1281     adjust_attr_string(0);
1282 }
1283
1284 static int
1285 show_attr(int row, int skip, bool arrow, chtype attr, const char *name)
1286 {
1287     int ncv = get_ncv();
1288     chtype test = attr & (chtype) (~A_ALTCHARSET);
1289
1290     if (arrow)
1291         mvprintw(row, 5, "-->");
1292     mvprintw(row, 8, "%s mode:", name);
1293     mvprintw(row, 24, "|");
1294     if (skip)
1295         printw("%*s", skip, " ");
1296     /*
1297      * Just for testing, write text using the alternate character set one
1298      * character at a time (to pass its rendition directly), and use the
1299      * string operation for the other attributes.
1300      */
1301     if (attr & A_ALTCHARSET) {
1302         const char *s;
1303         chtype ch;
1304
1305         for (s = attr_test_string; *s != '\0'; ++s) {
1306             ch = UChar(*s);
1307             addch(ch | attr);
1308         }
1309     } else {
1310         attrset(attr);
1311         addstr(attr_test_string);
1312         attroff(attr);
1313     }
1314     if (skip)
1315         printw("%*s", skip, " ");
1316     printw("|");
1317     if (test != A_NORMAL) {
1318         if (!(termattrs() & test)) {
1319             printw(" (N/A)");
1320         } else {
1321             if (ncv > 0 && (getbkgd(stdscr) & A_COLOR)) {
1322                 static const chtype table[] =
1323                 {
1324                     A_STANDOUT,
1325                     A_UNDERLINE,
1326                     A_REVERSE,
1327                     A_BLINK,
1328                     A_DIM,
1329                     A_BOLD,
1330 #ifdef A_INVIS
1331                     A_INVIS,
1332 #endif
1333                     A_PROTECT,
1334                     A_ALTCHARSET
1335                 };
1336                 unsigned n;
1337                 bool found = FALSE;
1338                 for (n = 0; n < SIZEOF(table); n++) {
1339                     if ((table[n] & attr) != 0
1340                         && ((1 << n) & ncv) != 0) {
1341                         found = TRUE;
1342                         break;
1343                     }
1344                 }
1345                 if (found)
1346                     printw(" (NCV)");
1347             }
1348             if ((termattrs() & test) != test)
1349                 printw(" (Part)");
1350         }
1351     }
1352     return row + 2;
1353 }
1354 /* *INDENT-OFF* */
1355 static const struct {
1356     chtype                      attr;
1357     NCURSES_CONST char *        name;
1358 } attrs_to_test[] = {
1359     { A_STANDOUT,       "STANDOUT" },
1360     { A_REVERSE,        "REVERSE" },
1361     { A_BOLD,           "BOLD" },
1362     { A_UNDERLINE,      "UNDERLINE" },
1363     { A_DIM,            "DIM" },
1364     { A_BLINK,          "BLINK" },
1365     { A_PROTECT,        "PROTECT" },
1366 #ifdef A_INVIS
1367     { A_INVIS,          "INVISIBLE" },
1368 #endif
1369     { A_NORMAL,         "NORMAL" },
1370 };
1371 /* *INDENT-ON* */
1372
1373 static bool
1374 attr_getc(int *skip, short *fg, short *bg, short *tx, int *ac, unsigned *kc)
1375 {
1376     bool result = TRUE;
1377     bool error = FALSE;
1378     WINDOW *helpwin;
1379
1380     do {
1381         int ch = Getchar();
1382
1383         error = FALSE;
1384         if (ch < 256 && isdigit(ch)) {
1385             *skip = (ch - '0');
1386         } else {
1387             switch (ch) {
1388             case CTRL('L'):
1389                 Repaint();
1390                 break;
1391             case '?':
1392                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1393                     box(helpwin, 0, 0);
1394                     attr_legend(helpwin);
1395                     wGetchar(helpwin);
1396                     delwin(helpwin);
1397                 }
1398                 break;
1399             case 'a':
1400                 *ac = 0;
1401                 break;
1402             case 'A':
1403                 *ac = A_ALTCHARSET;
1404                 break;
1405             case 'v':
1406                 if (*kc == 0)
1407                     *kc = SIZEOF(attrs_to_test) - 1;
1408                 else
1409                     *kc -= 1;
1410                 break;
1411             case 'V':
1412                 *kc += 1;
1413                 if (*kc >= SIZEOF(attrs_to_test))
1414                     *kc = 0;
1415                 break;
1416             case '<':
1417                 adjust_attr_string(-1);
1418                 break;
1419             case '>':
1420                 adjust_attr_string(1);
1421                 break;
1422             case case_QUIT:
1423                 result = FALSE;
1424                 break;
1425             default:
1426                 error = cycle_color_attr(ch, fg, bg, tx);
1427                 break;
1428             }
1429         }
1430     } while (error);
1431     return result;
1432 }
1433
1434 static void
1435 attr_test(void)
1436 /* test text attributes */
1437 {
1438     int n;
1439     int skip = get_xmc();
1440     short fg = COLOR_BLACK;     /* color pair 0 is special */
1441     short bg = COLOR_BLACK;
1442     short tx = -1;
1443     int ac = 0;
1444     unsigned j, k;
1445
1446     if (skip < 0)
1447         skip = 0;
1448
1449     n = skip;                   /* make it easy */
1450     k = SIZEOF(attrs_to_test) - 1;
1451     init_attr_string();
1452
1453     do {
1454         int row = 2;
1455         chtype normal = A_NORMAL | BLANK;
1456         chtype extras = (chtype) ac;
1457
1458         if (use_colors) {
1459             short pair = (short) (fg != COLOR_BLACK || bg != COLOR_BLACK);
1460             if (pair != 0) {
1461                 pair = 1;
1462                 if (init_pair(pair, fg, bg) == ERR) {
1463                     beep();
1464                 } else {
1465                     normal |= COLOR_PAIR(pair);
1466                 }
1467             }
1468             if (tx >= 0) {
1469                 pair = 2;
1470                 if (init_pair(pair, tx, bg) == ERR) {
1471                     beep();
1472                 } else {
1473                     extras |= COLOR_PAIR(pair);
1474                 }
1475             }
1476         }
1477         bkgd(normal);
1478         bkgdset(normal);
1479         erase();
1480
1481         box(stdscr, 0, 0);
1482         mvaddstr(0, 20, "Character attribute test display");
1483
1484         for (j = 0; j < SIZEOF(attrs_to_test); ++j) {
1485             bool arrow = (j == k);
1486             row = show_attr(row, n, arrow,
1487                             extras |
1488                             attrs_to_test[j].attr |
1489                             attrs_to_test[k].attr,
1490                             attrs_to_test[j].name);
1491         }
1492
1493         mvprintw(row, 8,
1494                  "This terminal does %shave the magic-cookie glitch",
1495                  get_xmc() > -1 ? "" : "not ");
1496         mvprintw(row + 1, 8, "Enter '?' for help.");
1497         show_color_attr(fg, bg, tx);
1498         printw("  ACS (%d)", ac != 0);
1499
1500         refresh();
1501     } while (attr_getc(&n, &fg, &bg, &tx, &ac, &k));
1502
1503     bkgdset(A_NORMAL | BLANK);
1504     erase();
1505     endwin();
1506 }
1507
1508 #if USE_WIDEC_SUPPORT
1509 static wchar_t wide_attr_test_string[MAX_ATTRSTRING + 1];
1510
1511 static void
1512 wide_adjust_attr_string(int adjust)
1513 {
1514     int first = ((int) UChar(wide_attr_test_string[0])) + adjust;
1515     int last = first + LEN_ATTRSTRING;
1516
1517     if (first >= ' ' && last <= '~') {  /* 32..126 */
1518         int j, k;
1519         for (j = 0, k = first; j < MAX_ATTRSTRING && k <= last; ++j, ++k) {
1520             wide_attr_test_string[j] = k;
1521             if (((k + 1 - first) % 5) == 0) {
1522                 if (++j >= MAX_ATTRSTRING)
1523                     break;
1524                 wide_attr_test_string[j] = ' ';
1525             }
1526         }
1527         while (j < MAX_ATTRSTRING)
1528             wide_attr_test_string[j++] = ' ';
1529         wide_attr_test_string[j] = '\0';
1530     } else {
1531         beep();
1532     }
1533 }
1534
1535 static void
1536 wide_init_attr_string(void)
1537 {
1538     wide_attr_test_string[0] = 'a';
1539     wide_adjust_attr_string(0);
1540 }
1541
1542 static void
1543 set_wide_background(short pair)
1544 {
1545     cchar_t normal;
1546     wchar_t blank[2];
1547
1548     blank[0] = ' ';
1549     blank[1] = 0;
1550     setcchar(&normal, blank, A_NORMAL, pair, 0);
1551     bkgrnd(&normal);
1552     bkgrndset(&normal);
1553 }
1554
1555 static attr_t
1556 get_wide_background(void)
1557 {
1558     attr_t result = A_NORMAL;
1559     attr_t attr;
1560     cchar_t ch;
1561     short pair;
1562     wchar_t wch[10];
1563
1564     if (getbkgrnd(&ch) != ERR) {
1565         if (getcchar(&ch, wch, &attr, &pair, 0) != ERR) {
1566             result = attr;
1567         }
1568     }
1569     return result;
1570 }
1571
1572 static int
1573 wide_show_attr(int row, int skip, bool arrow, chtype attr, short pair, const char *name)
1574 {
1575     int ncv = get_ncv();
1576     chtype test = attr & ~WA_ALTCHARSET;
1577
1578     if (arrow)
1579         mvprintw(row, 5, "-->");
1580     mvprintw(row, 8, "%s mode:", name);
1581     mvprintw(row, 24, "|");
1582     if (skip)
1583         printw("%*s", skip, " ");
1584
1585     /*
1586      * Just for testing, write text using the alternate character set one
1587      * character at a time (to pass its rendition directly), and use the
1588      * string operation for the other attributes.
1589      */
1590     if (attr & WA_ALTCHARSET) {
1591         const wchar_t *s;
1592         cchar_t ch;
1593
1594         for (s = wide_attr_test_string; *s != L'\0'; ++s) {
1595             wchar_t fill[2];
1596             fill[0] = *s;
1597             fill[1] = L'\0';
1598             setcchar(&ch, fill, attr, pair, 0);
1599             add_wch(&ch);
1600         }
1601     } else {
1602         attr_t old_attr;
1603         short old_pair;
1604
1605         attr_get(&old_attr, &old_pair, 0);
1606         attr_set(attr, pair, 0);
1607         addwstr(wide_attr_test_string);
1608         attr_set(old_attr, old_pair, 0);
1609     }
1610     if (skip)
1611         printw("%*s", skip, " ");
1612     printw("|");
1613     if (test != A_NORMAL) {
1614         if (!(term_attrs() & test)) {
1615             printw(" (N/A)");
1616         } else {
1617             if (ncv > 0 && (get_wide_background() & A_COLOR)) {
1618                 static const attr_t table[] =
1619                 {
1620                     WA_STANDOUT,
1621                     WA_UNDERLINE,
1622                     WA_REVERSE,
1623                     WA_BLINK,
1624                     WA_DIM,
1625                     WA_BOLD,
1626                     WA_INVIS,
1627                     WA_PROTECT,
1628                     WA_ALTCHARSET
1629                 };
1630                 unsigned n;
1631                 bool found = FALSE;
1632                 for (n = 0; n < SIZEOF(table); n++) {
1633                     if ((table[n] & attr) != 0
1634                         && ((1 << n) & ncv) != 0) {
1635                         found = TRUE;
1636                         break;
1637                     }
1638                 }
1639                 if (found)
1640                     printw(" (NCV)");
1641             }
1642             if ((term_attrs() & test) != test)
1643                 printw(" (Part)");
1644         }
1645     }
1646     return row + 2;
1647 }
1648
1649 static bool
1650 wide_attr_getc(int *skip, short *fg, short *bg, short *tx, int *ac, unsigned *kc)
1651 {
1652     bool result = TRUE;
1653     bool error = FALSE;
1654     WINDOW *helpwin;
1655
1656     do {
1657         int ch = Getchar();
1658
1659         error = FALSE;
1660         if (ch < 256 && isdigit(ch)) {
1661             *skip = (ch - '0');
1662         } else {
1663             switch (ch) {
1664             case CTRL('L'):
1665                 Repaint();
1666                 break;
1667             case '?':
1668                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1669                     box_set(helpwin, 0, 0);
1670                     attr_legend(helpwin);
1671                     wGetchar(helpwin);
1672                     delwin(helpwin);
1673                 }
1674                 break;
1675             case 'a':
1676                 *ac = 0;
1677                 break;
1678             case 'A':
1679                 *ac = A_ALTCHARSET;
1680                 break;
1681             case 'v':
1682                 if (*kc == 0)
1683                     *kc = SIZEOF(attrs_to_test) - 1;
1684                 else
1685                     *kc -= 1;
1686                 break;
1687             case 'V':
1688                 *kc += 1;
1689                 if (*kc >= SIZEOF(attrs_to_test))
1690                     *kc = 0;
1691                 break;
1692             case '<':
1693                 wide_adjust_attr_string(-1);
1694                 break;
1695             case '>':
1696                 wide_adjust_attr_string(1);
1697                 break;
1698             case case_QUIT:
1699                 result = FALSE;
1700                 break;
1701             default:
1702                 error = cycle_color_attr(ch, fg, bg, tx);
1703                 break;
1704             }
1705         }
1706     } while (error);
1707     return result;
1708 }
1709
1710 static void
1711 wide_attr_test(void)
1712 /* test text attributes using wide-character calls */
1713 {
1714     int n;
1715     int skip = get_xmc();
1716     short fg = COLOR_BLACK;     /* color pair 0 is special */
1717     short bg = COLOR_BLACK;
1718     short tx = -1;
1719     int ac = 0;
1720     unsigned j, k;
1721
1722     if (skip < 0)
1723         skip = 0;
1724
1725     n = skip;                   /* make it easy */
1726     k = SIZEOF(attrs_to_test) - 1;
1727     wide_init_attr_string();
1728
1729     do {
1730         int row = 2;
1731         short pair = 0;
1732         short extras = 0;
1733
1734         if (use_colors) {
1735             pair = (short) (fg != COLOR_BLACK || bg != COLOR_BLACK);
1736             if (pair != 0) {
1737                 pair = 1;
1738                 if (init_pair(pair, fg, bg) == ERR) {
1739                     beep();
1740                 }
1741             }
1742             extras = pair;
1743             if (tx >= 0) {
1744                 extras = 2;
1745                 if (init_pair(extras, tx, bg) == ERR) {
1746                     beep();
1747                 }
1748             }
1749         }
1750         set_wide_background(pair);
1751         erase();
1752
1753         box_set(stdscr, 0, 0);
1754         mvaddstr(0, 20, "Character attribute test display");
1755
1756         for (j = 0; j < SIZEOF(attrs_to_test); ++j) {
1757             row = wide_show_attr(row, n, j == k,
1758                                  ac |
1759                                  attrs_to_test[j].attr |
1760                                  attrs_to_test[k].attr,
1761                                  extras,
1762                                  attrs_to_test[j].name);
1763         }
1764
1765         mvprintw(row, 8,
1766                  "This terminal does %shave the magic-cookie glitch",
1767                  get_xmc() > -1 ? "" : "not ");
1768         mvprintw(row + 1, 8, "Enter '?' for help.");
1769         show_color_attr(fg, bg, tx);
1770         printw("  ACS (%d)", ac != 0);
1771
1772         refresh();
1773     } while (wide_attr_getc(&n, &fg, &bg, &tx, &ac, &k));
1774
1775     set_wide_background(0);
1776     erase();
1777     endwin();
1778 }
1779 #endif
1780
1781 /****************************************************************************
1782  *
1783  * Color support tests
1784  *
1785  ****************************************************************************/
1786
1787 static NCURSES_CONST char *the_color_names[] =
1788 {
1789     "black",
1790     "red",
1791     "green",
1792     "yellow",
1793     "blue",
1794     "magenta",
1795     "cyan",
1796     "white",
1797     "BLACK",
1798     "RED",
1799     "GREEN",
1800     "YELLOW",
1801     "BLUE",
1802     "MAGENTA",
1803     "CYAN",
1804     "WHITE"
1805 };
1806
1807 static void
1808 show_color_name(int y, int x, int color, bool wide)
1809 {
1810     if (move(y, x) != ERR) {
1811         char temp[80];
1812         int width = 8;
1813
1814         if (wide) {
1815             sprintf(temp, "%02d", color);
1816             width = 4;
1817         } else if (color >= 8) {
1818             sprintf(temp, "[%02d]", color);
1819         } else {
1820             strcpy(temp, the_color_names[color]);
1821         }
1822         printw("%-*.*s", width, width, temp);
1823     }
1824 }
1825
1826 static void
1827 color_legend(WINDOW *helpwin, bool wide)
1828 {
1829     int row = 1;
1830     int col = 1;
1831
1832     mvwprintw(helpwin, row++, col,
1833               "ESC to exit.");
1834     ++row;
1835     mvwprintw(helpwin, row++, col,
1836               "Use up/down arrow to scroll through the display if it is");
1837     mvwprintw(helpwin, row++, col,
1838               "longer than one screen. Control/N and Control/P can be used");
1839     mvwprintw(helpwin, row++, col,
1840               "in place of up/down arrow.  Use pageup/pagedown to scroll a");
1841     mvwprintw(helpwin, row++, col,
1842               "full screen; control/B and control/F can be used here.");
1843     ++row;
1844     mvwprintw(helpwin, row++, col,
1845               "Toggles:");
1846     mvwprintw(helpwin, row++, col,
1847               "  a/A     toggle altcharset off/on");
1848     mvwprintw(helpwin, row++, col,
1849               "  b/B     toggle bold off/on");
1850     mvwprintw(helpwin, row++, col,
1851               "  n/N     toggle text/number on/off");
1852     mvwprintw(helpwin, row++, col,
1853               "  w/W     toggle width between 8/16 colors");
1854 #if USE_WIDEC_SUPPORT
1855     if (wide) {
1856         mvwprintw(helpwin, row++, col,
1857                   "Wide characters:");
1858         mvwprintw(helpwin, row++, col,
1859                   "  x/X     toggle text between ASCII and wide-character");
1860     }
1861 #else
1862     (void) wide;
1863 #endif
1864 }
1865
1866 #define set_color_test(name, value) if (name != value) { name = value; base_row = 0; }
1867
1868 /* generate a color test pattern */
1869 static void
1870 color_test(void)
1871 {
1872     short i;
1873     int top = 0, width;
1874     int base_row = 0;
1875     int grid_top = top + 3;
1876     int page_size = (LINES - grid_top);
1877     int pairs_max = PAIR_NUMBER(A_COLOR) + 1;
1878     int row_limit;
1879     int per_row;
1880     char numbered[80];
1881     const char *hello;
1882     bool done = FALSE;
1883     bool opt_acsc = FALSE;
1884     bool opt_bold = FALSE;
1885     bool opt_wide = FALSE;
1886     bool opt_nums = FALSE;
1887     WINDOW *helpwin;
1888
1889     if (pairs_max > COLOR_PAIRS)
1890         pairs_max = COLOR_PAIRS;
1891
1892     while (!done) {
1893         int shown = 0;
1894
1895         /* this assumes an 80-column line */
1896         if (opt_wide) {
1897             width = 4;
1898             hello = "Test";
1899             per_row = (COLORS > 8) ? 16 : 8;
1900         } else {
1901             width = 8;
1902             hello = "Hello";
1903             per_row = 8;
1904         }
1905
1906         row_limit = (pairs_max + per_row - 1) / per_row;
1907
1908         move(0, 0);
1909         (void) printw("There are %d color pairs and %d colors\n",
1910                       pairs_max, COLORS);
1911
1912         clrtobot();
1913         (void) mvprintw(top + 1, 0,
1914                         "%dx%d matrix of foreground/background colors, bold *%s*\n",
1915                         row_limit,
1916                         per_row,
1917                         opt_bold ? "on" : "off");
1918
1919         /* show color names/numbers across the top */
1920         for (i = 0; i < per_row; i++)
1921             show_color_name(top + 2, (i + 1) * width, i, opt_wide);
1922
1923         /* show a grid of colors, with color names/ numbers on the left */
1924         for (i = (short) (base_row * per_row); i < pairs_max; i++) {
1925             int row = grid_top + (i / per_row) - base_row;
1926             int col = (i % per_row + 1) * width;
1927             short pair = i;
1928
1929             if (row >= 0 && move(row, col) != ERR) {
1930                 short fg = (short) (i % COLORS);
1931                 short bg = (short) (i / COLORS);
1932
1933                 init_pair(pair, fg, bg);
1934                 attron((attr_t) COLOR_PAIR(pair));
1935                 if (opt_acsc)
1936                     attron((attr_t) A_ALTCHARSET);
1937                 if (opt_bold)
1938                     attron((attr_t) A_BOLD);
1939
1940                 if (opt_nums) {
1941                     sprintf(numbered, "{%02X}", i);
1942                     hello = numbered;
1943                 }
1944                 printw("%-*.*s", width, width, hello);
1945                 attrset(A_NORMAL);
1946
1947                 if ((i % per_row) == 0 && (i % COLORS) == 0) {
1948                     show_color_name(row, 0, i / COLORS, opt_wide);
1949                 }
1950                 ++shown;
1951             } else if (shown) {
1952                 break;
1953             }
1954         }
1955
1956         switch (wGetchar(stdscr)) {
1957         case 'a':
1958             opt_acsc = FALSE;
1959             break;
1960         case 'A':
1961             opt_acsc = TRUE;
1962             break;
1963         case 'b':
1964             opt_bold = FALSE;
1965             break;
1966         case 'B':
1967             opt_bold = TRUE;
1968             break;
1969         case 'n':
1970             opt_nums = FALSE;
1971             break;
1972         case 'N':
1973             opt_nums = TRUE;
1974             break;
1975         case case_QUIT:
1976             done = TRUE;
1977             continue;
1978         case 'w':
1979             set_color_test(opt_wide, FALSE);
1980             break;
1981         case 'W':
1982             set_color_test(opt_wide, TRUE);
1983             break;
1984         case CTRL('p'):
1985         case KEY_UP:
1986             if (base_row <= 0) {
1987                 beep();
1988             } else {
1989                 base_row -= 1;
1990             }
1991             break;
1992         case CTRL('n'):
1993         case KEY_DOWN:
1994             if (base_row + page_size >= row_limit) {
1995                 beep();
1996             } else {
1997                 base_row += 1;
1998             }
1999             break;
2000         case CTRL('b'):
2001         case KEY_PREVIOUS:
2002         case KEY_PPAGE:
2003             if (base_row <= 0) {
2004                 beep();
2005             } else {
2006                 base_row -= (page_size - 1);
2007                 if (base_row < 0)
2008                     base_row = 0;
2009             }
2010             break;
2011         case CTRL('f'):
2012         case KEY_NEXT:
2013         case KEY_NPAGE:
2014             if (base_row + page_size >= row_limit) {
2015                 beep();
2016             } else {
2017                 base_row += page_size - 1;
2018                 if (base_row + page_size >= row_limit) {
2019                     base_row = row_limit - page_size - 1;
2020                 }
2021             }
2022             break;
2023         case '?':
2024             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2025                 box(helpwin, 0, 0);
2026                 color_legend(helpwin, FALSE);
2027                 wGetchar(helpwin);
2028                 delwin(helpwin);
2029             }
2030             break;
2031         default:
2032             beep();
2033             continue;
2034         }
2035     }
2036
2037     erase();
2038     endwin();
2039 }
2040
2041 #if USE_WIDEC_SUPPORT
2042 /* generate a color test pattern */
2043 static void
2044 wide_color_test(void)
2045 {
2046     int c;
2047     int i;
2048     int top = 0, width;
2049     int base_row = 0;
2050     int grid_top = top + 3;
2051     int page_size = (LINES - grid_top);
2052     int pairs_max = COLOR_PAIRS;
2053     int row_limit;
2054     int per_row;
2055     char numbered[80];
2056     const char *hello;
2057     bool done = FALSE;
2058     bool opt_acsc = FALSE;
2059     bool opt_bold = FALSE;
2060     bool opt_wide = FALSE;
2061     bool opt_nums = FALSE;
2062     bool opt_xchr = FALSE;
2063     wchar_t buffer[10];
2064     WINDOW *helpwin;
2065
2066     while (!done) {
2067         int shown = 0;
2068
2069         /* this assumes an 80-column line */
2070         if (opt_wide) {
2071             width = 4;
2072             hello = "Test";
2073             per_row = (COLORS > 8) ? 16 : 8;
2074         } else {
2075             width = 8;
2076             hello = "Hello";
2077             per_row = 8;
2078         }
2079         if (opt_xchr) {
2080             make_fullwidth_text(buffer, hello);
2081             width *= 2;
2082             per_row /= 2;
2083         } else {
2084             make_narrow_text(buffer, hello);
2085         }
2086
2087         row_limit = (pairs_max + per_row - 1) / per_row;
2088
2089         move(0, 0);
2090         (void) printw("There are %d color pairs and %d colors\n",
2091                       pairs_max, COLORS);
2092
2093         clrtobot();
2094         (void) mvprintw(top + 1, 0,
2095                         "%dx%d matrix of foreground/background colors, bold *%s*\n",
2096                         row_limit,
2097                         per_row,
2098                         opt_bold ? "on" : "off");
2099
2100         /* show color names/numbers across the top */
2101         for (i = 0; i < per_row; i++)
2102             show_color_name(top + 2, (i + 1) * width, i, opt_wide);
2103
2104         /* show a grid of colors, with color names/ numbers on the left */
2105         for (i = (base_row * per_row); i < pairs_max; i++) {
2106             int row = grid_top + (i / per_row) - base_row;
2107             int col = (i % per_row + 1) * width;
2108             short pair = (short) i;
2109
2110             if (row >= 0 && move(row, col) != ERR) {
2111                 init_pair(pair, (short) (i % COLORS), (short) (i / COLORS));
2112                 color_set(pair, NULL);
2113                 if (opt_acsc)
2114                     attr_on((attr_t) A_ALTCHARSET, NULL);
2115                 if (opt_bold)
2116                     attr_on((attr_t) A_BOLD, NULL);
2117
2118                 if (opt_nums) {
2119                     sprintf(numbered, "{%02X}", i);
2120                     if (opt_xchr) {
2121                         make_fullwidth_text(buffer, numbered);
2122                     } else {
2123                         make_narrow_text(buffer, numbered);
2124                     }
2125                 }
2126                 addnwstr(buffer, width);
2127                 attr_set(A_NORMAL, 0, NULL);
2128
2129                 if ((i % per_row) == 0 && (i % COLORS) == 0) {
2130                     show_color_name(row, 0, i / COLORS, opt_wide);
2131                 }
2132                 ++shown;
2133             } else if (shown) {
2134                 break;
2135             }
2136         }
2137
2138         switch (c = wGetchar(stdscr)) {
2139         case 'a':
2140             opt_acsc = FALSE;
2141             break;
2142         case 'A':
2143             opt_acsc = TRUE;
2144             break;
2145         case 'b':
2146             opt_bold = FALSE;
2147             break;
2148         case 'B':
2149             opt_bold = TRUE;
2150             break;
2151         case 'n':
2152             opt_nums = FALSE;
2153             break;
2154         case 'N':
2155             opt_nums = TRUE;
2156             break;
2157         case case_QUIT:
2158             done = TRUE;
2159             continue;
2160         case 'w':
2161             set_color_test(opt_wide, FALSE);
2162             break;
2163         case 'W':
2164             set_color_test(opt_wide, TRUE);
2165             break;
2166         case 'x':
2167             opt_xchr = FALSE;
2168             break;
2169         case 'X':
2170             opt_xchr = TRUE;
2171             break;
2172         case CTRL('p'):
2173         case KEY_UP:
2174             if (base_row <= 0) {
2175                 beep();
2176             } else {
2177                 base_row -= 1;
2178             }
2179             break;
2180         case CTRL('n'):
2181         case KEY_DOWN:
2182             if (base_row + page_size >= row_limit) {
2183                 beep();
2184             } else {
2185                 base_row += 1;
2186             }
2187             break;
2188         case CTRL('b'):
2189         case KEY_PREVIOUS:
2190         case KEY_PPAGE:
2191             if (base_row <= 0) {
2192                 beep();
2193             } else {
2194                 base_row -= (page_size - 1);
2195                 if (base_row < 0)
2196                     base_row = 0;
2197             }
2198             break;
2199         case CTRL('f'):
2200         case KEY_NEXT:
2201         case KEY_NPAGE:
2202             if (base_row + page_size >= row_limit) {
2203                 beep();
2204             } else {
2205                 base_row += page_size - 1;
2206                 if (base_row + page_size >= row_limit) {
2207                     base_row = row_limit - page_size - 1;
2208                 }
2209             }
2210             break;
2211         case '?':
2212             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2213                 box(helpwin, 0, 0);
2214                 color_legend(helpwin, TRUE);
2215                 wGetchar(helpwin);
2216                 delwin(helpwin);
2217             }
2218             break;
2219         default:
2220             beep();
2221             continue;
2222         }
2223     }
2224
2225     erase();
2226     endwin();
2227 }
2228 #endif /* USE_WIDEC_SUPPORT */
2229
2230 static void
2231 change_color(short current, int field, int value, int usebase)
2232 {
2233     short red, green, blue;
2234
2235     color_content(current, &red, &green, &blue);
2236
2237     switch (field) {
2238     case 0:
2239         red = (short) (usebase ? (red + value) : value);
2240         break;
2241     case 1:
2242         green = (short) (usebase ? (green + value) : value);
2243         break;
2244     case 2:
2245         blue = (short) (usebase ? (blue + value) : value);
2246         break;
2247     }
2248
2249     if (init_color(current, red, green, blue) == ERR)
2250         beep();
2251 }
2252
2253 static void
2254 init_all_colors(void)
2255 {
2256     short c;
2257
2258     for (c = 0; c < COLORS; ++c)
2259         init_color(c,
2260                    all_colors[c].red,
2261                    all_colors[c].green,
2262                    all_colors[c].blue);
2263 }
2264
2265 #define scaled_rgb(n) ((255 * (n)) / 1000)
2266
2267 static void
2268 color_edit(void)
2269 /* display the color test pattern, without trying to edit colors */
2270 {
2271     int i;
2272     int current = 0;
2273     int this_c = 0, value = 0, field = 0;
2274     int last_c;
2275     int top_color = 0;
2276     int page_size = (LINES - 6);
2277
2278     init_all_colors();
2279     refresh();
2280
2281     for (i = 0; i < max_colors; i++)
2282         init_pair((short) i, (short) COLOR_WHITE, (short) i);
2283
2284     mvprintw(LINES - 2, 0, "Number: %d", value);
2285
2286     do {
2287         short red, green, blue;
2288
2289         attron(A_BOLD);
2290         mvaddstr(0, 20, "Color RGB Value Editing");
2291         attroff(A_BOLD);
2292
2293         for (i = (short) top_color;
2294              (i - top_color < page_size)
2295              && (i < max_colors); i++) {
2296             char numeric[80];
2297
2298             sprintf(numeric, "[%d]", i);
2299             mvprintw(2 + i - top_color, 0, "%c %-8s:",
2300                      (i == current ? '>' : ' '),
2301                      (i < (int) SIZEOF(the_color_names)
2302                       ? the_color_names[i] : numeric));
2303             attrset(COLOR_PAIR(i));
2304             addstr("        ");
2305             attrset(A_NORMAL);
2306
2307             color_content((short) i, &red, &green, &blue);
2308             addstr("   R = ");
2309             if (current == i && field == 0)
2310                 attron(A_STANDOUT);
2311             printw("%04d", red);
2312             if (current == i && field == 0)
2313                 attrset(A_NORMAL);
2314             addstr(", G = ");
2315             if (current == i && field == 1)
2316                 attron(A_STANDOUT);
2317             printw("%04d", green);
2318             if (current == i && field == 1)
2319                 attrset(A_NORMAL);
2320             addstr(", B = ");
2321             if (current == i && field == 2)
2322                 attron(A_STANDOUT);
2323             printw("%04d", blue);
2324             if (current == i && field == 2)
2325                 attrset(A_NORMAL);
2326             attrset(A_NORMAL);
2327             printw(" ( %3d %3d %3d )",
2328                    scaled_rgb(red),
2329                    scaled_rgb(green),
2330                    scaled_rgb(blue));
2331         }
2332
2333         mvaddstr(LINES - 3, 0,
2334                  "Use up/down to select a color, left/right to change fields.");
2335         mvaddstr(LINES - 2, 0,
2336                  "Modify field by typing nnn=, nnn-, or nnn+.  ? for help.");
2337
2338         move(2 + current - top_color, 0);
2339
2340         last_c = this_c;
2341         this_c = Getchar();
2342         if (this_c < 256 && isdigit(this_c) && !isdigit(last_c))
2343             value = 0;
2344
2345         switch (this_c) {
2346         case CTRL('b'):
2347         case KEY_PPAGE:
2348             if (current > 0)
2349                 current -= (page_size - 1);
2350             else
2351                 beep();
2352             break;
2353
2354         case CTRL('f'):
2355         case KEY_NPAGE:
2356             if (current < (max_colors - 1))
2357                 current += (page_size - 1);
2358             else
2359                 beep();
2360             break;
2361
2362         case CTRL('p'):
2363         case KEY_UP:
2364             current = (current == 0 ? (max_colors - 1) : current - 1);
2365             break;
2366
2367         case CTRL('n'):
2368         case KEY_DOWN:
2369             current = (current == (max_colors - 1) ? 0 : current + 1);
2370             break;
2371
2372         case KEY_RIGHT:
2373             field = (field == 2 ? 0 : field + 1);
2374             break;
2375
2376         case KEY_LEFT:
2377             field = (field == 0 ? 2 : field - 1);
2378             break;
2379
2380         case '0':
2381         case '1':
2382         case '2':
2383         case '3':
2384         case '4':
2385         case '5':
2386         case '6':
2387         case '7':
2388         case '8':
2389         case '9':
2390             value = value * 10 + (this_c - '0');
2391             break;
2392
2393         case '+':
2394             change_color((short) current, field, value, 1);
2395             break;
2396
2397         case '-':
2398             change_color((short) current, field, -value, 1);
2399             break;
2400
2401         case '=':
2402             change_color((short) current, field, value, 0);
2403             break;
2404
2405         case '?':
2406             erase();
2407             P("                      RGB Value Editing Help");
2408             P("");
2409             P("You are in the RGB value editor.  Use the arrow keys to select one of");
2410             P("the fields in one of the RGB triples of the current colors; the one");
2411             P("currently selected will be reverse-video highlighted.");
2412             P("");
2413             P("To change a field, enter the digits of the new value; they are echoed");
2414             P("as entered.  Finish by typing `='.  The change will take effect instantly.");
2415             P("To increment or decrement a value, use the same procedure, but finish");
2416             P("with a `+' or `-'.");
2417             P("");
2418             P("Press 'm' to invoke the top-level menu with the current color settings.");
2419             P("To quit, do ESC");
2420
2421             Pause();
2422             erase();
2423             break;
2424
2425         case 'm':
2426             endwin();
2427             main_menu(FALSE);
2428             refresh();
2429             break;
2430
2431         case case_QUIT:
2432             break;
2433
2434         default:
2435             beep();
2436             break;
2437         }
2438
2439         if (current < 0)
2440             current = 0;
2441         if (current >= max_colors)
2442             current = max_colors - 1;
2443         if (current < top_color)
2444             top_color = current;
2445         if (current - top_color >= page_size)
2446             top_color = current - (page_size - 1);
2447
2448         mvprintw(LINES - 1, 0, "Number: %d", value);
2449         clrtoeol();
2450     } while
2451         (!isQuit(this_c));
2452
2453     erase();
2454
2455     /*
2456      * ncurses does not reset each color individually when calling endwin().
2457      */
2458     init_all_colors();
2459
2460     endwin();
2461 }
2462
2463 /****************************************************************************
2464  *
2465  * Soft-key label test
2466  *
2467  ****************************************************************************/
2468
2469 #if USE_SOFTKEYS
2470
2471 #define SLK_HELP 17
2472 #define SLK_WORK (SLK_HELP + 3)
2473
2474 static void
2475 slk_help(void)
2476 {
2477     static const char *table[] =
2478     {
2479         "Available commands are:"
2480         ,""
2481         ,"^L         -- repaint this message and activate soft keys"
2482         ,"a/d        -- activate/disable soft keys"
2483         ,"c          -- set centered format for labels"
2484         ,"l          -- set left-justified format for labels"
2485         ,"r          -- set right-justified format for labels"
2486         ,"[12345678] -- set label; labels are numbered 1 through 8"
2487         ,"e          -- erase stdscr (should not erase labels)"
2488         ,"s          -- test scrolling of shortened screen"
2489 #if HAVE_SLK_COLOR
2490         ,"F/B        -- cycle through foreground/background colors"
2491 #endif
2492         ,"ESC  -- return to main menu"
2493         ,""
2494         ,"Note: if activating the soft keys causes your terminal to scroll up"
2495         ,"one line, your terminal auto-scrolls when anything is written to the"
2496         ,"last screen position.  The ncurses code does not yet handle this"
2497         ,"gracefully."
2498     };
2499     unsigned j;
2500
2501     move(2, 0);
2502     for (j = 0; j < SIZEOF(table); ++j) {
2503         P(table[j]);
2504     }
2505     refresh();
2506 }
2507
2508 #if HAVE_SLK_COLOR
2509 static void
2510 call_slk_color(short fg, short bg)
2511 {
2512     init_pair(1, bg, fg);
2513     slk_color(1);
2514     mvprintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
2515     clrtoeol();
2516     refresh();
2517 }
2518 #endif
2519
2520 static void
2521 slk_test(void)
2522 /* exercise the soft keys */
2523 {
2524     int c, fmt = 1;
2525     char buf[9];
2526     char *s;
2527 #if HAVE_SLK_COLOR
2528     short fg = COLOR_BLACK;
2529     short bg = COLOR_WHITE;
2530 #endif
2531
2532     c = CTRL('l');
2533 #if HAVE_SLK_COLOR
2534     if (use_colors) {
2535         call_slk_color(fg, bg);
2536     }
2537 #endif
2538
2539     do {
2540         move(0, 0);
2541         switch (c) {
2542         case CTRL('l'):
2543             erase();
2544             attron(A_BOLD);
2545             mvaddstr(0, 20, "Soft Key Exerciser");
2546             attroff(A_BOLD);
2547
2548             slk_help();
2549             /* fall through */
2550
2551         case 'a':
2552             slk_restore();
2553             break;
2554
2555         case 'e':
2556             wclear(stdscr);
2557             break;
2558
2559         case 's':
2560             mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2561             while ((c = Getchar()) != 'Q' && (c != ERR))
2562                 addch((chtype) c);
2563             break;
2564
2565         case 'd':
2566             slk_clear();
2567             break;
2568
2569         case 'l':
2570             fmt = 0;
2571             break;
2572
2573         case 'c':
2574             fmt = 1;
2575             break;
2576
2577         case 'r':
2578             fmt = 2;
2579             break;
2580
2581         case '1':
2582         case '2':
2583         case '3':
2584         case '4':
2585         case '5':
2586         case '6':
2587         case '7':
2588         case '8':
2589             (void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
2590             strcpy(buf, "");
2591             if ((s = slk_label(c - '0')) != 0) {
2592                 strncpy(buf, s, 8);
2593             }
2594             wGetstring(stdscr, buf, 8);
2595             slk_set((c - '0'), buf, fmt);
2596             slk_refresh();
2597             move(SLK_WORK, 0);
2598             clrtobot();
2599             break;
2600
2601         case case_QUIT:
2602             goto done;
2603
2604 #if HAVE_SLK_COLOR
2605         case 'F':
2606             if (use_colors) {
2607                 fg = (short) ((fg + 1) % COLORS);
2608                 call_slk_color(fg, bg);
2609             }
2610             break;
2611         case 'B':
2612             if (use_colors) {
2613                 bg = (short) ((bg + 1) % COLORS);
2614                 call_slk_color(fg, bg);
2615             }
2616             break;
2617 #endif
2618 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
2619         case KEY_RESIZE:
2620             wnoutrefresh(stdscr);
2621             break;
2622 #endif
2623
2624         default:
2625             beep();
2626         }
2627     } while (!isQuit(c = Getchar()));
2628
2629   done:
2630     slk_clear();
2631     erase();
2632     endwin();
2633 }
2634
2635 #if USE_WIDEC_SUPPORT
2636 #define SLKLEN 8
2637 static void
2638 wide_slk_test(void)
2639 /* exercise the soft keys */
2640 {
2641     int c, fmt = 1;
2642     wchar_t buf[SLKLEN + 1];
2643     char *s;
2644     short fg = COLOR_BLACK;
2645     short bg = COLOR_WHITE;
2646
2647     c = CTRL('l');
2648     if (use_colors) {
2649         call_slk_color(fg, bg);
2650     }
2651     do {
2652         move(0, 0);
2653         switch (c) {
2654         case CTRL('l'):
2655             erase();
2656             attr_on(WA_BOLD, NULL);
2657             mvaddstr(0, 20, "Soft Key Exerciser");
2658             attr_off(WA_BOLD, NULL);
2659
2660             slk_help();
2661             /* fall through */
2662
2663         case 'a':
2664             slk_restore();
2665             break;
2666
2667         case 'e':
2668             wclear(stdscr);
2669             break;
2670
2671         case 's':
2672             mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2673             while ((c = Getchar()) != 'Q' && (c != ERR))
2674                 addch((chtype) c);
2675             break;
2676
2677         case 'd':
2678             slk_clear();
2679             break;
2680
2681         case 'l':
2682             fmt = 0;
2683             break;
2684
2685         case 'c':
2686             fmt = 1;
2687             break;
2688
2689         case 'r':
2690             fmt = 2;
2691             break;
2692
2693         case '1':
2694         case '2':
2695         case '3':
2696         case '4':
2697         case '5':
2698         case '6':
2699         case '7':
2700         case '8':
2701             (void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
2702             *buf = 0;
2703             if ((s = slk_label(c - '0')) != 0) {
2704                 char *temp = strdup(s);
2705                 size_t used = strlen(temp);
2706                 size_t want = SLKLEN;
2707                 size_t test;
2708                 mbstate_t state;
2709
2710                 buf[0] = L'\0';
2711                 while (want > 0 && used != 0) {
2712                     const char *base = s;
2713                     memset(&state, 0, sizeof(state));
2714                     test = mbsrtowcs(0, &base, 0, &state);
2715                     if (test == (size_t) -1) {
2716                         temp[--used] = 0;
2717                     } else if (test > want) {
2718                         temp[--used] = 0;
2719                     } else {
2720                         memset(&state, 0, sizeof(state));
2721                         mbsrtowcs(buf, &base, want, &state);
2722                         break;
2723                     }
2724                 }
2725                 free(temp);
2726             }
2727             wGet_wstring(stdscr, buf, SLKLEN);
2728             slk_wset((c - '0'), buf, fmt);
2729             slk_refresh();
2730             move(SLK_WORK, 0);
2731             clrtobot();
2732             break;
2733
2734         case case_QUIT:
2735             goto done;
2736
2737         case 'F':
2738             if (use_colors) {
2739                 fg = (short) ((fg + 1) % COLORS);
2740                 call_slk_color(fg, bg);
2741             }
2742             break;
2743         case 'B':
2744             if (use_colors) {
2745                 bg = (short) ((bg + 1) % COLORS);
2746                 call_slk_color(fg, bg);
2747             }
2748             break;
2749 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
2750         case KEY_RESIZE:
2751             wnoutrefresh(stdscr);
2752             break;
2753 #endif
2754         default:
2755             beep();
2756         }
2757     } while (!isQuit(c = Getchar()));
2758
2759   done:
2760     slk_clear();
2761     erase();
2762     endwin();
2763 }
2764 #endif
2765 #endif /* SLK_INIT */
2766
2767 /****************************************************************************
2768  *
2769  * Alternate character-set stuff
2770  *
2771  ****************************************************************************/
2772 /* *INDENT-OFF* */
2773 static struct {
2774     chtype attr;
2775     const char *name;
2776 } attrs_to_cycle[] = {
2777     { A_NORMAL,         "normal" },
2778     { A_BOLD,           "bold" },
2779     { A_REVERSE,        "reverse" },
2780     { A_UNDERLINE,      "underline" },
2781 };
2782 /* *INDENT-ON* */
2783
2784 static bool
2785 cycle_attr(int ch, unsigned *at_code, chtype *attr)
2786 {
2787     bool result = TRUE;
2788
2789     switch (ch) {
2790     case 'v':
2791         if ((*at_code += 1) >= SIZEOF(attrs_to_cycle))
2792             *at_code = 0;
2793         break;
2794     case 'V':
2795         if (*at_code == 1)
2796             *at_code = SIZEOF(attrs_to_cycle) - 1;
2797         else
2798             *at_code -= 1;
2799         break;
2800     default:
2801         result = FALSE;
2802         break;
2803     }
2804     if (result)
2805         *attr = attrs_to_cycle[*at_code].attr;
2806     return result;
2807 }
2808
2809 static bool
2810 cycle_colors(int ch, int *fg, int *bg, short *pair)
2811 {
2812     bool result = FALSE;
2813
2814     if (use_colors) {
2815         result = TRUE;
2816         switch (ch) {
2817         case 'F':
2818             if ((*fg -= 1) < 0)
2819                 *fg = COLORS - 1;
2820             break;
2821         case 'f':
2822             if ((*fg += 1) >= COLORS)
2823                 *fg = 0;
2824             break;
2825         case 'B':
2826             if ((*bg -= 1) < 0)
2827                 *bg = COLORS - 1;
2828             break;
2829         case 'b':
2830             if ((*bg += 1) >= COLORS)
2831                 *bg = 0;
2832             break;
2833         default:
2834             result = FALSE;
2835             break;
2836         }
2837         if (result) {
2838             *pair = (short) (*fg != COLOR_BLACK || *bg != COLOR_BLACK);
2839             if (*pair != 0) {
2840                 *pair = 1;
2841                 if (init_pair(*pair, (short) *fg, (short) *bg) == ERR) {
2842                     result = FALSE;
2843                 }
2844             }
2845         }
2846     }
2847     return result;
2848 }
2849
2850 /* ISO 6429:  codes 0x80 to 0x9f may be control characters that cause the
2851  * terminal to perform functions.  The remaining codes can be graphic.
2852  */
2853 static void
2854 show_upper_chars(unsigned first, int repeat, attr_t attr, short pair)
2855 {
2856     bool C1 = (first == 128);
2857     unsigned code;
2858     unsigned last = first + 31;
2859     int reply;
2860
2861     erase();
2862     attron(A_BOLD);
2863     mvprintw(0, 20, "Display of %s Character Codes %d to %d",
2864              C1 ? "C1" : "GR", first, last);
2865     attroff(A_BOLD);
2866     refresh();
2867
2868     for (code = first; code <= last; code++) {
2869         int count = repeat;
2870         int row = 2 + ((int) (code - first) % 16);
2871         int col = ((int) (code - first) / 16) * COLS / 2;
2872         char tmp[80];
2873         sprintf(tmp, "%3u (0x%x)", code, code);
2874         mvprintw(row, col, "%*s: ", COLS / 4, tmp);
2875
2876         do {
2877             if (C1)
2878                 nodelay(stdscr, TRUE);
2879             echochar(code | attr | COLOR_PAIR(pair));
2880             if (C1) {
2881                 /* (yes, this _is_ crude) */
2882                 while ((reply = Getchar()) != ERR) {
2883                     addch(UChar(reply));
2884                     napms(10);
2885                 }
2886                 nodelay(stdscr, FALSE);
2887             }
2888         } while (--count > 0);
2889     }
2890 }
2891
2892 #define PC_COLS 4
2893
2894 static void
2895 show_pc_chars(int repeat, attr_t attr, short pair)
2896 {
2897     unsigned code;
2898
2899     erase();
2900     attron(A_BOLD);
2901     mvprintw(0, 20, "Display of PC Character Codes");
2902     attroff(A_BOLD);
2903     refresh();
2904
2905     for (code = 0; code < 16; ++code) {
2906         mvprintw(2, (int) code * PC_COLS + 8, "%X", code);
2907     }
2908     for (code = 0; code < 256; code++) {
2909         int count = repeat;
2910         int row = 3 + (int) (code / 16) + (code >= 128);
2911         int col = 8 + (int) (code % 16) * PC_COLS;
2912         if ((code % 16) == 0)
2913             mvprintw(row, 0, "0x%02x:", code);
2914         move(row, col);
2915         do {
2916             switch (code) {
2917             case '\n':
2918             case '\r':
2919             case '\b':
2920             case '\f':
2921             case '\033':
2922             case 0x9b:
2923                 /*
2924                  * Skip the ones that do not work.
2925                  */
2926                 break;
2927             default:
2928                 addch(code | A_ALTCHARSET | attr | COLOR_PAIR(pair));
2929                 break;
2930             }
2931         } while (--count > 0);
2932     }
2933 }
2934
2935 static void
2936 show_box_chars(int repeat, attr_t attr, short pair)
2937 {
2938     (void) repeat;
2939     attr |= COLOR_PAIR(pair);
2940
2941     erase();
2942     attron(A_BOLD);
2943     mvaddstr(0, 20, "Display of the ACS Line-Drawing Set");
2944     attroff(A_BOLD);
2945     refresh();
2946     box(stdscr, 0, 0);
2947     /* *INDENT-OFF* */
2948     mvhline(LINES / 2, 0,        ACS_HLINE | attr, COLS);
2949     mvvline(0,         COLS / 2, ACS_VLINE | attr, LINES);
2950     mvaddch(0,         COLS / 2, ACS_TTEE | attr);
2951     mvaddch(LINES / 2, COLS / 2, ACS_PLUS | attr);
2952     mvaddch(LINES - 1, COLS / 2, ACS_BTEE | attr);
2953     mvaddch(LINES / 2, 0,        ACS_LTEE | attr);
2954     mvaddch(LINES / 2, COLS - 1, ACS_RTEE | attr);
2955     /* *INDENT-ON* */
2956
2957 }
2958
2959 static int
2960 show_1_acs(int n, int repeat, const char *name, chtype code)
2961 {
2962     const int height = 16;
2963     int row = 2 + (n % height);
2964     int col = (n / height) * COLS / 2;
2965
2966     mvprintw(row, col, "%*s : ", COLS / 4, name);
2967     do {
2968         addch(code);
2969     } while (--repeat > 0);
2970     return n + 1;
2971 }
2972
2973 static void
2974 show_acs_chars(int repeat, attr_t attr, short pair)
2975 /* display the ACS character set */
2976 {
2977     int n;
2978
2979 #define BOTH(name) #name, (name | attr | COLOR_PAIR(pair))
2980
2981     erase();
2982     attron(A_BOLD);
2983     mvaddstr(0, 20, "Display of the ACS Character Set");
2984     attroff(A_BOLD);
2985     refresh();
2986
2987     n = show_1_acs(0, repeat, BOTH(ACS_ULCORNER));
2988     n = show_1_acs(n, repeat, BOTH(ACS_URCORNER));
2989     n = show_1_acs(n, repeat, BOTH(ACS_LLCORNER));
2990     n = show_1_acs(n, repeat, BOTH(ACS_LRCORNER));
2991
2992     n = show_1_acs(n, repeat, BOTH(ACS_LTEE));
2993     n = show_1_acs(n, repeat, BOTH(ACS_RTEE));
2994     n = show_1_acs(n, repeat, BOTH(ACS_TTEE));
2995     n = show_1_acs(n, repeat, BOTH(ACS_BTEE));
2996
2997     n = show_1_acs(n, repeat, BOTH(ACS_HLINE));
2998     n = show_1_acs(n, repeat, BOTH(ACS_VLINE));
2999
3000     /*
3001      * HPUX's ACS definitions are broken here.  Just give up.
3002      */
3003 #if !(defined(__hpux) && !defined(NCURSES_VERSION))
3004     n = show_1_acs(n, repeat, BOTH(ACS_LARROW));
3005     n = show_1_acs(n, repeat, BOTH(ACS_RARROW));
3006     n = show_1_acs(n, repeat, BOTH(ACS_UARROW));
3007     n = show_1_acs(n, repeat, BOTH(ACS_DARROW));
3008
3009     n = show_1_acs(n, repeat, BOTH(ACS_BLOCK));
3010     n = show_1_acs(n, repeat, BOTH(ACS_BOARD));
3011     n = show_1_acs(n, repeat, BOTH(ACS_LANTERN));
3012     n = show_1_acs(n, repeat, BOTH(ACS_BULLET));
3013     n = show_1_acs(n, repeat, BOTH(ACS_CKBOARD));
3014     n = show_1_acs(n, repeat, BOTH(ACS_DEGREE));
3015     n = show_1_acs(n, repeat, BOTH(ACS_DIAMOND));
3016     n = show_1_acs(n, repeat, BOTH(ACS_PLMINUS));
3017     n = show_1_acs(n, repeat, BOTH(ACS_PLUS));
3018
3019     n = show_1_acs(n, repeat, BOTH(ACS_GEQUAL));
3020     n = show_1_acs(n, repeat, BOTH(ACS_NEQUAL));
3021     n = show_1_acs(n, repeat, BOTH(ACS_LEQUAL));
3022
3023     n = show_1_acs(n, repeat, BOTH(ACS_STERLING));
3024     n = show_1_acs(n, repeat, BOTH(ACS_PI));
3025     n = show_1_acs(n, repeat, BOTH(ACS_S1));
3026     n = show_1_acs(n, repeat, BOTH(ACS_S3));
3027     n = show_1_acs(n, repeat, BOTH(ACS_S7));
3028     n = show_1_acs(n, repeat, BOTH(ACS_S9));
3029 #endif
3030 }
3031
3032 static void
3033 acs_display(void)
3034 {
3035     int c = 'a';
3036     char *term = getenv("TERM");
3037     const char *pch_kludge = ((term != 0 && strstr(term, "linux"))
3038                               ? "p=PC, "
3039                               : "");
3040     chtype attr = A_NORMAL;
3041     int digit = 0;
3042     int repeat = 1;
3043     int fg = COLOR_BLACK;
3044     int bg = COLOR_BLACK;
3045     unsigned at_code = 0;
3046     short pair = 0;
3047     void (*last_show_acs) (int, attr_t, short) = 0;
3048
3049     do {
3050         switch (c) {
3051         case CTRL('L'):
3052             Repaint();
3053             break;
3054         case 'a':
3055             ToggleAcs(last_show_acs, show_acs_chars);
3056             break;
3057         case 'p':
3058             if (*pch_kludge)
3059                 ToggleAcs(last_show_acs, show_pc_chars);
3060             else
3061                 beep();
3062             break;
3063         case 'x':
3064             ToggleAcs(last_show_acs, show_box_chars);
3065             break;
3066         case '0':
3067         case '1':
3068         case '2':
3069         case '3':
3070             digit = (c - '0');
3071             last_show_acs = 0;
3072             break;
3073         case '-':
3074             if (digit > 0) {
3075                 --digit;
3076                 last_show_acs = 0;
3077             } else {
3078                 beep();
3079             }
3080             break;
3081         case '+':
3082             if (digit < 3) {
3083                 ++digit;
3084                 last_show_acs = 0;
3085             } else {
3086                 beep();
3087             }
3088             break;
3089         case '>':
3090             if (repeat < (COLS / 4))
3091                 ++repeat;
3092             break;
3093         case '<':
3094             if (repeat > 1)
3095                 --repeat;
3096             break;
3097         default:
3098             if (cycle_attr(c, &at_code, &attr)
3099                 || cycle_colors(c, &fg, &bg, &pair)) {
3100                 break;
3101             } else {
3102                 beep();
3103             }
3104             break;
3105         }
3106         if (last_show_acs != 0)
3107             last_show_acs(repeat, attr, pair);
3108         else
3109             show_upper_chars((unsigned) (digit * 32 + 128), repeat, attr, pair);
3110
3111         mvprintw(LINES - 3, 0,
3112                  "Note: ANSI terminals may not display C1 characters.");
3113         mvprintw(LINES - 2, 0,
3114                  "Select: a=ACS, x=box, %s0=C1, 1-3,+/- non-ASCII, </> repeat, ESC=quit",
3115                  pch_kludge);
3116         if (use_colors) {
3117             mvprintw(LINES - 1, 0,
3118                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3119                      attrs_to_cycle[at_code].name,
3120                      fg, bg);
3121         } else {
3122             mvprintw(LINES - 1, 0,
3123                      "v/V cycles through video attributes (%s).",
3124                      attrs_to_cycle[at_code].name);
3125         }
3126         refresh();
3127     } while (!isQuit(c = Getchar()));
3128
3129     Pause();
3130     erase();
3131     endwin();
3132 }
3133
3134 #if USE_WIDEC_SUPPORT
3135 static cchar_t *
3136 merge_wide_attr(cchar_t *dst, const cchar_t *src, attr_t attr, short pair)
3137 {
3138     int count = getcchar(src, NULL, NULL, NULL, 0);
3139     wchar_t *wch = 0;
3140     attr_t ignore_attr;
3141     short ignore_pair;
3142
3143     *dst = *src;
3144     if (count > 0) {
3145         if ((wch = typeMalloc(wchar_t, (unsigned) count + 1)) != 0) {
3146             if (getcchar(src, wch, &ignore_attr, &ignore_pair, 0) != ERR) {
3147                 attr |= (ignore_attr & A_ALTCHARSET);
3148                 setcchar(dst, wch, attr, pair, 0);
3149             }
3150             free(wch);
3151         }
3152     }
3153     return dst;
3154 }
3155
3156 static void
3157 show_upper_widechars(int first, int repeat, int space, attr_t attr, short pair)
3158 {
3159     cchar_t temp;
3160     wchar_t code;
3161     int last = first + 31;
3162
3163     erase();
3164     attron(A_BOLD);
3165     mvprintw(0, 20, "Display of Character Codes %d to %d", first, last);
3166     attroff(A_BOLD);
3167
3168     for (code = first; (int) code <= last; code++) {
3169         int row = 2 + ((code - first) % 16);
3170         int col = ((code - first) / 16) * COLS / 2;
3171         wchar_t codes[10];
3172         char tmp[80];
3173         int count = repeat;
3174         int y, x;
3175
3176         memset(&codes, 0, sizeof(codes));
3177         codes[0] = code;
3178         sprintf(tmp, "%3ld (0x%lx)", (long) code, (long) code);
3179         mvprintw(row, col, "%*s: ", COLS / 4, tmp);
3180         setcchar(&temp, codes, attr, pair, 0);
3181         do {
3182             /*
3183              * Give non-spacing characters something to combine with.  If we
3184              * don't, they'll bunch up in a heap on the space after the ":".
3185              * Mark them with reverse-video to make them simpler to find on
3186              * the display.
3187              */
3188             if (wcwidth(code) == 0)
3189                 addch(space | A_REVERSE);
3190             /*
3191              * This could use add_wch(), but is done for comparison with the
3192              * normal 'f' test (and to make a test-case for echo_wchar()).
3193              * The screen will flicker because the erase() at the top of the
3194              * function is met by the builtin refresh() in echo_wchar().
3195              */
3196             echo_wchar(&temp);
3197             /*
3198              * The repeat-count may make text wrap - avoid that.
3199              */
3200             getyx(stdscr, y, x);
3201             if (x >= col + (COLS / 2) - 2)
3202                 break;
3203         } while (--count > 0);
3204     }
3205 }
3206
3207 static int
3208 show_1_wacs(int n, int repeat, const char *name, const cchar_t *code)
3209 {
3210     const int height = 16;
3211     int row = 2 + (n % height);
3212     int col = (n / height) * COLS / 2;
3213
3214     mvprintw(row, col, "%*s : ", COLS / 4, name);
3215     while (repeat-- >= 0) {
3216         add_wch(code);
3217     }
3218     return n + 1;
3219 }
3220
3221 #define MERGE_ATTR(wch) merge_wide_attr(&temp, wch, attr, pair)
3222
3223 static void
3224 show_wacs_chars(int repeat, attr_t attr, short pair)
3225 /* display the wide-ACS character set */
3226 {
3227     cchar_t temp;
3228
3229     int n;
3230
3231 /*#define BOTH2(name) #name, &(name) */
3232 #define BOTH2(name) #name, MERGE_ATTR(name)
3233
3234     erase();
3235     attron(A_BOLD);
3236     mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
3237     attroff(A_BOLD);
3238     refresh();
3239
3240     n = show_1_wacs(0, repeat, BOTH2(WACS_ULCORNER));
3241     n = show_1_wacs(n, repeat, BOTH2(WACS_URCORNER));
3242     n = show_1_wacs(n, repeat, BOTH2(WACS_LLCORNER));
3243     n = show_1_wacs(n, repeat, BOTH2(WACS_LRCORNER));
3244
3245     n = show_1_wacs(n, repeat, BOTH2(WACS_LTEE));
3246     n = show_1_wacs(n, repeat, BOTH2(WACS_RTEE));
3247     n = show_1_wacs(n, repeat, BOTH2(WACS_TTEE));
3248     n = show_1_wacs(n, repeat, BOTH2(WACS_BTEE));
3249
3250     n = show_1_wacs(n, repeat, BOTH2(WACS_HLINE));
3251     n = show_1_wacs(n, repeat, BOTH2(WACS_VLINE));
3252
3253     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3254     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3255     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3256     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3257
3258     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3259     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3260     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3261     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3262     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3263     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3264     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3265     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3266     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3267
3268 #ifdef CURSES_WACS_ARRAY
3269     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3270     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3271     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3272
3273     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3274     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3275     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3276     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3277     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3278     n = show_1_wacs(n, repeat, BOTH2(WACS_S9));
3279 #endif
3280 }
3281
3282 #undef MERGE_ATTR
3283
3284 #define MERGE_ATTR(wch) merge_wide_attr(&temp, wch, attr, pair)
3285
3286 static void
3287 show_wbox_chars(int repeat, attr_t attr, short pair)
3288 {
3289     cchar_t temp;
3290
3291     (void) repeat;
3292     erase();
3293     attron(A_BOLD);
3294     mvaddstr(0, 20, "Display of the Wide-ACS Line-Drawing Set");
3295     attroff(A_BOLD);
3296     refresh();
3297
3298     attr_set(attr, pair, 0);
3299     box_set(stdscr, 0, 0);
3300     attr_set(A_NORMAL, 0, 0);
3301     /* *INDENT-OFF* */
3302     mvhline_set(LINES / 2, 0,        MERGE_ATTR(WACS_HLINE), COLS);
3303     mvvline_set(0,         COLS / 2, MERGE_ATTR(WACS_VLINE), LINES);
3304     mvadd_wch(0,           COLS / 2, MERGE_ATTR(WACS_TTEE));
3305     mvadd_wch(LINES / 2,   COLS / 2, MERGE_ATTR(WACS_PLUS));
3306     mvadd_wch(LINES - 1,   COLS / 2, MERGE_ATTR(WACS_BTEE));
3307     mvadd_wch(LINES / 2,   0,        MERGE_ATTR(WACS_LTEE));
3308     mvadd_wch(LINES / 2,   COLS - 1, MERGE_ATTR(WACS_RTEE));
3309     /* *INDENT-ON* */
3310
3311 }
3312
3313 #undef MERGE_ATTR
3314
3315 static int
3316 show_2_wacs(int n, const char *name, const char *code, attr_t attr, short pair)
3317 {
3318     const int height = 16;
3319     int row = 2 + (n % height);
3320     int col = (n / height) * COLS / 2;
3321     char temp[80];
3322
3323     mvprintw(row, col, "%*s : ", COLS / 4, name);
3324     attr_set(attr, pair, 0);
3325     addstr(strcpy(temp, code));
3326     attr_set(A_NORMAL, 0, 0);
3327     return n + 1;
3328 }
3329
3330 #define SHOW_UTF8(n, name, code) show_2_wacs(n, name, code, attr, pair)
3331
3332 static void
3333 show_utf8_chars(int repeat, attr_t attr, short pair)
3334 {
3335     int n;
3336
3337     (void) repeat;
3338     erase();
3339     attron(A_BOLD);
3340     mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
3341     attroff(A_BOLD);
3342     refresh();
3343     /* *INDENT-OFF* */
3344     n = SHOW_UTF8(0, "WACS_ULCORNER",   "\342\224\214");
3345     n = SHOW_UTF8(n, "WACS_URCORNER",   "\342\224\220");
3346     n = SHOW_UTF8(n, "WACS_LLCORNER",   "\342\224\224");
3347     n = SHOW_UTF8(n, "WACS_LRCORNER",   "\342\224\230");
3348
3349     n = SHOW_UTF8(n, "WACS_LTEE",       "\342\224\234");
3350     n = SHOW_UTF8(n, "WACS_RTEE",       "\342\224\244");
3351     n = SHOW_UTF8(n, "WACS_TTEE",       "\342\224\254");
3352     n = SHOW_UTF8(n, "WACS_BTEE",       "\342\224\264");
3353
3354     n = SHOW_UTF8(n, "WACS_HLINE",      "\342\224\200");
3355     n = SHOW_UTF8(n, "WACS_VLINE",      "\342\224\202");
3356
3357     n = SHOW_UTF8(n, "WACS_LARROW",     "\342\206\220");
3358     n = SHOW_UTF8(n, "WACS_RARROW",     "\342\206\222");
3359     n = SHOW_UTF8(n, "WACS_UARROW",     "\342\206\221");
3360     n = SHOW_UTF8(n, "WACS_DARROW",     "\342\206\223");
3361
3362     n = SHOW_UTF8(n, "WACS_BLOCK",      "\342\226\256");
3363     n = SHOW_UTF8(n, "WACS_BOARD",      "\342\226\222");
3364     n = SHOW_UTF8(n, "WACS_LANTERN",    "\342\230\203");
3365     n = SHOW_UTF8(n, "WACS_BULLET",     "\302\267");
3366     n = SHOW_UTF8(n, "WACS_CKBOARD",    "\342\226\222");
3367     n = SHOW_UTF8(n, "WACS_DEGREE",     "\302\260");
3368     n = SHOW_UTF8(n, "WACS_DIAMOND",    "\342\227\206");
3369     n = SHOW_UTF8(n, "WACS_PLMINUS",    "\302\261");
3370     n = SHOW_UTF8(n, "WACS_PLUS",       "\342\224\274");
3371     n = SHOW_UTF8(n, "WACS_GEQUAL",     "\342\211\245");
3372     n = SHOW_UTF8(n, "WACS_NEQUAL",     "\342\211\240");
3373     n = SHOW_UTF8(n, "WACS_LEQUAL",     "\342\211\244");
3374
3375     n = SHOW_UTF8(n, "WACS_STERLING",   "\302\243");
3376     n = SHOW_UTF8(n, "WACS_PI",         "\317\200");
3377     n = SHOW_UTF8(n, "WACS_S1",         "\342\216\272");
3378     n = SHOW_UTF8(n, "WACS_S3",         "\342\216\273");
3379     n = SHOW_UTF8(n, "WACS_S7",         "\342\216\274");
3380     n = SHOW_UTF8(n, "WACS_S9",         "\342\216\275");
3381     /* *INDENT-ON* */
3382
3383 }
3384
3385 /* display the wide-ACS character set */
3386 static void
3387 wide_acs_display(void)
3388 {
3389     int c = 'a';
3390     int digit = 0;
3391     int repeat = 1;
3392     int space = ' ';
3393     chtype attr = A_NORMAL;
3394     int fg = COLOR_BLACK;
3395     int bg = COLOR_BLACK;
3396     unsigned at_code = 0;
3397     short pair = 0;
3398     void (*last_show_wacs) (int, attr_t, short) = 0;
3399
3400     do {
3401         switch (c) {
3402         case CTRL('L'):
3403             Repaint();
3404             break;
3405         case 'a':
3406             ToggleAcs(last_show_wacs, show_wacs_chars);
3407             break;
3408         case 'x':
3409             ToggleAcs(last_show_wacs, show_wbox_chars);
3410             break;
3411         case 'u':
3412             ToggleAcs(last_show_wacs, show_utf8_chars);
3413             break;
3414         default:
3415             if (c < 256 && isdigit(c)) {
3416                 digit = (c - '0');
3417                 last_show_wacs = 0;
3418             } else if (c == '+') {
3419                 ++digit;
3420                 last_show_wacs = 0;
3421             } else if (c == '-' && digit > 0) {
3422                 --digit;
3423                 last_show_wacs = 0;
3424             } else if (c == '>' && repeat < (COLS / 4)) {
3425                 ++repeat;
3426             } else if (c == '<' && repeat > 1) {
3427                 --repeat;
3428             } else if (c == '_') {
3429                 space = (space == ' ') ? '_' : ' ';
3430                 last_show_wacs = 0;
3431             } else if (cycle_attr(c, &at_code, &attr)
3432                        || cycle_colors(c, &fg, &bg, &pair)) {
3433                 if (last_show_wacs != 0)
3434                     break;
3435             } else {
3436                 beep();
3437                 break;
3438             }
3439             break;
3440         }
3441         if (last_show_wacs != 0)
3442             last_show_wacs(repeat, attr, pair);
3443         else
3444             show_upper_widechars(digit * 32 + 128, repeat, space, attr, pair);
3445
3446         mvprintw(LINES - 3, 0,
3447                  "Select: a WACS, x box, u UTF-8, 0-9,+/- non-ASCII, </> repeat, ESC=quit");
3448         if (use_colors) {
3449             mvprintw(LINES - 2, 0,
3450                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3451                      attrs_to_cycle[at_code].name,
3452                      fg, bg);
3453         } else {
3454             mvprintw(LINES - 2, 0,
3455                      "v/V cycles through video attributes (%s).",
3456                      attrs_to_cycle[at_code].name);
3457         }
3458         refresh();
3459     } while (!isQuit(c = Getchar()));
3460
3461     Pause();
3462     erase();
3463     endwin();
3464 }
3465
3466 #endif
3467
3468 /*
3469  * Graphic-rendition test (adapted from vttest)
3470  */
3471 static void
3472 test_sgr_attributes(void)
3473 {
3474     int pass;
3475
3476     for (pass = 0; pass < 2; pass++) {
3477         chtype normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK;
3478
3479         /* Use non-default colors if possible to exercise bce a little */
3480         if (use_colors) {
3481             init_pair(1, COLOR_WHITE, COLOR_BLUE);
3482             normal |= COLOR_PAIR(1);
3483         }
3484         bkgdset(normal);
3485         erase();
3486         mvprintw(1, 20, "Graphic rendition test pattern:");
3487
3488         mvprintw(4, 1, "vanilla");
3489
3490 #define set_sgr(mask) bkgdset((normal^(mask)));
3491         set_sgr(A_BOLD);
3492         mvprintw(4, 40, "bold");
3493
3494         set_sgr(A_UNDERLINE);
3495         mvprintw(6, 6, "underline");
3496
3497         set_sgr(A_BOLD | A_UNDERLINE);
3498         mvprintw(6, 45, "bold underline");
3499
3500         set_sgr(A_BLINK);
3501         mvprintw(8, 1, "blink");
3502
3503         set_sgr(A_BLINK | A_BOLD);
3504         mvprintw(8, 40, "bold blink");
3505
3506         set_sgr(A_UNDERLINE | A_BLINK);
3507         mvprintw(10, 6, "underline blink");
3508
3509         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK);
3510         mvprintw(10, 45, "bold underline blink");
3511
3512         set_sgr(A_REVERSE);
3513         mvprintw(12, 1, "negative");
3514
3515         set_sgr(A_BOLD | A_REVERSE);
3516         mvprintw(12, 40, "bold negative");
3517
3518         set_sgr(A_UNDERLINE | A_REVERSE);
3519         mvprintw(14, 6, "underline negative");
3520
3521         set_sgr(A_BOLD | A_UNDERLINE | A_REVERSE);
3522         mvprintw(14, 45, "bold underline negative");
3523
3524         set_sgr(A_BLINK | A_REVERSE);
3525         mvprintw(16, 1, "blink negative");
3526
3527         set_sgr(A_BOLD | A_BLINK | A_REVERSE);
3528         mvprintw(16, 40, "bold blink negative");
3529
3530         set_sgr(A_UNDERLINE | A_BLINK | A_REVERSE);
3531         mvprintw(18, 6, "underline blink negative");
3532
3533         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK | A_REVERSE);
3534         mvprintw(18, 45, "bold underline blink negative");
3535
3536         bkgdset(normal);
3537         mvprintw(LINES - 2, 1, "%s background. ", pass == 0 ? "Dark" :
3538                  "Light");
3539         clrtoeol();
3540         Pause();
3541     }
3542
3543     bkgdset(A_NORMAL | BLANK);
3544     erase();
3545     endwin();
3546 }
3547
3548 /****************************************************************************
3549  *
3550  * Windows and scrolling tester.
3551  *
3552  ****************************************************************************/
3553
3554 #define BOTLINES        4       /* number of line stolen from screen bottom */
3555
3556 typedef struct {
3557     int y, x;
3558 } pair;
3559
3560 #define FRAME struct frame
3561 FRAME
3562 {
3563     FRAME *next, *last;
3564     bool do_scroll;
3565     bool do_keypad;
3566     WINDOW *wind;
3567 };
3568
3569 #if defined(NCURSES_VERSION)
3570 #if (NCURSES_VERSION_PATCH < 20070331) && NCURSES_EXT_FUNCS
3571 #define is_keypad(win)   (win)->_use_keypad
3572 #define is_scrollok(win) (win)->_scroll
3573 #elif !defined(is_keypad)
3574 #define is_keypad(win)   FALSE
3575 #define is_scrollok(win) FALSE
3576 #endif
3577 #else
3578 #define is_keypad(win)   FALSE
3579 #define is_scrollok(win) FALSE
3580 #endif
3581
3582 static WINDOW *
3583 frame_win(FRAME * curp)
3584 {
3585     return (curp != 0) ? curp->wind : stdscr;
3586 }
3587
3588 /* We need to know if these flags are actually set, so don't look in FRAME.
3589  * These names are known to work with SVr4 curses as well as ncurses.  The
3590  * _use_keypad name does not work with Solaris 8.
3591  */
3592 static bool
3593 HaveKeypad(FRAME * curp)
3594 {
3595     WINDOW *win = frame_win(curp);
3596     (void) win;
3597     return is_keypad(win);
3598 }
3599
3600 static bool
3601 HaveScroll(FRAME * curp)
3602 {
3603     WINDOW *win = frame_win(curp);
3604     (void) win;
3605     return is_scrollok(win);
3606 }
3607
3608 static void
3609 newwin_legend(FRAME * curp)
3610 {
3611     static const struct {
3612         const char *msg;
3613         int code;
3614     } legend[] = {
3615         {
3616             "^C = create window", 0
3617         },
3618         {
3619             "^N = next window", 0
3620         },
3621         {
3622             "^P = previous window", 0
3623         },
3624         {
3625             "^F = scroll forward", 0
3626         },
3627         {
3628             "^B = scroll backward", 0
3629         },
3630         {
3631             "^K = keypad(%s)", 1
3632         },
3633         {
3634             "^S = scrollok(%s)", 2
3635         },
3636         {
3637             "^W = save window to file", 0
3638         },
3639         {
3640             "^R = restore window", 0
3641         },
3642 #if HAVE_WRESIZE
3643         {
3644             "^X = resize", 0
3645         },
3646 #endif
3647         {
3648             "^Q%s = exit", 3
3649         }
3650     };
3651     size_t n;
3652     int x;
3653     bool do_keypad = HaveKeypad(curp);
3654     bool do_scroll = HaveScroll(curp);
3655     char buf[BUFSIZ];
3656
3657     move(LINES - 4, 0);
3658     for (n = 0; n < SIZEOF(legend); n++) {
3659         switch (legend[n].code) {
3660         default:
3661             strcpy(buf, legend[n].msg);
3662             break;
3663         case 1:
3664             sprintf(buf, legend[n].msg, do_keypad ? "yes" : "no");
3665             break;
3666         case 2:
3667             sprintf(buf, legend[n].msg, do_scroll ? "yes" : "no");
3668             break;
3669         case 3:
3670             sprintf(buf, legend[n].msg, do_keypad ? "/ESC" : "");
3671             break;
3672         }
3673         x = getcurx(stdscr);
3674         addstr((COLS < (x + 3 + (int) strlen(buf))) ? "\n" : (n ? ", " : ""));
3675         addstr(buf);
3676     }
3677     clrtoeol();
3678 }
3679
3680 static void
3681 transient(FRAME * curp, NCURSES_CONST char *msg)
3682 {
3683     newwin_legend(curp);
3684     if (msg) {
3685         mvaddstr(LINES - 1, 0, msg);
3686         refresh();
3687         napms(1000);
3688     }
3689
3690     move(LINES - 1, 0);
3691     printw("%s characters are echoed, window should %sscroll.",
3692            HaveKeypad(curp) ? "Non-arrow" : "All other",
3693            HaveScroll(curp) ? "" : "not ");
3694     clrtoeol();
3695 }
3696
3697 static void
3698 newwin_report(FRAME * curp)
3699 /* report on the cursor's current position, then restore it */
3700 {
3701     WINDOW *win = frame_win(curp);
3702     int y, x;
3703
3704     if (win != stdscr)
3705         transient(curp, (char *) 0);
3706     getyx(win, y, x);
3707     move(LINES - 1, COLS - 17);
3708     printw("Y = %2d X = %2d", y, x);
3709     if (win != stdscr)
3710         refresh();
3711     else
3712         wmove(win, y, x);
3713 }
3714
3715 static pair *
3716 selectcell(int uli, int ulj, int lri, int lrj)
3717 /* arrows keys move cursor, return location at current on non-arrow key */
3718 {
3719     static pair res;            /* result cell */
3720     int si = lri - uli + 1;     /* depth of the select area */
3721     int sj = lrj - ulj + 1;     /* width of the select area */
3722     int i = 0, j = 0;           /* offsets into the select area */
3723
3724     res.y = uli;
3725     res.x = ulj;
3726     for (;;) {
3727         move(uli + i, ulj + j);
3728         newwin_report((FRAME *) 0);
3729
3730         switch (Getchar()) {
3731         case KEY_UP:
3732             i += si - 1;
3733             break;
3734         case KEY_DOWN:
3735             i++;
3736             break;
3737         case KEY_LEFT:
3738             j += sj - 1;
3739             break;
3740         case KEY_RIGHT:
3741             j++;
3742             break;
3743         case case_QUIT:
3744             return ((pair *) 0);
3745 #ifdef NCURSES_MOUSE_VERSION
3746         case KEY_MOUSE:
3747             {
3748                 MEVENT event;
3749
3750                 getmouse(&event);
3751                 if (event.y > uli && event.x > ulj) {
3752                     i = event.y - uli;
3753                     j = event.x - ulj;
3754                 } else {
3755                     beep();
3756                     break;
3757                 }
3758             }
3759             /* FALLTHRU */
3760 #endif
3761         default:
3762             res.y = uli + i;
3763             res.x = ulj + j;
3764             return (&res);
3765         }
3766         i %= si;
3767         j %= sj;
3768     }
3769 }
3770
3771 static void
3772 outerbox(pair ul, pair lr, bool onoff)
3773 /* draw or erase a box *outside* the given pair of corners */
3774 {
3775     mvaddch(ul.y - 1, lr.x - 1, onoff ? ACS_ULCORNER : ' ');
3776     mvaddch(ul.y - 1, lr.x + 1, onoff ? ACS_URCORNER : ' ');
3777     mvaddch(lr.y + 1, lr.x + 1, onoff ? ACS_LRCORNER : ' ');
3778     mvaddch(lr.y + 1, ul.x - 1, onoff ? ACS_LLCORNER : ' ');
3779     move(ul.y - 1, ul.x);
3780     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
3781     move(ul.y, ul.x - 1);
3782     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
3783     move(lr.y + 1, ul.x);
3784     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
3785     move(ul.y, lr.x + 1);
3786     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
3787 }
3788
3789 static WINDOW *
3790 getwindow(void)
3791 /* Ask user for a window definition */
3792 {
3793     WINDOW *rwindow;
3794     pair ul, lr, *tmp;
3795
3796     move(0, 0);
3797     clrtoeol();
3798     addstr("Use arrows to move cursor, anything else to mark corner 1");
3799     refresh();
3800     if ((tmp = selectcell(2, 1, LINES - BOTLINES - 2, COLS - 2)) == (pair *) 0)
3801         return ((WINDOW *) 0);
3802     memcpy(&ul, tmp, sizeof(pair));
3803     mvaddch(ul.y - 1, ul.x - 1, ACS_ULCORNER);
3804     move(0, 0);
3805     clrtoeol();
3806     addstr("Use arrows to move cursor, anything else to mark corner 2");
3807     refresh();
3808     if ((tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2)) ==
3809         (pair *) 0)
3810         return ((WINDOW *) 0);
3811     memcpy(&lr, tmp, sizeof(pair));
3812
3813     rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
3814
3815     outerbox(ul, lr, TRUE);
3816     refresh();
3817
3818     wrefresh(rwindow);
3819
3820     move(0, 0);
3821     clrtoeol();
3822     return (rwindow);
3823 }
3824
3825 static void
3826 newwin_move(FRAME * curp, int dy, int dx)
3827 {
3828     WINDOW *win = frame_win(curp);
3829     int cur_y, cur_x;
3830     int max_y, max_x;
3831
3832     getyx(win, cur_y, cur_x);
3833     getmaxyx(win, max_y, max_x);
3834     if ((cur_x += dx) < 0)
3835         cur_x = 0;
3836     else if (cur_x >= max_x)
3837         cur_x = max_x - 1;
3838     if ((cur_y += dy) < 0)
3839         cur_y = 0;
3840     else if (cur_y >= max_y)
3841         cur_y = max_y - 1;
3842     wmove(win, cur_y, cur_x);
3843 }
3844
3845 static FRAME *
3846 delete_framed(FRAME * fp, bool showit)
3847 {
3848     FRAME *np = 0;
3849
3850     if (fp != 0) {
3851         fp->last->next = fp->next;
3852         fp->next->last = fp->last;
3853
3854         if (showit) {
3855             werase(fp->wind);
3856             wrefresh(fp->wind);
3857         }
3858         delwin(fp->wind);
3859
3860         np = (fp == fp->next) ? 0 : fp->next;
3861         free(fp);
3862     }
3863     return np;
3864 }
3865
3866 static void
3867 acs_and_scroll(void)
3868 /* Demonstrate windows */
3869 {
3870     int c;
3871     FRAME *current = (FRAME *) 0, *neww;
3872     WINDOW *usescr = stdscr;
3873 #if HAVE_PUTWIN && HAVE_GETWIN
3874     FILE *fp;
3875 #endif
3876
3877 #define DUMPFILE        "screendump"
3878
3879 #ifdef NCURSES_MOUSE_VERSION
3880     mousemask(BUTTON1_CLICKED, (mmask_t *) 0);
3881 #endif
3882     c = CTRL('C');
3883     raw();
3884     do {
3885         transient((FRAME *) 0, (char *) 0);
3886         switch (c) {
3887         case CTRL('C'):
3888             if ((neww = typeCalloc(FRAME, 1)) == 0) {
3889                 goto breakout;
3890             }
3891             if ((neww->wind = getwindow()) == (WINDOW *) 0) {
3892                 free(neww);
3893                 goto breakout;
3894             }
3895
3896             if (current == 0) { /* First element,  */
3897                 neww->next = neww;      /*   so point it at itself */
3898                 neww->last = neww;
3899             } else {
3900                 neww->next = current->next;
3901                 neww->last = current;
3902                 neww->last->next = neww;
3903                 neww->next->last = neww;
3904             }
3905             current = neww;
3906             /* SVr4 curses sets the keypad on all newly-created windows to
3907              * false.  Someone reported that PDCurses makes new windows inherit
3908              * this flag.  Remove the following 'keypad()' call to test this
3909              */
3910             keypad(current->wind, TRUE);
3911             current->do_keypad = HaveKeypad(current);
3912             current->do_scroll = HaveScroll(current);
3913             break;
3914
3915         case CTRL('N'): /* go to next window */
3916             if (current)
3917                 current = current->next;
3918             break;
3919
3920         case CTRL('P'): /* go to previous window */
3921             if (current)
3922                 current = current->last;
3923             break;
3924
3925         case CTRL('F'): /* scroll current window forward */
3926             if (current)
3927                 wscrl(frame_win(current), 1);
3928             break;
3929
3930         case CTRL('B'): /* scroll current window backwards */
3931             if (current)
3932                 wscrl(frame_win(current), -1);
3933             break;
3934
3935         case CTRL('K'): /* toggle keypad mode for current */
3936             if (current) {
3937                 current->do_keypad = !current->do_keypad;
3938                 keypad(current->wind, current->do_keypad);
3939             }
3940             break;
3941
3942         case CTRL('S'):
3943             if (current) {
3944                 current->do_scroll = !current->do_scroll;
3945                 scrollok(current->wind, current->do_scroll);
3946             }
3947             break;
3948
3949 #if HAVE_PUTWIN && HAVE_GETWIN
3950         case CTRL('W'): /* save and delete window */
3951             if ((current != 0) && (current == current->next)) {
3952                 transient(current, "Will not save/delete ONLY window");
3953                 break;
3954             } else if ((fp = fopen(DUMPFILE, "w")) == (FILE *) 0) {
3955                 transient(current, "Can't open screen dump file");
3956             } else {
3957                 (void) putwin(frame_win(current), fp);
3958                 (void) fclose(fp);
3959
3960                 current = delete_framed(current, TRUE);
3961             }
3962             break;
3963
3964         case CTRL('R'): /* restore window */
3965             if ((fp = fopen(DUMPFILE, "r")) == (FILE *) 0) {
3966                 transient(current, "Can't open screen dump file");
3967             } else {
3968                 if ((neww = typeCalloc(FRAME, 1)) != 0) {
3969
3970                     neww->next = current ? current->next : 0;
3971                     neww->last = current;
3972                     neww->last->next = neww;
3973                     neww->next->last = neww;
3974
3975                     neww->wind = getwin(fp);
3976
3977                     wrefresh(neww->wind);
3978                 }
3979                 (void) fclose(fp);
3980             }
3981             break;
3982 #endif
3983
3984 #if HAVE_WRESIZE
3985         case CTRL('X'): /* resize window */
3986             if (current) {
3987                 pair *tmp, ul, lr;
3988                 int i, mx, my;
3989
3990                 move(0, 0);
3991                 clrtoeol();
3992                 addstr("Use arrows to move cursor, anything else to mark new corner");
3993                 refresh();
3994
3995                 getbegyx(current->wind, ul.y, ul.x);
3996
3997                 tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2);
3998                 if (tmp == (pair *) 0) {
3999                     beep();
4000                     break;
4001                 }
4002
4003                 getmaxyx(current->wind, lr.y, lr.x);
4004                 lr.y += (ul.y - 1);
4005                 lr.x += (ul.x - 1);
4006                 outerbox(ul, lr, FALSE);
4007                 wnoutrefresh(stdscr);
4008
4009                 /* strictly cosmetic hack for the test */
4010                 getmaxyx(current->wind, my, mx);
4011                 if (my > tmp->y - ul.y) {
4012                     getyx(current->wind, lr.y, lr.x);
4013                     wmove(current->wind, tmp->y - ul.y + 1, 0);
4014                     wclrtobot(current->wind);
4015                     wmove(current->wind, lr.y, lr.x);
4016                 }
4017                 if (mx > tmp->x - ul.x)
4018                     for (i = 0; i < my; i++) {
4019                         wmove(current->wind, i, tmp->x - ul.x + 1);
4020                         wclrtoeol(current->wind);
4021                     }
4022                 wnoutrefresh(current->wind);
4023
4024                 memcpy(&lr, tmp, sizeof(pair));
4025                 (void) wresize(current->wind, lr.y - ul.y + 0, lr.x - ul.x + 0);
4026
4027                 getbegyx(current->wind, ul.y, ul.x);
4028                 getmaxyx(current->wind, lr.y, lr.x);
4029                 lr.y += (ul.y - 1);
4030                 lr.x += (ul.x - 1);
4031                 outerbox(ul, lr, TRUE);
4032                 wnoutrefresh(stdscr);
4033
4034                 wnoutrefresh(current->wind);
4035                 move(0, 0);
4036                 clrtoeol();
4037                 doupdate();
4038             }
4039             break;
4040 #endif /* HAVE_WRESIZE */
4041
4042         case KEY_F(10): /* undocumented --- use this to test area clears */
4043             selectcell(0, 0, LINES - 1, COLS - 1);
4044             clrtobot();
4045             refresh();
4046             break;
4047
4048         case KEY_UP:
4049             newwin_move(current, -1, 0);
4050             break;
4051         case KEY_DOWN:
4052             newwin_move(current, 1, 0);
4053             break;
4054         case KEY_LEFT:
4055             newwin_move(current, 0, -1);
4056             break;
4057         case KEY_RIGHT:
4058             newwin_move(current, 0, 1);
4059             break;
4060
4061         case KEY_BACKSPACE:
4062             /* FALLTHROUGH */
4063         case KEY_DC:
4064             {
4065                 int y, x;
4066                 getyx(frame_win(current), y, x);
4067                 if (--x < 0) {
4068                     if (--y < 0)
4069                         break;
4070                     x = getmaxx(frame_win(current)) - 1;
4071                 }
4072                 mvwdelch(frame_win(current), y, x);
4073             }
4074             break;
4075
4076         case '\r':
4077             c = '\n';
4078             /* FALLTHROUGH */
4079
4080         default:
4081             if (current)
4082                 waddch(current->wind, (chtype) c);
4083             else
4084                 beep();
4085             break;
4086         }
4087         newwin_report(current);
4088         usescr = frame_win(current);
4089         wrefresh(usescr);
4090     } while
4091         (!isQuit(c = wGetchar(usescr))
4092          && (c != ERR));
4093
4094   breakout:
4095     while (current != 0)
4096         current = delete_framed(current, FALSE);
4097
4098     scrollok(stdscr, TRUE);     /* reset to driver's default */
4099 #ifdef NCURSES_MOUSE_VERSION
4100     mousemask(0, (mmask_t *) 0);
4101 #endif
4102     noraw();
4103     erase();
4104     endwin();
4105 }
4106
4107 /****************************************************************************
4108  *
4109  * Panels tester
4110  *
4111  ****************************************************************************/
4112
4113 #if USE_LIBPANEL
4114 static int nap_msec = 1;
4115
4116 static NCURSES_CONST char *mod[] =
4117 {
4118     "test ",
4119     "TEST ",
4120     "(**) ",
4121     "*()* ",
4122     "<--> ",
4123     "LAST "
4124 };
4125
4126 /*+-------------------------------------------------------------------------
4127         wait_a_while(msec)
4128 --------------------------------------------------------------------------*/
4129 static void
4130 wait_a_while(int msec GCC_UNUSED)
4131 {
4132 #if HAVE_NAPMS
4133     if (nap_msec == 1)
4134         wGetchar(stdscr);
4135     else
4136         napms(nap_msec);
4137 #else
4138     if (nap_msec == 1)
4139         wGetchar(stdscr);
4140     else if (msec > 1000)
4141         sleep((unsigned) msec / 1000);
4142     else
4143         sleep(1);
4144 #endif
4145 }                               /* end of wait_a_while */
4146
4147 /*+-------------------------------------------------------------------------
4148         saywhat(text)
4149 --------------------------------------------------------------------------*/
4150 static void
4151 saywhat(NCURSES_CONST char *text)
4152 {
4153     wmove(stdscr, LINES - 1, 0);
4154     wclrtoeol(stdscr);
4155     if (text != 0 && *text != '\0') {
4156         waddstr(stdscr, text);
4157         waddstr(stdscr, "; ");
4158     }
4159     waddstr(stdscr, "press any key to continue");
4160 }                               /* end of saywhat */
4161
4162 /*+-------------------------------------------------------------------------
4163         mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
4164 --------------------------------------------------------------------------*/
4165 static PANEL *
4166 mkpanel(short color, int rows, int cols, int tly, int tlx)
4167 {
4168     WINDOW *win;
4169     PANEL *pan = 0;
4170
4171     if ((win = newwin(rows, cols, tly, tlx)) != 0) {
4172         if ((pan = new_panel(win)) == 0) {
4173             delwin(win);
4174         } else if (use_colors) {
4175             short fg = (short) ((color == COLOR_BLUE) ? COLOR_WHITE : COLOR_BLACK);
4176             short bg = color;
4177
4178             init_pair(color, fg, bg);
4179             wbkgdset(win, (chtype) (COLOR_PAIR(color) | ' '));
4180         } else {
4181             wbkgdset(win, A_BOLD | ' ');
4182         }
4183     }
4184     return pan;
4185 }                               /* end of mkpanel */
4186
4187 /*+-------------------------------------------------------------------------
4188         rmpanel(pan)
4189 --------------------------------------------------------------------------*/
4190 static void
4191 rmpanel(PANEL * pan)
4192 {
4193     WINDOW *win = panel_window(pan);
4194     del_panel(pan);
4195     delwin(win);
4196 }                               /* end of rmpanel */
4197
4198 /*+-------------------------------------------------------------------------
4199         pflush()
4200 --------------------------------------------------------------------------*/
4201 static void
4202 pflush(void)
4203 {
4204     update_panels();
4205     doupdate();
4206 }                               /* end of pflush */
4207
4208 /*+-------------------------------------------------------------------------
4209         fill_panel(win)
4210 --------------------------------------------------------------------------*/
4211 static void
4212 init_panel(void)
4213 {
4214     register int y, x;
4215
4216     for (y = 0; y < LINES - 1; y++) {
4217         for (x = 0; x < COLS; x++)
4218             wprintw(stdscr, "%d", (y + x) % 10);
4219     }
4220 }
4221
4222 static void
4223 fill_panel(PANEL * pan)
4224 {
4225     WINDOW *win = panel_window(pan);
4226     const char *userptr = (const char *) panel_userptr(pan);
4227     int num = (userptr && *userptr) ? userptr[1] : '?';
4228     int y, x;
4229
4230     wmove(win, 1, 1);
4231     wprintw(win, "-pan%c-", num);
4232     wclrtoeol(win);
4233     box(win, 0, 0);
4234     for (y = 2; y < getmaxy(win) - 1; y++) {
4235         for (x = 1; x < getmaxx(win) - 1; x++) {
4236             wmove(win, y, x);
4237             waddch(win, UChar(num));
4238         }
4239     }
4240 }
4241
4242 #if USE_WIDEC_SUPPORT
4243 static void
4244 init_wide_panel(void)
4245 {
4246     int digit;
4247     cchar_t temp[10];
4248
4249     for (digit = 0; digit < 10; ++digit)
4250         make_fullwidth_digit(&temp[digit], digit);
4251
4252     do {
4253         int y, x;
4254         getyx(stdscr, y, x);
4255         digit = (y + x / 2) % 10;
4256     } while (add_wch(&temp[digit]) != ERR);
4257 }
4258
4259 static void
4260 fill_wide_panel(PANEL * pan)
4261 {
4262     WINDOW *win = panel_window(pan);
4263     const char *userptr = (const char *) panel_userptr(pan);
4264     int num = (userptr && *userptr) ? userptr[1] : '?';
4265     int y, x;
4266
4267     wmove(win, 1, 1);
4268     wprintw(win, "-pan%c-", num);
4269     wclrtoeol(win);
4270     box(win, 0, 0);
4271     for (y = 2; y < getmaxy(win) - 1; y++) {
4272         for (x = 1; x < getmaxx(win) - 1; x++) {
4273             wmove(win, y, x);
4274             waddch(win, UChar(num));
4275         }
4276     }
4277 }
4278 #endif
4279
4280 #define MAX_PANELS 5
4281
4282 static void
4283 canned_panel(PANEL * px[MAX_PANELS + 1], NCURSES_CONST char *cmd)
4284 {
4285     int which = cmd[1] - '0';
4286
4287     saywhat(cmd);
4288     switch (*cmd) {
4289     case 'h':
4290         hide_panel(px[which]);
4291         break;
4292     case 's':
4293         show_panel(px[which]);
4294         break;
4295     case 't':
4296         top_panel(px[which]);
4297         break;
4298     case 'b':
4299         bottom_panel(px[which]);
4300         break;
4301     case 'd':
4302         rmpanel(px[which]);
4303         break;
4304     }
4305     pflush();
4306     wait_a_while(nap_msec);
4307 }
4308
4309 static void
4310 demo_panels(void (*InitPanel) (void), void (*FillPanel) (PANEL *))
4311 {
4312     int count;
4313     int itmp;
4314     PANEL *px[MAX_PANELS + 1];
4315
4316     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4317     refresh();
4318
4319     InitPanel();
4320     for (count = 0; count < 5; count++) {
4321         px[1] = mkpanel(COLOR_RED,
4322                         LINES / 2 - 2,
4323                         COLS / 8 + 1,
4324                         0,
4325                         0);
4326         set_panel_userptr(px[1], (NCURSES_CONST void *) "p1");
4327
4328         px[2] = mkpanel(COLOR_GREEN,
4329                         LINES / 2 + 1,
4330                         COLS / 7,
4331                         LINES / 4,
4332                         COLS / 10);
4333         set_panel_userptr(px[2], (NCURSES_CONST void *) "p2");
4334
4335         px[3] = mkpanel(COLOR_YELLOW,
4336                         LINES / 4,
4337                         COLS / 10,
4338                         LINES / 2,
4339                         COLS / 9);
4340         set_panel_userptr(px[3], (NCURSES_CONST void *) "p3");
4341
4342         px[4] = mkpanel(COLOR_BLUE,
4343                         LINES / 2 - 2,
4344                         COLS / 8,
4345                         LINES / 2 - 2,
4346                         COLS / 3);
4347         set_panel_userptr(px[4], (NCURSES_CONST void *) "p4");
4348
4349         px[5] = mkpanel(COLOR_MAGENTA,
4350                         LINES / 2 - 2,
4351                         COLS / 8,
4352                         LINES / 2,
4353                         COLS / 2 - 2);
4354         set_panel_userptr(px[5], (NCURSES_CONST void *) "p5");
4355
4356         FillPanel(px[1]);
4357         FillPanel(px[2]);
4358         FillPanel(px[3]);
4359         FillPanel(px[4]);
4360         FillPanel(px[5]);
4361
4362         hide_panel(px[4]);
4363         hide_panel(px[5]);
4364         pflush();
4365         saywhat("");
4366         wait_a_while(nap_msec);
4367
4368         saywhat("h3 s1 s2 s4 s5");
4369         move_panel(px[1], 0, 0);
4370         hide_panel(px[3]);
4371         show_panel(px[1]);
4372         show_panel(px[2]);
4373         show_panel(px[4]);
4374         show_panel(px[5]);
4375         pflush();
4376         wait_a_while(nap_msec);
4377
4378         canned_panel(px, "s1");
4379         canned_panel(px, "s2");
4380
4381         saywhat("m2");
4382         move_panel(px[2], LINES / 3 + 1, COLS / 8);
4383         pflush();
4384         wait_a_while(nap_msec);
4385
4386         canned_panel(px, "s3");
4387
4388         saywhat("m3");
4389         move_panel(px[3], LINES / 4 + 1, COLS / 15);
4390         pflush();
4391         wait_a_while(nap_msec);
4392
4393         canned_panel(px, "b3");
4394         canned_panel(px, "s4");
4395         canned_panel(px, "s5");
4396         canned_panel(px, "t3");
4397         canned_panel(px, "t1");
4398         canned_panel(px, "t2");
4399         canned_panel(px, "t3");
4400         canned_panel(px, "t4");
4401
4402         for (itmp = 0; itmp < 6; itmp++) {
4403             WINDOW *w4 = panel_window(px[4]);
4404             WINDOW *w5 = panel_window(px[5]);
4405
4406             saywhat("m4");
4407             wmove(w4, LINES / 8, 1);
4408             waddstr(w4, mod[itmp]);
4409             move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4410             wmove(w5, LINES / 6, 1);
4411             waddstr(w5, mod[itmp]);
4412             pflush();
4413             wait_a_while(nap_msec);
4414
4415             saywhat("m5");
4416             wmove(w4, LINES / 6, 1);
4417             waddstr(w4, mod[itmp]);
4418             move_panel(px[5], LINES / 3 - 1, (itmp * 10) + 6);
4419             wmove(w5, LINES / 8, 1);
4420             waddstr(w5, mod[itmp]);
4421             pflush();
4422             wait_a_while(nap_msec);
4423         }
4424
4425         saywhat("m4");
4426         move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4427         pflush();
4428         wait_a_while(nap_msec);
4429
4430         canned_panel(px, "t5");
4431         canned_panel(px, "t2");
4432         canned_panel(px, "t1");
4433         canned_panel(px, "d2");
4434         canned_panel(px, "h3");
4435         canned_panel(px, "d1");
4436         canned_panel(px, "d4");
4437         canned_panel(px, "d5");
4438         canned_panel(px, "d3");
4439
4440         wait_a_while(nap_msec);
4441         if (nap_msec == 1)
4442             break;
4443         nap_msec = 100L;
4444     }
4445
4446     erase();
4447     endwin();
4448 }
4449 #endif /* USE_LIBPANEL */
4450
4451 /****************************************************************************
4452  *
4453  * Pad tester
4454  *
4455  ****************************************************************************/
4456
4457 #define GRIDSIZE        3
4458
4459 static bool pending_pan = FALSE;
4460 static bool show_panner_legend = TRUE;
4461
4462 static int
4463 panner_legend(int line)
4464 {
4465     static const char *const legend[] =
4466     {
4467         "Use arrow keys (or U,D,L,R) to pan, ESC to quit, ! to shell-out.",
4468         "Use +,- (or j,k) to grow/shrink the panner vertically.",
4469         "Use <,> (or h,l) to grow/shrink the panner horizontally.",
4470         "Number repeats.  Toggle legend:? filler:a timer:t scrollmark:s."
4471     };
4472     int n = ((int) SIZEOF(legend) - (LINES - line));
4473     if (line < LINES && (n >= 0)) {
4474         move(line, 0);
4475         if (show_panner_legend)
4476             printw("%s", legend[n]);
4477         clrtoeol();
4478         return show_panner_legend;
4479     }
4480     return FALSE;
4481 }
4482
4483 static void
4484 panner_h_cleanup(int from_y, int from_x, int to_x)
4485 {
4486     if (!panner_legend(from_y))
4487         do_h_line(from_y, from_x, ' ', to_x);
4488 }
4489
4490 static void
4491 panner_v_cleanup(int from_y, int from_x, int to_y)
4492 {
4493     if (!panner_legend(from_y))
4494         do_v_line(from_y, from_x, ' ', to_y);
4495 }
4496
4497 static void
4498 fill_pad(WINDOW *panpad, bool pan_lines)
4499 {
4500     int y, x;
4501     unsigned gridcount = 0;
4502
4503     wmove(panpad, 0, 0);
4504     for (y = 0; y < getmaxy(panpad); y++) {
4505         for (x = 0; x < getmaxx(panpad); x++) {
4506             if (y % GRIDSIZE == 0 && x % GRIDSIZE == 0) {
4507                 if (y == 0 && x == 0)
4508                     waddch(panpad, pan_lines ? ACS_ULCORNER : '+');
4509                 else if (y == 0)
4510                     waddch(panpad, pan_lines ? ACS_TTEE : '+');
4511                 else if (y == 0 || x == 0)
4512                     waddch(panpad, pan_lines ? ACS_LTEE : '+');
4513                 else
4514                     waddch(panpad, (chtype) ((pan_lines ? 'a' : 'A') +
4515                                              (int) (gridcount++ % 26)));
4516             } else if (y % GRIDSIZE == 0)
4517                 waddch(panpad, pan_lines ? ACS_HLINE : '-');
4518             else if (x % GRIDSIZE == 0)
4519                 waddch(panpad, pan_lines ? ACS_VLINE : '|');
4520             else
4521                 waddch(panpad, ' ');
4522         }
4523     }
4524 }
4525
4526 static void
4527 panner(WINDOW *pad,
4528        int top_x, int top_y, int porty, int portx,
4529        int (*pgetc) (WINDOW *))
4530 {
4531 #if HAVE_GETTIMEOFDAY
4532     struct timeval before, after;
4533     bool timing = TRUE;
4534 #endif
4535     bool pan_lines = FALSE;
4536     bool scrollers = TRUE;
4537     int basex = 0;
4538     int basey = 0;
4539     int pxmax, pymax, lowend, highend, c;
4540
4541     getmaxyx(pad, pymax, pxmax);
4542     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4543
4544     c = KEY_REFRESH;
4545     do {
4546 #ifdef NCURSES_VERSION
4547         /*
4548          * During shell-out, the user may have resized the window.  Adjust
4549          * the port size of the pad to accommodate this.  Ncurses automatically
4550          * resizes all of the normal windows to fit on the new screen.
4551          */
4552         if (top_x > COLS)
4553             top_x = COLS;
4554         if (portx > COLS)
4555             portx = COLS;
4556         if (top_y > LINES)
4557             top_y = LINES;
4558         if (porty > LINES)
4559             porty = LINES;
4560 #endif
4561         switch (c) {
4562         case KEY_REFRESH:
4563             erase();
4564
4565             /* FALLTHRU */
4566         case '?':
4567             if (c == '?')
4568                 show_panner_legend = !show_panner_legend;
4569             panner_legend(LINES - 4);
4570             panner_legend(LINES - 3);
4571             panner_legend(LINES - 2);
4572             panner_legend(LINES - 1);
4573             break;
4574         case 'a':
4575             pan_lines = !pan_lines;
4576             fill_pad(pad, pan_lines);
4577             pending_pan = FALSE;
4578             break;
4579
4580 #if HAVE_GETTIMEOFDAY
4581         case 't':
4582             timing = !timing;
4583             if (!timing)
4584                 panner_legend(LINES - 1);
4585             break;
4586 #endif
4587         case 's':
4588             scrollers = !scrollers;
4589             break;
4590
4591             /* Move the top-left corner of the pad, keeping the bottom-right
4592              * corner fixed.
4593              */
4594         case 'h':               /* increase-columns: move left edge to left */
4595             if (top_x <= 0)
4596                 beep();
4597             else {
4598                 panner_v_cleanup(top_y, top_x, porty);
4599                 top_x--;
4600             }
4601             break;
4602
4603         case 'j':               /* decrease-lines: move top-edge down */
4604             if (top_y >= porty)
4605                 beep();
4606             else {
4607                 panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx);
4608                 top_y++;
4609             }
4610             break;
4611
4612         case 'k':               /* increase-lines: move top-edge up */
4613             if (top_y <= 0)
4614                 beep();
4615             else {
4616                 top_y--;
4617                 panner_h_cleanup(top_y, top_x, portx);
4618             }
4619             break;
4620
4621         case 'l':               /* decrease-columns: move left-edge to right */
4622             if (top_x >= portx)
4623                 beep();
4624             else {
4625                 panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty);
4626                 top_x++;
4627             }
4628             break;
4629
4630             /* Move the bottom-right corner of the pad, keeping the top-left
4631              * corner fixed.
4632              */
4633         case KEY_IC:            /* increase-columns: move right-edge to right */
4634             if (portx >= pxmax || portx >= COLS)
4635                 beep();
4636             else {
4637                 panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty);
4638                 ++portx;
4639             }
4640             break;
4641
4642         case KEY_IL:            /* increase-lines: move bottom-edge down */
4643             if (porty >= pymax || porty >= LINES)
4644                 beep();
4645             else {
4646                 panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx);
4647                 ++porty;
4648             }
4649             break;
4650
4651         case KEY_DC:            /* decrease-columns: move bottom edge up */
4652             if (portx <= top_x)
4653                 beep();
4654             else {
4655                 portx--;
4656                 panner_v_cleanup(top_y - (top_y > 0), portx, porty);
4657             }
4658             break;
4659
4660         case KEY_DL:            /* decrease-lines */
4661             if (porty <= top_y)
4662                 beep();
4663             else {
4664                 porty--;
4665                 panner_h_cleanup(porty, top_x - (top_x > 0), portx);
4666             }
4667             break;
4668
4669         case KEY_LEFT:          /* pan leftwards */
4670             if (basex > 0)
4671                 basex--;
4672             else
4673                 beep();
4674             break;
4675
4676         case KEY_RIGHT: /* pan rightwards */
4677             if (basex + portx - (pymax > porty) < pxmax)
4678                 basex++;
4679             else
4680                 beep();
4681             break;
4682
4683         case KEY_UP:            /* pan upwards */
4684             if (basey > 0)
4685                 basey--;
4686             else
4687                 beep();
4688             break;
4689
4690         case KEY_DOWN:          /* pan downwards */
4691             if (basey + porty - (pxmax > portx) < pymax)
4692                 basey++;
4693             else
4694                 beep();
4695             break;
4696
4697         case 'H':
4698         case KEY_HOME:
4699         case KEY_FIND:
4700             basey = 0;
4701             break;
4702
4703         case 'E':
4704         case KEY_END:
4705         case KEY_SELECT:
4706             basey = pymax - porty;
4707             if (basey < 0)
4708                 basey = 0;
4709             break;
4710
4711         default:
4712             beep();
4713             break;
4714         }
4715
4716         mvaddch(top_y - 1, top_x - 1, ACS_ULCORNER);
4717         do_v_line(top_y, top_x - 1, ACS_VLINE, porty);
4718         do_h_line(top_y - 1, top_x, ACS_HLINE, portx);
4719
4720         if (scrollers && (pxmax > portx - 1)) {
4721             int length = (portx - top_x - 1);
4722             float ratio = ((float) length) / ((float) pxmax);
4723
4724             lowend = (int) ((float) top_x + ((float) basex * ratio));
4725             highend = (int) ((float) top_x + ((float) (basex + length) * ratio));
4726
4727             do_h_line(porty - 1, top_x, ACS_HLINE, lowend);
4728             if (highend < portx) {
4729                 attron(A_REVERSE);
4730                 do_h_line(porty - 1, lowend, ' ', highend + 1);
4731                 attroff(A_REVERSE);
4732                 do_h_line(porty - 1, highend + 1, ACS_HLINE, portx);
4733             }
4734         } else
4735             do_h_line(porty - 1, top_x, ACS_HLINE, portx);
4736
4737         if (scrollers && (pymax > porty - 1)) {
4738             int length = (porty - top_y - 1);
4739             float ratio = ((float) length) / ((float) pymax);
4740
4741             lowend = (int) ((float) top_y + ((float) basey * ratio));
4742             highend = (int) ((float) top_y + ((float) (basey + length) * ratio));
4743
4744             do_v_line(top_y, portx - 1, ACS_VLINE, lowend);
4745             if (highend < porty) {
4746                 attron(A_REVERSE);
4747                 do_v_line(lowend, portx - 1, ' ', highend + 1);
4748                 attroff(A_REVERSE);
4749                 do_v_line(highend + 1, portx - 1, ACS_VLINE, porty);
4750             }
4751         } else
4752             do_v_line(top_y, portx - 1, ACS_VLINE, porty);
4753
4754         mvaddch(top_y - 1, portx - 1, ACS_URCORNER);
4755         mvaddch(porty - 1, top_x - 1, ACS_LLCORNER);
4756         mvaddch(porty - 1, portx - 1, ACS_LRCORNER);
4757
4758         if (!pending_pan) {
4759 #if HAVE_GETTIMEOFDAY
4760             gettimeofday(&before, 0);
4761 #endif
4762             wnoutrefresh(stdscr);
4763
4764             pnoutrefresh(pad,
4765                          basey, basex,
4766                          top_y, top_x,
4767                          porty - (pxmax > portx) - 1,
4768                          portx - (pymax > porty) - 1);
4769
4770             doupdate();
4771 #if HAVE_GETTIMEOFDAY
4772             if (timing) {
4773                 double elapsed;
4774                 gettimeofday(&after, 0);
4775                 elapsed = (after.tv_sec + after.tv_usec / 1.0e6)
4776                     - (before.tv_sec + before.tv_usec / 1.0e6);
4777                 move(LINES - 1, COLS - 12);
4778                 printw("Secs: %2.03f", elapsed);
4779                 refresh();
4780             }
4781 #endif
4782         }
4783
4784     } while
4785         ((c = pgetc(pad)) != KEY_EXIT);
4786
4787     scrollok(stdscr, TRUE);     /* reset to driver's default */
4788 }
4789
4790 static int
4791 padgetch(WINDOW *win)
4792 {
4793     static int count;
4794     static int last;
4795     int c;
4796
4797     if ((pending_pan = (count > 0)) != FALSE) {
4798         count--;
4799         pending_pan = (count != 0);
4800     } else {
4801         for (;;) {
4802             switch (c = wGetchar(win)) {
4803             case '!':
4804                 ShellOut(FALSE);
4805                 /* FALLTHRU */
4806             case CTRL('r'):
4807                 endwin();
4808                 refresh();
4809                 c = KEY_REFRESH;
4810                 break;
4811             case CTRL('l'):
4812                 c = KEY_REFRESH;
4813                 break;
4814             case 'U':
4815                 c = KEY_UP;
4816                 break;
4817             case 'D':
4818                 c = KEY_DOWN;
4819                 break;
4820             case 'R':
4821                 c = KEY_RIGHT;
4822                 break;
4823             case 'L':
4824                 c = KEY_LEFT;
4825                 break;
4826             case '+':
4827                 c = KEY_IL;
4828                 break;
4829             case '-':
4830                 c = KEY_DL;
4831                 break;
4832             case '>':
4833                 c = KEY_IC;
4834                 break;
4835             case '<':
4836                 c = KEY_DC;
4837                 break;
4838             case ERR:           /* FALLTHRU */
4839             case case_QUIT:
4840                 count = 0;
4841                 c = KEY_EXIT;
4842                 break;
4843             default:
4844                 if (c >= '0' && c <= '9') {
4845                     count = count * 10 + (c - '0');
4846                     continue;
4847                 }
4848                 break;
4849             }
4850             last = c;
4851             break;
4852         }
4853         if (count > 0)
4854             count--;
4855     }
4856     return (last);
4857 }
4858
4859 #define PAD_HIGH 200
4860 #define PAD_WIDE 200
4861
4862 static void
4863 demo_pad(void)
4864 /* Demonstrate pads. */
4865 {
4866     WINDOW *panpad = newpad(PAD_HIGH, PAD_WIDE);
4867
4868     if (panpad == 0) {
4869         Cannot("cannot create requested pad");
4870         return;
4871     }
4872
4873     fill_pad(panpad, FALSE);
4874
4875     panner_legend(LINES - 4);
4876     panner_legend(LINES - 3);
4877     panner_legend(LINES - 2);
4878     panner_legend(LINES - 1);
4879
4880     keypad(panpad, TRUE);
4881
4882     /* Make the pad (initially) narrow enough that a trace file won't wrap.
4883      * We'll still be able to widen it during a test, since that's required
4884      * for testing boundaries.
4885      */
4886     panner(panpad, 2, 2, LINES - 5, COLS - 15, padgetch);
4887
4888     delwin(panpad);
4889     endwin();
4890     erase();
4891 }
4892
4893 /****************************************************************************
4894  *
4895  * Tests from John Burnell's PDCurses tester
4896  *
4897  ****************************************************************************/
4898
4899 static void
4900 Continue(WINDOW *win)
4901 {
4902     noecho();
4903     wmove(win, 10, 1);
4904     mvwaddstr(win, 10, 1, " Press any key to continue");
4905     wrefresh(win);
4906     wGetchar(win);
4907 }
4908
4909 static void
4910 flushinp_test(WINDOW *win)
4911 /* Input test, adapted from John Burnell's PDCurses tester */
4912 {
4913     int w, h, bx, by, sw, sh, i;
4914
4915     WINDOW *subWin;
4916     wclear(win);
4917
4918     getmaxyx(win, h, w);
4919     getbegyx(win, by, bx);
4920     sw = w / 3;
4921     sh = h / 3;
4922     if ((subWin = subwin(win, sh, sw, by + h - sh - 2, bx + w - sw - 2)) == 0)
4923         return;
4924
4925 #ifdef A_COLOR
4926     if (use_colors) {
4927         init_pair(2, COLOR_CYAN, COLOR_BLUE);
4928         wbkgd(subWin, COLOR_PAIR(2) | ' ');
4929     }
4930 #endif
4931     wattrset(subWin, A_BOLD);
4932     box(subWin, ACS_VLINE, ACS_HLINE);
4933     mvwaddstr(subWin, 2, 1, "This is a subwindow");
4934     wrefresh(win);
4935
4936     /*
4937      * This used to set 'nocbreak()'.  However, Alexander Lukyanov says that
4938      * it only happened to "work" on SVr4 because that implementation does not
4939      * emulate nocbreak+noecho mode, whereas ncurses does.  To get the desired
4940      * test behavior, we're using 'cbreak()', which will allow a single
4941      * character to return without needing a newline. - T.Dickey 1997/10/11.
4942      */
4943     cbreak();
4944     mvwaddstr(win, 0, 1, "This is a test of the flushinp() call.");
4945
4946     mvwaddstr(win, 2, 1, "Type random keys for 5 seconds.");
4947     mvwaddstr(win, 3, 1,
4948               "These should be discarded (not echoed) after the subwindow goes away.");
4949     wrefresh(win);
4950
4951     for (i = 0; i < 5; i++) {
4952         mvwprintw(subWin, 1, 1, "Time = %d", i);
4953         wrefresh(subWin);
4954         napms(1000);
4955         flushinp();
4956     }
4957
4958     delwin(subWin);
4959     werase(win);
4960     flash();
4961     wrefresh(win);
4962     napms(1000);
4963
4964     mvwaddstr(win, 2, 1,
4965               "If you were still typing when the window timer expired,");
4966     mvwaddstr(win, 3, 1,
4967               "or else you typed nothing at all while it was running,");
4968     mvwaddstr(win, 4, 1,
4969               "test was invalid.  You'll see garbage or nothing at all. ");
4970     mvwaddstr(win, 6, 1, "Press a key");
4971     wmove(win, 9, 10);
4972     wrefresh(win);
4973     echo();
4974     wGetchar(win);
4975     flushinp();
4976     mvwaddstr(win, 12, 0,
4977               "If you see any key other than what you typed, flushinp() is broken.");
4978     Continue(win);
4979
4980     wmove(win, 9, 10);
4981     wdelch(win);
4982     wrefresh(win);
4983     wmove(win, 12, 0);
4984     clrtoeol();
4985     waddstr(win,
4986             "What you typed should now have been deleted; if not, wdelch() failed.");
4987     Continue(win);
4988
4989     cbreak();
4990 }
4991
4992 /****************************************************************************
4993  *
4994  * Menu test
4995  *
4996  ****************************************************************************/
4997
4998 #if USE_LIBMENU
4999
5000 #define MENU_Y  8
5001 #define MENU_X  8
5002
5003 static int
5004 menu_virtualize(int c)
5005 {
5006     if (c == '\n' || c == KEY_EXIT)
5007         return (MAX_COMMAND + 1);
5008     else if (c == 'u')
5009         return (REQ_SCR_ULINE);
5010     else if (c == 'd')
5011         return (REQ_SCR_DLINE);
5012     else if (c == 'b' || c == KEY_NPAGE)
5013         return (REQ_SCR_UPAGE);
5014     else if (c == 'f' || c == KEY_PPAGE)
5015         return (REQ_SCR_DPAGE);
5016     else if (c == 'n' || c == KEY_DOWN)
5017         return (REQ_NEXT_ITEM);
5018     else if (c == 'p' || c == KEY_UP)
5019         return (REQ_PREV_ITEM);
5020     else if (c == ' ')
5021         return (REQ_TOGGLE_ITEM);
5022     else {
5023         if (c != KEY_MOUSE)
5024             beep();
5025         return (c);
5026     }
5027 }
5028
5029 static CONST_MENUS char *animals[] =
5030 {
5031     "Lions",
5032     "Tigers",
5033     "Bears",
5034     "(Oh my!)",
5035     "Newts",
5036     "Platypi",
5037     "Lemurs",
5038     "(Oh really?!)",
5039     "Leopards",
5040     "Panthers",
5041     "Pumas",
5042     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
5043     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
5044     (char *) 0
5045 };
5046
5047 static void
5048 menu_test(void)
5049 {
5050     MENU *m;
5051     ITEM *items[SIZEOF(animals)];
5052     ITEM **ip = items;
5053     CONST_MENUS char **ap;
5054     int mrows, mcols, c;
5055     WINDOW *menuwin;
5056
5057 #ifdef NCURSES_MOUSE_VERSION
5058     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5059 #endif
5060     mvaddstr(0, 0, "This is the menu test:");
5061     mvaddstr(2, 0, "  Use up and down arrow to move the select bar.");
5062     mvaddstr(3, 0, "  'n' and 'p' act like arrows.");
5063     mvaddstr(4, 0,
5064              "  'b' and 'f' scroll up/down (page), 'u' and 'd' (line).");
5065     mvaddstr(5, 0, "  Press return to exit.");
5066     refresh();
5067
5068     for (ap = animals; *ap; ap++) {
5069         if ((*ip = new_item(*ap, "")) != 0)
5070             ++ip;
5071     }
5072     *ip = (ITEM *) 0;
5073
5074     m = new_menu(items);
5075
5076     set_menu_format(m, (SIZEOF(animals) + 1) / 2, 1);
5077     scale_menu(m, &mrows, &mcols);
5078
5079     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
5080     set_menu_win(m, menuwin);
5081     keypad(menuwin, TRUE);
5082     box(menuwin, 0, 0);
5083
5084     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
5085
5086     post_menu(m);
5087
5088     while ((c = menu_driver(m, menu_virtualize(wGetchar(menuwin)))) != E_UNKNOWN_COMMAND) {
5089         if (c == E_NOT_POSTED)
5090             break;
5091         if (c == E_REQUEST_DENIED)
5092             beep();
5093         continue;
5094     }
5095
5096     (void) mvprintw(LINES - 2, 0,
5097                     "You chose: %s\n", item_name(current_item(m)));
5098     (void) addstr("Press any key to continue...");
5099     wGetchar(stdscr);
5100
5101     unpost_menu(m);
5102     delwin(menuwin);
5103
5104     free_menu(m);
5105     for (ip = items; *ip; ip++)
5106         free_item(*ip);
5107 #ifdef NCURSES_MOUSE_VERSION
5108     mousemask(0, (mmask_t *) 0);
5109 #endif
5110 }
5111
5112 #ifdef TRACE
5113 #define T_TBL(name) { #name, name }
5114 static struct {
5115     const char *name;
5116     unsigned mask;
5117 } t_tbl[] = {
5118
5119     T_TBL(TRACE_DISABLE),
5120         T_TBL(TRACE_TIMES),
5121         T_TBL(TRACE_TPUTS),
5122         T_TBL(TRACE_UPDATE),
5123         T_TBL(TRACE_MOVE),
5124         T_TBL(TRACE_CHARPUT),
5125         T_TBL(TRACE_ORDINARY),
5126         T_TBL(TRACE_CALLS),
5127         T_TBL(TRACE_VIRTPUT),
5128         T_TBL(TRACE_IEVENT),
5129         T_TBL(TRACE_BITS),
5130         T_TBL(TRACE_ICALLS),
5131         T_TBL(TRACE_CCALLS),
5132         T_TBL(TRACE_DATABASE),
5133         T_TBL(TRACE_ATTRS),
5134         T_TBL(TRACE_MAXIMUM),
5135     {
5136         (char *) 0, 0
5137     }
5138 };
5139
5140 static char *
5141 tracetrace(unsigned tlevel)
5142 {
5143     static char *buf;
5144     int n;
5145
5146     if (buf == 0) {
5147         size_t need = 12;
5148         for (n = 0; t_tbl[n].name != 0; n++)
5149             need += strlen(t_tbl[n].name) + 2;
5150         buf = typeMalloc(char, need);
5151     }
5152     sprintf(buf, "0x%02x = {", tlevel);
5153     if (tlevel == 0) {
5154         sprintf(buf + strlen(buf), "%s, ", t_tbl[0].name);
5155     } else {
5156         for (n = 1; t_tbl[n].name != 0; n++)
5157             if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
5158                 strcat(buf, t_tbl[n].name);
5159                 strcat(buf, ", ");
5160             }
5161     }
5162     if (buf[strlen(buf) - 2] == ',')
5163         buf[strlen(buf) - 2] = '\0';
5164     return (strcat(buf, "}"));
5165 }
5166
5167 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
5168  * the others
5169  */
5170 static int
5171 run_trace_menu(MENU * m)
5172 {
5173     ITEM **items;
5174     ITEM *i, **p;
5175
5176     for (;;) {
5177         bool changed = FALSE;
5178         switch (menu_driver(m, menu_virtualize(wGetchar(menu_win(m))))) {
5179         case E_UNKNOWN_COMMAND:
5180             return FALSE;
5181         default:
5182             items = menu_items(m);
5183             i = current_item(m);
5184             if (i == items[0]) {
5185                 if (item_value(i)) {
5186                     for (p = items + 1; *p != 0; p++)
5187                         if (item_value(*p)) {
5188                             set_item_value(*p, FALSE);
5189                             changed = TRUE;
5190                         }
5191                 }
5192             } else {
5193                 for (p = items + 1; *p != 0; p++)
5194                     if (item_value(*p)) {
5195                         set_item_value(items[0], FALSE);
5196                         changed = TRUE;
5197                         break;
5198                     }
5199             }
5200             if (!changed)
5201                 return TRUE;
5202         }
5203     }
5204 }
5205
5206 static void
5207 trace_set(void)
5208 /* interactively set the trace level */
5209 {
5210     MENU *m;
5211     ITEM *items[SIZEOF(t_tbl)];
5212     ITEM **ip = items;
5213     int mrows, mcols;
5214     unsigned newtrace;
5215     int n;
5216     WINDOW *menuwin;
5217
5218     mvaddstr(0, 0, "Interactively set trace level:");
5219     mvaddstr(2, 0, "  Press space bar to toggle a selection.");
5220     mvaddstr(3, 0, "  Use up and down arrow to move the select bar.");
5221     mvaddstr(4, 0, "  Press return to set the trace level.");
5222     mvprintw(6, 0, "(Current trace level is %s)", tracetrace(_nc_tracing));
5223
5224     refresh();
5225
5226     for (n = 0; t_tbl[n].name != 0; n++) {
5227         if ((*ip = new_item(t_tbl[n].name, "")) != 0) {
5228             ++ip;
5229         }
5230     }
5231     *ip = (ITEM *) 0;
5232
5233     m = new_menu(items);
5234
5235     set_menu_format(m, 0, 2);
5236     scale_menu(m, &mrows, &mcols);
5237
5238     menu_opts_off(m, O_ONEVALUE);
5239     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
5240     set_menu_win(m, menuwin);
5241     keypad(menuwin, TRUE);
5242     box(menuwin, 0, 0);
5243
5244     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
5245
5246     post_menu(m);
5247
5248     for (ip = menu_items(m); *ip; ip++) {
5249         unsigned mask = t_tbl[item_index(*ip)].mask;
5250         if (mask == 0)
5251             set_item_value(*ip, _nc_tracing == 0);
5252         else if ((mask & _nc_tracing) == mask)
5253             set_item_value(*ip, TRUE);
5254     }
5255
5256     while (run_trace_menu(m))
5257         continue;
5258
5259     newtrace = 0;
5260     for (ip = menu_items(m); *ip; ip++)
5261         if (item_value(*ip))
5262             newtrace |= t_tbl[item_index(*ip)].mask;
5263     trace(newtrace);
5264     Trace(("trace level interactively set to %s", tracetrace(_nc_tracing)));
5265
5266     (void) mvprintw(LINES - 2, 0,
5267                     "Trace level is %s\n", tracetrace(_nc_tracing));
5268     (void) addstr("Press any key to continue...");
5269     wGetchar(stdscr);
5270
5271     unpost_menu(m);
5272     delwin(menuwin);
5273
5274     free_menu(m);
5275     for (ip = items; *ip; ip++)
5276         free_item(*ip);
5277 }
5278 #endif /* TRACE */
5279 #endif /* USE_LIBMENU */
5280
5281 /****************************************************************************
5282  *
5283  * Forms test
5284  *
5285  ****************************************************************************/
5286 #if USE_LIBFORM
5287 static FIELD *
5288 make_label(int frow, int fcol, NCURSES_CONST char *label)
5289 {
5290     FIELD *f = new_field(1, (int) strlen(label), frow, fcol, 0, 0);
5291
5292     if (f) {
5293         set_field_buffer(f, 0, label);
5294         set_field_opts(f, (int) (field_opts(f) & ~O_ACTIVE));
5295     }
5296     return (f);
5297 }
5298
5299 static FIELD *
5300 make_field(int frow, int fcol, int rows, int cols, bool secure)
5301 {
5302     FIELD *f = new_field(rows, cols, frow, fcol, 0, secure ? 1 : 0);
5303
5304     if (f) {
5305         set_field_back(f, A_UNDERLINE);
5306         set_field_userptr(f, (void *) 0);
5307     }
5308     return (f);
5309 }
5310
5311 static void
5312 display_form(FORM * f)
5313 {
5314     WINDOW *w;
5315     int rows, cols;
5316
5317     scale_form(f, &rows, &cols);
5318
5319     if ((w = newwin(rows + 2, cols + 4, 0, 0)) != (WINDOW *) 0) {
5320         set_form_win(f, w);
5321         set_form_sub(f, derwin(w, rows, cols, 1, 2));
5322         box(w, 0, 0);
5323         keypad(w, TRUE);
5324     }
5325
5326     if (post_form(f) != E_OK)
5327         wrefresh(w);
5328 }
5329
5330 static void
5331 erase_form(FORM * f)
5332 {
5333     WINDOW *w = form_win(f);
5334     WINDOW *s = form_sub(f);
5335
5336     unpost_form(f);
5337     werase(w);
5338     wrefresh(w);
5339     delwin(s);
5340     delwin(w);
5341 }
5342
5343 static int
5344 edit_secure(FIELD * me, int c)
5345 {
5346     int rows, cols, frow, fcol, nrow, nbuf;
5347
5348     if (field_info(me, &rows, &cols, &frow, &fcol, &nrow, &nbuf) == E_OK
5349         && nbuf > 0) {
5350         char *source = field_buffer(me, 1);
5351         char temp[80];
5352         long len;
5353
5354         strcpy(temp, source ? source : "");
5355         len = (long) (char *) field_userptr(me);
5356         if (c <= KEY_MAX) {
5357             if (isgraph(c) && (len + 1) < (int) sizeof(temp)) {
5358                 temp[len++] = (char) c;
5359                 temp[len] = 0;
5360                 set_field_buffer(me, 1, temp);
5361                 c = '*';
5362             } else {
5363                 c = 0;
5364             }
5365         } else {
5366             switch (c) {
5367             case REQ_BEG_FIELD:
5368             case REQ_CLR_EOF:
5369             case REQ_CLR_EOL:
5370             case REQ_DEL_LINE:
5371             case REQ_DEL_WORD:
5372             case REQ_DOWN_CHAR:
5373             case REQ_END_FIELD:
5374             case REQ_INS_CHAR:
5375             case REQ_INS_LINE:
5376             case REQ_LEFT_CHAR:
5377             case REQ_NEW_LINE:
5378             case REQ_NEXT_WORD:
5379             case REQ_PREV_WORD:
5380             case REQ_RIGHT_CHAR:
5381             case REQ_UP_CHAR:
5382                 c = 0;          /* we don't want to do inline editing */
5383                 break;
5384             case REQ_CLR_FIELD:
5385                 if (len) {
5386                     temp[0] = 0;
5387                     set_field_buffer(me, 1, temp);
5388                 }
5389                 break;
5390             case REQ_DEL_CHAR:
5391             case REQ_DEL_PREV:
5392                 if (len) {
5393                     temp[--len] = 0;
5394                     set_field_buffer(me, 1, temp);
5395                 }
5396                 break;
5397             }
5398         }
5399         set_field_userptr(me, (void *) len);
5400     }
5401     return c;
5402 }
5403
5404 static int
5405 form_virtualize(FORM * f, WINDOW *w)
5406 {
5407     /* *INDENT-OFF* */
5408     static const struct {
5409         int code;
5410         int result;
5411     } lookup[] = {
5412         { CTRL('A'),    REQ_NEXT_CHOICE },
5413         { CTRL('B'),    REQ_PREV_WORD },
5414         { CTRL('C'),    REQ_CLR_EOL },
5415         { CTRL('D'),    REQ_DOWN_FIELD },
5416         { CTRL('E'),    REQ_END_FIELD },
5417         { CTRL('F'),    REQ_NEXT_PAGE },
5418         { CTRL('G'),    REQ_DEL_WORD },
5419         { CTRL('H'),    REQ_DEL_PREV },
5420         { CTRL('I'),    REQ_INS_CHAR },
5421         { CTRL('K'),    REQ_CLR_EOF },
5422         { CTRL('L'),    REQ_LEFT_FIELD },
5423         { CTRL('M'),    REQ_NEW_LINE },
5424         { CTRL('N'),    REQ_NEXT_FIELD },
5425         { CTRL('O'),    REQ_INS_LINE },
5426         { CTRL('P'),    REQ_PREV_FIELD },
5427         { CTRL('R'),    REQ_RIGHT_FIELD },
5428         { CTRL('S'),    REQ_BEG_FIELD },
5429         { CTRL('U'),    REQ_UP_FIELD },
5430         { CTRL('V'),    REQ_DEL_CHAR },
5431         { CTRL('W'),    REQ_NEXT_WORD },
5432         { CTRL('X'),    REQ_CLR_FIELD },
5433         { CTRL('Y'),    REQ_DEL_LINE },
5434         { CTRL('Z'),    REQ_PREV_CHOICE },
5435         { ESCAPE,       MAX_FORM_COMMAND + 1 },
5436         { KEY_BACKSPACE, REQ_DEL_PREV },
5437         { KEY_DOWN,     REQ_DOWN_CHAR },
5438         { KEY_END,      REQ_LAST_FIELD },
5439         { KEY_HOME,     REQ_FIRST_FIELD },
5440         { KEY_LEFT,     REQ_LEFT_CHAR },
5441         { KEY_LL,       REQ_LAST_FIELD },
5442         { KEY_NEXT,     REQ_NEXT_FIELD },
5443         { KEY_NPAGE,    REQ_NEXT_PAGE },
5444         { KEY_PPAGE,    REQ_PREV_PAGE },
5445         { KEY_PREVIOUS, REQ_PREV_FIELD },
5446         { KEY_RIGHT,    REQ_RIGHT_CHAR },
5447         { KEY_UP,       REQ_UP_CHAR },
5448         { QUIT,         MAX_FORM_COMMAND + 1 }
5449     };
5450     /* *INDENT-ON* */
5451
5452     static int mode = REQ_INS_MODE;
5453     int c = wGetchar(w);
5454     unsigned n;
5455     FIELD *me = current_field(f);
5456     bool current = TRUE;
5457
5458     if (c == CTRL(']')) {
5459         if (mode == REQ_INS_MODE) {
5460             mode = REQ_OVL_MODE;
5461         } else {
5462             mode = REQ_INS_MODE;
5463         }
5464         c = mode;
5465     } else {
5466         for (n = 0; n < SIZEOF(lookup); n++) {
5467             if (lookup[n].code == c) {
5468                 c = lookup[n].result;
5469                 break;
5470             }
5471         }
5472     }
5473     mvprintw(0, COLS - 6, "(%s)", mode == REQ_INS_MODE ? "INS" : "OVL");
5474
5475     /*
5476      * Force the field that the user is typing into to be in reverse video,
5477      * while the other fields are shown underlined.
5478      */
5479     switch (c) {
5480     case REQ_BEG_FIELD:
5481     case REQ_CLR_EOF:
5482     case REQ_CLR_EOL:
5483     case REQ_CLR_FIELD:
5484     case REQ_DEL_CHAR:
5485     case REQ_DEL_LINE:
5486     case REQ_DEL_PREV:
5487     case REQ_DEL_WORD:
5488     case REQ_END_FIELD:
5489     case REQ_INS_CHAR:
5490     case REQ_INS_LINE:
5491     case REQ_LEFT_CHAR:
5492     case REQ_LEFT_FIELD:
5493     case REQ_NEXT_WORD:
5494     case REQ_RIGHT_CHAR:
5495         current = TRUE;
5496         break;
5497     default:
5498         current = (c < KEY_MAX);
5499         break;
5500     }
5501     if (current) {
5502         c = edit_secure(me, c);
5503         set_field_back(me, A_REVERSE);
5504     } else {
5505         c = edit_secure(me, c);
5506         set_field_back(me, A_UNDERLINE);
5507     }
5508     return c;
5509 }
5510
5511 static int
5512 my_form_driver(FORM * form, int c)
5513 {
5514     if (c == (MAX_FORM_COMMAND + 1)
5515         && form_driver(form, REQ_VALIDATION) == E_OK)
5516         return (TRUE);
5517     else {
5518         beep();
5519         return (FALSE);
5520     }
5521 }
5522
5523 #ifdef NCURSES_VERSION
5524 #define FIELDCHECK_CB(func) bool func(FIELD * fld, const void * data GCC_UNUSED)
5525 #define CHAR_CHECK_CB(func) bool func(int ch, const void *data GCC_UNUSED)
5526 #else
5527 #define FIELDCHECK_CB(func) int func(FIELD * fld, char * data GCC_UNUSED)
5528 #define CHAR_CHECK_CB(func) int func(int ch, char *data GCC_UNUSED)
5529 #endif
5530
5531 /*
5532  * Allow a middle initial, optionally with a '.' to end it.
5533  */
5534 static
5535 FIELDCHECK_CB(mi_field_check)
5536 {
5537     char *s = field_buffer(fld, 0);
5538     int state = 0;
5539     int n;
5540
5541     for (n = 0; s[n] != '\0'; ++n) {
5542         switch (state) {
5543         case 0:
5544             if (s[n] == '.') {
5545                 if (n != 1)
5546                     return FALSE;
5547                 state = 2;
5548             } else if (isspace(UChar(s[n]))) {
5549                 state = 2;
5550             }
5551             break;
5552         case 2:
5553             if (!isspace(UChar(s[n])))
5554                 return FALSE;
5555             break;
5556         }
5557     }
5558
5559     /* force the form to display a leading capital */
5560     if (islower(UChar(s[0]))) {
5561         s[0] = (char) toupper(UChar(s[0]));
5562         set_field_buffer(fld, 0, s);
5563     }
5564     return TRUE;
5565 }
5566
5567 static
5568 CHAR_CHECK_CB(mi_char_check)
5569 {
5570     return ((isalpha(ch) || ch == '.') ? TRUE : FALSE);
5571 }
5572
5573 /*
5574  * Passwords should be at least 6 characters.
5575  */
5576 static
5577 FIELDCHECK_CB(pw_field_check)
5578 {
5579     char *s = field_buffer(fld, 0);
5580     int n;
5581
5582     for (n = 0; s[n] != '\0'; ++n) {
5583         if (isspace(UChar(s[n]))) {
5584             if (n < 6)
5585                 return FALSE;
5586         }
5587     }
5588     return TRUE;
5589 }
5590
5591 static
5592 CHAR_CHECK_CB(pw_char_check)
5593 {
5594     return (isgraph(ch) ? TRUE : FALSE);
5595 }
5596
5597 static void
5598 demo_forms(void)
5599 {
5600     WINDOW *w;
5601     FORM *form;
5602     FIELD *f[12], *secure;
5603     FIELDTYPE *fty_middle = new_fieldtype(mi_field_check, mi_char_check);
5604     FIELDTYPE *fty_passwd = new_fieldtype(pw_field_check, pw_char_check);
5605     int finished = 0, c;
5606     unsigned n = 0;
5607
5608 #ifdef NCURSES_MOUSE_VERSION
5609     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5610 #endif
5611
5612     move(18, 0);
5613     addstr("Defined edit/traversal keys:   ^Q/ESC- exit form\n");
5614     addstr("^N   -- go to next field       ^P  -- go to previous field\n");
5615     addstr("Home -- go to first field      End -- go to last field\n");
5616     addstr("^L   -- go to field to left    ^R  -- go to field to right\n");
5617     addstr("^U   -- move upward to field   ^D  -- move downward to field\n");
5618     addstr("^W   -- go to next word        ^B  -- go to previous word\n");
5619     addstr("^S   -- go to start of field   ^E  -- go to end of field\n");
5620     addstr("^H   -- delete previous char   ^Y  -- delete line\n");
5621     addstr("^G   -- delete current word    ^C  -- clear to end of line\n");
5622     addstr("^K   -- clear to end of field  ^X  -- clear field\n");
5623     addstr("Arrow keys move within a field as you would expect. ^] toggles overlay mode.");
5624
5625     mvaddstr(4, 57, "Forms Entry Test");
5626
5627     refresh();
5628
5629     /* describe the form */
5630     memset(f, 0, sizeof(f));
5631     f[n++] = make_label(0, 15, "Sample Form");
5632
5633     f[n++] = make_label(2, 0, "Last Name");
5634     f[n++] = make_field(3, 0, 1, 18, FALSE);
5635     set_field_type(f[n - 1], TYPE_ALPHA, 1);
5636
5637     f[n++] = make_label(2, 20, "First Name");
5638     f[n++] = make_field(3, 20, 1, 12, FALSE);
5639     set_field_type(f[n - 1], TYPE_ALPHA, 1);
5640
5641     f[n++] = make_label(2, 34, "Middle Name");
5642     f[n++] = make_field(3, 34, 1, 12, FALSE);
5643     set_field_type(f[n - 1], fty_middle);
5644
5645     f[n++] = make_label(5, 0, "Comments");
5646     f[n++] = make_field(6, 0, 4, 46, FALSE);
5647
5648     f[n++] = make_label(5, 20, "Password:");
5649     secure =
5650         f[n++] = make_field(5, 30, 1, 9, TRUE);
5651     set_field_type(f[n - 1], fty_passwd);
5652     f[n++] = (FIELD *) 0;
5653
5654     if ((form = new_form(f)) != 0) {
5655
5656         display_form(form);
5657
5658         w = form_win(form);
5659         raw();
5660         nonl();                 /* lets us read ^M's */
5661         while (!finished) {
5662             switch (form_driver(form, c = form_virtualize(form, w))) {
5663             case E_OK:
5664                 mvaddstr(5, 57, field_buffer(secure, 1));
5665                 clrtoeol();
5666                 refresh();
5667                 break;
5668             case E_UNKNOWN_COMMAND:
5669                 finished = my_form_driver(form, c);
5670                 break;
5671             default:
5672                 beep();
5673                 break;
5674             }
5675         }
5676
5677         erase_form(form);
5678
5679         free_form(form);
5680     }
5681     for (c = 0; f[c] != 0; c++)
5682         free_field(f[c]);
5683     free_fieldtype(fty_middle);
5684     free_fieldtype(fty_passwd);
5685     noraw();
5686     nl();
5687
5688 #ifdef NCURSES_MOUSE_VERSION
5689     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5690 #endif
5691 }
5692 #endif /* USE_LIBFORM */
5693
5694 /****************************************************************************
5695  *
5696  * Overlap test
5697  *
5698  ****************************************************************************/
5699
5700 static void
5701 fillwin(WINDOW *win, char ch)
5702 {
5703     int y, x;
5704     int y1, x1;
5705
5706     getmaxyx(win, y1, x1);
5707     for (y = 0; y < y1; y++) {
5708         wmove(win, y, 0);
5709         for (x = 0; x < x1; x++)
5710             waddch(win, UChar(ch));
5711     }
5712 }
5713
5714 static void
5715 crosswin(WINDOW *win, char ch)
5716 {
5717     int y, x;
5718     int y1, x1;
5719
5720     getmaxyx(win, y1, x1);
5721     for (y = 0; y < y1; y++) {
5722         for (x = 0; x < x1; x++)
5723             if (((x > (x1 - 1) / 3) && (x <= (2 * (x1 - 1)) / 3))
5724                 || (((y > (y1 - 1) / 3) && (y <= (2 * (y1 - 1)) / 3)))) {
5725                 wmove(win, y, x);
5726                 waddch(win, UChar(ch));
5727             }
5728     }
5729 }
5730
5731 #define OVERLAP_FLAVORS 5
5732
5733 static void
5734 overlap_helpitem(int state, int item, char *message)
5735 {
5736     int row = (item / 2);
5737     int col = ((item % 2) ? COLS / 2 : 0);
5738
5739     move(LINES - 6 + row, col);
5740     printw("%c%c = %s", state == row ? '>' : ' ', 'a' + item, message);
5741     clrtoeol();
5742 }
5743
5744 static void
5745 overlap_test_1_attr(WINDOW *win, int flavor, int col)
5746 {
5747     short cpair = (short) (1 + (flavor * 2) + col);
5748
5749     switch (flavor) {
5750     case 0:
5751         wattrset(win, A_NORMAL);
5752         break;
5753     case 1:
5754         wattrset(win, A_BOLD);
5755         break;
5756     case 2:
5757         init_pair(cpair, COLOR_BLUE, COLOR_WHITE);
5758         wattrset(win, COLOR_PAIR(cpair) | A_NORMAL);
5759         break;
5760     case 3:
5761         init_pair(cpair, COLOR_WHITE, COLOR_BLUE);
5762         wattrset(win, COLOR_PAIR(cpair) | A_BOLD);
5763         break;
5764     }
5765 }
5766
5767 static void
5768 overlap_test_2_attr(WINDOW *win, int flavor, int col)
5769 {
5770     short cpair = (short) (9 + (flavor * 2) + col);
5771
5772     switch (flavor) {
5773     case 0:
5774         /* no effect */
5775         break;
5776     case 1:
5777         /* no effect */
5778         break;
5779     case 2:
5780         init_pair(cpair, COLOR_RED, COLOR_GREEN);
5781         wbkgdset(win, ' ' | A_BLINK | COLOR_PAIR(cpair));
5782         break;
5783     case 3:
5784         wbkgdset(win, ' ' | A_NORMAL);
5785         break;
5786     }
5787 }
5788
5789 static int
5790 overlap_help(int state, int flavors[OVERLAP_FLAVORS])
5791 {
5792     int row;
5793     int col;
5794     int item;
5795     const char *ths, *tht;
5796     char msg[80];
5797
5798     if (state < 0)
5799         state += OVERLAP_FLAVORS;
5800     state = state % OVERLAP_FLAVORS;
5801     assert(state >= 0 && state < OVERLAP_FLAVORS);
5802
5803     for (item = 0; item < (2 * OVERLAP_FLAVORS); ++item) {
5804         row = item / 2;
5805         col = item % 2;
5806         ths = col ? "B" : "A";
5807         tht = col ? "A" : "B";
5808
5809         switch (row) {
5810         case 0:
5811             flavors[row] = 0;
5812             sprintf(msg, "refresh %s, then %s, then doupdate.", ths, tht);
5813             break;
5814         case 1:
5815             if (use_colors) {
5816                 flavors[row] %= 4;
5817             } else {
5818                 flavors[row] %= 2;
5819             }
5820             overlap_test_1_attr(stdscr, flavors[row], col);
5821             sprintf(msg, "fill window %s with letter %s.", ths, ths);
5822             break;
5823         case 2:
5824             if (use_colors) {
5825                 flavors[row] %= 4;
5826             } else {
5827                 flavors[row] %= 2;
5828             }
5829             switch (flavors[row]) {
5830             case 0:
5831                 sprintf(msg, "cross pattern in window %s.", ths);
5832                 break;
5833             case 1:
5834                 sprintf(msg, "draw box in window %s.", ths);
5835                 break;
5836             case 2:
5837                 sprintf(msg, "set background of window %s.", ths);
5838                 break;
5839             case 3:
5840                 sprintf(msg, "reset background of window %s.", ths);
5841                 break;
5842             }
5843             break;
5844         case 3:
5845             flavors[row] = 0;
5846             sprintf(msg, "clear window %s.", ths);
5847             break;
5848         case 4:
5849             flavors[row] %= 4;
5850             switch (flavors[row]) {
5851             case 0:
5852                 sprintf(msg, "overwrite %s onto %s.", ths, tht);
5853                 break;
5854             case 1:
5855                 sprintf(msg, "copywin(FALSE) %s onto %s.", ths, tht);
5856                 break;
5857             case 2:
5858                 sprintf(msg, "copywin(TRUE) %s onto %s.", ths, tht);
5859                 break;
5860             case 3:
5861                 sprintf(msg, "overlay %s onto %s.", ths, tht);
5862                 break;
5863             }
5864             break;
5865         }
5866         overlap_helpitem(state, item, msg);
5867         wattrset(stdscr, A_NORMAL);
5868         wbkgdset(stdscr, ' ' | A_NORMAL);
5869     }
5870     move(LINES - 1, 0);
5871     printw("^Q/ESC = terminate test.  Up/down/space select test variations (%d %d).",
5872            state, flavors[state]);
5873
5874     return state;
5875 }
5876
5877 static void
5878 overlap_test_0(WINDOW *a, WINDOW *b)
5879 {
5880     touchwin(a);
5881     touchwin(b);
5882     wnoutrefresh(a);
5883     wnoutrefresh(b);
5884     doupdate();
5885 }
5886
5887 static void
5888 overlap_test_1(int flavor, int col, WINDOW *a, char fill)
5889 {
5890     overlap_test_1_attr(a, flavor, col);
5891     fillwin(a, fill);
5892     wattrset(a, A_NORMAL);
5893 }
5894
5895 static void
5896 overlap_test_2(int flavor, int col, WINDOW *a, char fill)
5897 {
5898     overlap_test_2_attr(a, flavor, col);
5899     switch (flavor) {
5900     case 0:
5901         crosswin(a, fill);
5902         break;
5903     case 1:
5904         box(a, 0, 0);
5905         break;
5906     case 2:
5907         /* done in overlap_test_2_attr */
5908         break;
5909     case 3:
5910         /* done in overlap_test_2_attr */
5911         break;
5912     }
5913 }
5914
5915 static void
5916 overlap_test_3(WINDOW *a)
5917 {
5918     wclear(a);
5919     wmove(a, 0, 0);
5920 }
5921
5922 static void
5923 overlap_test_4(int flavor, WINDOW *a, WINDOW *b)
5924 {
5925     switch (flavor) {
5926     case 0:
5927         overwrite(a, b);
5928         break;
5929     case 1:
5930         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), FALSE);
5931         break;
5932     case 2:
5933         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), TRUE);
5934         break;
5935     case 3:
5936         overlay(a, b);
5937         break;
5938     }
5939 }
5940
5941 /* test effects of overlapping windows */
5942 static void
5943 overlap_test(void)
5944 {
5945     int ch;
5946     int state, flavor[OVERLAP_FLAVORS];
5947
5948     WINDOW *win1 = newwin(9, 20, 3, 3);
5949     WINDOW *win2 = newwin(9, 20, 9, 16);
5950
5951     curs_set(0);
5952     raw();
5953     refresh();
5954     move(0, 0);
5955     printw("This test shows the behavior of wnoutrefresh() with respect to\n");
5956     printw("the shared region of two overlapping windows A and B.  The cross\n");
5957     printw("pattern in each window does not overlap the other.\n");
5958
5959     memset(flavor, 0, sizeof(flavor));
5960     state = overlap_help(0, flavor);
5961
5962     while (!isQuit(ch = Getchar()))
5963         switch (ch) {
5964         case 'a':               /* refresh window A first, then B */
5965             overlap_test_0(win1, win2);
5966             break;
5967
5968         case 'b':               /* refresh window B first, then A */
5969             overlap_test_0(win2, win1);
5970             break;
5971
5972         case 'c':               /* fill window A so it's visible */
5973             overlap_test_1(flavor[1], 0, win1, 'A');
5974             break;
5975
5976         case 'd':               /* fill window B so it's visible */
5977             overlap_test_1(flavor[1], 1, win2, 'B');
5978             break;
5979
5980         case 'e':               /* cross test pattern in window A */
5981             overlap_test_2(flavor[2], 0, win1, 'A');
5982             break;
5983
5984         case 'f':               /* cross test pattern in window A */
5985             overlap_test_2(flavor[2], 1, win2, 'B');
5986             break;
5987
5988         case 'g':               /* clear window A */
5989             overlap_test_3(win1);
5990             break;
5991
5992         case 'h':               /* clear window B */
5993             overlap_test_3(win2);
5994             break;
5995
5996         case 'i':               /* overwrite A onto B */
5997             overlap_test_4(flavor[4], win1, win2);
5998             break;
5999
6000         case 'j':               /* overwrite B onto A */
6001             overlap_test_4(flavor[4], win2, win1);
6002             break;
6003
6004         case CTRL('n'):
6005         case KEY_DOWN:
6006             state = overlap_help(state + 1, flavor);
6007             break;
6008
6009         case CTRL('p'):
6010         case KEY_UP:
6011             state = overlap_help(state - 1, flavor);
6012             break;
6013
6014         case ' ':
6015             flavor[state] += 1;
6016             state = overlap_help(state, flavor);
6017             break;
6018
6019         case '?':
6020             state = overlap_help(state, flavor);
6021             break;
6022
6023         default:
6024             beep();
6025             break;
6026         }
6027
6028     delwin(win2);
6029     delwin(win1);
6030     erase();
6031     curs_set(1);
6032     endwin();
6033 }
6034
6035 /****************************************************************************
6036  *
6037  * Main sequence
6038  *
6039  ****************************************************************************/
6040
6041 static bool
6042 do_single_test(const char c)
6043 /* perform a single specified test */
6044 {
6045     switch (c) {
6046     case 'a':
6047         getch_test();
6048         break;
6049
6050 #if USE_WIDEC_SUPPORT
6051     case 'A':
6052         get_wch_test();
6053         break;
6054 #endif
6055
6056     case 'b':
6057         attr_test();
6058         break;
6059
6060 #if USE_WIDEC_SUPPORT
6061     case 'B':
6062         wide_attr_test();
6063         break;
6064 #endif
6065
6066     case 'c':
6067         if (!use_colors)
6068             Cannot("does not support color.");
6069         else
6070             color_test();
6071         break;
6072
6073 #if USE_WIDEC_SUPPORT
6074     case 'C':
6075         if (!use_colors)
6076             Cannot("does not support color.");
6077         else
6078             wide_color_test();
6079         break;
6080 #endif
6081
6082     case 'd':
6083         if (!use_colors)
6084             Cannot("does not support color.");
6085         else if (!can_change_color())
6086             Cannot("has hardwired color values.");
6087         else
6088             color_edit();
6089         break;
6090
6091 #if USE_SOFTKEYS
6092     case 'e':
6093         slk_test();
6094         break;
6095 #endif
6096
6097 #if USE_WIDEC_SUPPORT
6098     case 'E':
6099         wide_slk_test();
6100         break;
6101 #endif
6102     case 'f':
6103         acs_display();
6104         break;
6105
6106 #if USE_WIDEC_SUPPORT
6107     case 'F':
6108         wide_acs_display();
6109         break;
6110 #endif
6111
6112 #if USE_LIBPANEL
6113     case 'o':
6114         demo_panels(init_panel, fill_panel);
6115         break;
6116 #endif
6117
6118 #if USE_WIDEC_SUPPORT && USE_LIBPANEL
6119     case 'O':
6120         demo_panels(init_wide_panel, fill_wide_panel);
6121         break;
6122 #endif
6123
6124     case 'g':
6125         acs_and_scroll();
6126         break;
6127
6128     case 'i':
6129         flushinp_test(stdscr);
6130         break;
6131
6132     case 'k':
6133         test_sgr_attributes();
6134         break;
6135
6136 #if USE_LIBMENU
6137     case 'm':
6138         menu_test();
6139         break;
6140 #endif
6141
6142     case 'p':
6143         demo_pad();
6144         break;
6145
6146 #if USE_LIBFORM
6147     case 'r':
6148         demo_forms();
6149         break;
6150 #endif
6151
6152     case 's':
6153         overlap_test();
6154         break;
6155
6156 #if USE_LIBMENU && defined(TRACE)
6157     case 't':
6158         trace_set();
6159         break;
6160 #endif
6161
6162     case '?':
6163         break;
6164
6165     default:
6166         return FALSE;
6167     }
6168
6169     return TRUE;
6170 }
6171
6172 static void
6173 usage(void)
6174 {
6175     static const char *const tbl[] =
6176     {
6177         "Usage: ncurses [options]"
6178         ,""
6179         ,"Options:"
6180 #ifdef NCURSES_VERSION
6181         ,"  -a f,b   set default-colors (assumed white-on-black)"
6182         ,"  -d       use default-colors if terminal supports them"
6183 #endif
6184 #if USE_SOFTKEYS
6185         ,"  -e fmt   specify format for soft-keys test (e)"
6186 #endif
6187 #if HAVE_RIPOFFLINE
6188         ,"  -f       rip-off footer line (can repeat)"
6189         ,"  -h       rip-off header line (can repeat)"
6190 #endif
6191         ,"  -m       do not use colors"
6192         ,"  -p file  rgb values to use in 'd' rather than ncurses's builtin"
6193 #if USE_LIBPANEL
6194         ,"  -s msec  specify nominal time for panel-demo (default: 1, to hold)"
6195 #endif
6196 #ifdef TRACE
6197         ,"  -t mask  specify default trace-level (may toggle with ^T)"
6198 #endif
6199     };
6200     size_t n;
6201     for (n = 0; n < SIZEOF(tbl); n++)
6202         fprintf(stderr, "%s\n", tbl[n]);
6203     ExitProgram(EXIT_FAILURE);
6204 }
6205
6206 static void
6207 set_terminal_modes(void)
6208 {
6209     noraw();
6210     cbreak();
6211     noecho();
6212     scrollok(stdscr, TRUE);
6213     idlok(stdscr, TRUE);
6214     keypad(stdscr, TRUE);
6215 }
6216
6217 #ifdef SIGUSR1
6218 static RETSIGTYPE
6219 announce_sig(int sig)
6220 {
6221     (void) fprintf(stderr, "Handled signal %d\r\n", sig);
6222 }
6223 #endif
6224
6225 #if HAVE_RIPOFFLINE
6226 static int
6227 rip_footer(WINDOW *win, int cols)
6228 {
6229     wbkgd(win, A_REVERSE);
6230     werase(win);
6231     wmove(win, 0, 0);
6232     wprintw(win, "footer: window %p, %d columns", win, cols);
6233     wnoutrefresh(win);
6234     return OK;
6235 }
6236
6237 static int
6238 rip_header(WINDOW *win, int cols)
6239 {
6240     wbkgd(win, A_REVERSE);
6241     werase(win);
6242     wmove(win, 0, 0);
6243     wprintw(win, "header: window %p, %d columns", win, cols);
6244     wnoutrefresh(win);
6245     return OK;
6246 }
6247 #endif /* HAVE_RIPOFFLINE */
6248
6249 static void
6250 main_menu(bool top)
6251 {
6252     char command;
6253
6254     do {
6255         (void) puts("This is the ncurses main menu");
6256         (void) puts("a = keyboard and mouse input test");
6257 #if USE_WIDEC_SUPPORT
6258         (void) puts("A = wide-character keyboard and mouse input test");
6259 #endif
6260         (void) puts("b = character attribute test");
6261 #if USE_WIDEC_SUPPORT
6262         (void) puts("B = wide-character attribute test");
6263 #endif
6264         (void) puts("c = color test pattern");
6265 #if USE_WIDEC_SUPPORT
6266         (void) puts("C = color test pattern using wide-character calls");
6267 #endif
6268         if (top)
6269             (void) puts("d = edit RGB color values");
6270 #if USE_SOFTKEYS
6271         (void) puts("e = exercise soft keys");
6272 #if USE_WIDEC_SUPPORT
6273         (void) puts("E = exercise soft keys using wide-characters");
6274 #endif
6275 #endif
6276         (void) puts("f = display ACS characters");
6277 #if USE_WIDEC_SUPPORT
6278         (void) puts("F = display Wide-ACS characters");
6279 #endif
6280         (void) puts("g = display windows and scrolling");
6281         (void) puts("i = test of flushinp()");
6282         (void) puts("k = display character attributes");
6283 #if USE_LIBMENU
6284         (void) puts("m = menu code test");
6285 #endif
6286 #if USE_LIBPANEL
6287         (void) puts("o = exercise panels library");
6288 #if USE_WIDEC_SUPPORT
6289         (void) puts("O = exercise panels with wide-characters");
6290 #endif
6291 #endif
6292         (void) puts("p = exercise pad features");
6293         (void) puts("q = quit");
6294 #if USE_LIBFORM
6295         (void) puts("r = exercise forms code");
6296 #endif
6297         (void) puts("s = overlapping-refresh test");
6298 #if USE_LIBMENU && defined(TRACE)
6299         (void) puts("t = set trace level");
6300 #endif
6301         (void) puts("? = repeat this command summary");
6302
6303         (void) fputs("> ", stdout);
6304         (void) fflush(stdout);  /* necessary under SVr4 curses */
6305
6306         /*
6307          * This used to be an 'fgets()' call.  However (on Linux, at least)
6308          * mixing stream I/O and 'read()' (used in the library) causes the
6309          * input stream to be flushed when switching between the two.
6310          */
6311         command = 0;
6312         for (;;) {
6313             char ch = '\0';
6314             if (read(fileno(stdin), &ch, 1) <= 0) {
6315                 if (command == 0)
6316                     command = 'q';
6317                 break;
6318             } else if (command == 0 && !isspace(UChar(ch))) {
6319                 command = ch;
6320             } else if (ch == '\n' || ch == '\r') {
6321                 if ((command == 'd') && !top) {
6322                     (void) fputs("Do not nest test-d\n", stdout);
6323                     command = 0;
6324                 }
6325                 if (command != 0)
6326                     break;
6327                 (void) fputs("> ", stdout);
6328                 (void) fflush(stdout);
6329             }
6330         }
6331
6332         if (do_single_test(command)) {
6333             /*
6334              * This may be overkill; it's intended to reset everything back
6335              * to the initial terminal modes so that tests don't get in
6336              * each other's way.
6337              */
6338             flushinp();
6339             set_terminal_modes();
6340             reset_prog_mode();
6341             clear();
6342             refresh();
6343             endwin();
6344             if (command == '?') {
6345                 (void) puts("This is the ncurses capability tester.");
6346                 (void)
6347                     puts("You may select a test from the main menu by typing the");
6348                 (void)
6349                     puts("key letter of the choice (the letter to left of the =)");
6350                 (void)
6351                     puts("at the > prompt.  Type `q' to exit.");
6352             }
6353             continue;
6354         }
6355     } while
6356         (command != 'q');
6357 }
6358
6359 /*+-------------------------------------------------------------------------
6360         main(argc,argv)
6361 --------------------------------------------------------------------------*/
6362
6363 #define okCOLOR(n) ((n) >= 0 && (n) < max_colors)
6364 #define okRGB(n)   ((n) >= 0 && (n) <= 1000)
6365
6366 int
6367 main(int argc, char *argv[])
6368 {
6369     int c;
6370     int my_e_param = 1;
6371 #ifdef NCURSES_VERSION
6372     int default_fg = COLOR_WHITE;
6373     int default_bg = COLOR_BLACK;
6374     bool assumed_colors = FALSE;
6375     bool default_colors = FALSE;
6376 #endif
6377     char *palette_file = 0;
6378     bool monochrome = FALSE;
6379
6380     setlocale(LC_ALL, "");
6381
6382     while ((c = getopt(argc, argv, "a:de:fhmp:s:t:")) != -1) {
6383         switch (c) {
6384 #ifdef NCURSES_VERSION
6385         case 'a':
6386             assumed_colors = TRUE;
6387             sscanf(optarg, "%d,%d", &default_fg, &default_bg);
6388             break;
6389         case 'd':
6390             default_colors = TRUE;
6391             break;
6392 #endif
6393         case 'e':
6394             my_e_param = atoi(optarg);
6395 #ifdef NCURSES_VERSION
6396             if (my_e_param > 3) /* allow extended layouts */
6397                 usage();
6398 #else
6399             if (my_e_param > 1)
6400                 usage();
6401 #endif
6402             break;
6403 #if HAVE_RIPOFFLINE
6404         case 'f':
6405             ripoffline(-1, rip_footer);
6406             break;
6407         case 'h':
6408             ripoffline(1, rip_header);
6409             break;
6410 #endif /* HAVE_RIPOFFLINE */
6411         case 'm':
6412             monochrome = TRUE;
6413             break;
6414         case 'p':
6415             palette_file = optarg;
6416             break;
6417 #if USE_LIBPANEL
6418         case 's':
6419             nap_msec = atol(optarg);
6420             break;
6421 #endif
6422 #ifdef TRACE
6423         case 't':
6424             save_trace = (unsigned) strtol(optarg, 0, 0);
6425             break;
6426 #endif
6427         default:
6428             usage();
6429         }
6430     }
6431
6432     /*
6433      * If there's no menus (unlikely for ncurses!), then we'll have to set
6434      * tracing on initially, just in case the user wants to test something that
6435      * doesn't involve wGetchar.
6436      */
6437 #ifdef TRACE
6438     /* enable debugging */
6439 #if !USE_LIBMENU
6440     trace(save_trace);
6441 #else
6442     if (!isatty(fileno(stdin)))
6443         trace(save_trace);
6444 #endif /* USE_LIBMENU */
6445 #endif /* TRACE */
6446
6447 #if USE_SOFTKEYS
6448     /* tell it we're going to play with soft keys */
6449     slk_init(my_e_param);
6450 #endif
6451
6452 #ifdef SIGUSR1
6453     /* set up null signal catcher so we can see what interrupts to getch do */
6454     signal(SIGUSR1, announce_sig);
6455 #endif
6456
6457     /* we must initialize the curses data structure only once */
6458     initscr();
6459     bkgdset(BLANK);
6460
6461     /* tests, in general, will want these modes */
6462     use_colors = monochrome ? FALSE : has_colors();
6463
6464     if (use_colors) {
6465         start_color();
6466 #ifdef NCURSES_VERSION_PATCH
6467         max_colors = COLORS;    /* was > 16 ? 16 : COLORS */
6468 #if HAVE_USE_DEFAULT_COLORS
6469         if (default_colors) {
6470             use_default_colors();
6471             min_colors = -1;
6472         }
6473 #if NCURSES_VERSION_PATCH >= 20000708
6474         else if (assumed_colors)
6475             assume_default_colors(default_fg, default_bg);
6476 #endif
6477 #endif
6478 #else /* normal SVr4 curses */
6479         max_colors = COLORS;    /* was > 8 ? 8 : COLORS */
6480 #endif
6481         max_pairs = COLOR_PAIRS;        /* was > 256 ? 256 : COLOR_PAIRS */
6482
6483         if (can_change_color()) {
6484             short cp;
6485             all_colors = typeMalloc(RGB_DATA, (unsigned) max_colors);
6486             for (cp = 0; cp < max_colors; ++cp) {
6487                 color_content(cp,
6488                               &all_colors[cp].red,
6489                               &all_colors[cp].green,
6490                               &all_colors[cp].blue);
6491             }
6492             if (palette_file != 0) {
6493                 FILE *fp = fopen(palette_file, "r");
6494                 if (fp != 0) {
6495                     char buffer[BUFSIZ];
6496                     int red, green, blue;
6497                     int scale = 1000;
6498                     while (fgets(buffer, sizeof(buffer), fp) != 0) {
6499                         if (sscanf(buffer, "scale:%d", &c) == 1) {
6500                             scale = c;
6501                         } else if (sscanf(buffer, "%d:%d %d %d",
6502                                           &c,
6503                                           &red,
6504                                           &green,
6505                                           &blue) == 4
6506                                    && okCOLOR(c)
6507                                    && okRGB(red)
6508                                    && okRGB(green)
6509                                    && okRGB(blue)) {
6510                             all_colors[c].red = (short) ((red * 1000) / scale);
6511                             all_colors[c].green = (short) ((green * 1000) / scale);
6512                             all_colors[c].blue = (short) ((blue * 1000) / scale);
6513                         }
6514                     }
6515                     fclose(fp);
6516                 }
6517             }
6518         }
6519     }
6520     set_terminal_modes();
6521     def_prog_mode();
6522
6523     /*
6524      * Return to terminal mode, so we're guaranteed of being able to
6525      * select terminal commands even if the capabilities are wrong.
6526      */
6527     endwin();
6528
6529 #if HAVE_CURSES_VERSION
6530     (void) printf("Welcome to %s.  Press ? for help.\n", curses_version());
6531 #elif defined(NCURSES_VERSION_MAJOR) && defined(NCURSES_VERSION_MINOR) && defined(NCURSES_VERSION_PATCH)
6532     (void) printf("Welcome to ncurses %d.%d.%d.  Press ? for help.\n",
6533                   NCURSES_VERSION_MAJOR,
6534                   NCURSES_VERSION_MINOR,
6535                   NCURSES_VERSION_PATCH);
6536 #else
6537     (void) puts("Welcome to ncurses.  Press ? for help.");
6538 #endif
6539
6540     main_menu(TRUE);
6541
6542     ExitProgram(EXIT_SUCCESS);
6543 }
6544
6545 /* ncurses.c ends here */