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